Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
DataMatcher.cs
Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 
5 using SharpDiff;
6 using SiliconStudio.Assets.Visitors;
7 using SiliconStudio.Core.Reflection;
8 
9 namespace SiliconStudio.Assets.Diff
10 {
11  public class DataMatcher
12  {
13  private readonly ModelNodeComparer modelNodeComparer;
14  private readonly ModelNodeCanAlign modelNodeCanAlign;
15  private readonly ModelNodeSimilarityComparer modelNodeSimilarityComparer;
16  private readonly Dictionary<NodeKey, DataMatch> matches = new Dictionary<NodeKey, DataMatch>();
17  private readonly TypeDescriptorFactory descriptorFactory;
18 
19  public DataMatcher(TypeDescriptorFactory descriptorFactory)
20  {
21  if (descriptorFactory == null) throw new ArgumentNullException("descriptorFactory");
22  this.descriptorFactory = descriptorFactory;
23  modelNodeComparer = new ModelNodeComparer(this);
24  modelNodeSimilarityComparer = new ModelNodeSimilarityComparer(this);
25  modelNodeCanAlign = new ModelNodeCanAlign(modelNodeSimilarityComparer);
26  }
27 
29  {
30  DataMatch match;
31  var key = new NodeKey(node1, node2);
32  if (matches.TryGetValue(key, out match))
33  {
34  return match;
35  }
36 
37 
38  match = MatchInternal(node1, node2);
39  Console.WriteLine("Match {0} : {1} vs {2}", node1, node2, match);
40  matches[key] = match;
41 
42  return match;
43  }
44 
45  private DataMatch MatchInternal(DataVisitNode node1, DataVisitNode node2)
46  {
47  // Check if nodes are null
48  if ((ReferenceEquals(node1, null) || ReferenceEquals(node2, null) || node1.GetType() != node2.GetType()))
49  {
50  return node1 == node2 ? DataMatch.Empty : UnMatched(node1, node2);
51  }
52 
53  var match = new DataMatch();
54 
55  if (node1 is DataVisitRootNode)
56  {
57  if (node1.Instance == null || node2.Instance == null || node1.Instance.GetType() != node2.Instance.GetType())
58  {
59  return ReferenceEquals(node1.Instance, node2.Instance) ? DataMatch.MatchOne : UnMatched(node1, node2);
60  }
61  }
62  else if (node1 is DataVisitMember)
63  {
64  match += MatchValue(node1, ((DataVisitMember)node1).Value, node2, ((DataVisitMember)node2).Value);
65  }
66  else if (node1 is DataVisitListItem)
67  {
68  match += MatchValue(node1, ((DataVisitListItem)node1).Item, node2, ((DataVisitListItem)node2).Item);
69  }
70 
71  match += MatchValues(node1, node1.Members, node2, node2.Members, true);
72  match += MatchValues(node1, node1.Items, node2, node2.Items, false);
73  return match;
74  }
75 
76  private DataMatch MatchValue(IDataVisitNode node1, object value1, IDataVisitNode node2, object value2)
77  {
78  // Match null vs non null and types
79  if (value1 == null || value2 == null || value1.GetType() != value2.GetType())
80  {
81  return ReferenceEquals(value1, value2) ? DataMatch.MatchOne : UnMatched(node1, node2);
82  }
83 
84  // Match value (only for value types)
85  // Matching of members is done in Match(DataVisitNode, DataVisitNode)
86  var type = descriptorFactory.Find(value1.GetType());
87  if (DataVisitNode.IsComparableOnlyType(value1, type))
88  {
89  return Equals(value1, value2) ? DataMatch.MatchOne : UnMatched(node1, node2);
90  }
91 
92  // Empty match
93  return DataMatch.Empty;
94  }
95 
96  private DataMatch MatchValues(DataVisitNode node1Parent, List<IDataVisitNode> nodes1, DataVisitNode node2Parent, List<IDataVisitNode> nodes2, bool expectSameCount)
97  {
98  if (nodes1 == null || nodes2 == null || (expectSameCount && nodes1.Count != nodes2.Count))
99  {
100  return ReferenceEquals(nodes1, nodes2) ? DataMatch.Empty : UnMatched(node1Parent, node2Parent);
101  }
102 
103  var match = new DataMatch();
104 
105  if (expectSameCount)
106  {
107  for (int i = 0; i < nodes1.Count; i++)
108  {
109  match += Match((DataVisitNode)nodes1[i], (DataVisitNode)nodes2[i]);
110  }
111  }
112  else
113  {
114  var alignedDiffs = Diff2.CompareAndAlign(nodes1, nodes2, modelNodeComparer, modelNodeSimilarityComparer, modelNodeCanAlign).ToList();
115  foreach (var alignedDiffChange in alignedDiffs)
116  {
117  switch (alignedDiffChange.Change)
118  {
119  case ChangeType.Same:
120  case ChangeType.Changed:
121  match += Match((DataVisitNode)nodes1[alignedDiffChange.Index1], (DataVisitNode)nodes2[alignedDiffChange.Index2]);
122  break;
123  case ChangeType.Added:
124  match += new DataMatch(0, nodes2[alignedDiffChange.Index2].CountNodes());
125  break;
126  case ChangeType.Deleted:
127  match += new DataMatch(0, nodes1[alignedDiffChange.Index1].CountNodes());
128  break;
129  }
130  }
131  }
132 
133  return match;
134  }
135 
136  private static DataMatch UnMatched(IDataVisitNode node1, IDataVisitNode node2)
137  {
138  return new DataMatch(0, Math.Max(node1 != null ? node1.CountNodes() : 0, node2 != null ? node2.CountNodes() : 0));
139  }
140 
141  private class ModelNodeComparer : IEqualityComparer<IDataVisitNode>
142  {
143  private readonly DataMatcher matcher;
144 
145  public ModelNodeComparer(DataMatcher matcher)
146  {
147  this.matcher = matcher;
148  }
149 
150  public bool Equals(IDataVisitNode x, IDataVisitNode y)
151  {
152  // An equatable is a perfect match
153  return matcher.Match((DataVisitNode)x, (DataVisitNode)y).Succeed;
154  }
155 
156  public int GetHashCode(IDataVisitNode obj)
157  {
158  // We always return the same hashcode
159  return 0;
160  }
161  }
162 
163  private class ModelNodeSimilarityComparer : ISimilarityComparer<IDataVisitNode>
164  {
165  private readonly DataMatcher matcher;
166 
167  public ModelNodeSimilarityComparer(DataMatcher matcher)
168  {
169  this.matcher = matcher;
170  }
171 
172  public double Compare(IDataVisitNode value1, IDataVisitNode value2)
173  {
174  var result = matcher.Match((DataVisitNode)value1, (DataVisitNode)value2);
175  return (double)result.Count/result.Total;
176  }
177  }
178 
179  private class ModelNodeCanAlign : IAlignmentFilter<IDataVisitNode>
180  {
181  private readonly ModelNodeSimilarityComparer comparer;
182 
183  public ModelNodeCanAlign(ModelNodeSimilarityComparer comparer)
184  {
185  this.comparer = comparer;
186  }
187 
188  public bool CanAlign(IDataVisitNode value1, IDataVisitNode value2)
189  {
190  return comparer.Compare(value1, value2) > 0.1;
191  }
192  }
193 
194  private struct NodeKey : IEquatable<NodeKey>
195  {
196  private readonly DataVisitNode node1;
197  private readonly DataVisitNode node2;
198 
199  public NodeKey(DataVisitNode node1, DataVisitNode node2)
200  {
201  this.node1 = node1;
202  this.node2 = node2;
203  }
204 
205  public bool Equals(NodeKey other)
206  {
207  return ReferenceEquals(node1, other.node1) && ReferenceEquals(node2, other.node2);
208  }
209 
210  public override bool Equals(object obj)
211  {
212  if (ReferenceEquals(null, obj)) return false;
213  return obj is NodeKey && Equals((NodeKey)obj);
214  }
215 
216  public override int GetHashCode()
217  {
218  unchecked
219  {
220  return ((node1 != null ? node1.GetHashCode() : 0) * 397) ^ (node2 != null ? node2.GetHashCode() : 0);
221  }
222  }
223 
224  public static bool operator ==(NodeKey left, NodeKey right)
225  {
226  return left.Equals(right);
227  }
228 
229  public static bool operator !=(NodeKey left, NodeKey right)
230  {
231  return !left.Equals(right);
232  }
233  }
234  }
235 }
A default implementation for the ITypeDescriptorFactory.
A diff element for a member (field or property) of a class.
_In_ size_t _In_ DXGI_FORMAT _In_ size_t _In_ float size_t y
Definition: DirectXTexP.h:191
DataMatch Match(DataVisitNode node1, DataVisitNode node2)
Definition: DataMatcher.cs:28
Base class for all items in a collection (array, list or dictionary)
DataMatcher(TypeDescriptorFactory descriptorFactory)
Definition: DataMatcher.cs:19