Welcome to mirror list, hosted at ThFree Co, Russian Federation.

StructuredDiff.cs « MonoDevelop.Components.Diff « MonoDevelop.Ide « core « src « main - github.com/mono/monodevelop.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 657532ee4fd3a7b43121e70781dee77824ede4c5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
using System;
using System.Collections;
using System.Xml;

namespace MonoDevelop.Components.Diff {
	public abstract class StructuredDiff {
		internal Hashtable nodeInterfaces = new Hashtable();
		internal Hashtable comparisonCache = new Hashtable();
		
		public virtual void AddInterface(Type type, NodeInterface nodeInterface) {
			nodeInterfaces[type] = nodeInterface;
		}
		
		internal NodeInterface GetInterface(object obj) {
			bool store = false;
			Type type = obj.GetType();
			while (type != null) {
				NodeInterface ret = (NodeInterface)nodeInterfaces[type];
				if (ret != null) {
					if (store) nodeInterfaces[obj.GetType()] = ret;
					return ret;
				}
				type = type.BaseType;
				store = true;
			}
			throw new ArgumentException("Node type has no interface defined: " + obj.GetType());
		}
		
		public float CompareLists(IList left, IList right) {
			return CompareLists(left, right, 0, false);
		}
		
		public void Compare(object left, object right) {
			comparisonCache.Clear();
			CompareLists(new object[] { left }, new object[] { right }, 0, true);
		}
		
		private float CompareLists(IList left, IList right, float threshold, bool output) {
			// Given two lists, find the elements in the list that correspond.
			// Two elements correspond if their 'difference metric' is less than
			// or equal to threshold.  For the hunks of correspondent items,
			// recursively descend into items not truly equal.  For hunks of
			// irreconsiliable material, raise the threshold to the next useful
			// level and rescan the items.
			
			if (left.Count == 0 && right.Count == 0)
				return 0;
			
			NodeComparerWrapper comparer = new NodeComparerWrapper(threshold, this);
			
			Diff diff = new Diff(left, right, comparer, comparer);
			
			int nitems = 0, ndiffs = 0;
			
			foreach (Diff.Hunk hunk in diff) {
				if (hunk.Same || (hunk.Left.Count == 1 && hunk.Right.Count == 1)) {
					// This comprises a block of correspondent items who
					// differ by no more than the threshold value.
					
					nitems += hunk.Left.Count;

					bool inSameRegion = false;
					
					for (int i = 0; i < hunk.Left.Count; i++) {
						object oleft = hunk.Left[i];
						object oright = hunk.Right[i];
						
						NodeInterface ileft = GetInterface(oleft);
						NodeInterface iright = GetInterface(oright);
						
						IList cleft = null, cright = null;
						cleft = ileft.GetChildren(oleft);
						cright = iright.GetChildren(oright);
						
						float comp = 0;
						if (ileft == iright)
							comp = ileft.Compare(oleft, oright, this);
						
						// If the nodes are equal, emit one node.
						if (ileft == iright && comp == 0) {
							if (output) {
								if (!inSameRegion) { WritePushSame(); inSameRegion = true; }
								WriteNodeSame(ileft, oleft, oright);
							}
							
						// Recurse into the lists of each node.
						} else if (ileft == iright && cleft != null && cright != null && cleft.Count > 0 && cright.Count > 0 && comp <= 1.0) {
							if (output && inSameRegion) { WritePopSame(); inSameRegion = false; }
							if (output) WritePushNode(ileft, oleft, oright);
							float d = CompareLists(cleft, cright, 0, output);
							d *= hunk.Left.Count;
							if (d < 1) d = 1;
							ndiffs += (int)d;
							if (output) WritePopNode();
							
						// The nodes are not equal, so emit removed and added nodes.
						} else {
							if (output && inSameRegion) { WritePopSame(); inSameRegion = false; }
							if (output) WriteNodeChange(ileft, oleft, iright, oright);
							ndiffs += hunk.Left.Count;
						}
					}
					
					if (output && inSameRegion) WritePopSame();
				} else {
					int ct = hunk.Left.Count + hunk.Right.Count;
					nitems += ct;
					ndiffs += ct;
					
					if (output) {
						bool noRecurse = comparer.minimumDifference >= 1;
						if (hunk.Right.Count == 0 || (hunk.Left.Count > 0 && noRecurse))
							WriteNodesRemoved(hunk.Left);
						if (hunk.Left.Count == 0 || (hunk.Right.Count > 0 && noRecurse))
							WriteNodesAdded(hunk.Right);
						if (hunk.Right.Count != 0 && hunk.Left.Count != 0 && !noRecurse)
							CompareLists(hunk.Left, hunk.Right, comparer.minimumDifference, output);
					}
				}
			}
			
			return (float)ndiffs / (float)nitems;
		}
		
		protected abstract void WritePushNode(NodeInterface nodeInterface, object left, object right);

		protected abstract void WritePushSame();
		
		protected abstract void WriteNodeSame(NodeInterface nodeInterface, object left, object right);

		protected abstract void WritePopSame();

		protected abstract void WriteNodeChange(NodeInterface leftInterface, object left, NodeInterface rightInterface, object right);

		protected abstract void WriteNodesRemoved(IList objects);

		protected abstract void WriteNodesAdded(IList objects);

		protected abstract void WritePopNode();
	}
		
	public class XmlOutputStructuredDiff : StructuredDiff {
		bool deepOutput, allContext;
		XmlWriter output;
		Hashtable contextNodes;
		
		public XmlOutputStructuredDiff(XmlWriter output, string context) {
			this.output = output;
			this.deepOutput = (context == null);
			this.allContext = (context != null && context == "*");
			
			if (!deepOutput && !allContext) {
				contextNodes = new Hashtable();
				foreach (string name in context.Split(','))
					contextNodes[name.Trim()] = contextNodes;
			}
		}
		
		public override void AddInterface(Type type, NodeInterface nodeInterface) {
			if (!(nodeInterface is XmlOutputNodeInterface))
				throw new ArgumentException("Node interfaces for the XmlOutputStructuredDiff must implement XmlOutputNodeInterface.");
			base.AddInterface(type, nodeInterface);
		}
		
		protected override void WritePushSame() {
		}
		
		protected override void WritePopSame() {
		}

		protected override void WriteNodeSame(NodeInterface nodeInterface, object left, object right) {
			bool deep = deepOutput;
			
			if (left is XmlNode && !deepOutput && !allContext) {
				if (!contextNodes.ContainsKey(((XmlNode)left).Name)) {
					return;
				} else {
					deep = true;
				}
			}

			((XmlOutputNodeInterface)nodeInterface).WriteBeginNode(left, left, (XmlWriter)output);
			output.WriteAttributeString("Status", "Same");
			if (deep)
				((XmlOutputNodeInterface)nodeInterface).WriteNodeChildren(left, (XmlWriter)output);
			output.WriteEndElement();
		}

		protected override void WriteNodeChange(NodeInterface leftInterface, object left, NodeInterface rightInterface, object right) {
			((XmlOutputNodeInterface)leftInterface).WriteBeginNode(left, right, (XmlWriter)output);
			output.WriteAttributeString("Status", "Changed");
				((XmlOutputNodeInterface)leftInterface).WriteBeginNode(left, left, (XmlWriter)output);
					((XmlOutputNodeInterface)leftInterface).WriteNodeChildren(left, (XmlWriter)output);
				output.WriteEndElement();
				((XmlOutputNodeInterface)rightInterface).WriteBeginNode(right, right, (XmlWriter)output);
					((XmlOutputNodeInterface)rightInterface).WriteNodeChildren(right, (XmlWriter)output);
				output.WriteEndElement();
			output.WriteEndElement();
		}

		protected override void WritePushNode(NodeInterface nodeInterface, object left, object right) {
			((XmlOutputNodeInterface)nodeInterface).WriteBeginNode(left, right, output);
		}

		protected override void WritePopNode() {
			output.WriteEndElement();
		}
		
		void AddRemove(IList objects, string status) {
			foreach (object obj in objects) {
				XmlOutputNodeInterface i = (XmlOutputNodeInterface)GetInterface(obj);
				((XmlOutputNodeInterface)i).WriteBeginNode(obj, obj, output);
				output.WriteAttributeString("Status", status);
				i.WriteBeginNode(obj, obj, output);
				i.WriteNodeChildren(obj, output);
				output.WriteEndElement();
				output.WriteEndElement();
			}
		}

		protected override void WriteNodesRemoved(IList objects) {
			AddRemove(objects, "Removed");
		}
		protected override void WriteNodesAdded(IList objects) {
			AddRemove(objects, "Added");
		}
	}
	
	public abstract class NodeInterface {
		public abstract IList GetChildren(object node);
		public abstract float Compare(object left, object right, StructuredDiff comparer);
		public virtual int GetHashCode(object node) {
			return node.GetHashCode();
		}
	}
	
	public interface XmlOutputNodeInterface {
		void WriteNodeChildren(object node, XmlWriter output);
		void WriteBeginNode(object left, object right, XmlWriter output);
	}
	
	internal class NodeComparerWrapper : IComparer, IEqualityComparer {
		float threshold;
		StructuredDiff differ;
		
		public float minimumDifference = 1;
		
		bool useCache = true;
		
		public NodeComparerWrapper(float threshold, StructuredDiff differ) {
			this.threshold = threshold;
			this.differ = differ;
		}
		
		private class Pair {
			object left, right;
			int code;
			public Pair(object a, object b, StructuredDiff differ) {
				left = a; right = b;
				code = unchecked(differ.GetInterface(left).GetHashCode(left) + differ.GetInterface(right).GetHashCode(right));
			}
			public override bool Equals(object o) {
				return ((Pair)o).left == left && ((Pair)o).right == right;
			}
			public override int GetHashCode() {
				return code;
			}
		}
		
		int IComparer.Compare(object left, object right) {
			float ret;
			
			Pair pair = new Pair(left, right, differ);
			
			if (left.GetType() != right.GetType()) {
				ret = 1;
			} else if (useCache && differ.comparisonCache.ContainsKey(pair)) {
				ret = (float)differ.comparisonCache[pair];
			} else {
				NodeInterface comparer = differ.GetInterface(left);
				ret = comparer.Compare(left, right, differ);
			}
			
			if (useCache)
				differ.comparisonCache[pair] = ret;
			
			if (ret < minimumDifference && ret > threshold)
				minimumDifference = ret;
			
			if (ret <= threshold)
				return 0;
			else
				return 1;
		}
		
		bool IEqualityComparer.Equals (object a, object b)
		{
			return ((IComparer)this).Compare (a, b) == 0;
		}
		
		int IEqualityComparer.GetHashCode (object obj) {
			return differ.GetInterface(obj).GetHashCode(obj);
		}
	}

}