4 using System.Collections.Generic;
7 using SiliconStudio.Assets.Visitors;
8 using SiliconStudio.Core.Reflection;
10 namespace SiliconStudio.Assets.Diff
17 private readonly
static List<DataVisitNode> EmptyNodes =
new List<DataVisitNode>();
19 private readonly
Asset baseAsset;
20 private readonly
Asset asset1;
21 private readonly
Asset asset2;
22 private readonly NodeEqualityComparer equalityComparer;
34 this.baseAsset = baseAsset;
37 this.equalityComparer =
new NodeEqualityComparer(
this);
40 public Asset BaseAsset
71 var diff3 =
new AssetDiff(baseAsset, asset1, asset2);
72 return diff3.Compute();
82 if (computed != null && !forceRecompute)
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);
95 var diff3 =
new Diff3Node(baseNode, asset1Node, asset2Node);
97 var baseNodeDesc = GetNodeDescription(baseNode);
98 var asset1NodeDesc = GetNodeDescription(asset1Node);
99 var asset2NodeDesc = GetNodeDescription(asset2Node);
101 bool hasMembers =
false;
104 Type nodeType = null;
105 if (baseNodeDesc.Type != null)
107 type = baseNodeDesc.Type;
108 hasMembers = baseNode.HasMembers;
109 nodeType = baseNode.GetType();
112 if (asset1NodeDesc.Type != null)
116 type = asset1NodeDesc.Type;
117 hasMembers = asset1Node.HasMembers;
118 nodeType = asset1Node.GetType();
122 if (nodeType != asset1Node.GetType())
124 diff3.ChangeType = Diff3ChangeType.InvalidNodeType;
128 if (type != asset1NodeDesc.Type)
130 diff3.ChangeType = Diff3ChangeType.ConflictType;
136 if (asset2NodeDesc.Type != null)
140 type = asset2NodeDesc.Type;
141 hasMembers = asset2Node.HasMembers;
145 if (nodeType != asset2Node.GetType())
147 diff3.ChangeType = Diff3ChangeType.InvalidNodeType;
151 if (type != asset2NodeDesc.Type)
153 diff3.ChangeType = Diff3ChangeType.ConflictType;
164 diff3.InstanceType = type;
168 if (isComparableType)
170 DiffValue(diff3, ref baseNodeDesc, ref asset1NodeDesc, ref asset2NodeDesc);
175 DiffMembers(diff3, baseNode, asset1Node, asset2Node);
179 DiffDictionary(diff3, baseNode, asset1Node, asset2Node);
183 DiffCollection(diff3, baseNode, asset1Node, asset2Node);
185 else if (type.IsArray)
187 DiffArray(diff3, baseNode, asset1Node, asset2Node);
193 private static void DiffValue(Diff3Node diff3, ref NodeDescription baseNodeDesc, ref NodeDescription asset1NodeDesc, ref NodeDescription asset2NodeDesc)
195 var baseAsset1Equals = Equals(baseNodeDesc.Instance, asset1NodeDesc.Instance);
196 var baseAsset2Equals = Equals(baseNodeDesc.Instance, asset2NodeDesc.Instance);
197 var asset1And2Equals = Equals(asset1NodeDesc.Instance, asset2NodeDesc.Instance);
199 diff3.ChangeType = baseAsset1Equals && baseAsset2Equals
200 ? Diff3ChangeType.None
201 : baseAsset2Equals ? Diff3ChangeType.MergeFromAsset1 : baseAsset1Equals ? Diff3ChangeType.MergeFromAsset2 : asset1And2Equals ? Diff3ChangeType.MergeFromAsset1And2 : Diff3ChangeType.Conflict;
206 var baseMembers = baseNode != null ? baseNode.Members : null;
207 var asset1Members = asset1Node != null ? asset1Node.Members : null;
208 var asset2Members = asset2Node != null ? asset2Node.Members : null;
211 if (baseMembers != null) memberCount = baseMembers.Count;
212 else if (asset1Members != null) memberCount = asset1Members.Count;
213 else if (asset2Members != null) memberCount = asset2Members.Count;
215 for (
int i = 0; i < memberCount; i++)
217 AddMember(diff3, DiffNode(baseMembers == null ? null : baseMembers[i],
218 asset1Members == null ? null : asset1Members[i],
219 asset2Members == null ? null : asset2Members[i]));
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;
229 equalityComparer.Reset();
230 var changes = Diff3.Compare(baseItems, asset1Items, asset2Items, equalityComparer);
231 foreach (var change
in changes)
233 switch (change.ChangeType)
235 case SharpDiff.Diff3ChangeType.Equal:
236 for (
int i = 0; i < change.Base.Length; i++)
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);
243 case SharpDiff.Diff3ChangeType.MergeFrom1:
244 for (
int i = 0; i < change.From1.Length; i++)
246 var diff3Node =
new Diff3Node(null, asset1Items[change.From1.From + i], null) { ChangeType = Diff3ChangeType.MergeFromAsset1 };
247 AddItem(diff3, diff3Node);
251 case SharpDiff.Diff3ChangeType.MergeFrom2:
252 for (
int i = 0; i < change.From2.Length; i++)
254 var diff3Node =
new Diff3Node(null, null, asset2Items[change.From2.From + i]) { ChangeType = Diff3ChangeType.MergeFromAsset2 };
255 AddItem(diff3, diff3Node);
259 case SharpDiff.Diff3ChangeType.MergeFrom1And2:
260 for (
int i = 0; i < change.From2.Length; i++)
262 var diff3Node =
new Diff3Node(null, asset1Items[change.From1.From + i], asset2Items[change.From2.From + i]) { ChangeType = Diff3ChangeType.MergeFromAsset1And2 };
263 AddItem(diff3, diff3Node);
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;
274 bool tryResolveConflict =
false;
277 if (from1Index >= 0 && from2Index >= 0)
279 if ((change.Base.Length == change.From1.Length && change.Base.Length == change.From2.Length)
280 || (change.From1.Length == change.From2.Length))
282 tryResolveConflict =
true;
285 else if (from1Index >= 0)
287 tryResolveConflict = change.Base.Length == change.From1.Length;
289 else if (from2Index >= 0)
291 tryResolveConflict = change.Base.Length == change.From2.Length;
295 tryResolveConflict =
true;
300 while ((baseIndex >= 0 && baseItems.Count > 0) || (from1Index >= 0 && asset1Items.Count > 0) || (from2Index >= 0 && asset2Items.Count > 0))
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);
306 var diff3Node = tryResolveConflict ?
307 DiffNode(baseItem, asset1Item, asset2Item) :
308 new Diff3Node(baseItem, asset1Item, asset2Item) { ChangeType = Diff3ChangeType.Conflict };
309 AddItem(diff3, diff3Node);
316 if (diff3.Items != null)
318 diff3.Items.Sort((left, right) =>
320 int leftAsset1Index = left.Asset1Node != null ? ((
DataVisitListItem)left.Asset1Node).Index : -1;
321 int rightAsset1Index = right.Asset1Node != null ? ((
DataVisitListItem)right.Asset1Node).Index : -1;
323 return rightAsset1Index.CompareTo(leftAsset1Index);
328 private static DataVisitNode GetSafeFromList(List<DataVisitNode> nodes, ref
int index, ref Span span)
330 if (nodes == null || index < 0)
return null;
331 if (index >= nodes.Count || (span.IsValid && index > span.To))
336 var value = nodes[index];
338 if (index >= nodes.Count) index = -1;
344 var baseItems = baseNode != null ? baseNode.Items : null;
345 var asset1Items = asset1Node != null ? asset1Node.Items : null;
346 var asset2Items = asset2Node != null ? asset2Node.Items : null;
349 var keyNodes =
new Dictionary<object, Diff3DictionaryItem>();
350 Diff3DictionaryItem diff3Item;
351 if (baseItems != null)
355 keyNodes.Add(dataVisitNode.Key,
new Diff3DictionaryItem() {
Base = dataVisitNode });
358 if (asset1Items != null)
362 keyNodes.TryGetValue(dataVisitNode.Key, out diff3Item);
363 diff3Item.Asset1 = dataVisitNode;
364 keyNodes[dataVisitNode.Key] = diff3Item;
367 if (asset2Items != null)
371 keyNodes.TryGetValue(dataVisitNode.Key, out diff3Item);
372 diff3Item.Asset2 = dataVisitNode;
373 keyNodes[dataVisitNode.Key] = diff3Item;
378 foreach (var keyNode
in keyNodes)
380 var valueNode = keyNode.Value;
388 if (valueNode.Asset1 != null && valueNode.Asset2 != null)
390 diffValue = DiffNode(valueNode.Base, valueNode.Asset1, valueNode.Asset2);
392 else if (valueNode.Asset1 == null)
397 diffValue =
new Diff3Node(valueNode.Base, null, valueNode.Asset2) { ChangeType = valueNode.Base == null ? Diff3ChangeType.MergeFromAsset2 : Diff3ChangeType.MergeFromAsset1 };
403 diffValue =
new Diff3Node(valueNode.Base, valueNode.Asset1, null) { ChangeType = valueNode.Base == null ? Diff3ChangeType.MergeFromAsset1 : Diff3ChangeType.Conflict };
406 AddItem(diff3, diffValue);
412 var baseItems = baseNode != null ? baseNode.Items : null;
413 var asset1Items = asset1Node != null ? asset1Node.Items : null;
414 var asset2Items = asset2Node != null ? asset2Node.Items : null;
417 if (baseItems != null)
419 itemCount = baseItems.Count;
422 if (asset1Items != null)
424 var newLength = asset1Items.Count;
425 if (itemCount >= 0 && itemCount != newLength)
427 diff3.ChangeType = Diff3ChangeType.ConflictArraySize;
430 itemCount = newLength;
433 if (asset2Items != null)
435 var newLength = asset2Items.Count;
436 if (itemCount >= 0 && itemCount != newLength)
438 diff3.ChangeType = Diff3ChangeType.ConflictArraySize;
441 itemCount = newLength;
444 for (
int i = 0; i < itemCount; i++)
446 AddItem(diff3, DiffNode(baseItems == null ? null : baseItems[i],
447 asset1Items == null ? null : asset1Items[i],
448 asset2Items == null ? null : asset2Items[i]));
459 private static void AddMember(Diff3Node thisObject, Diff3Node member)
461 if (member == null)
throw new ArgumentNullException(
"member");
462 if (thisObject.Members == null)
463 thisObject.Members =
new List<Diff3Node>();
465 member.Parent = thisObject;
468 thisObject.ChangeType = Diff3ChangeType.Children;
470 thisObject.Members.Add(member);
479 private static void AddItem(Diff3Node thisObject, Diff3Node item)
481 if (item == null)
throw new ArgumentNullException(
"item");
482 if (thisObject.Items == null)
483 thisObject.Items =
new List<Diff3Node>();
485 item.Parent = thisObject;
488 thisObject.ChangeType = Diff3ChangeType.Children;
490 thisObject.Items.Add(item);
493 private NodeDescription GetNodeDescription(
DataVisitNode node)
497 return new NodeDescription();
500 var instanceType = node.InstanceType;
503 instanceType = Nullable.GetUnderlyingType(instanceType);
506 return new NodeDescription(node.
Instance, instanceType);
509 private struct NodeDescription
511 public NodeDescription(
object instance, Type type)
517 public readonly
object Instance;
522 private struct Diff3DictionaryItem
531 private class NodeEqualityComparer : IEqualityComparer<DataVisitNode>
533 private Dictionary<KeyComparison, bool> equalityCache =
new Dictionary<KeyComparison, bool>();
534 private AssetDiff diffManager;
536 public NodeEqualityComparer(AssetDiff diffManager)
538 if (diffManager == null)
throw new ArgumentNullException(
"diffManager");
539 this.diffManager = diffManager;
544 equalityCache.Clear();
549 var key =
new KeyComparison(x, y);
551 if (equalityCache.TryGetValue(key, out result))
556 var diff3 = diffManager.DiffNode(x,
y, null);
557 result = !diff3.FindDifferences().
Any();
558 equalityCache.Add(key, result);
564 return obj == null ? 0 : obj.GetHashCode();
567 private struct KeyComparison : IEquatable<KeyComparison>
580 public bool Equals(KeyComparison other)
582 return ReferenceEquals(Node1, other.Node1) && ReferenceEquals(Node2, other.Node2);
585 public override bool Equals(
object obj)
587 if (ReferenceEquals(null, obj))
return false;
588 return obj is KeyComparison && Equals((KeyComparison)obj);
591 public override int GetHashCode()
595 return ((Node1 != null ? Node1.GetHashCode() : 0) * 397) ^ (Node2 != null ? Node2.GetHashCode() : 0);
599 public static bool operator ==(KeyComparison left, KeyComparison right)
601 return left.Equals(right);
604 public static bool operator !=(KeyComparison left, KeyComparison right)
606 return !left.Equals(right);
Provides a descriptor for a System.Collections.ICollection.
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
static Diff3Node Compute(Asset baseAsset, Asset asset1, Asset asset2)
Defines an item in a list.
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)
Defines a dictionary item (key-value).
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.
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.
Diff3Node Compute(bool forceRecompute=false)
Computes the diff3 between BaseAsset, Asset1 and Asset2.