From d131d9481736bb64122c3151260d16c0cfb10fd5 Mon Sep 17 00:00:00 2001 From: "dm.naumenko@gmail.com" Date: Sun, 4 Oct 2009 08:49:25 +0000 Subject: [PATCH] New version which was lost after removing repo. --- src/difflib/ChangeDelta.java | 54 +++- src/difflib/Chunk.java | 41 +++ src/difflib/DeleteDelta.java | 44 ++- src/difflib/Delta.java | 77 ++++- src/difflib/DiffAlgorithm.java | 18 ++ src/difflib/DiffException.java | 37 +++ src/difflib/DiffRow.java | 72 ++++- src/difflib/DiffRowGenerator.java | 405 ++++++++++++++++++++++++ src/difflib/DiffUtils.java | 63 ++-- src/difflib/InsertDelta.java | 48 ++- src/difflib/Patch.java | 77 ++++- src/difflib/PatchFailedException.java | 36 +++ src/difflib/StringUtills.java | 53 ++++ src/difflib/myers/package.html | 80 ++--- src/difflib/package.html | 70 ++++ test/testcase/diffutills/PatchTest.java | 24 +- 16 files changed, 1101 insertions(+), 98 deletions(-) create mode 100644 src/difflib/DiffException.java create mode 100644 src/difflib/DiffRowGenerator.java create mode 100644 src/difflib/PatchFailedException.java create mode 100644 src/difflib/StringUtills.java create mode 100644 src/difflib/package.html diff --git a/src/difflib/ChangeDelta.java b/src/difflib/ChangeDelta.java index c9a2e99..828e43f 100644 --- a/src/difflib/ChangeDelta.java +++ b/src/difflib/ChangeDelta.java @@ -1,3 +1,21 @@ +/* + 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.List; @@ -18,12 +36,14 @@ public class ChangeDelta extends Delta { /** * {@inheritDoc} + * @throws PatchFailedException */ @Override - public void applyTo(List target) { + public void applyTo(List target) throws PatchFailedException { + verify(target); int position = getOriginal().getPosition(); - int originalSize = getOriginal().getSize(); - for (int i = 0; i < originalSize; i++) { + int size = getOriginal().getSize(); + for (int i = 0; i < size; i++) { target.remove(position); } int i = 0; @@ -33,4 +53,32 @@ public class ChangeDelta extends Delta { } } + /** + * {@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"); + } + } + } diff --git a/src/difflib/Chunk.java b/src/difflib/Chunk.java index be2d217..3a389de 100644 --- a/src/difflib/Chunk.java +++ b/src/difflib/Chunk.java @@ -1,3 +1,21 @@ +/* + 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.*; @@ -46,6 +64,22 @@ public class Chunk { this.lines = Arrays.asList(lines); } + /** + * Verifies that this chunk's saved text matches the corresponding + * text in the given sequence. + * @param target the sequence to verify against. + */ + public void verify(List target) throws PatchFailedException{ + if (last() > target.size()) { + throw new PatchFailedException("Incorrect Chunk: the position of chunk > 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"); + } + } + } + /** * @return the start position of chunk in the text */ @@ -84,6 +118,13 @@ public class Chunk { public void setLines(List lines) { this.lines = lines; } + + /** + * Returns the index of the last line of the chunk. + */ + public int last(){ + return getPosition() + getSize() - 1; + } /* (non-Javadoc) * @see java.lang.Object#hashCode() diff --git a/src/difflib/DeleteDelta.java b/src/difflib/DeleteDelta.java index 334cfe6..6a0ee71 100644 --- a/src/difflib/DeleteDelta.java +++ b/src/difflib/DeleteDelta.java @@ -1,3 +1,21 @@ +/* + 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.List; @@ -18,9 +36,11 @@ public class DeleteDelta extends Delta { /** * {@inheritDoc} + * @throws PatchFailedException */ @Override - public void applyTo(List target) { + public void applyTo(List target) throws PatchFailedException { + verify(target); int position = getOriginal().getPosition(); int size = getOriginal().getSize(); for (int i = 0; i < size; i++) { @@ -28,4 +48,26 @@ public class DeleteDelta extends Delta { } } + /** + * {@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 c6731cb..58242e7 100644 --- a/src/difflib/Delta.java +++ b/src/difflib/Delta.java @@ -1,3 +1,21 @@ +/* + 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.*; @@ -22,12 +40,27 @@ public abstract class Delta { 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 + * @param target the given target + * @throws PatchFailedException */ - public abstract void applyTo(List target); + 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 @@ -54,4 +87,44 @@ public abstract class Delta { 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 f49a804..1a71695 100644 --- a/src/difflib/DiffAlgorithm.java +++ b/src/difflib/DiffAlgorithm.java @@ -1,3 +1,21 @@ +/* + 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.*; diff --git a/src/difflib/DiffException.java b/src/difflib/DiffException.java new file mode 100644 index 0000000..5bcf9da --- /dev/null +++ b/src/difflib/DiffException.java @@ -0,0 +1,37 @@ +/* + 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; + +/** + * 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() { + } + + public DiffException(String msg) { + super(msg); + } +} + diff --git a/src/difflib/DiffRow.java b/src/difflib/DiffRow.java index a8d3aeb..444980c 100644 --- a/src/difflib/DiffRow.java +++ b/src/difflib/DiffRow.java @@ -1,3 +1,21 @@ +/* + 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; /** @@ -11,8 +29,14 @@ public class DiffRow { 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 + INSERT, DELETE, CHANGE, EQUAL } /** @@ -53,4 +77,50 @@ public class DiffRow { 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 new file mode 100644 index 0000000..da79dd4 --- /dev/null +++ b/src/difflib/DiffRowGenerator.java @@ -0,0 +1,405 @@ +/* + 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.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import difflib.DiffRow.Tag; + +/** + * This class for generating DiffRows for side-by-sidy view. + * You can customize the way of generating. For example, show inline diffs on not, ignoring + * white spaces or/and blank lines and so on. All parameters for generating are optional. If you do + * not specify them, the class will use the default values. + * + * These values are: + * showInlineDiffs = false; + * ignoreWhiteSpaces = true; + * ignoreBlankLines = true; + * ... + * + * For instantiating the DiffRowGenerator you should use the its builder. Like in example + * + * DiffRowGenerator generator = new DiffRowGenerator.Builder().showInlineDiffs(true). + * ignoreWhiteSpaces(true).columnWidth(100).build(); + * + * + * @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(); + } +} diff --git a/src/difflib/DiffUtils.java b/src/difflib/DiffUtils.java index 0302c7c..479dca0 100644 --- a/src/difflib/DiffUtils.java +++ b/src/difflib/DiffUtils.java @@ -1,3 +1,21 @@ +/* + 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.*; @@ -10,6 +28,7 @@ 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(); @@ -45,8 +64,9 @@ public class DiffUtils { * @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) { + public static List patch(List original, Patch patch) throws PatchFailedException { return patch.applyTo(original); } @@ -57,15 +77,15 @@ public class DiffUtils { * @param patch the given patch * @return the original text */ - public static String unpatch(String revised, Patch patch) { - // TODO: Not-implemented - return null; + 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; @@ -104,15 +124,25 @@ public class DiffUtils { 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)); - old_n = Integer.parseInt(m.group(2)); - new_ln = Integer.parseInt(m.group(3)); - new_n = Integer.parseInt(m.group(4)); + + if (old_ln == 0) { + old_ln += 1; + } + if (new_ln == 0) { + new_ln += 1; + } } else { - tag = line.substring(0, 1); - rest = line.substring(1); - if (tag.equals(" ") || tag.equals("+") || tag.equals("-")) { - rawChunk.add(new Object[] {tag, rest}); + 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}); + } } } } @@ -141,15 +171,4 @@ public class DiffUtils { return patch; } - /** - * Get the DiffRows describing the difference between original and revised texts. Useful for - * displaying side-by-side diff. Computes the Patch before generate the diffs. - * - * @param original the original text - * @param revised the revised text - * @return the DiffRows between original and revised texts - */ - public static List getDiffRows(List original, List revised) { - return null; - } } diff --git a/src/difflib/InsertDelta.java b/src/difflib/InsertDelta.java index 3c30475..4785144 100644 --- a/src/difflib/InsertDelta.java +++ b/src/difflib/InsertDelta.java @@ -1,3 +1,21 @@ +/* + 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.List; @@ -18,9 +36,11 @@ public class InsertDelta extends Delta { /** * {@inheritDoc} + * @throws PatchFailedException */ @Override - public void applyTo(List target) { + 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++) { @@ -28,4 +48,30 @@ public class InsertDelta extends Delta { } } + /** + * {@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 379b8d7..d24ae00 100644 --- a/src/difflib/Patch.java +++ b/src/difflib/Patch.java @@ -1,3 +1,21 @@ +/* + 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.*; @@ -9,21 +27,46 @@ import java.util.*; */ public class Patch { private List deltas = new LinkedList(); + private boolean isSorted = false; - public List applyTo(List target) { + /** + * 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); - for (Delta delta: deltas) { - delta.applyTo(result); - } + 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()); + 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; } /** @@ -31,12 +74,38 @@ public class Patch { */ 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 new file mode 100644 index 0000000..c30fc0f --- /dev/null +++ b/src/difflib/PatchFailedException.java @@ -0,0 +1,36 @@ +/* + 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; + +/** + * Thrown whenever a delta cannot be applied as a patch to a given text. + * + * @author Juanco Anez + */ +public class PatchFailedException extends DiffException { + + private static final long serialVersionUID = 1L; + + public PatchFailedException() { + } + + public PatchFailedException(String msg) { + super(msg); + } +} \ No newline at end of file diff --git a/src/difflib/StringUtills.java b/src/difflib/StringUtills.java new file mode 100644 index 0000000..8cd5bd3 --- /dev/null +++ b/src/difflib/StringUtills.java @@ -0,0 +1,53 @@ +package difflib; + +import java.util.*; + +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; + } + + public static String wrapText(String line, int columnWidth) { + if (line.length() > columnWidth) { + return line.subSequence(0, columnWidth) + "
" + line.substring(columnWidth); + } + return line; + } +} diff --git a/src/difflib/myers/package.html b/src/difflib/myers/package.html index eb148c7..3ccec66 100644 --- a/src/difflib/myers/package.html +++ b/src/difflib/myers/package.html @@ -1,58 +1,23 @@ @@ -61,8 +26,8 @@ -

- The {@link org.apache.maven.jrcs.diff.myers diff.myers} package +

+ The {@link difflib.myers diff.myers} package implements Gene Myers' differencing algorithm. @@ -71,10 +36,7 @@ Myer's algorithm produces optimum results (minimum diffs), but consumes considerably more memory than SimpleDiff, so its not suitable for very large files. -

+

@author Juanco Anez -@version $Id: package.html 60 2003-05-10 18:56:10Z juanco $ -@see Diff -@see org.apache.maven.jrcs.rcs.Archive diff --git a/src/difflib/package.html b/src/difflib/package.html new file mode 100644 index 0000000..0f82244 --- /dev/null +++ b/src/difflib/package.html @@ -0,0 +1,70 @@ + + + + + + + + +

+ The {@link difflib difflib} package + implements general operation with diff files. +

+@author Dmitry Naumenko + + diff --git a/test/testcase/diffutills/PatchTest.java b/test/testcase/diffutills/PatchTest.java index 2f1dd93..6c9506b 100644 --- a/test/testcase/diffutills/PatchTest.java +++ b/test/testcase/diffutills/PatchTest.java @@ -1,9 +1,11 @@ package testcase.diffutills; -import java.util.*; +import java.util.Arrays; +import java.util.List; import junit.framework.TestCase; -import difflib.*; +import difflib.DiffUtils; +import difflib.Patch; public class PatchTest extends TestCase { private List changeTest_from = Arrays.asList("aaa", "bbb", "ccc", "ddd"); @@ -17,16 +19,28 @@ public class PatchTest extends TestCase { public void testPatch_Insert() { Patch patch = DiffUtils.diff(insertTest_from, insertTest_to); - assertEquals(insertTest_to, DiffUtils.patch(insertTest_from, patch)); + 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); - assertEquals(deleteTest_to, DiffUtils.patch(deleteTest_from, patch)); + 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); - assertEquals(changeTest_to, DiffUtils.patch(changeTest_from, patch)); + try { + assertEquals(changeTest_to, DiffUtils.patch(changeTest_from, patch)); + } catch (Exception e) { + e.printStackTrace(); + } } }