From 449a413ced044f9ccbff24d8b751213f8e1b5f26 Mon Sep 17 00:00:00 2001 From: "dm.naumenko" Date: Fri, 23 Jul 2010 09:45:17 +0000 Subject: [PATCH] rename branch --- build.xml | 43 ++ src/difflib/ChangeDelta.java | 109 +-- src/difflib/Chunk.java | 273 +++---- src/difflib/DeleteDelta.java | 91 +-- src/difflib/Delta.java | 198 ++--- src/difflib/DiffAlgorithm.java | 24 +- src/difflib/DiffException.java | 15 +- src/difflib/DiffRow.java | 210 +++--- src/difflib/DiffRowGenerator.java | 709 +++++++++--------- src/difflib/DiffUtils.java | 451 +++++++---- src/difflib/InsertDelta.java | 96 +-- src/difflib/Patch.java | 149 ++-- src/difflib/PatchFailedException.java | 12 +- src/difflib/StringUtills.java | 120 +-- src/difflib/myers/DiffException.java | 18 +- src/difflib/myers/DiffNode.java | 16 +- .../myers/DifferentiationFailedException.java | 16 +- src/difflib/myers/MyersDiff.java | 144 ++-- src/difflib/myers/PathNode.java | 28 +- src/difflib/myers/Snake.java | 14 +- test/mocks/one_delta_test_original.txt | 54 ++ test/mocks/one_delta_test_revised.txt | 11 + test/mocks/original.txt | 174 +++++ test/mocks/revised.txt | 308 ++++++++ test/testcase/diffutills/DiffTest.java | 106 +-- .../diffutills/GenerateUnifiedDiffTest.java | 88 +++ test/testcase/diffutills/PatchTest.java | 70 +- 27 files changed, 2214 insertions(+), 1333 deletions(-) create mode 100644 build.xml create mode 100644 test/mocks/one_delta_test_original.txt create mode 100644 test/mocks/one_delta_test_revised.txt create mode 100644 test/mocks/original.txt create mode 100644 test/mocks/revised.txt create mode 100644 test/testcase/diffutills/GenerateUnifiedDiffTest.java diff --git a/build.xml b/build.xml new file mode 100644 index 0000000..7561561 --- /dev/null +++ b/build.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/difflib/ChangeDelta.java b/src/difflib/ChangeDelta.java index 828e43f..5d85eda 100644 --- a/src/difflib/ChangeDelta.java +++ b/src/difflib/ChangeDelta.java @@ -15,7 +15,7 @@ You should have received a copy of the GNU General Public License along with Java Diff Utills Library. If not, see . -*/ + */ package difflib; import java.util.List; @@ -26,59 +26,60 @@ * @author Dmitry Naumenko */ public class ChangeDelta extends Delta { - - /** - * {@inheritDoc} - */ - public ChangeDelta(Chunk original, Chunk revised) { - super(original, revised); - } - - /** - * {@inheritDoc} - * @throws PatchFailedException - */ - @Override - public void applyTo(List target) throws PatchFailedException { - verify(target); - int position = getOriginal().getPosition(); - int size = getOriginal().getSize(); - for (int i = 0; i < size; i++) { - target.remove(position); - } - int i = 0; - for (Object line: getRevised().getLines()) { - target.add(position + i, line); - i++; - } - } - - /** - * {@inheritDoc} - */ - @Override - public void restore(List target) { - int position = getRevised().getPosition(); - int size = getRevised().getSize(); - for (int i = 0; i < size; i++) { - target.remove(position); - } - int i = 0; - for (Object line: getOriginal().getLines()) { - target.add(position + i, line); - i++; - } - } - - /** - * {@inheritDoc} - */ - public void verify(List target) throws PatchFailedException { - getOriginal().verify(target); + + /** + * {@inheritDoc} + */ + public ChangeDelta(Chunk original, Chunk revised) { + super(original, revised); + } + + /** + * {@inheritDoc} + * + * @throws PatchFailedException + */ + @Override + public void applyTo(List target) throws PatchFailedException { + verify(target); + int position = getOriginal().getPosition(); + int size = getOriginal().getSize(); + for (int i = 0; i < size; i++) { + target.remove(position); + } + int i = 0; + for (Object line : getRevised().getLines()) { + target.add(position + i, line); + i++; + } + } + + /** + * {@inheritDoc} + */ + @Override + public void restore(List target) { + int position = getRevised().getPosition(); + int size = getRevised().getSize(); + for (int i = 0; i < size; i++) { + target.remove(position); + } + int i = 0; + for (Object line : getOriginal().getLines()) { + target.add(position + i, line); + i++; + } + } + + /** + * {@inheritDoc} + */ + public void verify(List target) throws PatchFailedException { + getOriginal().verify(target); if (getOriginal().getPosition() > target.size()) { - throw new PatchFailedException("Incorrect patch for delta: " + - "delta original position > target size"); + throw new PatchFailedException("Incorrect patch for delta: " + + "delta original position > target size"); } - } - + } + } diff --git a/src/difflib/Chunk.java b/src/difflib/Chunk.java index 3a389de..bc64e6b 100644 --- a/src/difflib/Chunk.java +++ b/src/difflib/Chunk.java @@ -15,7 +15,7 @@ You should have received a copy of the GNU General Public License along with Java Diff Utills Library. If not, see . -*/ + */ package difflib; import java.util.*; @@ -23,149 +23,168 @@ /** * Holds the information about the part of text involved in the diff process * - *

Text is represented as Object[] because - * the diff engine is capable of handling more than plain ascci. In fact, - * arrays or lists of any type that implements - * {@link java.lang.Object#hashCode hashCode()} and - * {@link java.lang.Object#equals equals()} - * correctly can be subject to differencing using this - * library.

+ *

+ * Text is represented as Object[] because the diff engine is + * capable of handling more than plain ascci. In fact, arrays or lists of any + * type that implements {@link java.lang.Object#hashCode hashCode()} and + * {@link java.lang.Object#equals equals()} correctly can be subject to + * differencing using this library. + *

* * @author target size"); } for (int i = 0; i < size; i++) { if (!target.get(position + i).equals(lines.get(i))) { - throw new PatchFailedException("Incorrect Chunk: the chunk content doesn't match the target"); + throw new PatchFailedException( + "Incorrect Chunk: the chunk content doesn't match the target"); } } } - - /** - * @return the start position of chunk in the text - */ - public int getPosition() { - return position; - } - /** - * @param position the start position to set - */ - public void setPosition(int position) { - this.position = position; - } - - /** - * @return the size of Chunk (size of affected lines) - */ - public int getSize() { - return size; - } - /** - * @param size the size of affected lines to set - */ - public void setSize(int size) { - this.size = size; - } - - /** - * @return the affected lines - */ - public List getLines() { - return lines; - } - /** - * @param lines the affected lines to set - */ - public void setLines(List lines) { - this.lines = lines; - } - + + /** + * @return the start position of chunk in the text + */ + public int getPosition() { + return position; + } + + /** + * @param position + * the start position to set + */ + public void setPosition(int position) { + this.position = position; + } + + /** + * @return the size of Chunk (size of affected lines) + */ + public int getSize() { + return size; + } + + /** + * @param size + * the size of affected lines to set + */ + public void setSize(int size) { + this.size = size; + } + + /** + * @return the affected lines + */ + public List getLines() { + return lines; + } + + /** + * @param lines + * the affected lines to set + */ + public void setLines(List lines) { + this.lines = lines; + } + /** * Returns the index of the last line of the chunk. */ - public int last(){ + public int last() { return getPosition() + getSize() - 1; } - - /* (non-Javadoc) - * @see java.lang.Object#hashCode() - */ - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((lines == null) ? 0 : lines.hashCode()); - result = prime * result + position; - result = prime * result + size; - return result; - } - - /* (non-Javadoc) - * @see java.lang.Object#equals(java.lang.Object) - */ - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - Chunk other = (Chunk) obj; - if (lines == null) { - if (other.lines != null) - return false; - } else if (!lines.equals(other.lines)) - return false; - if (position != other.position) - return false; - if (size != other.size) - return false; - return true; - } - - @Override - public String toString() { - return "[position: " + position + ", size: " + size + ", lines: " + lines + "]"; - } - + + /* + * (non-Javadoc) + * + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((lines == null) ? 0 : lines.hashCode()); + result = prime * result + position; + result = prime * result + size; + return result; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Chunk other = (Chunk) obj; + if (lines == null) { + if (other.lines != null) + return false; + } else if (!lines.equals(other.lines)) + return false; + if (position != other.position) + return false; + if (size != other.size) + return false; + return true; + } + + @Override + public String toString() { + return "[position: " + position + ", size: " + size + ", lines: " + lines + "]"; + } + } diff --git a/src/difflib/DeleteDelta.java b/src/difflib/DeleteDelta.java index 6a0ee71..f9d9c87 100644 --- a/src/difflib/DeleteDelta.java +++ b/src/difflib/DeleteDelta.java @@ -15,7 +15,7 @@ You should have received a copy of the GNU General Public License along with Java Diff Utills Library. If not, see . -*/ + */ package difflib; import java.util.List; @@ -26,48 +26,49 @@ * @author Dmitry Naumenko */ public class DeleteDelta extends Delta { - - /** - * {@inheritDoc} - */ - public DeleteDelta(Chunk original, Chunk revised) { - super(original, revised); - } - - /** - * {@inheritDoc} - * @throws PatchFailedException - */ - @Override - public void applyTo(List target) throws PatchFailedException { - verify(target); - int position = getOriginal().getPosition(); - int size = getOriginal().getSize(); - for (int i = 0; i < size; i++) { - target.remove(position); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void restore(List target) { - int position = this.getRevised().getPosition(); - List lines = this.getOriginal().getLines(); - for (int i = 0; i < lines.size(); i++) { - target.add(position + i, lines.get(i)); - } - } - - @Override - public void verify(List target) throws PatchFailedException { - getOriginal().verify(target); - } - - @Override - public String toString() { - return "[DeleteDelta, position: " + getOriginal().getPosition() + ", lines: " + - getOriginal().getLines() + "]"; - } + + /** + * {@inheritDoc} + */ + public DeleteDelta(Chunk original, Chunk revised) { + super(original, revised); + } + + /** + * {@inheritDoc} + * + * @throws PatchFailedException + */ + @Override + public void applyTo(List target) throws PatchFailedException { + verify(target); + int position = getOriginal().getPosition(); + int size = getOriginal().getSize(); + for (int i = 0; i < size; i++) { + target.remove(position); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void restore(List target) { + int position = this.getRevised().getPosition(); + List lines = this.getOriginal().getLines(); + for (int i = 0; i < lines.size(); i++) { + target.add(position + i, lines.get(i)); + } + } + + @Override + public void verify(List target) throws PatchFailedException { + getOriginal().verify(target); + } + + @Override + public String toString() { + return "[DeleteDelta, position: " + getOriginal().getPosition() + ", lines: " + + getOriginal().getLines() + "]"; + } } diff --git a/src/difflib/Delta.java b/src/difflib/Delta.java index 58242e7..aaa5bc6 100644 --- a/src/difflib/Delta.java +++ b/src/difflib/Delta.java @@ -15,7 +15,7 @@ You should have received a copy of the GNU General Public License along with Java Diff Utills Library. If not, see . -*/ + */ package difflib; import java.util.*; @@ -26,105 +26,111 @@ * @author Dmitry Naumenko */ public abstract class Delta { - private Chunk original; - private Chunk revised; - - /** - * Construct the delta for original and revised chunks - * - * @param original chunk describes the original text - * @param revised chunk describes the revised text - */ - public Delta(Chunk original, Chunk revised) { - this.original = original; - this.revised = revised; - } - + private Chunk original; + private Chunk revised; + + /** + * Construct the delta for original and revised chunks + * + * @param original chunk describes the original text + * @param revised chunk describes the revised text + */ + public Delta(Chunk original, Chunk revised) { + this.original = original; + this.revised = revised; + } + /** * Verifies that this delta can be used to patch the given text. + * * @param target the text to patch. * @throws PatchFailedException if the patch cannot be applied. */ public abstract void verify(List target) throws PatchFailedException; - - /** - * Applies this delta as the patch for a given target - * - * @param target the given target - * @throws PatchFailedException - */ - public abstract void applyTo(List target) throws PatchFailedException; - - /** - * Cancel this delta for a given revised text. The action is opposite to patch. - * - * @param target the given revised text - */ - public abstract void restore(List target); - - /** - * @return the Chunk describing the original text - */ - public Chunk getOriginal() { - return original; - } - /** - * @param original the Chunk describing the original text to set - */ - public void setOriginal(Chunk original) { - this.original = original; - } - - /** - * @return the Chunk describing the revised text - */ - public Chunk getRevised() { - return revised; - } - /** - * @param revised the Chunk describing the revised text to set - */ - public void setRevised(Chunk revised) { - this.revised = revised; - } - - /* (non-Javadoc) - * @see java.lang.Object#hashCode() - */ - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result - + ((original == null) ? 0 : original.hashCode()); - result = prime * result + ((revised == null) ? 0 : revised.hashCode()); - return result; - } - - /* (non-Javadoc) - * @see java.lang.Object#equals(java.lang.Object) - */ - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - Delta other = (Delta) obj; - if (original == null) { - if (other.original != null) - return false; - } else if (!original.equals(other.original)) - return false; - if (revised == null) { - if (other.revised != null) - return false; - } else if (!revised.equals(other.revised)) - return false; - return true; - } - - + + /** + * Applies this delta as the patch for a given target + * + * @param target the given target + * @throws PatchFailedException + */ + public abstract void applyTo(List target) throws PatchFailedException; + + /** + * Cancel this delta for a given revised text. The action is opposite to + * patch. + * + * @param target the given revised text + */ + public abstract void restore(List target); + + /** + * @return the Chunk describing the original text + */ + public Chunk getOriginal() { + return original; + } + + /** + * @param original the Chunk describing the original text to set + */ + public void setOriginal(Chunk original) { + this.original = original; + } + + /** + * @return the Chunk describing the revised text + */ + public Chunk getRevised() { + return revised; + } + + /** + * @param revised the Chunk describing the revised text to set + */ + public void setRevised(Chunk revised) { + this.revised = revised; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((original == null) ? 0 : original.hashCode()); + result = prime * result + ((revised == null) ? 0 : revised.hashCode()); + return result; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Delta other = (Delta) obj; + if (original == null) { + if (other.original != null) + return false; + } else if (!original.equals(other.original)) + return false; + if (revised == null) { + if (other.revised != null) + return false; + } else if (!revised.equals(other.revised)) + return false; + return true; + } + } diff --git a/src/difflib/DiffAlgorithm.java b/src/difflib/DiffAlgorithm.java index 1a71695..b9806de 100644 --- a/src/difflib/DiffAlgorithm.java +++ b/src/difflib/DiffAlgorithm.java @@ -15,7 +15,7 @@ You should have received a copy of the GNU General Public License along with Java Diff Utills Library. If not, see . -*/ + */ package difflib; import java.util.*; @@ -26,26 +26,24 @@ * @author Dmitry Naumenko */ public interface DiffAlgorithm { - + /** - * Computes the difference between the original - * sequence and the revised sequence and returns it - * as a {@link difflib.Patch Patch} object. - * + * Computes the difference between the original sequence and the revised + * sequence and returns it as a {@link difflib.Patch Patch} object. + * * @param original the original text * @param revised the revised text * @return the patch */ - public Patch diff(Object[] original, Object[] revised); - + public Patch diff(Object[] original, Object[] revised); + /** - * Computes the difference between the original - * sequence and the revised sequence and returns it - * as a {@link difflib.Patch Patch} object. - * + * Computes the difference between the original sequence and the revised + * sequence and returns it as a {@link difflib.Patch Patch} object. + * * @param original the original text * @param revised the revised text * @return the patch */ - public Patch diff(List original, List revised); + public Patch diff(List original, List revised); } diff --git a/src/difflib/DiffException.java b/src/difflib/DiffException.java index 5bcf9da..ac0b1ed 100644 --- a/src/difflib/DiffException.java +++ b/src/difflib/DiffException.java @@ -15,23 +15,22 @@ You should have received a copy of the GNU General Public License along with Java Diff Utills Library. If not, see . -*/ + */ package difflib; /** * Base class for all exceptions emanating from this package. - * + * * @author Juanco Anez */ public class DiffException extends Exception { - - private static final long serialVersionUID = 1L; - - public DiffException() { + + private static final long serialVersionUID = 1L; + + public DiffException() { } - + public DiffException(String msg) { super(msg); } } - diff --git a/src/difflib/DiffRow.java b/src/difflib/DiffRow.java index 444980c..7cd3acc 100644 --- a/src/difflib/DiffRow.java +++ b/src/difflib/DiffRow.java @@ -15,112 +15,120 @@ You should have received a copy of the GNU General Public License along with Java Diff Utills Library. If not, see . -*/ + */ package difflib; /** - * Describes the diff row in form [tag, oldLine, newLine) for showing the difference between two - * texts + * Describes the diff row in form [tag, oldLine, newLine) for showing the + * difference between two texts * * @author Dmitry Naumenko */ public class DiffRow { - private Tag tag; - private String oldLine; - private String newLine; - - public DiffRow(Tag tag, String oldLine, String newLine) { - this.tag = tag; - this.oldLine = oldLine; - this.newLine = newLine; - } - - public enum Tag { - INSERT, DELETE, CHANGE, EQUAL - } - - /** - * @return the tag - */ - public Tag getTag() { - return tag; - } - /** - * @param tag the tag to set - */ - public void setTag(Tag tag) { - this.tag = tag; - } - - /** - * @return the oldLine - */ - public String getOldLine() { - return oldLine; - } - /** - * @param oldLine the oldLine to set - */ - public void setOldLine(String oldLine) { - this.oldLine = oldLine; - } - - /** - * @return the newLine - */ - public String getNewLine() { - return newLine; - } - /** - * @param newLine the newLine to set - */ - public void setNewLine(String newLine) { - this.newLine = newLine; - } - - /* (non-Javadoc) - * @see java.lang.Object#hashCode() - */ - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((newLine == null) ? 0 : newLine.hashCode()); - result = prime * result + ((oldLine == null) ? 0 : oldLine.hashCode()); - result = prime * result + ((tag == null) ? 0 : tag.hashCode()); - return result; - } - /* (non-Javadoc) - * @see java.lang.Object#equals(java.lang.Object) - */ - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - DiffRow other = (DiffRow) obj; - if (newLine == null) { - if (other.newLine != null) - return false; - } else if (!newLine.equals(other.newLine)) - return false; - if (oldLine == null) { - if (other.oldLine != null) - return false; - } else if (!oldLine.equals(other.oldLine)) - return false; - if (tag == null) { - if (other.tag != null) - return false; - } else if (!tag.equals(other.tag)) - return false; - return true; - } - - public String toString() { - return "[" + this.tag + "," + this.oldLine + "," + this.newLine + "]"; - } + private Tag tag; + private String oldLine; + private String newLine; + + public DiffRow(Tag tag, String oldLine, String newLine) { + this.tag = tag; + this.oldLine = oldLine; + this.newLine = newLine; + } + + public enum Tag { + INSERT, DELETE, CHANGE, EQUAL + } + + /** + * @return the tag + */ + public Tag getTag() { + return tag; + } + + /** + * @param tag the tag to set + */ + public void setTag(Tag tag) { + this.tag = tag; + } + + /** + * @return the oldLine + */ + public String getOldLine() { + return oldLine; + } + + /** + * @param oldLine the oldLine to set + */ + public void setOldLine(String oldLine) { + this.oldLine = oldLine; + } + + /** + * @return the newLine + */ + public String getNewLine() { + return newLine; + } + + /** + * @param newLine the newLine to set + */ + public void setNewLine(String newLine) { + this.newLine = newLine; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((newLine == null) ? 0 : newLine.hashCode()); + result = prime * result + ((oldLine == null) ? 0 : oldLine.hashCode()); + result = prime * result + ((tag == null) ? 0 : tag.hashCode()); + return result; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + DiffRow other = (DiffRow) obj; + if (newLine == null) { + if (other.newLine != null) + return false; + } else if (!newLine.equals(other.newLine)) + return false; + if (oldLine == null) { + if (other.oldLine != null) + return false; + } else if (!oldLine.equals(other.oldLine)) + return false; + if (tag == null) { + if (other.tag != null) + return false; + } else if (!tag.equals(other.tag)) + return false; + return true; + } + + public String toString() { + return "[" + this.tag + "," + this.oldLine + "," + this.newLine + "]"; + } } diff --git a/src/difflib/DiffRowGenerator.java b/src/difflib/DiffRowGenerator.java index da79dd4..77aa3fb 100644 --- a/src/difflib/DiffRowGenerator.java +++ b/src/difflib/DiffRowGenerator.java @@ -15,7 +15,7 @@ You should have received a copy of the GNU General Public License along with Java Diff Utills Library. If not, see . -*/ + */ package difflib; import java.util.ArrayList; @@ -48,358 +48,357 @@ * @author Dmitry Naumenko */ public class DiffRowGenerator { - private final boolean showInlineDiffs; - private final boolean ignoreWhiteSpaces; - private final boolean ignoreBlankLines; - private final String InlineOldTag; - private final String InlineNewTag; - private final String InlineOldCssClass; - private final String InlineNewCssClass; - private final int columnWidth; - - /** - * This class used for building the DiffRowGenerator. - * @author dmitry - * - */ - public static class Builder { - private boolean showInlineDiffs = false; - private boolean ignoreWhiteSpaces = true; - private boolean ignoreBlankLines = true; - private String InlineOldTag = "span"; - private String InlineNewTag = "span"; - private String InlineOldCssClass = "editOldInline"; - private String InlineNewCssClass = "editNewInline"; - private int columnWidth = 80; - - /** - * Show inline diffs in generating diff rows or not. - * @param val the value to set. Default: false. - * @return builder with configured showInlineDiff parameter - */ - public Builder showInlineDiffs(boolean val) { - showInlineDiffs = val; - return this; - } - - /** - * Ignore white spaces in generating diff rows or not. - * @param val the value to set. Default: true. - * @return builder with configured ignoreWhiteSpaces parameter - */ - public Builder ignoreWhiteSpaces(boolean val) { - ignoreWhiteSpaces = val; - return this; - } - - /** - * Ignore blank lines in generating diff rows or not. - * @param val the value to set. Default: true. - * @return builder with configured ignoreBlankLines parameter - */ - public Builder ignoreBlankLines(boolean val) { - ignoreBlankLines = val; - return this; - } - - /** - * Set the tag used for displaying changes in the original text. - * @param tag the tag to set. Without angle brackets. Default: span. - * @return builder with configured ignoreBlankLines parameter - */ - public Builder InlineOldTag(String tag) { - InlineOldTag = tag; - return this; - } - - /** - * Set the tag used for displaying changes in the revised text. - * @param tag the tag to set. Without angle brackets. Default: span. - * @return builder with configured ignoreBlankLines parameter - */ - public Builder InlineNewTag(String tag) { - InlineNewTag = tag; - return this; - } - - /** - * Set the css class used for displaying changes in the original text. - * @param cssClass the tag to set. Without any quotes, just word. Default: editOldInline. - * @return builder with configured ignoreBlankLines parameter - */ - public Builder InlineOldCssClass(String cssClass) { - InlineOldCssClass = cssClass; - return this; - } - - /** - * Set the css class used for displaying changes in the revised text. - * @param cssClass the tag to set. Without any quotes, just word. Default: editNewInline. - * @return builder with configured ignoreBlankLines parameter - */ - public Builder InlineNewCssClass(String cssClass) { - InlineNewCssClass = cssClass; - return this; - } - - /** - * Set the column with of generated lines of original and revised texts. - * @param width the width to set. Making it < 0 doesn't have any sense. Default 80. - * @return builder with configured ignoreBlankLines parameter - */ - public Builder columnWidth(int width) { - if (width > 0) { - columnWidth = width; - } - return this; - } - - /** - * Build the DiffRowGenerator. If some parameters is not set, the default values are used. - * @return the customized DiffRowGenerator - */ - public DiffRowGenerator build() { - return new DiffRowGenerator(this); - } - } - - private DiffRowGenerator(Builder builder) { - showInlineDiffs = builder.showInlineDiffs; - ignoreWhiteSpaces = builder.ignoreWhiteSpaces; // - ignoreBlankLines = builder.ignoreBlankLines; // - InlineOldTag = builder.InlineOldTag; - InlineNewTag = builder.InlineNewTag; - InlineOldCssClass = builder.InlineOldCssClass; - InlineNewCssClass = builder.InlineNewCssClass; - columnWidth = builder.columnWidth; // - } - - /** - * Get the DiffRows describing the difference between original and revised texts using the - * given patch. Useful for displaying side-by-side diff. - * - * @param original the original text - * @param revised the revised text - * @return the DiffRows between original and revised texts - */ - public List generateDiffRows(List original, List revised) { - return generateDiffRows(original, revised, DiffUtils.diff(original, revised)); - } - - /** - * Generates the DiffRows describing the difference between original and revised texts using the - * given patch. Useful for displaying side-by-side diff. - * - * @param original the original text - * @param revised the revised text - * @param patch the given patch - * @return the DiffRows between original and revised texts - */ - @SuppressWarnings("unchecked") - public List generateDiffRows(List original, List revised, Patch patch) { - // normalize the lines (expand tabs, escape html entities) - original = StringUtills.normalize((List)original); - revised = StringUtills.normalize((List)revised); - - // wrap to the column width - original = StringUtills.wrapText((List)original, this.columnWidth); - revised = StringUtills.wrapText((List)revised, this.columnWidth); - - List diffRows = new ArrayList(); - int endPos = 0; - for (int i = 0; i < patch.getDeltas().size(); i++) { - Delta delta = patch.getDelta(i); - Chunk orig = delta.getOriginal(); - Chunk rev = delta.getRevised(); - - // We should normalize and wrap lines in deltas too. - orig.setLines(StringUtills.normalize((List)orig.getLines())); - rev.setLines(StringUtills.normalize((List)rev.getLines())); - - orig.setLines(StringUtills.wrapText((List)orig.getLines(), this.columnWidth)); - rev.setLines(StringUtills.wrapText((List)rev.getLines(), this.columnWidth)); - - // catch the equal prefix for each chunk - for (String line: original.subList(endPos, orig.getPosition())) { - diffRows.add(new DiffRow(Tag.EQUAL, line, line)); - } - - // Inserted DiffRow - if (delta.getClass().equals(InsertDelta.class)) { - endPos = orig.last() + 1; - for (String line: (List)rev.getLines()) { - diffRows.add(new DiffRow(Tag.INSERT, "", line)); - } - continue; - } - - // Deleted DiffRow - if (delta.getClass().equals(DeleteDelta.class)) { - endPos = orig.last() + 1; - for (String line: (List)orig.getLines()) { - diffRows.add(new DiffRow(Tag.DELETE, line, "")); - } - continue; - } - - if (showInlineDiffs) { - addInlineDiffs(delta); - } - // the changed size is match - if (orig.getSize() == rev.getSize()) { - for (int j = 0; j < orig.getSize(); j++) { - diffRows.add(new DiffRow(Tag.CHANGE, (String)orig.getLines().get(j), - (String)rev.getLines().get(j))); - } - } else if (orig.getSize() > rev.getSize()) { - for (int j = 0; j < orig.getSize(); j++) { - diffRows.add(new DiffRow(Tag.CHANGE, (String)orig.getLines().get(j), - rev.getLines().size() > j ? (String)rev.getLines().get(j) : "")); - } - } else { - for (int j = 0; j < rev.getSize(); j++) { - diffRows.add(new DiffRow(Tag.CHANGE, orig.getLines().size() > j ? - (String)orig.getLines().get(j) : "", (String)rev.getLines().get(j))); - } - } - endPos = orig.last() + 1; - } - - // Copy the final matching chunk if any. - for (String line: original.subList(endPos, original.size())) { - diffRows.add(new DiffRow(Tag.EQUAL, line, line)); - } - return diffRows; - } - - /** - * Add the inline diffs for given delta - * @param delta the given delta - */ - @SuppressWarnings("unchecked") - private void addInlineDiffs(Delta delta) { - List orig = (List)delta.getOriginal().getLines(); - List rev = (List)delta.getRevised().getLines(); - LinkedList origList = new LinkedList(); - for (Character character: join(orig, "\n").toCharArray()) { - origList.add(character.toString()); - } - LinkedList revList = new LinkedList(); - for (Character character: join(rev, "\n").toCharArray()) { - revList.add(character.toString()); - } - List inlineDeltas = DiffUtils.diff(origList, revList).getDeltas(); - if (inlineDeltas.size() < 3) { - Collections.reverse(inlineDeltas); - for (Delta inlineDelta: inlineDeltas) { - Chunk inlineOrig = inlineDelta.getOriginal(); - Chunk inlineRev = inlineDelta.getRevised(); - if (inlineDelta.getClass().equals(DeleteDelta.class)) { - origList = wrapInTag(origList, inlineOrig.getPosition(), - inlineOrig.getPosition() + inlineOrig.getSize() + 1, - this.InlineOldTag, this.InlineOldCssClass); - } else if (inlineDelta.getClass().equals(InsertDelta.class)) { - revList = wrapInTag(revList, inlineRev.getPosition(), - inlineRev.getPosition() + inlineRev.getSize() + 1, - this.InlineNewTag, this.InlineNewCssClass); - } else if (inlineDelta.getClass().equals(ChangeDelta.class)) { - origList = wrapInTag(origList, inlineOrig.getPosition(), - inlineOrig.getPosition() + inlineOrig.getSize() + 1, - this.InlineOldTag, this.InlineOldCssClass); - revList = wrapInTag(revList, inlineRev.getPosition(), - inlineRev.getPosition() + inlineRev.getSize() + 1, - this.InlineNewTag, this.InlineNewCssClass); - } - } - StringBuilder origResult = new StringBuilder(), revResult = new StringBuilder(); - for (String character: origList) { - origResult.append(character); - } - for (String character: revList) { - revResult.append(character); - } - delta.getOriginal().setLines(Arrays.asList(origResult.toString().split("\n"))); - delta.getRevised().setLines(Arrays.asList(revResult.toString().split("\n"))); - } - } - - /** - * Wrap the elements in the sequence with the given tag - * @param startPosition the position from which tag should start. The counting start from a zero. - * @param endPosition the position before which tag should should be closed. - * @param tag the tag name without angle brackets, just a word - * @param cssClass the optional css class - */ - public static LinkedList wrapInTag(LinkedList sequence, int startPosition, int endPosition, String tag, String cssClass) { - LinkedList result = (LinkedList)sequence.clone(); - StringBuilder tagBuilder = new StringBuilder(); - tagBuilder.append("<"); - tagBuilder.append(tag); - if (cssClass != null) { - tagBuilder.append(" class=\""); - tagBuilder.append(cssClass); - tagBuilder.append("\""); - } - tagBuilder.append(">"); - String startTag = tagBuilder.toString(); - - tagBuilder.delete(0, tagBuilder.length()); - - tagBuilder.append(""); - String endTag = tagBuilder.toString(); - - result.add(startPosition, startTag); - result.add(endPosition, endTag); - return result; - } - - /** - * Wrap the given line with the given tag - * @param line the given line - * @param tag the tag name without angle brackets, just a word - * @param cssClass the optional css class - * @return the wrapped string - */ - public static String wrapInTag(String line, String tag, String cssClass) { - StringBuilder tagBuilder = new StringBuilder(); - tagBuilder.append("<"); - tagBuilder.append(tag); - if (cssClass != null) { - tagBuilder.append(" class=\""); - tagBuilder.append(cssClass); - tagBuilder.append("\""); - } - tagBuilder.append(">"); - String startTag = tagBuilder.toString(); - - tagBuilder.delete(0, tagBuilder.length()); - - tagBuilder.append(""); - String endTag = tagBuilder.toString(); - - return startTag + line + endTag; - } - - /** - * The helper method for joining collections - * @param - * @param objs the collection to join - * @param delimiter the delimiter to use - * @return the joined string - */ - private static String join(final Iterable objs, final String delimiter) { - Iterator iter = objs.iterator(); - if (!iter.hasNext()) { - return ""; - } - StringBuffer buffer = new StringBuffer(String.valueOf(iter.next())); - while (iter.hasNext()) { - buffer.append(delimiter).append(String.valueOf(iter.next())); - } - return buffer.toString(); - } + private final boolean showInlineDiffs; + private final boolean ignoreWhiteSpaces; + private final boolean ignoreBlankLines; + private final String InlineOldTag; + private final String InlineNewTag; + private final String InlineOldCssClass; + private final String InlineNewCssClass; + private final int columnWidth; + + /** + * This class used for building the DiffRowGenerator. + * @author dmitry + * + */ + public static class Builder { + private boolean showInlineDiffs = false; + private boolean ignoreWhiteSpaces = true; + private boolean ignoreBlankLines = true; + private String InlineOldTag = "span"; + private String InlineNewTag = "span"; + private String InlineOldCssClass = "editOldInline"; + private String InlineNewCssClass = "editNewInline"; + private int columnWidth = 80; + + /** + * Show inline diffs in generating diff rows or not. + * @param val the value to set. Default: false. + * @return builder with configured showInlineDiff parameter + */ + public Builder showInlineDiffs(boolean val) { + showInlineDiffs = val; + return this; + } + + /** + * Ignore white spaces in generating diff rows or not. + * @param val the value to set. Default: true. + * @return builder with configured ignoreWhiteSpaces parameter + */ + public Builder ignoreWhiteSpaces(boolean val) { + ignoreWhiteSpaces = val; + return this; + } + + /** + * Ignore blank lines in generating diff rows or not. + * @param val the value to set. Default: true. + * @return builder with configured ignoreBlankLines parameter + */ + public Builder ignoreBlankLines(boolean val) { + ignoreBlankLines = val; + return this; + } + + /** + * Set the tag used for displaying changes in the original text. + * @param tag the tag to set. Without angle brackets. Default: span. + * @return builder with configured ignoreBlankLines parameter + */ + public Builder InlineOldTag(String tag) { + InlineOldTag = tag; + return this; + } + + /** + * Set the tag used for displaying changes in the revised text. + * @param tag the tag to set. Without angle brackets. Default: span. + * @return builder with configured ignoreBlankLines parameter + */ + public Builder InlineNewTag(String tag) { + InlineNewTag = tag; + return this; + } + + /** + * Set the css class used for displaying changes in the original text. + * @param cssClass the tag to set. Without any quotes, just word. Default: editOldInline. + * @return builder with configured ignoreBlankLines parameter + */ + public Builder InlineOldCssClass(String cssClass) { + InlineOldCssClass = cssClass; + return this; + } + + /** + * Set the css class used for displaying changes in the revised text. + * @param cssClass the tag to set. Without any quotes, just word. Default: editNewInline. + * @return builder with configured ignoreBlankLines parameter + */ + public Builder InlineNewCssClass(String cssClass) { + InlineNewCssClass = cssClass; + return this; + } + + /** + * Set the column with of generated lines of original and revised texts. + * @param width the width to set. Making it < 0 doesn't have any sense. Default 80. + * @return builder with configured ignoreBlankLines parameter + */ + public Builder columnWidth(int width) { + if (width > 0) { + columnWidth = width; + } + return this; + } + + /** + * Build the DiffRowGenerator. If some parameters is not set, the default values are used. + * @return the customized DiffRowGenerator + */ + public DiffRowGenerator build() { + return new DiffRowGenerator(this); + } + } + + private DiffRowGenerator(Builder builder) { + showInlineDiffs = builder.showInlineDiffs; + ignoreWhiteSpaces = builder.ignoreWhiteSpaces; // + ignoreBlankLines = builder.ignoreBlankLines; // + InlineOldTag = builder.InlineOldTag; + InlineNewTag = builder.InlineNewTag; + InlineOldCssClass = builder.InlineOldCssClass; + InlineNewCssClass = builder.InlineNewCssClass; + columnWidth = builder.columnWidth; // + } + + /** + * Get the DiffRows describing the difference between original and revised texts using the + * given patch. Useful for displaying side-by-side diff. + * + * @param original the original text + * @param revised the revised text + * @return the DiffRows between original and revised texts + */ + public List generateDiffRows(List original, List revised) { + return generateDiffRows(original, revised, DiffUtils.diff(original, revised)); + } + + /** + * Generates the DiffRows describing the difference between original and revised texts using the + * given patch. Useful for displaying side-by-side diff. + * + * @param original the original text + * @param revised the revised text + * @param patch the given patch + * @return the DiffRows between original and revised texts + */ + @SuppressWarnings("unchecked") + public List generateDiffRows(List original, List revised, Patch patch) { + // normalize the lines (expand tabs, escape html entities) + original = StringUtills.normalize((List) original); + revised = StringUtills.normalize((List) revised); + + // wrap to the column width + original = StringUtills.wrapText((List) original, this.columnWidth); + revised = StringUtills.wrapText((List) revised, this.columnWidth); + + List diffRows = new ArrayList(); + int endPos = 0; + for (int i = 0; i < patch.getDeltas().size(); i++) { + Delta delta = patch.getDelta(i); + Chunk orig = delta.getOriginal(); + Chunk rev = delta.getRevised(); + + // We should normalize and wrap lines in deltas too. + orig.setLines(StringUtills.normalize((List) orig.getLines())); + rev.setLines(StringUtills.normalize((List) rev.getLines())); + + orig.setLines(StringUtills.wrapText((List) orig.getLines(), this.columnWidth)); + rev.setLines(StringUtills.wrapText((List) rev.getLines(), this.columnWidth)); + + // catch the equal prefix for each chunk + for (String line : original.subList(endPos, orig.getPosition())) { + diffRows.add(new DiffRow(Tag.EQUAL, line, line)); + } + + // Inserted DiffRow + if (delta.getClass().equals(InsertDelta.class)) { + endPos = orig.last() + 1; + for (String line : (List) rev.getLines()) { + diffRows.add(new DiffRow(Tag.INSERT, "", line)); + } + continue; + } + + // Deleted DiffRow + if (delta.getClass().equals(DeleteDelta.class)) { + endPos = orig.last() + 1; + for (String line : (List) orig.getLines()) { + diffRows.add(new DiffRow(Tag.DELETE, line, "")); + } + continue; + } + + if (showInlineDiffs) { + addInlineDiffs(delta); + } + // the changed size is match + if (orig.getSize() == rev.getSize()) { + for (int j = 0; j < orig.getSize(); j++) { + diffRows.add(new DiffRow(Tag.CHANGE, (String) orig.getLines().get(j), + (String) rev.getLines().get(j))); + } + } else if (orig.getSize() > rev.getSize()) { + for (int j = 0; j < orig.getSize(); j++) { + diffRows.add(new DiffRow(Tag.CHANGE, (String) orig.getLines().get(j), rev + .getLines().size() > j ? (String) rev.getLines().get(j) : "")); + } + } else { + for (int j = 0; j < rev.getSize(); j++) { + diffRows.add(new DiffRow(Tag.CHANGE, orig.getLines().size() > j ? (String) orig + .getLines().get(j) : "", (String) rev.getLines().get(j))); + } + } + endPos = orig.last() + 1; + } + + // Copy the final matching chunk if any. + for (String line : original.subList(endPos, original.size())) { + diffRows.add(new DiffRow(Tag.EQUAL, line, line)); + } + return diffRows; + } + + /** + * Add the inline diffs for given delta + * @param delta the given delta + */ + @SuppressWarnings("unchecked") + private void addInlineDiffs(Delta delta) { + List orig = (List) delta.getOriginal().getLines(); + List rev = (List) delta.getRevised().getLines(); + LinkedList origList = new LinkedList(); + for (Character character : join(orig, "\n").toCharArray()) { + origList.add(character.toString()); + } + LinkedList revList = new LinkedList(); + for (Character character : join(rev, "\n").toCharArray()) { + revList.add(character.toString()); + } + List inlineDeltas = DiffUtils.diff(origList, revList).getDeltas(); + if (inlineDeltas.size() < 3) { + Collections.reverse(inlineDeltas); + for (Delta inlineDelta : inlineDeltas) { + Chunk inlineOrig = inlineDelta.getOriginal(); + Chunk inlineRev = inlineDelta.getRevised(); + if (inlineDelta.getClass().equals(DeleteDelta.class)) { + origList = wrapInTag(origList, inlineOrig.getPosition(), inlineOrig + .getPosition() + + inlineOrig.getSize() + 1, this.InlineOldTag, this.InlineOldCssClass); + } else if (inlineDelta.getClass().equals(InsertDelta.class)) { + revList = wrapInTag(revList, inlineRev.getPosition(), inlineRev.getPosition() + + inlineRev.getSize() + 1, this.InlineNewTag, this.InlineNewCssClass); + } else if (inlineDelta.getClass().equals(ChangeDelta.class)) { + origList = wrapInTag(origList, inlineOrig.getPosition(), inlineOrig + .getPosition() + + inlineOrig.getSize() + 1, this.InlineOldTag, this.InlineOldCssClass); + revList = wrapInTag(revList, inlineRev.getPosition(), inlineRev.getPosition() + + inlineRev.getSize() + 1, this.InlineNewTag, this.InlineNewCssClass); + } + } + StringBuilder origResult = new StringBuilder(), revResult = new StringBuilder(); + for (String character : origList) { + origResult.append(character); + } + for (String character : revList) { + revResult.append(character); + } + delta.getOriginal().setLines(Arrays.asList(origResult.toString().split("\n"))); + delta.getRevised().setLines(Arrays.asList(revResult.toString().split("\n"))); + } + } + + /** + * Wrap the elements in the sequence with the given tag + * @param startPosition the position from which tag should start. The counting start from a zero. + * @param endPosition the position before which tag should should be closed. + * @param tag the tag name without angle brackets, just a word + * @param cssClass the optional css class + */ + public static LinkedList wrapInTag(LinkedList sequence, int startPosition, + int endPosition, String tag, String cssClass) { + LinkedList result = (LinkedList) sequence.clone(); + StringBuilder tagBuilder = new StringBuilder(); + tagBuilder.append("<"); + tagBuilder.append(tag); + if (cssClass != null) { + tagBuilder.append(" class=\""); + tagBuilder.append(cssClass); + tagBuilder.append("\""); + } + tagBuilder.append(">"); + String startTag = tagBuilder.toString(); + + tagBuilder.delete(0, tagBuilder.length()); + + tagBuilder.append(""); + String endTag = tagBuilder.toString(); + + result.add(startPosition, startTag); + result.add(endPosition, endTag); + return result; + } + + /** + * Wrap the given line with the given tag + * @param line the given line + * @param tag the tag name without angle brackets, just a word + * @param cssClass the optional css class + * @return the wrapped string + */ + public static String wrapInTag(String line, String tag, String cssClass) { + StringBuilder tagBuilder = new StringBuilder(); + tagBuilder.append("<"); + tagBuilder.append(tag); + if (cssClass != null) { + tagBuilder.append(" class=\""); + tagBuilder.append(cssClass); + tagBuilder.append("\""); + } + tagBuilder.append(">"); + String startTag = tagBuilder.toString(); + + tagBuilder.delete(0, tagBuilder.length()); + + tagBuilder.append(""); + String endTag = tagBuilder.toString(); + + return startTag + line + endTag; + } + + /** + * The helper method for joining collections + * @param + * @param objs the collection to join + * @param delimiter the delimiter to use + * @return the joined string + */ + private static String join(final Iterable objs, final String delimiter) { + Iterator iter = objs.iterator(); + if (!iter.hasNext()) { + return ""; + } + StringBuffer buffer = new StringBuffer(String.valueOf(iter.next())); + while (iter.hasNext()) { + buffer.append(delimiter).append(String.valueOf(iter.next())); + } + return buffer.toString(); + } } diff --git a/src/difflib/DiffUtils.java b/src/difflib/DiffUtils.java index 479dca0..569d96a 100644 --- a/src/difflib/DiffUtils.java +++ b/src/difflib/DiffUtils.java @@ -15,7 +15,7 @@ You should have received a copy of the GNU General Public License along with Java Diff Utills Library. If not, see . -*/ + */ package difflib; import java.util.*; @@ -31,144 +31,313 @@ * @version 0.4.1 */ public class DiffUtils { - private static DiffAlgorithm defaultDiffAlgorithm = new MyersDiff(); - private static Pattern unifiedDiffChunkRe = - Pattern.compile("@@\\s+-(?:(\\d+)(?:,(\\d+))?)\\s+\\+(?:(\\d+)(?:,(\\d+))?)\\s+@@"); - - /** - * Compute the difference between the original and revised texts with default diff algorithm - * - * @param original the original text - * @param revised the revised text - * @return the patch describing the difference between the original and revised texts - */ - public static Patch diff(List original, List revised) { - return DiffUtils.diff(original, revised, defaultDiffAlgorithm); - } - - /** - * Compute the difference between the original and revised texts with given diff algorithm - * - * @param original the original text - * @param revised the revised text - * @param algorithm the given algorithm - * @return the patch describing the difference between the original and revised texts - */ - public static Patch diff(List original, List revised, DiffAlgorithm algorithm) { - return algorithm.diff(original, revised); - } - - /** - * Patch the original text with given patch - * - * @param original the original text - * @param patch the given patch - * @return the revised text - * @throws PatchFailedException if can't apply patch - */ - public static List patch(List original, Patch patch) throws PatchFailedException { - return patch.applyTo(original); - } - - /** - * Unpatch the revised text for a given patch - * - * @param revised the revised text - * @param patch the given patch - * @return the original text - */ - public static List unpatch(List revised, Patch patch) { - return patch.restore(revised); - } - - /** - * Parse the given text in unified format and creates the list of deltas for it. - * - * @param diff the text in unified format - * @return the patch with deltas. - */ - public static Patch parseUnifiedDiff(List diff) { - boolean inPrelude = true; - List rawChunk = new ArrayList(); - Patch patch = new Patch(); - - int old_ln = 0, old_n = 0, new_ln = 0, new_n = 0; - String tag = "", rest = ""; - for (String line: diff) { - // Skip leading lines until after we've seen one starting with '+++' - if (inPrelude) { - if (line.startsWith("+++")) { - inPrelude = false; - } - continue; - } - Matcher m = unifiedDiffChunkRe.matcher(line); - if (m.find()) { - // Process the lines in the previous chunk - if (rawChunk.size() != 0) { - List oldChunkLines = new ArrayList(); - List newChunkLines = new ArrayList(); - - for (Object[] raw_line: rawChunk) { - tag = (String)raw_line[0]; - rest = (String)raw_line[1]; - if (tag.equals(" ") || tag.equals("-")) { - oldChunkLines.add(rest); - } - if (tag.equals(" ") || tag.equals("+")) { - newChunkLines.add(rest); - } - } - patch.addDelta(new ChangeDelta(new Chunk(old_ln - 1, old_n, oldChunkLines), - new Chunk(new_ln - 1, new_n, newChunkLines))); - rawChunk.clear(); - } - // Parse the @@ header - old_ln = m.group(1) == null ? 1 : Integer.parseInt(m.group(1)); - old_n = m.group(2) == null ? 1 : Integer.parseInt(m.group(2)); - new_ln = m.group(3) == null ? 1 : Integer.parseInt(m.group(3)); - new_n = m.group(4) == null ? 1 : Integer.parseInt(m.group(4)); - old_ln = Integer.parseInt(m.group(1)); - - if (old_ln == 0) { - old_ln += 1; - } - if (new_ln == 0) { - new_ln += 1; - } - } else { - if (line.length() > 0) { - tag = line.substring(0, 1); - rest = line.substring(1); - if (tag.equals(" ") || tag.equals("+") || tag.equals("-")) { - rawChunk.add(new Object[] {tag, rest}); - } - } - } - } - - // Process the lines in the last chunk - if (rawChunk.size() != 0) { - List oldChunkLines = new ArrayList(); - List newChunkLines = new ArrayList(); - - for (Object[] raw_line: rawChunk) { - tag = (String)raw_line[0]; - rest = (String)raw_line[1]; - if (tag.equals(" ") || tag.equals("-")) { - oldChunkLines.add(rest); - } - if (tag.equals(" ") || tag.equals("+")) { - newChunkLines.add(rest); - } - } - - patch.addDelta(new ChangeDelta(new Chunk(old_ln - 1, old_n, oldChunkLines), - new Chunk(new_ln - 1, new_n, newChunkLines))); - rawChunk.clear(); - } - - return patch; - } - + private static DiffAlgorithm defaultDiffAlgorithm = new MyersDiff(); + private static Pattern unifiedDiffChunkRe = + Pattern.compile("@@\\s+-(?:(\\d+)(?:,(\\d+))?)\\s+\\+(?:(\\d+)(?:,(\\d+))?)\\s+@@"); + + /** + * Compute the difference between the original and revised texts with default diff algorithm + * + * @param original the original text + * @param revised the revised text + * @return the patch describing the difference between the original and revised texts + */ + public static Patch diff(List original, List revised) { + return DiffUtils.diff(original, revised, defaultDiffAlgorithm); + } + + /** + * Compute the difference between the original and revised texts with given diff algorithm + * + * @param original the original text + * @param revised the revised text + * @param algorithm the given algorithm + * @return the patch describing the difference between the original and revised texts + */ + public static Patch diff(List original, List revised, DiffAlgorithm algorithm) { + return algorithm.diff(original, revised); + } + + /** + * Patch the original text with given patch + * + * @param original the original text + * @param patch the given patch + * @return the revised text + * @throws PatchFailedException if can't apply patch + */ + public static List patch(List original, Patch patch) throws PatchFailedException { + return patch.applyTo(original); + } + + /** + * Unpatch the revised text for a given patch + * + * @param revised the revised text + * @param patch the given patch + * @return the original text + */ + public static List unpatch(List revised, Patch patch) { + return patch.restore(revised); + } + + /** + * Parse the given text in unified format and creates the list of deltas for it. + * + * @param diff the text in unified format + * @return the patch with deltas. + */ + public static Patch parseUnifiedDiff(List diff) { + boolean inPrelude = true; + List rawChunk = new ArrayList(); + Patch patch = new Patch(); + + int old_ln = 0, old_n = 0, new_ln = 0, new_n = 0; + String tag = "", rest = ""; + for (String line : diff) { + // Skip leading lines until after we've seen one starting with '+++' + if (inPrelude) { + if (line.startsWith("+++")) { + inPrelude = false; + } + continue; + } + Matcher m = unifiedDiffChunkRe.matcher(line); + if (m.find()) { + // Process the lines in the previous chunk + if (rawChunk.size() != 0) { + List oldChunkLines = new ArrayList(); + List newChunkLines = new ArrayList(); + + for (Object[] raw_line : rawChunk) { + tag = (String) raw_line[0]; + rest = (String) raw_line[1]; + if (tag.equals(" ") || tag.equals("-")) { + oldChunkLines.add(rest); + } + if (tag.equals(" ") || tag.equals("+")) { + newChunkLines.add(rest); + } + } + patch.addDelta(new ChangeDelta(new Chunk(old_ln - 1, old_n, oldChunkLines), + new Chunk(new_ln - 1, new_n, newChunkLines))); + rawChunk.clear(); + } + // Parse the @@ header + old_ln = m.group(1) == null ? 1 : Integer.parseInt(m.group(1)); + old_n = m.group(2) == null ? 1 : Integer.parseInt(m.group(2)); + new_ln = m.group(3) == null ? 1 : Integer.parseInt(m.group(3)); + new_n = m.group(4) == null ? 1 : Integer.parseInt(m.group(4)); + old_ln = Integer.parseInt(m.group(1)); + + if (old_ln == 0) { + old_ln += 1; + } + if (new_ln == 0) { + new_ln += 1; + } + } else { + if (line.length() > 0) { + tag = line.substring(0, 1); + rest = line.substring(1); + if (tag.equals(" ") || tag.equals("+") || tag.equals("-")) { + rawChunk.add(new Object[] { tag, rest }); + } + } + } + } + + // Process the lines in the last chunk + if (rawChunk.size() != 0) { + List oldChunkLines = new ArrayList(); + List newChunkLines = new ArrayList(); + + for (Object[] raw_line : rawChunk) { + tag = (String) raw_line[0]; + rest = (String) raw_line[1]; + if (tag.equals(" ") || tag.equals("-")) { + oldChunkLines.add(rest); + } + if (tag.equals(" ") || tag.equals("+")) { + newChunkLines.add(rest); + } + } + + patch.addDelta(new ChangeDelta(new Chunk(old_ln - 1, old_n, oldChunkLines), new Chunk( + new_ln - 1, new_n, newChunkLines))); + rawChunk.clear(); + } + + return patch; + } + + /** + * generateUnifiedDiff takes a Patch and some other arguments, returning the Unified Diff format text representing the Patch. + * @author Bill James (tankerbay@gmail.com) + * + * @param original - Filename of the original (unrevised file) + * @param revised - Filename of the revised file + * @param originalLines - Lines of the original file + * @param patch - Patch created by the diff() function + * @param contextSize - number of lines of context output around each difference in the file. + * @return List of strings representing the Unified Diff representation of the Patch argument. + */ + public static List generateUnifiedDiff(String original, String revised, + List originalLines, Patch patch, int contextSize) { + List ret = new ArrayList(); + ret.add("--- " + original); + ret.add("+++ " + revised); + + // Hmm, I thought the Deltas were sorted already... turns out they're not. + List patchDeltas = new ArrayList( patch.getDeltas() ); + Collections.sort( patchDeltas, new Comparator() { + public int compare( Delta a, Delta b ) { + return new Integer(a.getOriginal().getPosition()).compareTo( b.getOriginal().getPosition() ); + } + }); + + // code outside the if block also works for single-delta issues. + List deltas = new ArrayList(); // current list of Delta's to process + Delta delta = patchDeltas.get(0); + deltas.add(delta); // add the first Delta to the current set + // if there's more than 1 Delta, we may need to output them together + if (patchDeltas.size() > 1) { + for (int i = 1; i < patchDeltas.size(); i++) { + int position = delta.getOriginal().getPosition(); // store the current position of + // the first Delta + + // Check if the next Delta is too close to the current position. + // And if it is, add it to the current set + Delta nextDelta = patchDeltas.get(i); + if ((position + delta.getOriginal().getSize() + contextSize) >= + (nextDelta.getOriginal().getPosition() - contextSize)) { + deltas.add(nextDelta); + } else { + // if it isn't, output the current set, + // then create a new set and add the current Delta to it. + List curBlock = processDeltas(originalLines, deltas, contextSize); + ret.addAll(curBlock); + deltas.clear(); + deltas.add(nextDelta); + } + delta = nextDelta; + } + + } + // don't forget to process the last set of Deltas + List curBlock = processDeltas(originalLines, deltas, contextSize); + ret.addAll(curBlock); + + return ret; + } + + /** + * processDeltas takes a list of Deltas and outputs them together in a single block of Unified-Diff-format text. + * @author Bill James (tankerbay@gmail.com) + * + * @param origLines - the lines of the original file + * @param deltas - the Deltas to be output as a single block + * @param contextSize - the number of lines of context to place around block + * @return + */ + private static List processDeltas(List origLines, List deltas, + int contextSize) { + List buffer = new ArrayList(); + int origTotal = 0; // counter for total lines output from Original + int revTotal = 0; // counter for total lines output from Original + int line; + + Delta curDelta = deltas.get(0); + + // NOTE: +1 to overcome the 0-offset Position + int origStart = curDelta.getOriginal().getPosition() + 1 - contextSize; + if (origStart < 1) { + origStart = 1; + } + + int revStart = curDelta.getRevised().getPosition() + 1 - contextSize; + if (revStart < 1) { + revStart = 1; + } + + // find the start of the wrapper context code + int contextStart = curDelta.getOriginal().getPosition() - contextSize; + if (contextStart < 0) { + contextStart = 0; // clamp to the start of the file + } + + // output the context before the first Delta + for (line = contextStart; line < curDelta.getOriginal().getPosition(); line++) { // + buffer.add(" " + origLines.get(line)); + origTotal++; + revTotal++; + } + + // output the first Delta + buffer.addAll(getDeltaText(curDelta)); + origTotal += curDelta.getOriginal().getLines().size(); + revTotal += curDelta.getRevised().getLines().size(); + + int deltaIndex = 1; + while (deltaIndex < deltas.size()) { // for each of the other Deltas + Delta nextDelta = deltas.get(deltaIndex); + int intermediateStart = curDelta.getOriginal().getPosition() + + curDelta.getOriginal().getLines().size(); + for (line = intermediateStart; line < nextDelta.getOriginal().getPosition(); line++) { + // output the code between the last Delta and this one + buffer.add(" " + origLines.get(line)); + origTotal++; + revTotal++; + } + buffer.addAll(getDeltaText(nextDelta)); // output the Delta + origTotal += nextDelta.getOriginal().getLines().size(); + revTotal += nextDelta.getRevised().getLines().size(); + curDelta = nextDelta; + deltaIndex++; + } + + // Now output the post-Delta context code, clamping the end of the file + contextStart = curDelta.getOriginal().getPosition() + + curDelta.getOriginal().getLines().size(); + for (line = contextStart; (line < (contextStart + contextSize)) + && (line < origLines.size()); line++) { + buffer.add(" " + origLines.get(line)); + origTotal++; + revTotal++; + } + + // Create and insert the block header, conforming to the Unified Diff standard + StringBuffer header = new StringBuffer(); + header.append("@@ -"); + header.append(origStart); + header.append(","); + header.append(origTotal); + header.append(" +"); + header.append(revStart); + header.append(","); + header.append(revTotal); + header.append(" @@"); + buffer.add(0, header.toString()); + + return buffer; + } + + /** + * getDeltaText returns the lines to be added to the Unified Diff text from the Delta parameter + * @author Bill James (tankerbay@gmail.com) + * + * @param delta - the Delta to output + * @return list of String lines of code. + */ + private static List getDeltaText(Delta delta) { + List buffer = new ArrayList(); + for (Object line : delta.getOriginal().getLines()) { + buffer.add("-" + line); + } + for (Object line : delta.getRevised().getLines()) { + buffer.add("+" + line); + } + return buffer; + } + } diff --git a/src/difflib/InsertDelta.java b/src/difflib/InsertDelta.java index 4785144..ffc72d3 100644 --- a/src/difflib/InsertDelta.java +++ b/src/difflib/InsertDelta.java @@ -15,7 +15,7 @@ You should have received a copy of the GNU General Public License along with Java Diff Utills Library. If not, see . -*/ + */ package difflib; import java.util.List; @@ -26,52 +26,52 @@ * @author Dmitry Naumenko */ public class InsertDelta extends Delta { - - /** - * {@inheritDoc} - */ - public InsertDelta(Chunk original, Chunk revised) { - super(original, revised); - } - - /** - * {@inheritDoc} - * @throws PatchFailedException - */ - @Override - public void applyTo(List target) throws PatchFailedException { - verify(target); - int position = this.getOriginal().getPosition(); - List lines = this.getRevised().getLines(); - for (int i = 0; i < lines.size(); i++) { - target.add(position + i, lines.get(i)); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void restore(List target) { - int position = getRevised().getPosition(); - int size = getRevised().getSize(); - for (int i = 0; i < size; i++) { - target.remove(position); - } - } - - @Override - public void verify(List target) throws PatchFailedException { - if (getOriginal().getPosition() > target.size()) { - throw new PatchFailedException("Incorrect patch for delta: " + - "delta original position > target size"); + + /** + * {@inheritDoc} + */ + public InsertDelta(Chunk original, Chunk revised) { + super(original, revised); + } + + /** + * {@inheritDoc} + * @throws PatchFailedException + */ + @Override + public void applyTo(List target) throws PatchFailedException { + verify(target); + int position = this.getOriginal().getPosition(); + List lines = this.getRevised().getLines(); + for (int i = 0; i < lines.size(); i++) { + target.add(position + i, lines.get(i)); } - - } - - @Override - public String toString() { - return "[InsertDelta, position: " + getOriginal().getPosition() + ", lines: " + - getRevised().getLines() + "]"; - } + } + + /** + * {@inheritDoc} + */ + @Override + public void restore(List target) { + int position = getRevised().getPosition(); + int size = getRevised().getSize(); + for (int i = 0; i < size; i++) { + target.remove(position); + } + } + + @Override + public void verify(List target) throws PatchFailedException { + if (getOriginal().getPosition() > target.size()) { + throw new PatchFailedException("Incorrect patch for delta: " + + "delta original position > target size"); + } + + } + + @Override + public String toString() { + return "[InsertDelta, position: " + getOriginal().getPosition() + ", lines: " + + getRevised().getLines() + "]"; + } } diff --git a/src/difflib/Patch.java b/src/difflib/Patch.java index d24ae00..b46fd37 100644 --- a/src/difflib/Patch.java +++ b/src/difflib/Patch.java @@ -15,7 +15,7 @@ You should have received a copy of the GNU General Public License along with Java Diff Utills Library. If not, see . -*/ + */ package difflib; import java.util.*; @@ -26,86 +26,85 @@ * @author Dmitry Naumenko */ public class Patch { - private List deltas = new LinkedList(); - private boolean isSorted = false; - - /** - * Apply this patch to the given target - * @param target - * @return the patched text - * @throws PatchFailedException if can't apply patch - */ - public List applyTo(List target) throws PatchFailedException { - List result = new LinkedList(target); - ListIterator it = getDeltas().listIterator(deltas.size()); + private List deltas = new LinkedList(); + private boolean isSorted = false; + + /** + * Apply this patch to the given target + * @param target + * @return the patched text + * @throws PatchFailedException if can't apply patch + */ + public List applyTo(List target) throws PatchFailedException { + List result = new LinkedList(target); + ListIterator it = getDeltas().listIterator(deltas.size()); while (it.hasPrevious()) { Delta delta = (Delta) it.previous(); delta.applyTo(result); } - return result; - } - - /** - * Restore the text to original. Opposite to applyTo() method. - * @param target the given target - * @return the restored text - */ - public List restore(List target) { - List result = new LinkedList(target); - ListIterator it = getDeltas().listIterator(deltas.size()); + return result; + } + + /** + * Restore the text to original. Opposite to applyTo() method. + * @param target the given target + * @return the restored text + */ + public List restore(List target) { + List result = new LinkedList(target); + ListIterator it = getDeltas().listIterator(deltas.size()); while (it.hasPrevious()) { Delta delta = (Delta) it.previous(); delta.restore(result); } - return result; - } - - /** - * Add the given delta to this patch - * @param delta the given delta - */ - public void addDelta(Delta delta) { - deltas.add(delta); - isSorted = false; - } - - /** - * @param deltas the deltas to set - */ - public void setDeltas(List deltas) { - this.deltas = deltas; - isSorted = false; - } - - /** - * Get the list of computed deltas - * @return the deltas - */ - public List getDeltas() { - if (!this.isSorted) { - Collections.sort(deltas, new Comparator() { - public int compare(Delta d1, Delta d2) { - if (d1.getOriginal().getPosition() > d2.getOriginal().getPosition()) { - return 1; - } else if (d1.getOriginal().getPosition() > d2.getOriginal().getPosition()) { - return -1; - } else { - return 0; - } - } - } - ); - isSorted = true; - } - return deltas; - } - - /** - * Get the specific delta from patch deltas - * @param index the index of delta - * @return the needed delta - */ - public Delta getDelta(int index) { - return deltas.get(index); - } + return result; + } + + /** + * Add the given delta to this patch + * @param delta the given delta + */ + public void addDelta(Delta delta) { + deltas.add(delta); + isSorted = false; + } + + /** + * @param deltas the deltas to set + */ + public void setDeltas(List deltas) { + this.deltas = deltas; + isSorted = false; + } + + /** + * Get the list of computed deltas + * @return the deltas + */ + public List getDeltas() { + if (!this.isSorted) { + Collections.sort(deltas, new Comparator() { + public int compare(Delta d1, Delta d2) { + if (d1.getOriginal().getPosition() > d2.getOriginal().getPosition()) { + return 1; + } else if (d1.getOriginal().getPosition() > d2.getOriginal().getPosition()) { + return -1; + } else { + return 0; + } + } + }); + isSorted = true; + } + return deltas; + } + + /** + * Get the specific delta from patch deltas + * @param index the index of delta + * @return the needed delta + */ + public Delta getDelta(int index) { + return deltas.get(index); + } } diff --git a/src/difflib/PatchFailedException.java b/src/difflib/PatchFailedException.java index c30fc0f..6678f00 100644 --- a/src/difflib/PatchFailedException.java +++ b/src/difflib/PatchFailedException.java @@ -15,7 +15,7 @@ You should have received a copy of the GNU General Public License along with Java Diff Utills Library. If not, see . -*/ + */ package difflib; /** @@ -24,12 +24,12 @@ * @author Juanco Anez */ public class PatchFailedException extends DiffException { - - private static final long serialVersionUID = 1L; - - public PatchFailedException() { + + private static final long serialVersionUID = 1L; + + public PatchFailedException() { } - + public PatchFailedException(String msg) { super(msg); } diff --git a/src/difflib/StringUtills.java b/src/difflib/StringUtills.java index 312cd28..9e35e44 100644 --- a/src/difflib/StringUtills.java +++ b/src/difflib/StringUtills.java @@ -5,64 +5,64 @@ import java.util.List; public class StringUtills { - - public static String join(final Iterable objs, final String delimiter) { - Iterator iter = objs.iterator(); - if (!iter.hasNext()) { - return ""; - } - StringBuffer buffer = new StringBuffer(String.valueOf(iter.next())); - while (iter.hasNext()) { - buffer.append(delimiter).append(String.valueOf(iter.next())); - } - return buffer.toString(); - } - - public static String expandTabs(String str) { - return str.replace("\t", " "); - } - - public static String htmlEntites(String str) { - return str.replace("<", "<").replace(">", ">"); - } - - public static String normalize(String str) { - return expandTabs(htmlEntites(str)); - } - - public static List normalize(List list) { - List result = new LinkedList(); - for (String line: list) { - result.add(normalize(line)); - } - return result; - } - - public static List wrapText(List list, int columnWidth) { - List result = new LinkedList(); - for (String line: list) { - result.add(wrapText(line, columnWidth)); - } - return result; - } - - /** - * Wrap the text with the given column width - * @param line the text - * @param columnWidth the given column - * @return the wrapped text - */ - public static String wrapText(String line, int columnWidth) { - int lenght = line.length(); - int delimiter = "
".length(); - int widthIndex = columnWidth; - - for (int count = 0; lenght > widthIndex; count++) { - line = line.subSequence(0, widthIndex + delimiter * count) + "
" - + line.substring(widthIndex + delimiter * count); - widthIndex += columnWidth; - } - - return line; - } + + public static String join(final Iterable objs, final String delimiter) { + Iterator iter = objs.iterator(); + if (!iter.hasNext()) { + return ""; + } + StringBuffer buffer = new StringBuffer(String.valueOf(iter.next())); + while (iter.hasNext()) { + buffer.append(delimiter).append(String.valueOf(iter.next())); + } + return buffer.toString(); + } + + public static String expandTabs(String str) { + return str.replace("\t", " "); + } + + public static String htmlEntites(String str) { + return str.replace("<", "<").replace(">", ">"); + } + + public static String normalize(String str) { + return expandTabs(htmlEntites(str)); + } + + public static List normalize(List list) { + List result = new LinkedList(); + for (String line : list) { + result.add(normalize(line)); + } + return result; + } + + public static List wrapText(List list, int columnWidth) { + List result = new LinkedList(); + for (String line : list) { + result.add(wrapText(line, columnWidth)); + } + return result; + } + + /** + * Wrap the text with the given column width + * @param line the text + * @param columnWidth the given column + * @return the wrapped text + */ + public static String wrapText(String line, int columnWidth) { + int lenght = line.length(); + int delimiter = "
".length(); + int widthIndex = columnWidth; + + for (int count = 0; lenght > widthIndex; count++) { + line = line.subSequence(0, widthIndex + delimiter * count) + "
" + + line.substring(widthIndex + delimiter * count); + widthIndex += columnWidth; + } + + return line; + } } diff --git a/src/difflib/myers/DiffException.java b/src/difflib/myers/DiffException.java index 7c78adf..d3802ff 100644 --- a/src/difflib/myers/DiffException.java +++ b/src/difflib/myers/DiffException.java @@ -64,18 +64,14 @@ * * @author Juanco Anez */ -public class DiffException extends Exception -{ - - private static final long serialVersionUID = 1L; - - public DiffException() - { +public class DiffException extends Exception { + + private static final long serialVersionUID = 1L; + + public DiffException() { } - - public DiffException(String msg) - { + + public DiffException(String msg) { super(msg); } } - diff --git a/src/difflib/myers/DiffNode.java b/src/difflib/myers/DiffNode.java index 92c3330..a2fb071 100644 --- a/src/difflib/myers/DiffNode.java +++ b/src/difflib/myers/DiffNode.java @@ -25,9 +25,7 @@ * @author Juanco Anez * */ -public final class DiffNode - extends PathNode -{ +public final class DiffNode extends PathNode { /** * Constructs a DiffNode. *

@@ -40,18 +38,16 @@ public final class DiffNode * @param the position in the revised sequence * @param prev the previous node in the path. */ - public DiffNode(int i, int j, PathNode prev) - { - super(i, j, (prev == null ? null : prev.previousSnake()) ); + public DiffNode(int i, int j, PathNode prev) { + super(i, j, (prev == null ? null : prev.previousSnake())); } - + /** * {@inheritDoc} * @return false, always */ - public boolean isSnake() - { + public boolean isSnake() { return false; } - + } \ No newline at end of file diff --git a/src/difflib/myers/DifferentiationFailedException.java b/src/difflib/myers/DifferentiationFailedException.java index 131a075..a1cdc16 100644 --- a/src/difflib/myers/DifferentiationFailedException.java +++ b/src/difflib/myers/DifferentiationFailedException.java @@ -67,17 +67,13 @@ * @see Diff * @see DiffAlgorithm */ -public class DifferentiationFailedException extends DiffException -{ - private static final long serialVersionUID = 1L; - - public DifferentiationFailedException() - { +public class DifferentiationFailedException extends DiffException { + private static final long serialVersionUID = 1L; + + public DifferentiationFailedException() { } - - public DifferentiationFailedException(String msg) - { + + public DifferentiationFailedException(String msg) { super(msg); } } - diff --git a/src/difflib/myers/MyersDiff.java b/src/difflib/myers/MyersDiff.java index 4130814..31605ed 100644 --- a/src/difflib/myers/MyersDiff.java +++ b/src/difflib/myers/MyersDiff.java @@ -57,10 +57,16 @@ package difflib.myers; -import java.util.Arrays; +import java.lang.reflect.Array; import java.util.List; -import difflib.*; +import difflib.ChangeDelta; +import difflib.Chunk; +import difflib.DeleteDelta; +import difflib.Delta; +import difflib.DiffAlgorithm; +import difflib.InsertDelta; +import difflib.Patch; /** * A clean-room implementation of @@ -71,25 +77,22 @@ * * @author Juanco Anez */ -public class MyersDiff - implements DiffAlgorithm -{ +public class MyersDiff implements DiffAlgorithm { /** * Constructs an instance of the Myers differencing algorithm. */ - public MyersDiff() - { + public MyersDiff() { } - + /** * {@inheritDoc} * * Return empty diff if get the error while procession the difference. */ - @Override - public Patch diff(List original, List revised) { - return diff(original.toArray(), revised.toArray()); - } + @Override + public Patch diff(List original, List revised) { + return diff(original.toArray(), revised.toArray()); + } /** * {@inheritDoc} @@ -97,18 +100,17 @@ public Patch diff(List original, List revised) { * Return empty diff if get the error while procession the difference. */ @Override - public Patch diff(Object[] orig, Object[] rev) - { + public Patch diff(Object[] orig, Object[] rev) { PathNode path; - try { - path = buildPath(orig, rev); - return buildRevision(path, orig, rev); - } catch (DifferentiationFailedException e) { - e.printStackTrace(); - } + try { + path = buildPath(orig, rev); + return buildRevision(path, orig, rev); + } catch (DifferentiationFailedException e) { + e.printStackTrace(); + } return new Patch(); } - + /** * Computes the minimum diffpath that expresses de differences * between the original and revised sequences, according @@ -120,76 +122,67 @@ public Patch diff(Object[] orig, Object[] rev) * @throws DifferentiationFailedException if a diff path could not be found. */ public static PathNode buildPath(Object[] orig, Object[] rev) - throws DifferentiationFailedException - { + throws DifferentiationFailedException { if (orig == null) throw new IllegalArgumentException("original sequence is null"); if (rev == null) throw new IllegalArgumentException("revised sequence is null"); - + // these are local constants final int N = orig.length; final int M = rev.length; - + final int MAX = N + M + 1; final int size = 1 + 2 * MAX; final int middle = (size + 1) / 2; final PathNode diagonal[] = new PathNode[size]; - + diagonal[middle + 1] = new Snake(0, -1, null); - for (int d = 0; d < MAX; d++) - { - for (int k = -d; k <= d; k += 2) - { + for (int d = 0; d < MAX; d++) { + for (int k = -d; k <= d; k += 2) { final int kmiddle = middle + k; final int kplus = kmiddle + 1; final int kminus = kmiddle - 1; PathNode prev = null; - + int i; - if ( (k == -d) || - (k != d && diagonal[kminus].i < diagonal[kplus].i)) - { + if ((k == -d) || (k != d && diagonal[kminus].i < diagonal[kplus].i)) { i = diagonal[kplus].i; prev = diagonal[kplus]; - } - else - { + } else { i = diagonal[kminus].i + 1; prev = diagonal[kminus]; } - + diagonal[kminus] = null; // no longer used - + int j = i - k; - + PathNode node = new DiffNode(i, j, prev); - + // orig and rev are zero-based // but the algorithm is one-based // that's why there's no +1 when indexing the sequences - while (i < N && j < M && orig[i].equals(rev[j])) - { + while (i < N && j < M && orig[i].equals(rev[j])) { i++; j++; } if (i > node.i) node = new Snake(i, j, node); - + diagonal[kmiddle] = node; - - if (i >= N && j >= M) - { + + if (i >= N && j >= M) { return diagonal[kmiddle]; } } - diagonal[middle+d-1] = null; - + diagonal[middle + d - 1] = null; + } // According to Myers, this cannot happen throw new DifferentiationFailedException("could not find a diff path"); } - + /** * Constructs a {@link Revision} from a difference path. * @@ -200,46 +193,67 @@ public static PathNode buildPath(Object[] orig, Object[] rev) * @throws DifferentiationFailedException if a {@link Revision} could * not be built from the given path. */ - public static Patch buildRevision(PathNode path, Object[] orig, Object[] rev) - { + public static Patch buildRevision(PathNode path, Object[] orig, Object[] rev) { if (path == null) throw new IllegalArgumentException("path is null"); if (orig == null) throw new IllegalArgumentException("original sequence is null"); if (rev == null) throw new IllegalArgumentException("revised sequence is null"); - + Patch patch = new Patch(); if (path.isSnake()) path = path.prev; - while (path != null && path.prev != null && path.prev.j >= 0) - { - if(path.isSnake()) - throw new IllegalStateException("bad diffpath: found snake when looking for diff"); + while (path != null && path.prev != null && path.prev.j >= 0) { + if (path.isSnake()) + throw new IllegalStateException("bad diffpath: found snake when looking for diff"); int i = path.i; int j = path.j; - + path = path.prev; int ianchor = path.i; int janchor = path.j; - Chunk original = new Chunk(ianchor, i - ianchor, Arrays.copyOfRange(orig, ianchor, i)); - Chunk revised = new Chunk(janchor, j - janchor, Arrays.copyOfRange(rev, janchor, j)); + Chunk original = new Chunk(ianchor, i - ianchor, copyOfRange(orig, ianchor, i)); + Chunk revised = new Chunk(janchor, j - janchor, copyOfRange(rev, janchor, j)); Delta delta = null; if (original.getSize() == 0 && revised.getSize() != 0) { - delta = new InsertDelta(original, revised); + delta = new InsertDelta(original, revised); } else if (original.getSize() > 0 && revised.getSize() == 0) { - delta = new DeleteDelta(original, revised); + delta = new DeleteDelta(original, revised); } else { - delta = new ChangeDelta(original, revised); + delta = new ChangeDelta(original, revised); } - patch.addDelta(delta); if (path.isSnake()) path = path.prev; } return patch; } - + + /** + * Copied here from JDK 1.6 + */ + + @SuppressWarnings("unchecked") + public static T[] copyOfRange(T[] original, int from, int to) { + return copyOfRange(original, from, to, (Class) original.getClass()); + } + + /** + * Copied here from JDK 1.6 + */ + @SuppressWarnings("unchecked") + public static T[] copyOfRange(U[] original, int from, int to, + Class newType) { + int newLength = to - from; + if (newLength < 0) + throw new IllegalArgumentException(from + " > " + to); + T[] copy = ((Object) newType == (Object) Object[].class) ? (T[]) new Object[newLength] + : (T[]) Array.newInstance(newType.getComponentType(), newLength); + System.arraycopy(original, from, copy, 0, Math.min(original.length - from, newLength)); + return copy; + } + } diff --git a/src/difflib/myers/PathNode.java b/src/difflib/myers/PathNode.java index 4e98082..4ead5f2 100644 --- a/src/difflib/myers/PathNode.java +++ b/src/difflib/myers/PathNode.java @@ -67,34 +67,32 @@ * @see Snake * */ -public abstract class PathNode -{ +public abstract class PathNode { /** Position in the original sequence. */ public final int i; /** Position in the revised sequence. */ public final int j; /** The previous node in the path. */ public final PathNode prev; - + /** * Concatenates a new path node with an existing diffpath. * @param i The position in the original sequence for the new node. * @param j The position in the revised sequence for the new node. * @param prev The previous node in the path. */ - public PathNode(int i, int j, PathNode prev) - { + public PathNode(int i, int j, PathNode prev) { this.i = i; this.j = j; this.prev = prev; } - + /** * Is this node a {@link Snake Snake node}? * @return true if this is a {@link Snake Snake node} */ public abstract boolean isSnake(); - + /** * Is this a bootstrap node? *

@@ -102,11 +100,10 @@ public PathNode(int i, int j, PathNode prev) * less than zero. * @return tru if this is a bootstrap node. */ - public boolean isBootstrap() - { + public boolean isBootstrap() { return i < 0 || j < 0; } - + /** * Skips sequences of {@link DiffNode DiffNodes} until a * {@link Snake} or bootstrap node is found, or the end @@ -115,24 +112,21 @@ public boolean isBootstrap() * null * if none found. */ - public final PathNode previousSnake() - { + public final PathNode previousSnake() { if (isBootstrap()) return null; if (!isSnake() && prev != null) return prev.previousSnake(); return this; } - + /** * {@inheritDoc} */ - public String toString() - { + public String toString() { StringBuffer buf = new StringBuffer("["); PathNode node = this; - while (node != null) - { + while (node != null) { buf.append("("); buf.append(Integer.toString(node.i)); buf.append(","); diff --git a/src/difflib/myers/Snake.java b/src/difflib/myers/Snake.java index b19b17e..452829b 100644 --- a/src/difflib/myers/Snake.java +++ b/src/difflib/myers/Snake.java @@ -70,9 +70,7 @@ * @author Juanco Anez * */ -public final class Snake - extends PathNode -{ +public final class Snake extends PathNode { /** * Constructs a snake node. * @@ -80,18 +78,16 @@ public final class Snake * @param the position in the revised sequence * @param prev the previous node in the path. */ - public Snake(int i, int j, PathNode prev) - { + public Snake(int i, int j, PathNode prev) { super(i, j, prev); } - + /** * {@inheritDoc} * @return true always */ - public boolean isSnake() - { + public boolean isSnake() { return true; } - + } \ No newline at end of file diff --git a/test/mocks/one_delta_test_original.txt b/test/mocks/one_delta_test_original.txt new file mode 100644 index 0000000..5d54330 --- /dev/null +++ b/test/mocks/one_delta_test_original.txt @@ -0,0 +1,54 @@ +

Esta é uma obra Online.

+

 

+

Este texto é negrito

+

Este texto é itálico

+

Este texto está sublinhado

+

Este texto está riscado

+

Este texto está centralizado

+

Este texto está alinhado a direita

+
    +
  1. Este texto está em uma lista numérica
      +
    1. Este texto está identado
    2. +
  2. +
+

Este +aqui é um link

+

 

+

 

+

 

, +

 

+

Página 1

+

 

+

 

+

 

+

 

+

 

+

 

+

 

+

 

+

 

+

 

+

 

+

 

+

Página 2

+

 

+

 

+

 

+

 

+

 

+

 

+

 

+

 

+

 

+

 

+

 

+

 

+

 

+

 

+

 

+

 

+

 

+

 

+

 

+

Página 3

+

 

\ No newline at end of file diff --git a/test/mocks/one_delta_test_revised.txt b/test/mocks/one_delta_test_revised.txt new file mode 100644 index 0000000..9197a73 --- /dev/null +++ b/test/mocks/one_delta_test_revised.txt @@ -0,0 +1,11 @@ +

Revisão 3

 

Esta é uma obra +Online.

 

Este texto é +negrit

Este texto é itálico/p>

Este texto está +sublinhado

Este texto está riscado agora não +está mais

p style="text-align: left;">Este texto está +centralizado nem este

Este texto +está alinhado a direita

  1. Este texto está em uma +lista numérica

Este aqui é um +link

\ No newline at end of file diff --git a/test/mocks/original.txt b/test/mocks/original.txt new file mode 100644 index 0000000..479dca0 --- /dev/null +++ b/test/mocks/original.txt @@ -0,0 +1,174 @@ +/* + Copyright 2009 Dmitry Naumenko (dm.naumenko@gmail.com) + + This file is part of Java Diff Utills Library. + + Java Diff Utills Library is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Java Diff Utills Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Java Diff Utills Library. If not, see . +*/ +package difflib; + +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import difflib.myers.*; + +/** + * Implements the difference and patching engine + * + * @author Dmitry Naumenko + * @version 0.4.1 + */ +public class DiffUtils { + private static DiffAlgorithm defaultDiffAlgorithm = new MyersDiff(); + private static Pattern unifiedDiffChunkRe = + Pattern.compile("@@\\s+-(?:(\\d+)(?:,(\\d+))?)\\s+\\+(?:(\\d+)(?:,(\\d+))?)\\s+@@"); + + /** + * Compute the difference between the original and revised texts with default diff algorithm + * + * @param original the original text + * @param revised the revised text + * @return the patch describing the difference between the original and revised texts + */ + public static Patch diff(List original, List revised) { + return DiffUtils.diff(original, revised, defaultDiffAlgorithm); + } + + /** + * Compute the difference between the original and revised texts with given diff algorithm + * + * @param original the original text + * @param revised the revised text + * @param algorithm the given algorithm + * @return the patch describing the difference between the original and revised texts + */ + public static Patch diff(List original, List revised, DiffAlgorithm algorithm) { + return algorithm.diff(original, revised); + } + + /** + * Patch the original text with given patch + * + * @param original the original text + * @param patch the given patch + * @return the revised text + * @throws PatchFailedException if can't apply patch + */ + public static List patch(List original, Patch patch) throws PatchFailedException { + return patch.applyTo(original); + } + + /** + * Unpatch the revised text for a given patch + * + * @param revised the revised text + * @param patch the given patch + * @return the original text + */ + public static List unpatch(List revised, Patch patch) { + return patch.restore(revised); + } + + /** + * Parse the given text in unified format and creates the list of deltas for it. + * + * @param diff the text in unified format + * @return the patch with deltas. + */ + public static Patch parseUnifiedDiff(List diff) { + boolean inPrelude = true; + List rawChunk = new ArrayList(); + Patch patch = new Patch(); + + int old_ln = 0, old_n = 0, new_ln = 0, new_n = 0; + String tag = "", rest = ""; + for (String line: diff) { + // Skip leading lines until after we've seen one starting with '+++' + if (inPrelude) { + if (line.startsWith("+++")) { + inPrelude = false; + } + continue; + } + Matcher m = unifiedDiffChunkRe.matcher(line); + if (m.find()) { + // Process the lines in the previous chunk + if (rawChunk.size() != 0) { + List oldChunkLines = new ArrayList(); + List newChunkLines = new ArrayList(); + + for (Object[] raw_line: rawChunk) { + tag = (String)raw_line[0]; + rest = (String)raw_line[1]; + if (tag.equals(" ") || tag.equals("-")) { + oldChunkLines.add(rest); + } + if (tag.equals(" ") || tag.equals("+")) { + newChunkLines.add(rest); + } + } + patch.addDelta(new ChangeDelta(new Chunk(old_ln - 1, old_n, oldChunkLines), + new Chunk(new_ln - 1, new_n, newChunkLines))); + rawChunk.clear(); + } + // Parse the @@ header + old_ln = m.group(1) == null ? 1 : Integer.parseInt(m.group(1)); + old_n = m.group(2) == null ? 1 : Integer.parseInt(m.group(2)); + new_ln = m.group(3) == null ? 1 : Integer.parseInt(m.group(3)); + new_n = m.group(4) == null ? 1 : Integer.parseInt(m.group(4)); + old_ln = Integer.parseInt(m.group(1)); + + if (old_ln == 0) { + old_ln += 1; + } + if (new_ln == 0) { + new_ln += 1; + } + } else { + if (line.length() > 0) { + tag = line.substring(0, 1); + rest = line.substring(1); + if (tag.equals(" ") || tag.equals("+") || tag.equals("-")) { + rawChunk.add(new Object[] {tag, rest}); + } + } + } + } + + // Process the lines in the last chunk + if (rawChunk.size() != 0) { + List oldChunkLines = new ArrayList(); + List newChunkLines = new ArrayList(); + + for (Object[] raw_line: rawChunk) { + tag = (String)raw_line[0]; + rest = (String)raw_line[1]; + if (tag.equals(" ") || tag.equals("-")) { + oldChunkLines.add(rest); + } + if (tag.equals(" ") || tag.equals("+")) { + newChunkLines.add(rest); + } + } + + patch.addDelta(new ChangeDelta(new Chunk(old_ln - 1, old_n, oldChunkLines), + new Chunk(new_ln - 1, new_n, newChunkLines))); + rawChunk.clear(); + } + + return patch; + } + +} diff --git a/test/mocks/revised.txt b/test/mocks/revised.txt new file mode 100644 index 0000000..3c19df3 --- /dev/null +++ b/test/mocks/revised.txt @@ -0,0 +1,308 @@ +/* + Copyright 2009 Dmitry Naumenko (dm.naumenko@gmail.com) + + This file is part of Java Diff Utils Library. + + Java Diff Utils Library is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Java Diff Utils Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Java Diff Utils Library. If not, see . +*/ +package difflib; + +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import difflib.myers.*; + +/** + * Implements the difference and patching engine + * + * @author Dmitry Naumenko + * @version 0.4.1 + */ +public class DiffUtils { + private static DiffAlgorithm defaultDiffAlgorithm = new MyersDiff(); + private static Pattern unifiedDiffChunkRe = + Pattern.compile("@@\\s+-(?:(\\d+)(?:,(\\d+))?)\\s+\\+(?:(\\d+)(?:,(\\d+))?)\\s+@@"); + + /** + * Compute the difference between the original and revised texts with default diff algorithm + * + * @param original the original text + * @param revised the revised text + * @return the patch describing the difference between the original and revised texts + */ + public static Patch diff(List original, List revised) { + return DiffUtils.diff(original, revised, defaultDiffAlgorithm); + } + + /** + * Compute the difference between the original and revised texts with given diff algorithm + * + * @param original the original text + * @param revised the revised text + * @param algorithm the given algorithm + * @return the patch describing the difference between the original and revised texts + */ + public static Patch diff(List original, List revised, DiffAlgorithm algorithm) { + return algorithm.diff(original, revised); + } + + /** + * Patch the original text with given patch + * + * @param original the original text + * @param patch the given patch + * @return the revised text + * @throws PatchFailedException if can't apply patch + */ + public static List patch(List original, Patch patch) throws PatchFailedException { + return patch.applyTo(original); + } + + /** + * Unpatch the revised text for a given patch + * + * @param revised the revised text + * @param patch the given patch + * @return the original text + */ + public static List unpatch(List revised, Patch patch) { + return patch.restore(revised); // bla-bla-bla + } + + /** + * Parse the given text in unified format and creates the list of deltas for it. + * + * @param diff the text in unified format + * @return the patch with deltas. + */ + public static Patch parseUnifiedDiff(List diff) { + boolean inPrelude = true; + List rawChunk = new ArrayList(); + Patch patch = new Patch(); + + int old_ln = 0, old_n = 0, new_ln = 0, new_n = 0; + String tag = "", rest = ""; + for (String line: diff) { + // Skip leading lines until after we've seen one starting with '+++' + if (inPrelude) { + if (line.startsWith("+++")) { + inPrelude = false; + } + continue; + } + Matcher m = unifiedDiffChunkRe.matcher(line); + if (m.find()) { + // Process the lines in the previous chunk + if (rawChunk.size() != 0) { + List oldChunkLines = new ArrayList(); + List newChunkLines = new ArrayList(); + + for (Object[] raw_line: rawChunk) { + tag = (String)raw_line[0]; + rest = (String)raw_line[1]; + if (tag.equals(" ") || tag.equals("-")) { + oldChunkLines.add(rest); + } + if (tag.equals(" ") || tag.equals("+")) { + newChunkLines.add(rest); + } + } + patch.addDelta(new ChangeDelta(new Chunk(old_ln - 1, old_n, oldChunkLines), + new Chunk(new_ln - 1, new_n, newChunkLines))); + rawChunk.clear(); + } + // Parse the @@ header + old_ln = m.group(1) == null ? 1 : Integer.parseInt(m.group(1)); + old_n = m.group(2) == null ? 1 : Integer.parseInt(m.group(2)); + new_ln = m.group(3) == null ? 1 : Integer.parseInt(m.group(3)); + new_n = m.group(4) == null ? 1 : Integer.parseInt(m.group(4)); + old_ln = Integer.parseInt(m.group(1)); + + if (old_ln == 0) { + old_ln += 1; + } + if (new_ln == 0) { + new_ln += 1; + } + } else { + if (line.length() > 0) { + tag = line.substring(0, 1); + rest = line.substring(1); + if (tag.equals(" ") || tag.equals("+") || tag.equals("-")) { + rawChunk.add(new Object[] {tag, rest}); + } + } + } + } + + // Process the lines in the last chunk + if (rawChunk.size() != 0) { + List oldChunkLines = new ArrayList(); + List newChunkLines = new ArrayList(); + + for (Object[] raw_line: rawChunk) + { + tag = (String)raw_line[0]; + rest = (String)raw_line[1]; + if (tag.equals(" ") || tag.equals("-")) + { + oldChunkLines.add(rest); + } + if (tag.equals(" ") || tag.equals("+")) + { + newChunkLines.add(rest); + } + } + + patch.addDelta(new ChangeDelta(new Chunk(old_ln - 1, old_n, oldChunkLines), + new Chunk(new_ln - 1, new_n, newChunkLines))); + rawChunk.clear(); + } + + return patch; + } + + /** + * generateUnifiedDiff takes a Patch and some other arguments, returning the Unified Diff format text representing the Patch. + * @author Bill James (tankerbay@gmail.com) + * + * @param fname1 - Filename of the original (unrevised file) + * @param fname2 - Filename of the revised file + * @param originalLines - Lines of the original file + * @param patch - Patch created by the diff() function + * @param contextSize - number of lines of context output around each difference in the file. + * @return List of strings representing the Unified Diff representation of the Patch argument. + */ + public static List generateUnifiedDiff(String fname1, String fname2, List originalLines, Patch patch, int contextSize ) { + List ret = new ArrayList(); + ret.add( "--- " + fname1 ); + ret.add( "+++ " + fname2 ); + + List cur = new ArrayList(); // current list of Delta's to process + int deltact = patch.getDeltas().size(); + // if there's more than 1 Delta, we may need to output them together + if ( deltact > 1 ) { + Delta curDelta = patch.getDelta(0); + cur.add( curDelta ); // add the first Delta to the current set + for ( int i = 1; i < deltact; i++ ) { + int curpos = curDelta.getOriginal().getPosition(); // store the current position of the first Delta + Delta nextDelta = patch.getDelta(i); // Check if the next Delta is too close to the current position + if ( (curpos + curDelta.getOriginal().getSize() + contextSize) >= ( nextDelta.getOriginal().getPosition()-contextSize ) ) { + cur.add( nextDelta ); // if it is, add it to the current set + } else { + List curBlock = processDeltas( originalLines, cur, contextSize ); + ret.addAll( curBlock ); // if it isn't, output the current set, then create a new + cur.clear(); // set and add the current Delta to it. + cur.add( nextDelta ); + } + curDelta = nextDelta; + } + List curBlock = processDeltas( originalLines, cur, contextSize ); // don't forget to process the last set of Deltas + ret.addAll( curBlock ); + } + + return ret; + } + + /** + * processDeltas takes a list of Deltas and outputs them together in a single block of Unified-Diff-format text. + * @author Bill James (tankerbay@gmail.com) + * + * @param origLines - the lines of the original file + * @param deltas - the Deltas to be output as a single block + * @param contextSize - the number of lines of context to place around block + * @return + */ + private static List processDeltas( List origLines, List deltas, int contextSize ) { + List buffer = new ArrayList(); + int origTotal = 0; // counter for total lines output from Original + int revTotal = 0; // counter for total lines output from Original + int line; + + Delta curDelta = deltas.get(0); // start with the first Delta + int origStart = curDelta.getOriginal().getPosition()+1 - contextSize; // note the +1 to overcome the 0-offset Position + if ( origStart < 1 ) origStart = 1; // clamp to the start of the file + int revStart = curDelta.getRevised().getPosition()+1 - contextSize; // note the +1 to overcome the 0-offset Position + if ( revStart < 1 ) revStart = 1; // clamp to the start of the file + int contextStart = curDelta.getOriginal().getPosition() - contextSize; // find the start of the wrapper context code + if ( contextStart < 0 ) contextStart = 0; // clamp to the start of the file + for ( line = contextStart; line < curDelta.getOriginal().getPosition(); line++ ) { // output the context before the first Delta + buffer.add( " " + origLines.get( line ) ); + origTotal++; + revTotal++; + } + buffer.addAll( getDeltaText( curDelta ) ); // output the first Delta + origTotal += curDelta.getOriginal().getLines().size(); + revTotal += curDelta.getRevised().getLines().size(); + + int deltaIndex = 1; + while ( deltaIndex < deltas.size() ) { // for each of the other Deltas + Delta nextDelta = deltas.get( deltaIndex ); + int intermediateStart = curDelta.getOriginal().getPosition() + curDelta.getOriginal().getLines().size(); + for ( line = intermediateStart; line < nextDelta.getOriginal().getPosition(); line++ ) { + buffer.add( " " + origLines.get( line ) ); // output the code between the last Delta and this one + origTotal++; + revTotal++; + } + buffer.addAll( getDeltaText( nextDelta ) ); // output the Delta + origTotal += nextDelta.getOriginal().getLines().size(); + revTotal += nextDelta.getRevised().getLines().size(); + curDelta = nextDelta; + deltaIndex++; // increment the iterator + } + + // Now output the post-Delta context code, clamping the end of the file + contextStart = curDelta.getOriginal().getPosition() + curDelta.getOriginal().getLines().size(); + for ( line = contextStart; ( line < (contextStart + contextSize )) && ( line < origLines.size() ); line++ ) { + buffer.add( " " + origLines.get( line ) ); + origTotal++; + revTotal++; + } + + // Create and insert the block header, conforming to the Unified Diff standard + StringBuffer header = new StringBuffer(); + header.append( "@@ -" ); + header.append( origStart ); + header.append( "," ); + header.append( origTotal ); + header.append( " +" ); + header.append( revStart ); + header.append( "," ); + header.append( revTotal ); + header.append( " @@" ); + buffer.add( 0, header.toString() ); + + return buffer; + } + + /** + * getDeltaText returns the lines to be added to the Unified Diff text from the Delta parameter + * @author Bill James (tankerbay@gmail.com) + * + * @param delta - the Delta to output + * @return list of String lines of code. + */ + private static List getDeltaText( Delta delta ) { + List buffer = new ArrayList(); + for ( Object line: delta.getOriginal().getLines() ) { + buffer.add( "-" + line ); + } + for ( Object line: delta.getRevised().getLines() ) { + buffer.add( "+" + line ); + } + return buffer; + } + +} diff --git a/test/testcase/diffutills/DiffTest.java b/test/testcase/diffutills/DiffTest.java index 4e8450c..8e340de 100644 --- a/test/testcase/diffutills/DiffTest.java +++ b/test/testcase/diffutills/DiffTest.java @@ -6,51 +6,63 @@ import difflib.*; public class DiffTest extends TestCase { - private List changeTest_from = Arrays.asList("aaa", "bbb", "ccc"); - private List changeTest_to = Arrays.asList("aaa", "zzz", "ccc"); - - private List deleteTest_from = Arrays.asList("ddd", "fff", "ggg"); - private List deleteTest_to = Arrays.asList("ggg"); - - private List insertTest_from = Arrays.asList("hhh"); - private List insertTest_to = Arrays.asList("hhh", "jjj", "kkk"); - - @SuppressWarnings("serial") - public void testDiff_Insert() { - Patch patch = DiffUtils.diff(insertTest_from, insertTest_to); - assertNotNull(patch); - assertEquals(patch.getDeltas().size(), 1); - Delta delta = patch.getDeltas().get(0); - assertEquals(InsertDelta.class, delta.getClass()); - assertEquals(new Chunk(1, 0, new LinkedList()), - delta.getOriginal()); - assertEquals(new Chunk(1, 2, new LinkedList() {{ add("jjj"); add("kkk"); }}), - delta.getRevised()); - } - - @SuppressWarnings("serial") - public void testDiff_Delete() { - Patch patch = DiffUtils.diff(deleteTest_from, deleteTest_to); - assertNotNull(patch); - assertEquals(patch.getDeltas().size(), 1); - Delta delta = patch.getDeltas().get(0); - assertEquals(DeleteDelta.class, delta.getClass()); - assertEquals(new Chunk(0, 2, new LinkedList() {{ add("ddd"); add("fff"); }}), - delta.getOriginal()); - assertEquals(new Chunk(0, 0, new LinkedList()), - delta.getRevised()); - } - - @SuppressWarnings("serial") - public void testDiff_Change() { - Patch patch = DiffUtils.diff(changeTest_from, changeTest_to); - assertNotNull(patch); - assertEquals(patch.getDeltas().size(), 1); - Delta delta = patch.getDeltas().get(0); - assertEquals(ChangeDelta.class, delta.getClass()); - assertEquals(new Chunk(1, 1, new LinkedList() {{ add("bbb"); }}), - delta.getOriginal()); - assertEquals(new Chunk(1, 1, new LinkedList() {{ add("zzz"); }}), - delta.getRevised()); - } + private List changeTest_from = Arrays.asList("aaa", "bbb", "ccc"); + private List changeTest_to = Arrays.asList("aaa", "zzz", "ccc"); + + private List deleteTest_from = Arrays.asList("ddd", "fff", "ggg"); + private List deleteTest_to = Arrays.asList("ggg"); + + private List insertTest_from = Arrays.asList("hhh"); + private List insertTest_to = Arrays.asList("hhh", "jjj", "kkk"); + + @SuppressWarnings("serial") + public void testDiff_Insert() { + Patch patch = DiffUtils.diff(insertTest_from, insertTest_to); + assertNotNull(patch); + assertEquals(patch.getDeltas().size(), 1); + Delta delta = patch.getDeltas().get(0); + assertEquals(InsertDelta.class, delta.getClass()); + assertEquals(new Chunk(1, 0, new LinkedList()), delta.getOriginal()); + assertEquals(new Chunk(1, 2, new LinkedList() { + { + add("jjj"); + add("kkk"); + } + }), delta.getRevised()); + } + + @SuppressWarnings("serial") + public void testDiff_Delete() { + Patch patch = DiffUtils.diff(deleteTest_from, deleteTest_to); + assertNotNull(patch); + assertEquals(patch.getDeltas().size(), 1); + Delta delta = patch.getDeltas().get(0); + assertEquals(DeleteDelta.class, delta.getClass()); + assertEquals(new Chunk(0, 2, new LinkedList() { + { + add("ddd"); + add("fff"); + } + }), delta.getOriginal()); + assertEquals(new Chunk(0, 0, new LinkedList()), delta.getRevised()); + } + + @SuppressWarnings("serial") + public void testDiff_Change() { + Patch patch = DiffUtils.diff(changeTest_from, changeTest_to); + assertNotNull(patch); + assertEquals(patch.getDeltas().size(), 1); + Delta delta = patch.getDeltas().get(0); + assertEquals(ChangeDelta.class, delta.getClass()); + assertEquals(new Chunk(1, 1, new LinkedList() { + { + add("bbb"); + } + }), delta.getOriginal()); + assertEquals(new Chunk(1, 1, new LinkedList() { + { + add("zzz"); + } + }), delta.getRevised()); + } } diff --git a/test/testcase/diffutills/GenerateUnifiedDiffTest.java b/test/testcase/diffutills/GenerateUnifiedDiffTest.java new file mode 100644 index 0000000..8a3e037 --- /dev/null +++ b/test/testcase/diffutills/GenerateUnifiedDiffTest.java @@ -0,0 +1,88 @@ +package testcase.diffutills; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +import difflib.DiffUtils; +import difflib.Patch; +import difflib.PatchFailedException; + +import junit.framework.TestCase; + +public class GenerateUnifiedDiffTest extends TestCase { + static final String FS = File.separator; + static final String originalFilename = "test" + FS + "mocks" + FS + "original.txt"; + static final String revisedFilename = "test" + FS + "mocks" + FS + "revised.txt"; + static final String originalFilenameOneDelta = "test" + FS + "mocks" + FS + "one_delta_test_original.txt"; + static final String revisedFilenameOneDelta = "test" + FS + "mocks" + FS + "one_delta_test_revised.txt"; + + public List fileToLines(String filename) { + List lines = new LinkedList(); + String line = ""; + try { + BufferedReader in = new BufferedReader(new FileReader(filename)); + while ((line = in.readLine()) != null) { + lines.add(line); + } + } catch (IOException e) { + e.printStackTrace(); + } + return lines; + } + + /** + * Tests the Unified Diff generation by creating a Patch, then + * creating the Unified Diff representation, then parsing that + * Unified Diff, and applying the patch to the original unrevised + * text, then comparing that to the original revised text. + * + * @author Bill James (tankerbay@gmail.com) + */ + public void testGenerateUnified() { + List origLines = fileToLines(originalFilename); + List revLines = fileToLines(revisedFilename); + + testGenerateUnified(origLines, revLines); + } + + /** + * Tests the Unified Diff generation for diff with one delta. + */ + public void testGenerateUnifiedWithOneDelta() { + List origLines = fileToLines(originalFilenameOneDelta); + List revLines = fileToLines(revisedFilenameOneDelta); + + testGenerateUnified(origLines, revLines); + } + + private void testGenerateUnified(List origLines, List revLines) { + Patch p = DiffUtils.diff(origLines, revLines); + List unifiedDiff = DiffUtils.generateUnifiedDiff( + originalFilename, revisedFilename, origLines, p, 10); + + Patch fromUnifiedPatch = DiffUtils.parseUnifiedDiff(unifiedDiff); + List patchedLines = new ArrayList(); + try { + patchedLines = (List) fromUnifiedPatch.applyTo(origLines); + } catch (PatchFailedException e) { + // TODO Auto-generated catch block + fail(e.getMessage()); + } + + assertTrue(revLines.size() == patchedLines.size()); + for (int i = 0; i < revLines.size(); i++) { + String l1 = revLines.get(i); + String l2 = patchedLines.get(i); + if (l1.equals(l2) == false) { + fail("Line " + (i + 1) + " of the patched file did not match the revised original"); + } + } + } + + +} diff --git a/test/testcase/diffutills/PatchTest.java b/test/testcase/diffutills/PatchTest.java index 6c9506b..7b1ef0d 100644 --- a/test/testcase/diffutills/PatchTest.java +++ b/test/testcase/diffutills/PatchTest.java @@ -8,39 +8,39 @@ import difflib.Patch; public class PatchTest extends TestCase { - private List changeTest_from = Arrays.asList("aaa", "bbb", "ccc", "ddd"); - private List changeTest_to = Arrays.asList("aaa", "bxb", "cxc", "ddd"); - - private List deleteTest_from = Arrays.asList("ddd", "fff", "ggg", "hhh"); - private List deleteTest_to = Arrays.asList("ggg"); - - private List insertTest_from = Arrays.asList("hhh"); - private List insertTest_to = Arrays.asList("hhh", "jjj", "kkk", "lll"); - - public void testPatch_Insert() { - Patch patch = DiffUtils.diff(insertTest_from, insertTest_to); - try { - assertEquals(insertTest_to, DiffUtils.patch(insertTest_from, patch)); - } catch (Exception e) { - e.printStackTrace(); - } - } - - public void testPatch_Delete() { - Patch patch = DiffUtils.diff(deleteTest_from, deleteTest_to); - try { - assertEquals(deleteTest_to, DiffUtils.patch(deleteTest_from, patch)); - } catch (Exception e) { - e.printStackTrace(); - } - } - - public void testPatch_Change() { - Patch patch = DiffUtils.diff(changeTest_from, changeTest_to); - try { - assertEquals(changeTest_to, DiffUtils.patch(changeTest_from, patch)); - } catch (Exception e) { - e.printStackTrace(); - } - } + private List changeTest_from = Arrays.asList("aaa", "bbb", "ccc", "ddd"); + private List changeTest_to = Arrays.asList("aaa", "bxb", "cxc", "ddd"); + + private List deleteTest_from = Arrays.asList("ddd", "fff", "ggg", "hhh"); + private List deleteTest_to = Arrays.asList("ggg"); + + private List insertTest_from = Arrays.asList("hhh"); + private List insertTest_to = Arrays.asList("hhh", "jjj", "kkk", "lll"); + + public void testPatch_Insert() { + Patch patch = DiffUtils.diff(insertTest_from, insertTest_to); + try { + assertEquals(insertTest_to, DiffUtils.patch(insertTest_from, patch)); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void testPatch_Delete() { + Patch patch = DiffUtils.diff(deleteTest_from, deleteTest_to); + try { + assertEquals(deleteTest_to, DiffUtils.patch(deleteTest_from, patch)); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void testPatch_Change() { + Patch patch = DiffUtils.diff(changeTest_from, changeTest_to); + try { + assertEquals(changeTest_to, DiffUtils.patch(changeTest_from, patch)); + } catch (Exception e) { + e.printStackTrace(); + } + } }