/xwiki-platform-core/xwiki-platform-wysiwyg/xwiki-platform-wysiwyg-client/src/main/java/org/xwiki/gwt/wysiwyg/client/diff/myers/MyersDiff.java

https://github.com/sorzu/xwiki-platform · Java · 197 lines · 106 code · 20 blank · 71 comment · 35 complexity · 2f28c28baf492c4294a736c825bc828d MD5 · raw file

  1. /*
  2. * See the NOTICE file distributed with this work for additional
  3. * information regarding copyright ownership.
  4. *
  5. * This is free software; you can redistribute it and/or modify it
  6. * under the terms of the GNU Lesser General Public License as
  7. * published by the Free Software Foundation; either version 2.1 of
  8. * the License, or (at your option) any later version.
  9. *
  10. * This software is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13. * Lesser General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Lesser General Public
  16. * License along with this software; if not, write to the Free
  17. * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  18. * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
  19. */
  20. package org.xwiki.gwt.wysiwyg.client.diff.myers;
  21. import org.xwiki.gwt.wysiwyg.client.diff.*;
  22. /**
  23. * A clean-room implementation of <a
  24. * href="http://www.cs.arizona.edu/people/gene/"> Eugene Myers</a> differencing
  25. * algorithm.
  26. * <p>
  27. * See the paper at <a
  28. * href="http://www.cs.arizona.edu/people/gene/PAPERS/diff.ps">
  29. * http://www.cs.arizona.edu/people/gene/PAPERS/diff.ps</a>
  30. *
  31. * @version $Revision: 1.1 $ $Date: 2006/03/12 00:24:21 $
  32. * @author <a href="mailto:juanco@suigeneris.org">Juanco Anez</a>
  33. * @see Delta
  34. * @see Revision
  35. * @see Diff
  36. */
  37. public class MyersDiff implements DiffAlgorithm
  38. {
  39. /**
  40. * Constructs an instance of the Myers differencing algorithm.
  41. */
  42. public MyersDiff()
  43. {
  44. }
  45. /**
  46. * {@inheritDoc}
  47. */
  48. public Revision diff(Object[] orig, Object[] rev)
  49. throws DifferentiationFailedException
  50. {
  51. PathNode path = buildPath(orig, rev);
  52. return buildRevision(path, orig, rev);
  53. }
  54. /**
  55. * Computes the minimum diffpath that expresses de differences between the
  56. * original and revised sequences, according to Gene Myers differencing
  57. * algorithm.
  58. *
  59. * @param orig
  60. * The original sequence.
  61. * @param rev
  62. * The revised sequence.
  63. * @return A minimum {@link PathNode Path} accross the differences graph.
  64. * @throws DifferentiationFailedException
  65. * if a diff path could not be found.
  66. */
  67. public static PathNode buildPath(Object[] orig, Object[] rev)
  68. throws DifferentiationFailedException
  69. {
  70. if (orig == null)
  71. throw new IllegalArgumentException("original sequence is null");
  72. if (rev == null)
  73. throw new IllegalArgumentException("revised sequence is null");
  74. // these are local constants
  75. final int N = orig.length;
  76. final int M = rev.length;
  77. final int MAX = N + M + 1;
  78. final int size = 1 + 2 * MAX;
  79. final int middle = (size + 1) / 2;
  80. final PathNode diagonal[] = new PathNode[size];
  81. diagonal[middle + 1] = new Snake(0, -1, null);
  82. for (int d = 0; d < MAX; d++)
  83. {
  84. for (int k = -d; k <= d; k += 2)
  85. {
  86. final int kmiddle = middle + k;
  87. final int kplus = kmiddle + 1;
  88. final int kminus = kmiddle - 1;
  89. PathNode prev = null;
  90. int i;
  91. if ((k == -d)
  92. || (k != d && diagonal[kminus].i < diagonal[kplus].i))
  93. {
  94. i = diagonal[kplus].i;
  95. prev = diagonal[kplus];
  96. }
  97. else
  98. {
  99. i = diagonal[kminus].i + 1;
  100. prev = diagonal[kminus];
  101. }
  102. diagonal[kminus] = null; // no longer used
  103. int j = i - k;
  104. PathNode node = new DiffNode(i, j, prev);
  105. // orig and rev are zero-based
  106. // but the algorithm is one-based
  107. // that's why there's no +1 when indexing the sequences
  108. while (i < N && j < M && orig[i].equals(rev[j]))
  109. {
  110. i++;
  111. j++;
  112. }
  113. if (i > node.i)
  114. node = new Snake(i, j, node);
  115. diagonal[kmiddle] = node;
  116. if (i >= N && j >= M)
  117. {
  118. return diagonal[kmiddle];
  119. }
  120. }
  121. diagonal[middle + d - 1] = null;
  122. }
  123. // According to Myers, this cannot happen
  124. throw new DifferentiationFailedException("could not find a diff path");
  125. }
  126. /**
  127. * Constructs a {@link Revision} from a difference path.
  128. *
  129. * @param path
  130. * The path.
  131. * @param orig
  132. * The original sequence.
  133. * @param rev
  134. * The revised sequence.
  135. * @return A {@link Revision} script corresponding to the path.
  136. * @throws DifferentiationFailedException
  137. * if a {@link Revision} could not be built from the given path.
  138. */
  139. public static Revision buildRevision(PathNode path, Object[] orig,
  140. Object[] rev)
  141. {
  142. if (path == null)
  143. throw new IllegalArgumentException("path is null");
  144. if (orig == null)
  145. throw new IllegalArgumentException("original sequence is null");
  146. if (rev == null)
  147. throw new IllegalArgumentException("revised sequence is null");
  148. Revision revision = new Revision();
  149. if (path.isSnake())
  150. path = path.prev;
  151. while (path != null && path.prev != null && path.prev.j >= 0)
  152. {
  153. if (path.isSnake())
  154. throw new IllegalStateException(
  155. "bad diffpath: found snake when looking for diff");
  156. int i = path.i + 1;
  157. int j = path.j + 1;
  158. if (i>orig.length)
  159. i--;
  160. if (j>rev.length)
  161. j--;
  162. path = path.prev;
  163. int ianchor = path.i - 1;
  164. if (ianchor<0)
  165. ianchor = 0;
  166. int janchor = path.j - 1;
  167. if (janchor<0)
  168. janchor = 0;
  169. Delta delta = Delta.newDelta(new Chunk(orig, ianchor, i - ianchor),
  170. new Chunk(rev, janchor, j - janchor));
  171. revision.insertDelta(delta);
  172. if (path.isSnake())
  173. path = path.prev;
  174. }
  175. return revision;
  176. }
  177. }