Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
AssetDiff.cs
Go to the documentation of this file.
1 // Copyright (c) 2014 Silicon Studio Corp. (http://siliconstudio.co.jp)
2 // This file is distributed under GPL v3. See LICENSE.md for details.
3 using System;
4 using System.Collections.Generic;
5 using System.Linq;
6 using SharpDiff;
7 using SiliconStudio.Assets.Visitors;
8 using SiliconStudio.Core.Reflection;
9 
10 namespace SiliconStudio.Assets.Diff
11 {
12  /// <summary>
13  /// Class AssetDiff. This class cannot be inherited.
14  /// </summary>
15  public sealed class AssetDiff
16  {
17  private readonly static List<DataVisitNode> EmptyNodes = new List<DataVisitNode>();
18 
19  private readonly Asset baseAsset;
20  private readonly Asset asset1;
21  private readonly Asset asset2;
22  private readonly NodeEqualityComparer equalityComparer;
23  private Diff3Node computed;
24 
25  /// <summary>
26  /// Initializes a new instance of the <see cref="AssetDiff"/> class.
27  /// </summary>
28  /// <param name="baseAsset">The base asset.</param>
29  /// <param name="asset1">The asset1.</param>
30  /// <param name="asset2">The asset2.</param>
31  public AssetDiff(Asset baseAsset, Asset asset1, Asset asset2)
32  {
33  // TODO handle some null values (no asset2....etc.)
34  this.baseAsset = baseAsset;
35  this.asset1 = asset1;
36  this.asset2 = asset2;
37  this.equalityComparer = new NodeEqualityComparer(this);
38  }
39 
40  public Asset BaseAsset
41  {
42  get
43  {
44  return baseAsset;
45  }
46  }
47 
48  public Asset Asset1
49  {
50  get
51  {
52  return asset1;
53  }
54  }
55 
56  public Asset Asset2
57  {
58  get
59  {
60  return asset2;
61  }
62  }
63 
64  public void Reset()
65  {
66  computed = null;
67  }
68 
69  public static Diff3Node Compute(Asset baseAsset, Asset asset1, Asset asset2)
70  {
71  var diff3 = new AssetDiff(baseAsset, asset1, asset2);
72  return diff3.Compute();
73  }
74 
75  /// <summary>
76  /// Computes the diff3 between <see cref="BaseAsset" />, <see cref="Asset1" /> and <see cref="Asset2" />.
77  /// </summary>
78  /// <param name="forceRecompute">if set to <c>true</c> force to recompute the diff.</param>
79  /// <returns>The result of the diff. This result is cached so next call will return it directly.</returns>
80  public Diff3Node Compute(bool forceRecompute = false)
81  {
82  if (computed != null && !forceRecompute)
83  {
84  return computed;
85  }
86  var baseNodes = DataVisitNodeBuilder.Run(TypeDescriptorFactory.Default, baseAsset);
87  var asset1Nodes = DataVisitNodeBuilder.Run(TypeDescriptorFactory.Default, asset1);
88  var asset2Nodes = DataVisitNodeBuilder.Run(TypeDescriptorFactory.Default, asset2);
89  computed = DiffNode(baseNodes, asset1Nodes, asset2Nodes);
90  return computed;
91  }
92 
93  private Diff3Node DiffNode(DataVisitNode baseNode, DataVisitNode asset1Node, DataVisitNode asset2Node)
94  {
95  var diff3 = new Diff3Node(baseNode, asset1Node, asset2Node);
96 
97  var baseNodeDesc = GetNodeDescription(baseNode);
98  var asset1NodeDesc = GetNodeDescription(asset1Node);
99  var asset2NodeDesc = GetNodeDescription(asset2Node);
100 
101  bool hasMembers = false;
102 
103  Type type = null;
104  Type nodeType = null;
105  if (baseNodeDesc.Type != null)
106  {
107  type = baseNodeDesc.Type;
108  hasMembers = baseNode.HasMembers;
109  nodeType = baseNode.GetType();
110  }
111 
112  if (asset1NodeDesc.Type != null)
113  {
114  if (type == null)
115  {
116  type = asset1NodeDesc.Type;
117  hasMembers = asset1Node.HasMembers;
118  nodeType = asset1Node.GetType();
119  }
120  else
121  {
122  if (nodeType != asset1Node.GetType())
123  {
124  diff3.ChangeType = Diff3ChangeType.InvalidNodeType;
125  return diff3;
126  }
127 
128  if (type != asset1NodeDesc.Type)
129  {
130  diff3.ChangeType = Diff3ChangeType.ConflictType;
131  return diff3;
132  }
133  }
134  }
135 
136  if (asset2NodeDesc.Type != null)
137  {
138  if (type == null)
139  {
140  type = asset2NodeDesc.Type;
141  hasMembers = asset2Node.HasMembers;
142  }
143  else
144  {
145  if (nodeType != asset2Node.GetType())
146  {
147  diff3.ChangeType = Diff3ChangeType.InvalidNodeType;
148  return diff3;
149  }
150 
151  if (type != asset2NodeDesc.Type)
152  {
153  diff3.ChangeType = Diff3ChangeType.ConflictType;
154  return diff3;
155  }
156  }
157  }
158 
159  if (type == null)
160  {
161  return diff3;
162  }
163 
164  diff3.InstanceType = type;
165 
166  // A comparable type doesn't have any members, is not a collection or dictionary or array.
167  bool isComparableType = !hasMembers && !CollectionDescriptor.IsCollection(type) && !DictionaryDescriptor.IsDictionary(type) && !type.IsArray;
168  if (isComparableType)
169  {
170  DiffValue(diff3, ref baseNodeDesc, ref asset1NodeDesc, ref asset2NodeDesc);
171  return diff3;
172  }
173 
174  // Diff members
175  DiffMembers(diff3, baseNode, asset1Node, asset2Node);
176 
178  {
179  DiffDictionary(diff3, baseNode, asset1Node, asset2Node);
180  }
181  else if (CollectionDescriptor.IsCollection(type))
182  {
183  DiffCollection(diff3, baseNode, asset1Node, asset2Node);
184  }
185  else if (type.IsArray)
186  {
187  DiffArray(diff3, baseNode, asset1Node, asset2Node);
188  }
189 
190  return diff3;
191  }
192 
193  private static void DiffValue(Diff3Node diff3, ref NodeDescription baseNodeDesc, ref NodeDescription asset1NodeDesc, ref NodeDescription asset2NodeDesc)
194  {
195  var baseAsset1Equals = Equals(baseNodeDesc.Instance, asset1NodeDesc.Instance);
196  var baseAsset2Equals = Equals(baseNodeDesc.Instance, asset2NodeDesc.Instance);
197  var asset1And2Equals = Equals(asset1NodeDesc.Instance, asset2NodeDesc.Instance);
198 
199  diff3.ChangeType = baseAsset1Equals && baseAsset2Equals
200  ? Diff3ChangeType.None
201  : baseAsset2Equals ? Diff3ChangeType.MergeFromAsset1 : baseAsset1Equals ? Diff3ChangeType.MergeFromAsset2 : asset1And2Equals ? Diff3ChangeType.MergeFromAsset1And2 : Diff3ChangeType.Conflict;
202  }
203 
204  private void DiffMembers(Diff3Node diff3, DataVisitNode baseNode, DataVisitNode asset1Node, DataVisitNode asset2Node)
205  {
206  var baseMembers = baseNode != null ? baseNode.Members : null;
207  var asset1Members = asset1Node != null ? asset1Node.Members : null;
208  var asset2Members = asset2Node != null ? asset2Node.Members : null;
209  int memberCount = 0;
210 
211  if (baseMembers != null) memberCount = baseMembers.Count;
212  else if (asset1Members != null) memberCount = asset1Members.Count;
213  else if (asset2Members != null) memberCount = asset2Members.Count;
214 
215  for (int i = 0; i < memberCount; i++)
216  {
217  AddMember(diff3, DiffNode(baseMembers == null ? null : baseMembers[i],
218  asset1Members == null ? null : asset1Members[i],
219  asset2Members == null ? null : asset2Members[i]));
220  }
221  }
222 
223  private void DiffCollection(Diff3Node diff3, DataVisitNode baseNode, DataVisitNode asset1Node, DataVisitNode asset2Node)
224  {
225  var baseItems = baseNode != null ? baseNode.Items ?? EmptyNodes : EmptyNodes;
226  var asset1Items = asset1Node != null ? asset1Node.Items ?? EmptyNodes : EmptyNodes;
227  var asset2Items = asset2Node != null ? asset2Node.Items ?? EmptyNodes : EmptyNodes;
228 
229  equalityComparer.Reset();
230  var changes = Diff3.Compare(baseItems, asset1Items, asset2Items, equalityComparer);
231  foreach (var change in changes)
232  {
233  switch (change.ChangeType)
234  {
235  case SharpDiff.Diff3ChangeType.Equal:
236  for (int i = 0; i < change.Base.Length; i++)
237  {
238  var diff3Node = new Diff3Node(baseItems[change.Base.From + i], asset1Items[change.From1.From + i], asset2Items[change.From2.From + i]) { ChangeType = Diff3ChangeType.None };
239  AddItem(diff3, diff3Node);
240  }
241  break;
242 
243  case SharpDiff.Diff3ChangeType.MergeFrom1:
244  for (int i = 0; i < change.From1.Length; i++)
245  {
246  var diff3Node = new Diff3Node(null, asset1Items[change.From1.From + i], null) { ChangeType = Diff3ChangeType.MergeFromAsset1 };
247  AddItem(diff3, diff3Node);
248  }
249  break;
250 
251  case SharpDiff.Diff3ChangeType.MergeFrom2:
252  for (int i = 0; i < change.From2.Length; i++)
253  {
254  var diff3Node = new Diff3Node(null, null, asset2Items[change.From2.From + i]) { ChangeType = Diff3ChangeType.MergeFromAsset2 };
255  AddItem(diff3, diff3Node);
256  }
257  break;
258 
259  case SharpDiff.Diff3ChangeType.MergeFrom1And2:
260  for (int i = 0; i < change.From2.Length; i++)
261  {
262  var diff3Node = new Diff3Node(null, asset1Items[change.From1.From + i], asset2Items[change.From2.From + i]) { ChangeType = Diff3ChangeType.MergeFromAsset1And2 };
263  AddItem(diff3, diff3Node);
264  }
265  break;
266 
267  case SharpDiff.Diff3ChangeType.Conflict:
268  int baseIndex = change.Base.IsValid ? change.Base.From : -1;
269  int from1Index = change.From1.IsValid ? change.From1.From : -1;
270  int from2Index = change.From2.IsValid ? change.From2.From : -1;
271 
272  // If there are changes only from 1 or 2 or base.Length == list1.Length == list2.Length, then try to make a diff per item
273  // else output the conflict as a full conflict
274  bool tryResolveConflict = false;
275  if (baseIndex >= 0)
276  {
277  if (from1Index >= 0 && from2Index >= 0)
278  {
279  if ((change.Base.Length == change.From1.Length && change.Base.Length == change.From2.Length)
280  || (change.From1.Length == change.From2.Length))
281  {
282  tryResolveConflict = true;
283  }
284  }
285  else if (from1Index >= 0)
286  {
287  tryResolveConflict = change.Base.Length == change.From1.Length;
288  }
289  else if (from2Index >= 0)
290  {
291  tryResolveConflict = change.Base.Length == change.From2.Length;
292  }
293  else
294  {
295  tryResolveConflict = true;
296  }
297  }
298 
299  // Iterate on items
300  while ((baseIndex >= 0 && baseItems.Count > 0) || (from1Index >= 0 && asset1Items.Count > 0) || (from2Index >= 0 && asset2Items.Count > 0))
301  {
302  var baseItem = GetSafeFromList(baseItems, ref baseIndex, ref change.Base);
303  var asset1Item = GetSafeFromList(asset1Items, ref from1Index, ref change.From1);
304  var asset2Item = GetSafeFromList(asset2Items, ref from2Index, ref change.From2);
305 
306  var diff3Node = tryResolveConflict ?
307  DiffNode(baseItem, asset1Item, asset2Item) :
308  new Diff3Node(baseItem, asset1Item, asset2Item) { ChangeType = Diff3ChangeType.Conflict };
309  AddItem(diff3, diff3Node);
310  }
311  break;
312  }
313  }
314 
315  // Order by descending index
316  if (diff3.Items != null)
317  {
318  diff3.Items.Sort((left, right) =>
319  {
320  int leftAsset1Index = left.Asset1Node != null ? ((DataVisitListItem)left.Asset1Node).Index : -1;
321  int rightAsset1Index = right.Asset1Node != null ? ((DataVisitListItem)right.Asset1Node).Index : -1;
322 
323  return rightAsset1Index.CompareTo(leftAsset1Index);
324  });
325  }
326  }
327 
328  private static DataVisitNode GetSafeFromList(List<DataVisitNode> nodes, ref int index, ref Span span)
329  {
330  if (nodes == null || index < 0) return null;
331  if (index >= nodes.Count || (span.IsValid && index > span.To))
332  {
333  index = -1;
334  return null;
335  }
336  var value = nodes[index];
337  index++;
338  if (index >= nodes.Count) index = -1;
339  return value;
340  }
341 
342  private void DiffDictionary(Diff3Node diff3, DataVisitNode baseNode, DataVisitNode asset1Node, DataVisitNode asset2Node)
343  {
344  var baseItems = baseNode != null ? baseNode.Items : null;
345  var asset1Items = asset1Node != null ? asset1Node.Items : null;
346  var asset2Items = asset2Node != null ? asset2Node.Items : null;
347 
348  // Build dictionary: key => base, v1, v2
349  var keyNodes = new Dictionary<object, Diff3DictionaryItem>();
350  Diff3DictionaryItem diff3Item;
351  if (baseItems != null)
352  {
353  foreach (var dataVisitNode in baseItems.OfType<DataVisitDictionaryItem>())
354  {
355  keyNodes.Add(dataVisitNode.Key, new Diff3DictionaryItem() { Base = dataVisitNode });
356  }
357  }
358  if (asset1Items != null)
359  {
360  foreach (var dataVisitNode in asset1Items.OfType<DataVisitDictionaryItem>())
361  {
362  keyNodes.TryGetValue(dataVisitNode.Key, out diff3Item);
363  diff3Item.Asset1 = dataVisitNode;
364  keyNodes[dataVisitNode.Key] = diff3Item;
365  }
366  }
367  if (asset2Items != null)
368  {
369  foreach (var dataVisitNode in asset2Items.OfType<DataVisitDictionaryItem>())
370  {
371  keyNodes.TryGetValue(dataVisitNode.Key, out diff3Item);
372  diff3Item.Asset2 = dataVisitNode;
373  keyNodes[dataVisitNode.Key] = diff3Item;
374  }
375  }
376 
377  // Perform merge on dictionary
378  foreach (var keyNode in keyNodes)
379  {
380  var valueNode = keyNode.Value;
381 
382  Diff3Node diffValue;
383 
384  // base v1 v2 action
385  // ---- -- -- ------
386  // a b c Diff(a,b,c)
387  // null b c Diff(null, b, c)
388  if (valueNode.Asset1 != null && valueNode.Asset2 != null)
389  {
390  diffValue = DiffNode(valueNode.Base, valueNode.Asset1, valueNode.Asset2);
391  }
392  else if (valueNode.Asset1 == null)
393  {
394  // a null c MergeFrom1 (unchanged)
395  // null null c MergeFrom2
396  // a null null MergeFrom1 (unchanged)
397  diffValue = new Diff3Node(valueNode.Base, null, valueNode.Asset2) { ChangeType = valueNode.Base == null ? Diff3ChangeType.MergeFromAsset2 : Diff3ChangeType.MergeFromAsset1 };
398  }
399  else
400  {
401  // a b null Conflict
402  // null b null MergeFrom1 (unchanged)
403  diffValue = new Diff3Node(valueNode.Base, valueNode.Asset1, null) { ChangeType = valueNode.Base == null ? Diff3ChangeType.MergeFromAsset1 : Diff3ChangeType.Conflict };
404  }
405 
406  AddItem(diff3, diffValue);
407  }
408  }
409 
410  private void DiffArray(Diff3Node diff3, DataVisitNode baseNode, DataVisitNode asset1Node, DataVisitNode asset2Node)
411  {
412  var baseItems = baseNode != null ? baseNode.Items : null;
413  var asset1Items = asset1Node != null ? asset1Node.Items : null;
414  var asset2Items = asset2Node != null ? asset2Node.Items : null;
415  int itemCount = -1;
416 
417  if (baseItems != null)
418  {
419  itemCount = baseItems.Count;
420  }
421 
422  if (asset1Items != null)
423  {
424  var newLength = asset1Items.Count;
425  if (itemCount >= 0 && itemCount != newLength)
426  {
427  diff3.ChangeType = Diff3ChangeType.ConflictArraySize;
428  return;
429  }
430  itemCount = newLength;
431  }
432 
433  if (asset2Items != null)
434  {
435  var newLength = asset2Items.Count;
436  if (itemCount >= 0 && itemCount != newLength)
437  {
438  diff3.ChangeType = Diff3ChangeType.ConflictArraySize;
439  return;
440  }
441  itemCount = newLength;
442  }
443 
444  for (int i = 0; i < itemCount; i++)
445  {
446  AddItem(diff3, DiffNode(baseItems == null ? null : baseItems[i],
447  asset1Items == null ? null : asset1Items[i],
448  asset2Items == null ? null : asset2Items[i]));
449  }
450  }
451 
452 
453  /// <summary>
454  /// Adds a member to this instance.
455  /// </summary>
456  /// <param name="thisObject">The this object.</param>
457  /// <param name="member">The member.</param>
458  /// <exception cref="System.ArgumentNullException">member</exception>
459  private static void AddMember(Diff3Node thisObject, Diff3Node member)
460  {
461  if (member == null) throw new ArgumentNullException("member");
462  if (thisObject.Members == null)
463  thisObject.Members = new List<Diff3Node>();
464 
465  member.Parent = thisObject;
466  if (member.ChangeType != Diff3ChangeType.None)
467  {
468  thisObject.ChangeType = Diff3ChangeType.Children;
469  }
470  thisObject.Members.Add(member);
471  }
472 
473  /// <summary>
474  /// Adds an item (array, list or dictionary item) to this instance.
475  /// </summary>
476  /// <param name="thisObject">The this object.</param>
477  /// <param name="item">The item.</param>
478  /// <exception cref="System.ArgumentNullException">item</exception>
479  private static void AddItem(Diff3Node thisObject, Diff3Node item)
480  {
481  if (item == null) throw new ArgumentNullException("item");
482  if (thisObject.Items == null)
483  thisObject.Items = new List<Diff3Node>();
484 
485  item.Parent = thisObject;
486  if (item.ChangeType != Diff3ChangeType.None)
487  {
488  thisObject.ChangeType = Diff3ChangeType.Children;
489  }
490  thisObject.Items.Add(item);
491  }
492 
493  private NodeDescription GetNodeDescription(DataVisitNode node)
494  {
495  if (node == null)
496  {
497  return new NodeDescription();
498  }
499 
500  var instanceType = node.InstanceType;
501  if (NullableDescriptor.IsNullable(instanceType))
502  {
503  instanceType = Nullable.GetUnderlyingType(instanceType);
504  }
505 
506  return new NodeDescription(node.Instance, instanceType);
507  }
508 
509  private struct NodeDescription
510  {
511  public NodeDescription(object instance, Type type)
512  {
513  Instance = instance;
514  Type = type;
515  }
516 
517  public readonly object Instance;
518 
519  public readonly Type Type;
520  }
521 
522  private struct Diff3DictionaryItem
523  {
525 
526  public DataVisitDictionaryItem Asset1;
527 
528  public DataVisitDictionaryItem Asset2;
529  }
530 
531  private class NodeEqualityComparer : IEqualityComparer<DataVisitNode>
532  {
533  private Dictionary<KeyComparison, bool> equalityCache = new Dictionary<KeyComparison, bool>();
534  private AssetDiff diffManager;
535 
536  public NodeEqualityComparer(AssetDiff diffManager)
537  {
538  if (diffManager == null) throw new ArgumentNullException("diffManager");
539  this.diffManager = diffManager;
540  }
541 
542  public void Reset()
543  {
544  equalityCache.Clear();
545  }
546 
547  public bool Equals(DataVisitNode x, DataVisitNode y)
548  {
549  var key = new KeyComparison(x, y);
550  bool result;
551  if (equalityCache.TryGetValue(key, out result))
552  {
553  return result;
554  }
555 
556  var diff3 = diffManager.DiffNode(x, y, null);
557  result = !diff3.FindDifferences().Any();
558  equalityCache.Add(key, result);
559  return result;
560  }
561 
562  public int GetHashCode(DataVisitNode obj)
563  {
564  return obj == null ? 0 : obj.GetHashCode();
565  }
566 
567  private struct KeyComparison : IEquatable<KeyComparison>
568  {
569  public KeyComparison(DataVisitNode node1, DataVisitNode node2)
570  {
571  Node1 = node1;
572  Node2 = node2;
573  }
574 
575  public readonly DataVisitNode Node1;
576 
577  public readonly DataVisitNode Node2;
578 
579 
580  public bool Equals(KeyComparison other)
581  {
582  return ReferenceEquals(Node1, other.Node1) && ReferenceEquals(Node2, other.Node2);
583  }
584 
585  public override bool Equals(object obj)
586  {
587  if (ReferenceEquals(null, obj)) return false;
588  return obj is KeyComparison && Equals((KeyComparison)obj);
589  }
590 
591  public override int GetHashCode()
592  {
593  unchecked
594  {
595  return ((Node1 != null ? Node1.GetHashCode() : 0) * 397) ^ (Node2 != null ? Node2.GetHashCode() : 0);
596  }
597  }
598 
599  public static bool operator ==(KeyComparison left, KeyComparison right)
600  {
601  return left.Equals(right);
602  }
603 
604  public static bool operator !=(KeyComparison left, KeyComparison right)
605  {
606  return !left.Equals(right);
607  }
608  }
609  }
610  }
611 }
Provides a descriptor for a System.Collections.ICollection.
Base class for Asset.
Definition: Asset.cs:14
The value is taken from a base value or this instance if no base (default).
_In_ size_t _In_ DXGI_FORMAT _In_ size_t _In_ float size_t y
Definition: DirectXTexP.h:191
static Diff3Node Compute(Asset baseAsset, Asset asset1, Asset asset2)
Definition: AssetDiff.cs:69
Provides a descriptor for a System.Collections.IDictionary.
Let the emitter choose the style.
static bool IsNullable(Type type)
Determines whether the specified type is nullable.
static bool IsDictionary(Type type)
Determines whether the specified type is a .NET dictionary.
Describes a descriptor for a nullable type Nullable{T}.
The type of the serialized type will be passed as a generic arguments of the serializer. Example: serializer of A becomes instantiated as Serializer{A}.
Base class for all items in a collection (array, list or dictionary)
static bool IsCollection(Type type)
Determines whether the specified type is collection.
AssetDiff(Asset baseAsset, Asset asset1, Asset asset2)
Initializes a new instance of the AssetDiff class.
Definition: AssetDiff.cs:31
The device failed due to a badly formed command. This is a run-time issue; The application should des...
Class AssetDiff. This class cannot be inherited.
Definition: AssetDiff.cs:15
Diff3Node Compute(bool forceRecompute=false)
Computes the diff3 between BaseAsset, Asset1 and Asset2.
Definition: AssetDiff.cs:80