2
using System.Collections;
4
namespace Algorithm.Diff {
6
public class TextDiff : IDiff {
8
ArrayList hunks = new ArrayList();
10
public TextDiff(string left, string right) : this (left, right, null) {
13
public TextDiff(string left, string right, IComparer comparer) {
14
this.left = new Node(left, 0, left.Length, 0);
15
this.right = new Node(right, 0, right.Length, 0);
18
TextDiffStructuredDiff diff = new TextDiffStructuredDiff(hunks, left.ToCharArray(), right.ToCharArray());
19
diff.AddInterface(typeof(Node), new TextDiffNodeInterface(comparer));
20
diff.Compare(this.left, this.right);
23
public IList Left { get { return left.ToString().ToCharArray(); } }
24
public IList Right { get { return right.ToString().ToCharArray(); } }
26
IEnumerator IEnumerable.GetEnumerator() {
27
return hunks.GetEnumerator();
30
private class TextDiffStructuredDiff : StructuredDiff {
32
IList leftlist, rightlist;
34
public TextDiffStructuredDiff(ArrayList hunks, IList leftlist, IList rightlist) {
36
this.leftlist = leftlist;
37
this.rightlist = rightlist;
40
protected override void WritePushNode(NodeInterface nodeInterface, object left, object right) {
43
protected override void WritePushSame() {
46
private void AddHunk(int lcount, int rcount, bool same) {
47
if (hunks.Count > 0 && same == ((Diff.Hunk)hunks[hunks.Count-1]).Same) {
48
Diff.Hunk prev = (Diff.Hunk)hunks[hunks.Count-1];
49
hunks[hunks.Count-1] = new Diff.Hunk(
53
prev.Left.End + lcount,
55
prev.Right.End + rcount,
62
if (hunks.Count > 0) {
63
Diff.Hunk prev = (Diff.Hunk)hunks[hunks.Count-1];
68
hunks.Add( new Diff.Hunk(
78
protected override void WriteNodeSame(NodeInterface nodeInterface, object left, object right) {
79
AddHunk(((Node)left).count, ((Node)right).count, true);
82
protected override void WritePopSame() {
85
protected override void WriteNodeChange(NodeInterface leftInterface, object left, NodeInterface rightInterface, object right) {
86
AddHunk(((Node)left).count, ((Node)right).count, false);
89
protected override void WriteNodesRemoved(IList objects) {
90
int start = ((Node)objects[0]).start;
91
int end = ((Node)objects[objects.Count-1]).start + ((Node)objects[objects.Count-1]).count - 1;
93
AddHunk(end - start + 1, 0, false);
96
protected override void WriteNodesAdded(IList objects) {
97
int start = ((Node)objects[0]).start;
98
int end = ((Node)objects[objects.Count-1]).start + ((Node)objects[objects.Count-1]).count - 1;
100
AddHunk(0, end - start + 1, false);
103
protected override void WritePopNode() {
107
private class TextDiffNodeInterface : NodeInterface {
110
public TextDiffNodeInterface(IComparer comparer) { this.comparer = comparer; }
112
public override IList GetChildren(object node) {
113
if (((Node)node).children.Count == 0) return null;
114
return ((Node)node).children;
117
private bool Equal(string a, string b) {
118
if (comparer == null)
120
return comparer.Compare(a, b) == 0;
123
public override float Compare(object left, object right, StructuredDiff comparer) {
124
string l = left.ToString(), r = right.ToString();
125
if (Equal(l, r)) return 0;
126
if (l.Length == 1 || r.Length == 1) return 1;
127
float d = comparer.CompareLists(GetChildren(left), GetChildren(right));
128
if (((Node)left).level == 2 && d >= .75) d = 1.1f;
132
public override int GetHashCode(object node) {
133
return node.ToString().GetHashCode();
138
public string source;
139
public int start, count;
142
public ArrayList children = new ArrayList();
144
static char[][] delims = new char[][] {
145
new char[] { '\n', '\r' },
146
new char[] { ' ', '\t', '.', ',' }
149
public Node(string source, int start, int count, int level) {
150
this.source = source;
157
foreach (string child in ToString().Split(delims[level])) {
158
if (child.Length >= 1)
159
children.Add(new Node(source, pos, child.Length, level+1));
160
if (pos + child.Length < count)
161
children.Add(new Node(source, pos+child.Length, 1, level+1));
162
pos += child.Length + 1;
164
} else if (level == 2) {
165
for (int i = start; i < start + count; i++)
166
children.Add(new Node(source, i, 1, level+1));
171
public override string ToString() {
172
return source.Substring(start, count);