SONAR-3072 Add sonar-diff library

This commit is contained in:
Evgeny Mandrikov 2012-03-14 08:52:07 +04:00
parent 6a907c8f5d
commit abb8bac7ab
20 changed files with 1095 additions and 1 deletions

View File

@ -21,6 +21,7 @@
<module>sonar-colorizer</module>
<module>sonar-core</module>
<module>sonar-deprecated</module>
<module>sonar-diff</module>
<module>sonar-duplications</module>
<module>sonar-graph</module>
<module>sonar-gwt-api</module>
@ -1086,7 +1087,7 @@
</plugins>
</build>
</profile>
<profile>
<id>javadoc</id>
<build>

35
sonar-diff/pom.xml Normal file
View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.codehaus.sonar</groupId>
<artifactId>sonar</artifactId>
<version>2.15-SNAPSHOT</version>
</parent>
<artifactId>sonar-diff</artifactId>
<name>Sonar :: Diff</name>
<description>Detect changes in files</description>
<dependencies>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,95 @@
/*
* Sonar, open source software quality management tool.
* Copyright (C) 2008-2012 SonarSource
* mailto:contact AT sonarsource DOT com
*
* Sonar is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* Sonar 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with Sonar; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
*/
package org.sonar.diff;
import com.google.common.io.Files;
import java.io.File;
import java.util.BitSet;
import java.util.List;
public final class CodeChurn {
public static void main(String[] args) {
if (args.length != 2) {
System.err.println("2 arguments required");
System.exit(1);
}
try {
Text a = new Text(Files.toByteArray(new File(args[0])));
Text b = new Text(Files.toByteArray(new File(args[1])));
CodeChurn r = new CodeChurn(a, b, TextComparator.IGNORE_WHITESPACE);
System.out.println("Deleted: " + r.getDeleted());
System.out.println("Added: " + r.getAdded());
for (Edit edit : r.getDiff()) {
System.out.println(edit);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public CodeChurn(Text a, Text b, TextComparator cmp) {
diff = new DiffAlgorithm().diff(a, b, cmp);
int added = 0;
int deleted = 0;
BitSet remains = new BitSet(a.length());
for (Edit edit : diff) {
switch (edit.getType()) {
case INSERT:
added += edit.endB - edit.beginB + 1;
break;
case MOVE:
for (int i = edit.beginA; i <= edit.endA; i++) {
remains.set(i);
}
break;
default:
throw new IllegalStateException();
}
}
for (int i = 0; i < a.length(); i++) {
if (!remains.get(i)) {
deleted++;
}
}
this.added = added;
this.deleted = deleted;
}
private final List<Edit> diff;
private final int added;
private final int deleted;
public int getAdded() {
return added;
}
public int getDeleted() {
return deleted;
}
public List<Edit> getDiff() {
return diff;
}
}

View File

@ -0,0 +1,112 @@
/*
* Sonar, open source software quality management tool.
* Copyright (C) 2008-2012 SonarSource
* mailto:contact AT sonarsource DOT com
*
* Sonar is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* Sonar 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with Sonar; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
*/
package org.sonar.diff;
import com.google.common.collect.Lists;
import com.google.common.io.Files;
import java.io.File;
import java.util.List;
/**
* Diff algorithm, based on
* "The String-to-String Correction Problem with Block Moves", by Waller F. Tichy.
*/
public class DiffAlgorithm {
public static void main(String[] args) {
if (args.length != 2) {
System.err.println("2 arguments required");
System.exit(1);
}
try {
Text a = new Text(Files.toByteArray(new File(args[0])));
Text b = new Text(Files.toByteArray(new File(args[1])));
List<Edit> r = new DiffAlgorithm().diff(a, b, TextComparator.IGNORE_WHITESPACE);
for (Edit edit : r) {
System.out.println(edit);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public List<Edit> diff(Text a, Text b, TextComparator cmp) {
return diff(wrap(a, cmp), wrap(b, cmp), cmp);
}
private static HashedSequence<Text> wrap(Text seq, TextComparator cmp) {
int size = seq.length();
int[] hashes = new int[size];
for (int i = 0; i < size; i++) {
hashes[i] = cmp.hash(seq, i);
}
return new HashedSequence<Text>(seq, hashes);
}
private List<Edit> diff(HashedSequence<Text> s, HashedSequence<Text> t, TextComparator cmp) {
HashedSequenceComparator<Text> comparator = new HashedSequenceComparator<Text>(cmp);
Edit lastEdit = null;
List<Edit> r = Lists.newArrayList();
int m = s.length();
int n = t.length();
int q = 0;
while (q < n) {
// find p and l such that (p,q,l) is a maximal block move
int l = 0;
int p = 0;
int pCur = 0;
while ((pCur + l < m) && (q + l < n)) {
int lCur = 0;
while ((pCur + lCur < m) && (q + lCur < n)
&& (comparator.equals(s, pCur + lCur, t, q + lCur))) {
lCur++;
}
if (lCur > l) {
l = lCur;
p = pCur;
}
pCur++;
}
if (l > 0) {
Edit edit = new Edit(Edit.Type.MOVE, p, p + l - 1, q, q + l - 1);
r.add(edit);
lastEdit = edit;
q += l;
} else {
if (lastEdit == null || lastEdit.getType() != Edit.Type.INSERT) {
Edit edit = new Edit(Edit.Type.INSERT, -1, -1, q, q);
r.add(edit);
lastEdit = edit;
} else {
lastEdit.endB++;
}
q++;
}
}
return r;
}
}

View File

@ -0,0 +1,73 @@
/*
* Sonar, open source software quality management tool.
* Copyright (C) 2008-2012 SonarSource
* mailto:contact AT sonarsource DOT com
*
* Sonar is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* Sonar 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with Sonar; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
*/
package org.sonar.diff;
import com.google.common.base.Objects;
public class Edit {
public static enum Type {
INSERT,
MOVE
}
int beginA;
int endA;
int beginB;
int endB;
final Type type;
public Edit(Type type, int beginA, int endA, int beginB, int endB) {
this.beginA = beginA;
this.endA = endA;
this.beginB = beginB;
this.endB = endB;
this.type = type;
}
public Type getType() {
return type;
}
@Override
public boolean equals(Object obj) {
if (obj == null || !(obj instanceof Edit)) {
return false;
}
Edit e = (Edit) obj;
return type == e.type
&& beginA == e.beginA
&& endA == e.endA
&& beginB == e.beginB
&& endB == e.endB;
}
@Override
public String toString() {
return Objects.toStringHelper(this)
.add("type", type)
.add("beginA", beginA)
.add("endA", endA)
.add("beginB", beginB)
.add("endB", endB)
.toString();
}
}

View File

@ -0,0 +1,39 @@
/*
* Sonar, open source software quality management tool.
* Copyright (C) 2008-2012 SonarSource
* mailto:contact AT sonarsource DOT com
*
* Sonar is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* Sonar 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with Sonar; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
*/
package org.sonar.diff;
/**
* Wraps a {@link Sequence} to assign hash codes to elements.
*/
public class HashedSequence<S extends Sequence> implements Sequence {
final S base;
final int[] hashes;
public HashedSequence(S base, int[] hashes) {
this.base = base;
this.hashes = hashes;
}
public int length() {
return base.length();
}
}

View File

@ -0,0 +1,43 @@
/*
* Sonar, open source software quality management tool.
* Copyright (C) 2008-2012 SonarSource
* mailto:contact AT sonarsource DOT com
*
* Sonar is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* Sonar 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with Sonar; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
*/
package org.sonar.diff;
/**
* Wrap another {@link SequenceComparator} for use with {@link HashedSequence}.
*/
public class HashedSequenceComparator<S extends Sequence> extends SequenceComparator<HashedSequence<S>> {
private final SequenceComparator<? super S> cmp;
public HashedSequenceComparator(SequenceComparator<? super S> cmp) {
this.cmp = cmp;
}
@Override
public boolean equals(HashedSequence<S> a, int ai, HashedSequence<S> b, int bi) {
return a.hashes[ai] == b.hashes[bi] && cmp.equals(a.base, ai, b.base, bi);
}
@Override
public int hash(HashedSequence<S> seq, int i) {
return seq.hashes[i];
}
}

View File

@ -0,0 +1,32 @@
/*
* Sonar, open source software quality management tool.
* Copyright (C) 2008-2012 SonarSource
* mailto:contact AT sonarsource DOT com
*
* Sonar is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* Sonar 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with Sonar; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
*/
package org.sonar.diff;
/**
* Arbitrary sequence of elements.
*/
public interface Sequence {
/**
* @return total number of items in the sequence
*/
int length();
}

View File

@ -0,0 +1,42 @@
/*
* Sonar, open source software quality management tool.
* Copyright (C) 2008-2012 SonarSource
* mailto:contact AT sonarsource DOT com
*
* Sonar is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* Sonar 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with Sonar; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
*/
package org.sonar.diff;
/**
* Equivalence function for a {@link Sequence}.
*/
public abstract class SequenceComparator<S extends Sequence> {
/**
* Compare two items to determine if they are equivalent.
*/
public abstract boolean equals(S a, int ai, S b, int bi);
/**
* Get a hash value for an item in a sequence.
*
* If two items are equal according to this comparator's
* {@link #equals(Sequence, int, Sequence, int)} method,
* then this hash method must produce the same integer result for both items.
* However not required to have different hash values for different items.
*/
public abstract int hash(S seq, int i);
}

View File

@ -0,0 +1,87 @@
/*
* Sonar, open source software quality management tool.
* Copyright (C) 2008-2012 SonarSource
* mailto:contact AT sonarsource DOT com
*
* Sonar is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* Sonar 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with Sonar; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
*/
package org.sonar.diff;
import com.google.common.collect.Lists;
import java.util.List;
/**
* Text is a {@link Sequence} of lines.
*/
public class Text implements Sequence {
final byte[] content;
/**
* Map of line number to starting position within {@link #content}.
*/
final List<Integer> lines;
public Text(byte[] bytes) {
this.content = bytes;
lines = lineMap(content, 0, content.length);
}
public int length() {
return lines.size() - 2;
}
/**
* Get the text for a single line.
*/
public String getString(int line) {
int s = getStart(line);
int e = getEnd(line);
return new String(content, s, e - s);
}
private int getStart(final int line) {
return lines.get(line + 1);
}
private int getEnd(final int line) {
return lines.get(line + 2);
}
private static List<Integer> lineMap(final byte[] buf, int ptr, int end) {
List<Integer> lines = Lists.newArrayList();
lines.add(Integer.MIN_VALUE);
for (; ptr < end; ptr = nextLF(buf, ptr)) {
lines.add(ptr);
}
lines.add(end);
return lines;
}
private static final int nextLF(final byte[] b, int ptr) {
return next(b, ptr, '\n');
}
private static final int next(final byte[] b, int ptr, final char chrA) {
final int sz = b.length;
while (ptr < sz) {
if (b[ptr++] == chrA)
return ptr;
}
return ptr;
}
}

View File

@ -0,0 +1,141 @@
/*
* Sonar, open source software quality management tool.
* Copyright (C) 2008-2012 SonarSource
* mailto:contact AT sonarsource DOT com
*
* Sonar is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* Sonar 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with Sonar; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
*/
package org.sonar.diff;
/**
* Equivalence function for {@link Text}.
*/
public abstract class TextComparator extends SequenceComparator<Text> {
public static final TextComparator DEFAULT = new TextComparator() {
@Override
public boolean equals(Text a, int ai, Text b, int bi) {
ai++;
bi++;
int as = a.lines.get(ai);
int bs = b.lines.get(bi);
int ae = a.lines.get(ai + 1);
int be = b.lines.get(bi + 1);
if (ae - as != be - bs) {
return false;
}
while (as < ae) {
if (a.content[as++] != b.content[bs++]) {
return false;
}
}
return true;
}
@Override
protected int hashRegion(final byte[] raw, int start, final int end) {
int hash = 5381;
for (; start < end; start++) {
hash = ((hash << 5) + hash) + (raw[start] & 0xff);
}
return hash;
}
};
/**
* Ignores all whitespace.
*/
public static final TextComparator IGNORE_WHITESPACE = new TextComparator() {
@Override
public boolean equals(Text a, int ai, Text b, int bi) {
ai++;
bi++;
int as = a.lines.get(ai);
int bs = b.lines.get(bi);
int ae = a.lines.get(ai + 1);
int be = b.lines.get(bi + 1);
ae = trimTrailingWhitespace(a.content, as, ae);
be = trimTrailingWhitespace(b.content, bs, be);
while ((as < ae) && (bs < be)) {
byte ac = a.content[as];
byte bc = b.content[bs];
while ((as < ae - 1) && (isWhitespace(ac))) {
as++;
ac = a.content[as];
}
while ((bs < be - 1) && (isWhitespace(bc))) {
bs++;
bc = b.content[bs];
}
if (ac != bc) {
return false;
}
as++;
bs++;
}
return (as == ae) && (bs == be);
}
@Override
protected int hashRegion(byte[] raw, int start, int end) {
int hash = 5381;
for (; start < end; start++) {
byte c = raw[start];
if (!isWhitespace(c)) {
hash = ((hash << 5) + hash) + (c & 0xff);
}
}
return hash;
}
};
@Override
public int hash(Text seq, int line) {
final int begin = seq.lines.get(line + 1);
final int end = seq.lines.get(line + 2);
return hashRegion(seq.content, begin, end);
}
protected abstract int hashRegion(final byte[] raw, int start, final int end);
private static final boolean[] WHITESPACE = new boolean[256];
static {
WHITESPACE['\r'] = true;
WHITESPACE['\n'] = true;
WHITESPACE['\t'] = true;
WHITESPACE[' '] = true;
}
public static boolean isWhitespace(byte c) {
return WHITESPACE[c & 0xff];
}
public static int trimTrailingWhitespace(byte[] raw, int start, int end) {
end--;
while (start <= end && isWhitespace(raw[end])) {
end--;
}
return end + 1;
}
public static int trimLeadingWhitespace(byte[] raw, int start, int end) {
while (start < end && isWhitespace(raw[start])) {
start++;
}
return start;
}
}

View File

@ -0,0 +1,25 @@
/*
* Sonar, open source software quality management tool.
* Copyright (C) 2008-2012 SonarSource
* mailto:contact AT sonarsource DOT com
*
* Sonar is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* Sonar 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with Sonar; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
*/
@ParametersAreNonnullByDefault
package org.sonar.diff;
import javax.annotation.ParametersAreNonnullByDefault;

View File

@ -0,0 +1,58 @@
/*
* Sonar, open source software quality management tool.
* Copyright (C) 2008-2012 SonarSource
* mailto:contact AT sonarsource DOT com
*
* Sonar is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* Sonar 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with Sonar; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
*/
package org.sonar.diff;
import com.google.common.io.Resources;
import org.junit.Test;
import java.io.IOException;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
public class DiffFunctionalTest {
@Test
public void example0() throws Exception {
CodeChurn r = diff("example0");
assertThat(r.getAdded(), is(5));
assertThat(r.getDeleted(), is(0));
assertThat(r.getDiff().size(), is(8));
}
@Test
public void example1() throws Exception {
CodeChurn r = diff("example1");
assertThat(r.getAdded(), is(2));
assertThat(r.getDeleted(), is(1));
assertThat(r.getDiff().size(), is(4));
}
private CodeChurn diff(String name) throws IOException {
return diff("examples/" + name + "/v1.java", "examples/" + name + "/v2.java");
}
private CodeChurn diff(String resourceA, String resourceB) throws IOException {
Text a = new Text(Resources.toByteArray(Resources.getResource(resourceA)));
Text b = new Text(Resources.toByteArray(Resources.getResource(resourceB)));
return new CodeChurn(a, b, TextComparator.IGNORE_WHITESPACE);
}
}

View File

@ -0,0 +1,171 @@
/*
* Sonar, open source software quality management tool.
* Copyright (C) 2008-2012 SonarSource
* mailto:contact AT sonarsource DOT com
*
* Sonar is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* Sonar 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with Sonar; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
*/
package org.sonar.diff;
import org.junit.Test;
import org.sonar.diff.Edit.Type;
import java.io.UnsupportedEncodingException;
import java.util.List;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
public class DiffTest {
@Test
public void emptyInputs() {
List<Edit> r = diff(t(""), t(""));
assertThat(r.isEmpty(), is(true));
}
@Test
public void createFile() {
List<Edit> r = diff(t(""), t("AB"));
assertThat(r.size(), is(1));
assertThat(r.get(0), is(new Edit(Type.INSERT, -1, -1, 0, 1)));
}
@Test
public void deleteFile() {
List<Edit> r = diff(t("AB"), t(""));
assertThat(r.size(), is(0));
}
@Test
public void insertMiddle() {
List<Edit> r = diff(t("ac"), t("aBc"));
assertThat(r.size(), is(3));
assertThat(r.get(0), is(new Edit(Type.MOVE, 0, 0, 0, 0)));
assertThat(r.get(1), is(new Edit(Type.INSERT, -1, -1, 1, 1)));
assertThat(r.get(2), is(new Edit(Type.MOVE, 1, 1, 2, 2)));
}
@Test
public void deleteMiddle() {
List<Edit> r = diff(t("aBc"), t("ac"));
assertThat(r.size(), is(2));
assertThat(r.get(0), is(new Edit(Type.MOVE, 0, 0, 0, 0)));
assertThat(r.get(1), is(new Edit(Type.MOVE, 2, 2, 1, 1)));
}
@Test
public void replaceMiddle() {
List<Edit> r = diff(t("aBc"), t("aDc"));
assertThat(r.size(), is(3));
assertThat(r.get(0), is(new Edit(Type.MOVE, 0, 0, 0, 0)));
assertThat(r.get(1), is(new Edit(Type.INSERT, -1, -1, 1, 1)));
assertThat(r.get(2), is(new Edit(Type.MOVE, 2, 2, 2, 2)));
}
@Test
public void insertStart() {
List<Edit> r = diff(t("bc"), t("Abc"));
assertThat(r.size(), is(2));
assertThat(r.get(0), is(new Edit(Type.INSERT, -1, -1, 0, 0)));
assertThat(r.get(1), is(new Edit(Type.MOVE, 0, 1, 1, 2)));
}
@Test
public void deleteStart() {
List<Edit> r = diff(t("Abc"), t("bc"));
assertThat(r.size(), is(1));
assertThat(r.get(0), is(new Edit(Type.MOVE, 1, 2, 0, 1)));
}
@Test
public void insertEnd() {
List<Edit> r = diff(t("ab"), t("abC"));
assertThat(r.size(), is(2));
assertThat(r.get(0), is(new Edit(Type.MOVE, 0, 1, 0, 1)));
assertThat(r.get(1), is(new Edit(Type.INSERT, -1, -1, 2, 2)));
}
@Test
public void deleteEnd() {
List<Edit> r = diff(t("abC"), t("ab"));
assertThat(r.size(), is(1));
assertThat(r.get(0), is(new Edit(Type.MOVE, 0, 1, 0, 1)));
}
/**
* This is important special case, for which other algorithms can not detect movement.
*/
@Test
public void move() {
List<Edit> r = diff(t("Abc"), t("bcA"));
assertThat(r.size(), is(2));
assertThat(r.get(0), is(new Edit(Type.MOVE, 1, 2, 0, 1)));
assertThat(r.get(1), is(new Edit(Type.MOVE, 0, 0, 2, 2)));
}
@Test
public void move2() {
List<Edit> r = diff(t("abcd"), t("abcda"));
assertThat(r.size(), is(2));
assertThat(r.get(0), is(new Edit(Type.MOVE, 0, 3, 0, 3)));
assertThat(r.get(1), is(new Edit(Type.MOVE, 0, 0, 4, 4)));
}
@Test
public void move3() {
List<Edit> r = diff(t("abcd"), t("bcdaa"));
assertThat(r.size(), is(3));
assertThat(r.get(0), is(new Edit(Type.MOVE, 1, 3, 0, 2)));
assertThat(r.get(1), is(new Edit(Type.MOVE, 0, 0, 3, 3)));
assertThat(r.get(2), is(new Edit(Type.MOVE, 0, 0, 4, 4)));
}
@Test
public void severalInserts() {
List<Edit> r = diff(t("ac"), t("aBcD"));
assertThat(r.size(), is(4));
assertThat(r.get(0), is(new Edit(Type.MOVE, 0, 0, 0, 0)));
assertThat(r.get(1), is(new Edit(Type.INSERT, -1, -1, 1, 1)));
assertThat(r.get(2), is(new Edit(Type.MOVE, 1, 1, 2, 2)));
assertThat(r.get(3), is(new Edit(Type.INSERT, -1, -1, 3, 3)));
}
@Test
public void insertSeveralLines() {
List<Edit> r = diff(t("ade"), t("aBCde"));
assertThat(r.size(), is(3));
assertThat(r.get(0), is(new Edit(Type.MOVE, 0, 0, 0, 0)));
assertThat(r.get(1), is(new Edit(Type.INSERT, -1, -1, 1, 2)));
assertThat(r.get(2), is(new Edit(Type.MOVE, 1, 2, 3, 4)));
}
private List<Edit> diff(Text a, Text b) {
return new DiffAlgorithm().diff(a, b, TextComparator.DEFAULT);
}
public static Text t(String text) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < text.length(); i++) {
sb.append(text.charAt(i)).append('\n');
}
try {
return new Text(sb.toString().getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
}

View File

@ -0,0 +1,57 @@
/*
* Sonar, open source software quality management tool.
* Copyright (C) 2008-2012 SonarSource
* mailto:contact AT sonarsource DOT com
*
* Sonar is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* Sonar 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with Sonar; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
*/
package org.sonar.diff;
import org.junit.Test;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
public class TextComparatorTest {
@Test
public void testEqualsWithoutWhitespace() {
TextComparator cmp = TextComparator.DEFAULT;
Text a = new Text("abc\nabc\na bc".getBytes());
Text b = new Text("abc\nabc d\nab c".getBytes());
assertThat("abc == abc", cmp.equals(a, 0, b, 0), is(true));
assertThat("abc != abc d", cmp.equals(a, 1, b, 1), is(false));
assertThat("a bc == ab c", cmp.equals(a, 2, b, 2), is(false));
assertThat(cmp.hash(a, 0), equalTo(cmp.hash(b, 0)));
}
@Test
public void testEqualsWithWhitespace() {
TextComparator cmp = TextComparator.IGNORE_WHITESPACE;
Text a = new Text("abc\nabc\na bc".getBytes());
Text b = new Text("abc\nabc d\nab c".getBytes());
assertThat("abc == abc", cmp.equals(a, 0, b, 0), is(true));
assertThat("abc != abc d", cmp.equals(a, 1, b, 1), is(false));
assertThat("a bc == ab c", cmp.equals(a, 2, b, 2), is(true));
assertThat(cmp.hash(a, 0), equalTo(cmp.hash(b, 0)));
assertThat(cmp.hash(a, 2), equalTo(cmp.hash(b, 2)));
}
}

View File

@ -0,0 +1,41 @@
/*
* Sonar, open source software quality management tool.
* Copyright (C) 2008-2012 SonarSource
* mailto:contact AT sonarsource DOT com
*
* Sonar is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* Sonar 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with Sonar; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
*/
package org.sonar.diff;
import org.junit.Test;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
public class TextTest {
@Test
public void testEmpty() {
Text r = new Text(new byte[0]);
assertThat(r.length(), is(0));
}
@Test
public void testTwoLines() {
Text r = new Text("a\nb".getBytes());
assertThat(r.length(), is(2));
}
}

View File

@ -0,0 +1,10 @@
public class Toto {
public void doSomething() {
System.out.println("doSomething");
}
public void doSomethingElse() {
System.out.println("doSomethingElse");
}
}

View File

@ -0,0 +1,20 @@
public class Toto {
public Toto(){}
public void doSomethingNew() {
System.out.println("doSomethingNew");
}
public void doSomethingElseNew() {
System.out.println("doSomethingElseNew");
}
public void doSomething() {
System.out.println("doSomething");
}
public void doSomethingElse() {
System.out.println("doSomethingElse");
}
}

View File

@ -0,0 +1,5 @@
public class HelloWorld {
public void sayHello() {
System.out.println("Hello");
}
}

View File

@ -0,0 +1,7 @@
public class HelloWorld {
public void sayHello(int i) {
if (i > 0) {
System.out.println("Hello");
}
}
}