/*
* (c) Copyright 2010-2011 AgileBirds
*
* This file is part of OpenFlexo.
*
* OpenFlexo 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.
*
* OpenFlexo 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 OpenFlexo. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.openflexo.diff;
import java.io.File;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Vector;
/**
* Java wrapper above diff
*
* @author sylvain
*
*/
public class ComputeDiff {
public static class Base {
protected Base(String[] a, String[] b) {
file0 = a;
file1 = b;
}
/**
* Set to ignore certain kinds of lines when printing an edit script. For example, ignoring blank lines or comments.
*/
protected UnaryPredicate ignore = null;
/**
* Set to the lines of the files being compared.
*/
private String[] file0, file1;
/**
* Divide SCRIPT into pieces by calling HUNKFUN and print each piece with PRINTFUN. Both functions take one arg, an edit script.
*
* PRINTFUN takes a subscript which belongs together (with a null link at the end) and prints it.
*/
protected DiffReport buildDiffReport(Diff.change script, DiffSource source1, DiffSource source2) {
DiffReport returned = new DiffReport(source1, source2);
Diff.change next = script;
while (next != null) {
Diff.change t, end;
/* Find a set of changes that belong together. */
t = next;
end = hunkfun(next);
/* Disconnect them from the rest of the changes,
making them a hunk, and remember the rest for next iteration. */
next = end.link;
end.link = null;
addChange(returned, t);
/* Reconnect the script so it will all be freed properly. */
end.link = next;
}
// outfile.flush();
return returned;
}
/**
* Called with the tail of the script and returns the last link that belongs together with the start of the tail.
*/
private Diff.change hunkfun(Diff.change hunk) {
return hunk;
}
private int first0, last0, first1, last1, deletes, inserts;
// protected PrintWriter outfile;
// protected StringBuffer diffResult;
/**
* Look at a hunk of edit script and report the range of lines in each file that it applies to. HUNK is the start of the hunk, which
* is a chain of `struct change'. The first and last line numbers of file 0 are stored in *FIRST0 and *LAST0, and likewise for file
* 1 in *FIRST1 and *LAST1. Note that these are internal line numbers that count from 0.
*
* If no lines from file 0 are deleted, then FIRST0 is LAST0+1.
*
* Also set *DELETES nonzero if any lines of file 0 are deleted and set *INSERTS nonzero if any lines of file 1 are inserted. If
* only ignorable lines are inserted or deleted, both are set to 0.
*/
private void analyze_hunk(Diff.change hunk) {
int f0, l0 = 0, f1, l1 = 0, show_from = 0, show_to = 0;
int i;
Diff.change next;
boolean nontrivial = ignore == null;
show_from = show_to = 0;
f0 = hunk.line0;
f1 = hunk.line1;
for (next = hunk; next != null; next = next.link) {
l0 = next.line0 + next.deleted - 1;
l1 = next.line1 + next.inserted - 1;
show_from += next.deleted;
show_to += next.inserted;
for (i = next.line0; i <= l0 && !nontrivial; i++) {
if (!ignore.execute(file0[i])) {
nontrivial = true;
}
}
for (i = next.line1; i <= l1 && !nontrivial; i++) {
if (!ignore.execute(file1[i])) {
nontrivial = true;
}
}
}
first0 = f0;
last0 = l0;
first1 = f1;
last1 = l1;
/* If all inserted or deleted lines are ignorable,
tell the caller to ignore this hunk. */
if (!nontrivial) {
show_from = show_to = 0;
}
deletes = show_from;
inserts = show_to;
}
/**
* Print a hunk of a normal diff. This is a contiguous portion of a complete edit script, describing changes in consecutive lines.
*/
private void addChange(DiffReport report, Diff.change hunk) {
DiffChange change;
/* Determine range of line numbers involved in each file. */
analyze_hunk(hunk);
if (deletes == 0 && inserts == 0) {
return;
}
if (inserts == 0) {
change = new RemovalChange();
} else if (deletes == 0) {
change = new AdditionChange();
} else {
change = new ModificationChange();
}
change.first0 = first0;
change.first1 = first1;
change.last0 = last0;
change.last1 = last1;
/* Print the lines that the first file has. */
if (deletes != 0) {
StringBuffer removedString = new StringBuffer();
for (int i = first0; i <= last0; i++) {
removedString.append(file0[i] + "\n");
}
change.removedString = removedString.toString();
}
/* Print the lines that the second file has. */
if (inserts != 0) {
StringBuffer addedString = new StringBuffer();
for (int i = first1; i <= last1; i++) {
addedString.append(file1[i] + "\n");
}
change.addedString = addedString.toString();
}
report.addChange(change);
}
}
public static DiffReport diff(DiffSource source, DiffSource anOtherSource) {
return computeDiff(source, anOtherSource);
}
public static DiffReport diff(File aFile, String aString) throws IOException {
DiffSource source0 = new DiffSource(aFile);
DiffSource source1 = new DiffSource(aString);
return computeDiff(source0, source1);
}
public static DiffReport diff(String aString, String anOtherString) {
DiffSource source0 = new DiffSource(aString);
DiffSource source1 = new DiffSource(anOtherString);
return computeDiff(source0, source1);
}
public static DiffReport diff(File aFile, File anOtherFile) throws IOException {
DiffSource source0 = new DiffSource(aFile);
DiffSource source1 = new DiffSource(anOtherFile);
return computeDiff(source0, source1);
}
public static DiffReport diff(File aFile, String aString, int ignoredCols) throws IOException {
DiffSource source0 = new DiffSource(aFile, ignoredCols);
DiffSource source1 = new DiffSource(aString, ignoredCols);
return computeDiff(source0, source1);
}
public static DiffReport diff(String aString, String anOtherString, int ignoredCols) throws IOException {
DiffSource source0 = new DiffSource(aString, ignoredCols);
DiffSource source1 = new DiffSource(anOtherString, ignoredCols);
return computeDiff(source0, source1);
}
public static DiffReport diff(File aFile, File anOtherFile, int ignoredCols) throws IOException {
DiffSource source0 = new DiffSource(aFile, ignoredCols);
DiffSource source1 = new DiffSource(anOtherFile, ignoredCols);
return computeDiff(source0, source1);
}
public static DiffReport diff(DiffSource source, DiffSource anOtherSource, DelimitingMethod method) {
return computeDiff(source, anOtherSource);
}
public static DiffReport diff(File aFile, String aString, DelimitingMethod method) throws IOException {
DiffSource source0 = new DiffSource(aFile, method);
DiffSource source1 = new DiffSource(aString, method);
return computeDiff(source0, source1);
}
public static DiffReport diff(String aString, String anOtherString, DelimitingMethod method) {
DiffSource source0 = new DiffSource(aString, method);
DiffSource source1 = new DiffSource(anOtherString, method);
return computeDiff(source0, source1);
}
public static DiffReport diff(File aFile, File anOtherFile, DelimitingMethod method) throws IOException {
DiffSource source0 = new DiffSource(aFile, method);
DiffSource source1 = new DiffSource(anOtherFile, method);
return computeDiff(source0, source1);
}
public static DiffReport diff(File aFile, String aString, DelimitingMethod method, int ignoredCols) throws IOException {
DiffSource source0 = new DiffSource(aFile, method, ignoredCols);
DiffSource source1 = new DiffSource(aString, method, ignoredCols);
return computeDiff(source0, source1);
}
public static DiffReport diff(String aString, String anOtherString, DelimitingMethod method, int ignoredCols) throws IOException {
DiffSource source0 = new DiffSource(aString, method, ignoredCols);
DiffSource source1 = new DiffSource(anOtherString, method, ignoredCols);
return computeDiff(source0, source1);
}
public static DiffReport diff(File aFile, File anOtherFile, DelimitingMethod method, int ignoredCols) throws IOException {
DiffSource source0 = new DiffSource(aFile, method, ignoredCols);
DiffSource source1 = new DiffSource(anOtherFile, method, ignoredCols);
return computeDiff(source0, source1);
}
private static DiffReport computeDiff(DiffSource source0, DiffSource source1) {
Diff d = new Diff(source0.getSignificativeTokens(), source1.getSignificativeTokens());
Diff.change script = d.diff_2(false);
if (script == null) {
// No differences
return new DiffReport(source0, source1);
} else {
Base p = null;
p = new Base(source0.getSignificativeTokens(), source1.getSignificativeTokens());
return p.buildDiffReport(script, source0, source1);
}
}
public static class DiffReport {
private Vector<DiffChange> changes;
private DiffSource source0;
private DiffSource source1;
public void print(boolean isLeftOriented) {
Enumeration<DiffChange> en = getChanges().elements();
while (en.hasMoreElements()) {
DiffChange change = en.nextElement();
System.out.println(change.toNiceString(isLeftOriented));
System.out.println("\tADDED:" + change.addedString);
System.out.println("\tREMOVED:" + change.removedString);
}
}
protected DiffReport(DiffSource source0, DiffSource source1) {
changes = new Vector<DiffChange>();
this.source0 = source0;
this.source1 = source1;
}
protected void addChange(DiffChange change) {
changes.add(change);
}
public Vector<DiffChange> getChanges() {
return changes;
}
@Override
public String toString() {
if (changes.size() == 0) {
return "DiffReport: no changes";
} else {
StringBuffer returned = new StringBuffer();
for (DiffChange c : getChanges()) {
returned.append(c + "\n");
}
return returned.toString();
}
}
public String[] getInput0() {
return source0.getSignificativeTokens();
}
public String[] getInput1() {
return source1.getSignificativeTokens();
}
public DiffChange changeBefore(DiffChange change) {
int index = changes.indexOf(change);
if (index >= 1) {
return changes.get(index - 1);
}
return null;
}
public int getAdditionChangeCount() {
int returned = 0;
for (DiffChange change : getChanges()) {
if (change instanceof AdditionChange) {
returned++;
}
}
return returned;
}
public int getRemovalChangeCount() {
int returned = 0;
for (DiffChange change : getChanges()) {
if (change instanceof RemovalChange) {
returned++;
}
}
return returned;
}
public int getModificationChangeCount() {
int returned = 0;
for (DiffChange change : getChanges()) {
if (change instanceof ModificationChange) {
returned++;
}
}
return returned;
}
}
public abstract static class DiffChange {
protected int first0, last0, first1, last1;
protected String addedString;
protected String removedString;
public int getFirst0() {
return first0;
}
public int getFirst1() {
return first1;
}
public int getLast0() {
return last0;
}
public int getLast1() {
return last1;
}
public abstract String toNiceString(boolean isLeftOriented);
public void setFirst0(int first0) {
this.first0 = first0;
}
public void setFirst1(int first1) {
this.first1 = first1;
}
public void setLast0(int last0) {
this.last0 = last0;
}
public void setLast1(int last1) {
this.last1 = last1;
}
}
public static class AdditionChange extends DiffChange {
@Override
public String toString() {
return ">>>>>> ADDITION " + first0 + "," + last0 + " " + first1 + "," + last1/*+" of TEXT:\n"+addedString*/;
}
@Override
public String toNiceString(boolean isLeftOriented) {
return (isLeftOriented ? "REMOVAL" : "ADDITION") + " " + first0 + "," + last0 + " " + first1 + "," + last1;
}
}
public static class RemovalChange extends DiffChange {
@Override
public String toString() {
return ">>>>>> REMOVAL " + first0 + "," + last0 + " " + first1 + "," + last1/*+" of TEXT:\n"+removedString*/;
}
@Override
public String toNiceString(boolean isLeftOriented) {
return (isLeftOriented ? "ADDITION" : "REMOVAL") + " " + first0 + "," + last0 + " " + first1 + "," + last1;
}
}
public static class ModificationChange extends DiffChange {
@Override
public String toString() {
return ">>>>>> MODIFICATION " + first0 + "," + last0 + " " + first1 + "," + last1/*+" of TEXT:\n"+removedString+"BY:\n"+addedString*/;
}
@Override
public String toNiceString(boolean isLeftOriented) {
return "MODIFICATION " + first0 + "," + last0 + " " + first1 + "," + last1;
}
public String toNiceStringDebugVersion(boolean isLeftOriented) {
StringBuffer removedStringBuffer = new StringBuffer();
removedStringBuffer.append("[");
for (int i = 0; i < removedString.length(); i++) {
removedStringBuffer.append(" " + (int) removedString.charAt(i));
}
removedStringBuffer.append("]" + "(" + removedString.length() + ")");
StringBuffer addedStringBuffer = new StringBuffer();
addedStringBuffer.append("[");
for (int i = 0; i < addedString.length(); i++) {
addedStringBuffer.append(" " + (int) addedString.charAt(i));
}
addedStringBuffer.append("]" + "(" + addedString.length() + ")");
return "MODIFICATION " + first0 + "," + last0 + " " + first1 + "," + last1 + " of TEXT:\n" + removedStringBuffer + "BY:\n"
+ addedStringBuffer;
}
}
}