Пример #1
0
        /// <summary>
        /// Adds an item (array, list or dictionary item) to a <see cref="DataVisitNode"/> instance.
        /// </summary>
        /// <param name="thisObject">The this object.</param>
        /// <param name="item">The item.</param>
        /// <exception cref="System.ArgumentNullException">item</exception>
        private static void AddItem(DataVisitNode thisObject, DataVisitNode item)
        {
            if (item == null) throw new ArgumentNullException("item");
            if (thisObject.Items == null)
                thisObject.Items = new List<DataVisitNode>();

            item.Parent = thisObject;
            thisObject.Items.Add(item);
        }
Пример #2
0
 private static object SafeNodeInstance(DataVisitNode node)
 {
     return node != null ? node.Instance : null;
 }
Пример #3
0
        /// <summary>
        /// Adds a member to a <see cref="DataVisitNode"/> instance.
        /// </summary>
        /// <param name="thisObject">The this object.</param>
        /// <param name="member">The member.</param>
        /// <exception cref="System.ArgumentNullException">member</exception>
        private static void AddMember(DataVisitNode thisObject, DataVisitMember member)
        {
            if (member == null) throw new ArgumentNullException("member");
            if (thisObject.Members == null)
                thisObject.Members = new List<DataVisitNode>();

            member.Parent = thisObject;
            thisObject.Members.Add(member);
        }
Пример #4
0
        private void DiffCollection(Diff3Node diff3, DataVisitNode baseNode, DataVisitNode asset1Node, DataVisitNode asset2Node)
        {
            diff3.Type = Diff3NodeType.Collection;

            var baseItems   = baseNode != null ? baseNode.Items ?? EmptyNodes : EmptyNodes;
            var asset1Items = asset1Node != null ? asset1Node.Items ?? EmptyNodes : EmptyNodes;
            var asset2Items = asset2Node != null ? asset2Node.Items ?? EmptyNodes : EmptyNodes;

            var itemEqualityComparer = equalityComparer;

            var node = diff3.Asset1Node ?? diff3.Asset2Node ?? diff3.BaseNode;

            IEnumerable <Diff3Change> changes;
            bool recurseDiff = false;

            // Find an item in any of the list
            var firstItem = baseItems.FirstOrDefault(item => item.Instance != null) ?? asset1Items.FirstOrDefault(item => item.Instance != null) ?? asset2Items.FirstOrDefault(item => item.Instance != null);

            // For now, in the context of UseOverrideMode and we have identifiers per item, use DiffCollectionByIds instead
            if (UseOverrideMode && firstItem != null)
            {
                if (IdentifiableHelper.IsIdentifiable(firstItem.Instance.GetType()))
                {
                    DiffCollectionByIds(diff3, baseNode, asset1Node, asset2Node);
                    return;
                }
                else if (firstItem.Instance is Guid)
                {
                    DiffCollectionByGuids(diff3, baseNode, asset1Node, asset2Node);
                    return;
                }
            }

            // If we have a DiffUseAsset1Attribute, list of Asset1Node becomes authoritative.
            var dataVisitMember     = node as DataVisitMember;
            var diffMemberAttribute = dataVisitMember != null?dataVisitMember.MemberDescriptor.GetCustomAttributes <DiffMemberAttribute>(true).FirstOrDefault() : null;

            if (diffMemberAttribute != null && diffMemberAttribute.PreferredChange.HasValue)
            {
                diff3.Weight = diffMemberAttribute.Weight;
            }

            if (diffMemberAttribute != null && diffMemberAttribute.PreferredChange.HasValue)
            {
                var diffChange = diffMemberAttribute.PreferredChange.Value == Diff3ChangeType.MergeFromAsset2
                    ? new Diff3Change {
                    ChangeType = SharpDiff.Diff3ChangeType.MergeFrom2, From2 = new Span(0, asset2Items.Count - 1)
                }
                    : new Diff3Change {
                    ChangeType = SharpDiff.Diff3ChangeType.MergeFrom1, From1 = new Span(0, asset1Items.Count - 1)
                };

                changes = new[] { diffChange };
                // TODO: Try to merge back data of matching nodes
            }
            else if (firstItem != null && typeof(IDiffKey).IsAssignableFrom(firstItem.InstanceType))
            {
                // If item implement IDataDiffKey, we will use that as equality key
                changes = Diff3.Compare(
                    baseItems.Select(x => ((IDiffKey)x.Instance).GetDiffKey()).ToList(),
                    asset1Items.Select(x => ((IDiffKey)x.Instance).GetDiffKey()).ToList(),
                    asset2Items.Select(x => ((IDiffKey)x.Instance).GetDiffKey()).ToList());
                recurseDiff = true;
            }
            else
            {
                // Otherwise, do a full node comparison
                itemEqualityComparer.Reset();
                changes = Diff3.Compare(baseItems, asset1Items, asset2Items, itemEqualityComparer);
            }

            foreach (var change in changes)
            {
                switch (change.ChangeType)
                {
                case SharpDiff.Diff3ChangeType.Equal:
                    for (int i = 0; i < change.Base.Length; i++)
                    {
                        var diff3Node = recurseDiff
                                ? DiffNode(baseItems[change.Base.From + i], asset1Items[change.From1.From + i], asset2Items[change.From2.From + i])
                                : new Diff3Node(baseItems[change.Base.From + i], asset1Items[change.From1.From + i], asset2Items[change.From2.From + i])
                        {
                            ChangeType = Diff3ChangeType.None
                        };
                        AddItem(diff3, diff3Node, change.From1.From != 0);
                    }
                    break;

                case SharpDiff.Diff3ChangeType.MergeFrom1:
                    for (int i = 0; i < change.From1.Length; i++)
                    {
                        var diff3Node = new Diff3Node(null, asset1Items[change.From1.From + i], null)
                        {
                            ChangeType = Diff3ChangeType.MergeFromAsset1
                        };
                        AddItem(diff3, diff3Node, change.From1.From != 0);
                    }
                    break;

                case SharpDiff.Diff3ChangeType.MergeFrom2:
                    for (int i = 0; i < change.From2.Length; i++)
                    {
                        var diff3Node = new Diff3Node(null, null, asset2Items[change.From2.From + i])
                        {
                            ChangeType = Diff3ChangeType.MergeFromAsset2
                        };
                        AddItem(diff3, diff3Node, true);
                    }
                    break;

                case SharpDiff.Diff3ChangeType.MergeFrom1And2:
                    for (int i = 0; i < change.From2.Length; i++)
                    {
                        var diff3Node = recurseDiff
                                ? DiffNode(null, asset1Items[change.From1.From + i], asset2Items[change.From2.From + i])
                                : new Diff3Node(null, asset1Items[change.From1.From + i], asset2Items[change.From2.From + i])
                        {
                            ChangeType = Diff3ChangeType.MergeFromAsset1And2
                        };
                        AddItem(diff3, diff3Node, change.From1.From != 0);
                    }
                    break;

                case SharpDiff.Diff3ChangeType.Conflict:
                    int baseIndex  = change.Base.IsValid ? change.Base.From : -1;
                    int from1Index = change.From1.IsValid ? change.From1.From : -1;
                    int from2Index = change.From2.IsValid ? change.From2.From : -1;

                    // If there are changes only from 1 or 2 or base.Length == list1.Length == list2.Length, then try to make a diff per item
                    // else output the conflict as a full conflict
                    bool tryResolveConflict = false;
                    if (baseIndex >= 0)
                    {
                        if (from1Index >= 0 && from2Index >= 0)
                        {
                            if ((change.Base.Length == change.From1.Length && change.Base.Length == change.From2.Length) ||
                                (change.From1.Length == change.From2.Length))
                            {
                                tryResolveConflict = true;
                            }
                        }
                        else if (from1Index >= 0)
                        {
                            tryResolveConflict = change.Base.Length == change.From1.Length;
                        }
                        else if (from2Index >= 0)
                        {
                            tryResolveConflict = change.Base.Length == change.From2.Length;
                        }
                        else
                        {
                            tryResolveConflict = true;
                        }
                    }

                    // Iterate on items
                    while ((baseIndex >= 0 && baseItems.Count > 0) || (from1Index >= 0 && asset1Items.Count > 0) || (from2Index >= 0 && asset2Items.Count > 0))
                    {
                        var baseItem   = GetSafeFromList(baseItems, ref baseIndex, ref change.Base);
                        var asset1Item = GetSafeFromList(asset1Items, ref from1Index, ref change.From1);
                        var asset2Item = GetSafeFromList(asset2Items, ref from2Index, ref change.From2);

                        var diff3Node = tryResolveConflict || recurseDiff?
                                        DiffNode(baseItem, asset1Item, asset2Item) :
                                            new Diff3Node(baseItem, asset1Item, asset2Item)
                        {
                            ChangeType = Diff3ChangeType.Conflict
                        };
                        AddItem(diff3, diff3Node, true);
                    }
                    break;
                }
            }

            // Any missing item? (we can detect this only at the end)
            var newItemCount = diff3.Items != null ? diff3.Items.Count : 0;

            if (asset1Items.Count != newItemCount)
            {
                diff3.ChangeType = Diff3ChangeType.Children;
            }
        }
Пример #5
0
 private void DiffCollectionByIds(Diff3Node diff3, DataVisitNode baseNode, DataVisitNode asset1Node, DataVisitNode asset2Node)
 {
     DiffCollectionByIdsGeneric(diff3, baseNode, asset1Node, asset2Node, IdentifiableHelper.GetId, DiffNode);
 }
Пример #6
0
        private Diff3Node DiffNodeWithUniformType(DataVisitNode baseNode, DataVisitNode asset1Node, DataVisitNode asset2Node)
        {
            var baseNodeDesc   = GetNodeDescription(baseNode);
            var asset1NodeDesc = GetNodeDescription(asset1Node);
            var asset2NodeDesc = GetNodeDescription(asset2Node);

            var node = baseNode ?? asset1Node ?? asset2Node;
            var type = baseNodeDesc.Type ?? asset1NodeDesc.Type ?? asset2NodeDesc.Type;

            var diff3 = new Diff3Node(baseNode, asset1Node, asset2Node)
            {
                InstanceType = type
            };

            if (type == null)
            {
                // All nodes are null. This should only happen as part of a temporary diff in DiffNode()
                diff3.ChangeType = Diff3ChangeType.None;
            }
            else if (IsComparableType(node.HasMembers, type))
            {
                DiffValue(diff3, ref baseNodeDesc, ref asset1NodeDesc, ref asset2NodeDesc);
            }
            else
            {
                DiffMembers(diff3, baseNode, asset1Node, asset2Node);

                if (DictionaryDescriptor.IsDictionary(type))
                {
                    DiffDictionary(diff3, baseNode, asset1Node, asset2Node);
                }
                else if (CollectionDescriptor.IsCollection(type))
                {
                    DiffCollection(diff3, baseNode, asset1Node, asset2Node);
                }
                else if (type.IsArray)
                {
                    DiffArray(diff3, baseNode, asset1Node, asset2Node);
                }
            }

            return(diff3);
        }
Пример #7
0
        private void DiffMembers(Diff3Node diff3, DataVisitNode baseNode, DataVisitNode asset1Node, DataVisitNode asset2Node)
        {
            var baseMembers   = baseNode != null ? baseNode.Members : null;
            var asset1Members = asset1Node != null ? asset1Node.Members : null;
            var asset2Members = asset2Node != null ? asset2Node.Members : null;
            int memberCount   = 0;

            if (baseMembers != null)
            {
                memberCount = baseMembers.Count;
            }
            else if (asset1Members != null)
            {
                memberCount = asset1Members.Count;
            }
            else if (asset2Members != null)
            {
                memberCount = asset2Members.Count;
            }

            for (int i = 0; i < memberCount; i++)
            {
                AddMember(diff3, DiffNode(baseMembers == null ? null : baseMembers[i],
                                          asset1Members == null ? null : asset1Members[i],
                                          asset2Members == null ? null : asset2Members[i]));
            }
        }
Пример #8
0
        private Diff3Node DiffNode(DataVisitNode baseNode, DataVisitNode asset1Node, DataVisitNode asset2Node)
        {
            if (UseOverrideMode)
            {
                return(DiffNodeByOverride(baseNode, asset1Node, asset2Node));
            }

            var diff3 = new Diff3Node(baseNode, asset1Node, asset2Node);

            var baseNodeDesc   = GetNodeDescription(baseNode);
            var asset1NodeDesc = GetNodeDescription(asset1Node);
            var asset2NodeDesc = GetNodeDescription(asset2Node);

            if (asset1NodeDesc.Type == asset2NodeDesc.Type)
            {
                if (baseNodeDesc.Type == asset1NodeDesc.Type)
                {
                    // If all types are the same, perform a normal diff.
                    return(DiffNodeWithUniformType(baseNode, asset1Node, asset2Node));
                }
                else
                {
                    // If base has a different type, but asset1 and asset2 are equal, use them. Otherwise there is a conflict with base.
                    var temp = DiffNodeWithUniformType(asset1Node, asset1Node, asset2Node);
                    diff3.ChangeType   = temp.ChangeType == Diff3ChangeType.None ? Diff3ChangeType.MergeFromAsset1And2 : Diff3ChangeType.Conflict;
                    diff3.InstanceType = asset1NodeDesc.Type;
                }
            }
            else if (baseNodeDesc.Type == asset1NodeDesc.Type)
            {
                // If base and asset 1 are equal, use asset 2.
                var temp = DiffNodeWithUniformType(baseNode, asset1Node, asset1Node);
                diff3.ChangeType   = temp.ChangeType == Diff3ChangeType.None ? Diff3ChangeType.MergeFromAsset2 : Diff3ChangeType.Conflict;
                diff3.InstanceType = asset2NodeDesc.Type;
            }
            else if (baseNodeDesc.Type == asset2NodeDesc.Type)
            {
                // If base and asset 2 are equal, use asset 1.
                var temp = DiffNodeWithUniformType(baseNode, asset2Node, asset2Node);
                diff3.ChangeType   = temp.ChangeType == Diff3ChangeType.None ? Diff3ChangeType.MergeFromAsset1 : Diff3ChangeType.Conflict;
                diff3.InstanceType = asset1NodeDesc.Type;
            }
            else
            {
                // If one asset is unspecified, use the other.
                // If all types are different, there is a type conflict.
                if (asset1Node == null)
                {
                    diff3.ChangeType   = Diff3ChangeType.MergeFromAsset2;
                    diff3.InstanceType = asset2NodeDesc.Type;
                }
                else if (asset2Node == null)
                {
                    diff3.ChangeType   = Diff3ChangeType.MergeFromAsset1;
                    diff3.InstanceType = asset1NodeDesc.Type;
                }
                else
                {
                    diff3.ChangeType = Diff3ChangeType.ConflictType;
                }
            }
            return(diff3);
        }
Пример #9
0
        private Diff3Node DiffNodeByOverride(DataVisitNode baseNode, DataVisitNode asset1Node, DataVisitNode asset2Node)
        {
            var diff3 = new Diff3Node(baseNode, asset1Node, asset2Node);

            // TODO Add merge when types are non uniform but we are in UseOverrideMode

            var baseNodeDesc   = GetNodeDescription(baseNode);
            var asset1NodeDesc = GetNodeDescription(asset1Node);
            var asset2NodeDesc = GetNodeDescription(asset2Node);

            var memberBase      = (diff3.BaseNode) as DataVisitMember;
            var memberAsset1    = (diff3.Asset1Node) as DataVisitMember;
            var memberAsset2    = (diff3.Asset2Node) as DataVisitMember;
            var dataVisitMember = memberBase ?? memberAsset1 ?? memberAsset2;

            // Currently, only properties/fields can have override information, so we process them separately here
            if (dataVisitMember != null)
            {
                var type = baseNodeDesc.Type ?? asset1NodeDesc.Type ?? asset2NodeDesc.Type;
                diff3.InstanceType = type;

                if (IsComparableType(dataVisitMember.HasMembers, type))
                {
                    ApplyOverrideOnValue(diff3);
                }
                else
                {
                    var baseOverride    = memberBase?.Parent?.Instance?.GetOverride(memberBase.MemberDescriptor) ?? OverrideType.Base;
                    var member1Override = memberAsset1?.Parent?.Instance?.GetOverride(memberAsset1.MemberDescriptor) ?? OverrideType.Base;
                    var member2Override = memberAsset2?.Parent?.Instance?.GetOverride(memberAsset2.MemberDescriptor) ?? OverrideType.Base;

                    //            base         asset1       asset2
                    //            ----         ------       ------
                    // Type:       TB            T1           T2
                    // Override: whatever    base|whatever     (new|base)+sealed    -> If member on asset2 is sealed, or member on asset1 is base
                    //
                    //   if T1 == T2 and TB == T1,  merge all of them
                    //   If T1 == T2
                    //       merge T1-T2
                    //   if T1 != T2
                    //       replace asset1 by asset2 value, don't perform merge on instance
                    //
                    //   In all case, replace override on asset1 by Base|Sealed

                    if (member2Override.IsSealed() || member1Override.IsBase())
                    {
                        if (asset1NodeDesc.Type == asset2NodeDesc.Type)
                        {
                            diff3 = DiffNodeWithUniformType(baseNodeDesc.Type == asset1NodeDesc.Type ? baseNode : asset2Node, asset1Node, asset2Node);
                        }
                        else
                        {
                            diff3.InstanceType = asset2NodeDesc.Type;
                            diff3.ChangeType   = Diff3ChangeType.MergeFromAsset2;
                        }

                        // Force asset1 override to be base|sealed
                        diff3.FinalOverride = OverrideType.Base;
                        if (member2Override.IsSealed())
                        {
                            diff3.FinalOverride |= OverrideType.Sealed;
                        }
                    }
                    else
                    {
                        if (asset1NodeDesc.Type == asset2NodeDesc.Type)
                        {
                            diff3 = DiffNodeWithUniformType(baseNodeDesc.Type == asset1NodeDesc.Type ? baseNode : asset2Node, asset1Node, asset2Node);
                        }
                        else
                        {
                            diff3.InstanceType = asset1NodeDesc.Type;
                            diff3.ChangeType   = Diff3ChangeType.MergeFromAsset1;
                        }

                        diff3.FinalOverride = member1Override;
                    }

                    // If base changed from Sealed to non-sealed, and asset1 was base|sealed, change it back to plain base.
                    if (baseOverride.IsSealed() && !member2Override.IsSealed() && member1Override == (OverrideType.Base | OverrideType.Sealed))
                    {
                        diff3.FinalOverride = OverrideType.Base;
                    }
                }
            }
            else
            {
                if (baseNodeDesc.Type != null &&
                    asset1NodeDesc.Type != null &&
                    asset2NodeDesc.Type != null)
                {
                    // cases : base  asset1  asset2
                    // case 1:  T      T       T      => Merge all instances
                    if (baseNodeDesc.Type == asset1NodeDesc.Type && baseNodeDesc.Type == asset2NodeDesc.Type)
                    {
                        // If all types are the same, perform a normal diff.
                        return(DiffNodeWithUniformType(baseNode, asset1Node, asset2Node));
                    }

                    // case 2:  T      T1      T      => Only from asset1
                    if (baseNodeDesc.Type != asset1NodeDesc.Type && baseNodeDesc.Type == asset2NodeDesc.Type)
                    {
                        diff3.ChangeType   = Diff3ChangeType.MergeFromAsset1;
                        diff3.InstanceType = asset1NodeDesc.Type;
                        return(diff3);
                    }

                    // case 3:  T      T       T1     => Only from asset2
                    if (baseNodeDesc.Type == asset1NodeDesc.Type && baseNodeDesc.Type != asset2NodeDesc.Type)
                    {
                        diff3.ChangeType   = Diff3ChangeType.MergeFromAsset2;
                        diff3.InstanceType = asset2NodeDesc.Type;
                        return(diff3);
                    }
                }
                else if (baseNodeDesc.Type != null && asset1NodeDesc.Type != null && baseNodeDesc.Type == asset1NodeDesc.Type)
                {
                    // case 3:  T      T       null     => Merge from asset2 (set null on asset1)
                    diff3.ChangeType   = Diff3ChangeType.MergeFromAsset2;
                    diff3.InstanceType = null;
                    return(diff3);
                }

                // other cases: Always merge from asset1 (should be a conflict, but we assume that we can only use asset1
                diff3.ChangeType   = Diff3ChangeType.MergeFromAsset1;
                diff3.InstanceType = asset1NodeDesc.Type;
                return(diff3);
            }

            return(diff3);
        }
Пример #10
0
 public Diff3Node(DataVisitNode baseNode, DataVisitNode asset1Node, DataVisitNode asset2Node)
 {
     BaseNode   = baseNode;
     Asset1Node = asset1Node;
     Asset2Node = asset2Node;
 }
Пример #11
0
 public KeyComparison(DataVisitNode node1, DataVisitNode node2)
 {
     Node1 = node1;
     Node2 = node2;
 }
Пример #12
0
        private void DiffArray(Diff3Node diff3, DataVisitNode baseNode, DataVisitNode asset1Node, DataVisitNode asset2Node)
        {
            var baseItems   = baseNode != null ? baseNode.Items : null;
            var asset1Items = asset1Node != null ? asset1Node.Items : null;
            var asset2Items = asset2Node != null ? asset2Node.Items : null;
            int itemCount   = -1;

            if (baseItems != null)
            {
                itemCount = baseItems.Count;
            }

            if (asset1Items != null)
            {
                var newLength = asset1Items.Count;
                if (itemCount >= 0 && itemCount != newLength)
                {
                    diff3.ChangeType = Diff3ChangeType.ConflictArraySize;
                    return;
                }
                itemCount = newLength;
            }

            if (asset2Items != null)
            {
                var newLength = asset2Items.Count;
                if (itemCount >= 0 && itemCount != newLength)
                {
                    diff3.ChangeType = Diff3ChangeType.ConflictArraySize;
                    return;
                }
                itemCount = newLength;
            }

            for (int i = 0; i < itemCount; i++)
            {
                AddItem(diff3, DiffNode(baseItems == null ? null : baseItems[i],
                                        asset1Items == null ? null : asset1Items[i],
                                        asset2Items == null ? null : asset2Items[i]));
            }
        }
Пример #13
0
        private void DiffDictionary(Diff3Node diff3, DataVisitNode baseNode, DataVisitNode asset1Node, DataVisitNode asset2Node)
        {
            diff3.Type = Diff3NodeType.Dictionary;

            var baseItems   = baseNode != null ? baseNode.Items : null;
            var asset1Items = asset1Node != null ? asset1Node.Items : null;
            var asset2Items = asset2Node != null ? asset2Node.Items : null;

            // Build dictionary: key => base, v1, v2
            var keyNodes = new Dictionary <object, Diff3DictionaryItem>();
            Diff3DictionaryItem diff3Item;

            if (baseItems != null)
            {
                foreach (var dataVisitNode in baseItems.OfType <DataVisitDictionaryItem>())
                {
                    keyNodes.Add(dataVisitNode.Key, new Diff3DictionaryItem()
                    {
                        Base = dataVisitNode
                    });
                }
            }
            if (asset1Items != null)
            {
                foreach (var dataVisitNode in asset1Items.OfType <DataVisitDictionaryItem>())
                {
                    keyNodes.TryGetValue(dataVisitNode.Key, out diff3Item);
                    diff3Item.Asset1            = dataVisitNode;
                    keyNodes[dataVisitNode.Key] = diff3Item;
                }
            }
            if (asset2Items != null)
            {
                foreach (var dataVisitNode in asset2Items.OfType <DataVisitDictionaryItem>())
                {
                    keyNodes.TryGetValue(dataVisitNode.Key, out diff3Item);
                    diff3Item.Asset2            = dataVisitNode;
                    keyNodes[dataVisitNode.Key] = diff3Item;
                }
            }

            // Perform merge on dictionary
            foreach (var keyNode in keyNodes)
            {
                var valueNode = keyNode.Value;

                Diff3Node diffValue;

                //  base     v1      v2     action
                //  ----     --      --     ------
                //   a        b       c     Diff(a,b,c)
                //  null      b       c     Diff(null, b, c)
                if (valueNode.Asset1 != null && valueNode.Asset2 != null)
                {
                    diffValue = DiffNode(valueNode.Base, valueNode.Asset1, valueNode.Asset2);
                }
                else if (valueNode.Asset1 == null)
                {
                    //   a       null     c     MergeFrom1 (unchanged)
                    //  null     null     c     MergeFrom2
                    //   a       null    null   MergeFrom1 (unchanged)
                    diffValue = new Diff3Node(valueNode.Base, null, valueNode.Asset2)
                    {
                        ChangeType = valueNode.Base == null ? Diff3ChangeType.MergeFromAsset2 : Diff3ChangeType.MergeFromAsset1
                    };
                }
                else
                {
                    //   a        a      null   MergeFrom2 (removed)
                    //   a        b      null   Conflict
                    //  null      b      null   MergeFrom1 (unchanged)
                    var changeType = Diff3ChangeType.MergeFromAsset1;
                    if (valueNode.Base != null)
                    {
                        var diffNode = DiffNode(valueNode.Base, valueNode.Asset1, valueNode.Base);
                        changeType = diffNode.FindDifferences().Any()
                            ? Diff3ChangeType.Conflict
                            : Diff3ChangeType.MergeFromAsset2;
                    }

                    diffValue = new Diff3Node(valueNode.Base, valueNode.Asset1, null)
                    {
                        ChangeType = changeType
                    };
                }

                AddItem(diff3, diffValue);
            }
        }
Пример #14
0
        private Diff3Node DiffNode(DataVisitNode baseNode, DataVisitNode asset1Node, DataVisitNode asset2Node)
        {
            var diff3 = new Diff3Node(baseNode, asset1Node, asset2Node);

            var baseNodeDesc   = GetNodeDescription(baseNode);
            var asset1NodeDesc = GetNodeDescription(asset1Node);
            var asset2NodeDesc = GetNodeDescription(asset2Node);

            bool hasMembers = false;

            Type type     = null;
            Type nodeType = null;

            if (baseNodeDesc.Type != null)
            {
                type       = baseNodeDesc.Type;
                hasMembers = baseNode.HasMembers;
                nodeType   = baseNode.GetType();
            }

            if (asset1NodeDesc.Type != null)
            {
                if (type == null)
                {
                    type       = asset1NodeDesc.Type;
                    hasMembers = asset1Node.HasMembers;
                    nodeType   = asset1Node.GetType();
                }
                else
                {
                    if (nodeType != asset1Node.GetType())
                    {
                        diff3.ChangeType = Diff3ChangeType.InvalidNodeType;
                        return(diff3);
                    }

                    if (type != asset1NodeDesc.Type)
                    {
                        diff3.ChangeType = Diff3ChangeType.ConflictType;
                        return(diff3);
                    }
                }
            }

            if (asset2NodeDesc.Type != null)
            {
                if (type == null)
                {
                    type       = asset2NodeDesc.Type;
                    hasMembers = asset2Node.HasMembers;
                }
                else
                {
                    if (nodeType != asset2Node.GetType())
                    {
                        diff3.ChangeType = Diff3ChangeType.InvalidNodeType;
                        return(diff3);
                    }

                    if (type != asset2NodeDesc.Type)
                    {
                        diff3.ChangeType = Diff3ChangeType.ConflictType;
                        return(diff3);
                    }
                }
            }

            if (type == null)
            {
                return(diff3);
            }

            diff3.InstanceType = type;

            if (IsComparableType(hasMembers, type))
            {
                DiffValue(diff3, ref baseNodeDesc, ref asset1NodeDesc, ref asset2NodeDesc);
                return(diff3);
            }

            // Diff members
            DiffMembers(diff3, baseNode, asset1Node, asset2Node);

            if (DictionaryDescriptor.IsDictionary(type))
            {
                DiffDictionary(diff3, baseNode, asset1Node, asset2Node);
            }
            else if (CollectionDescriptor.IsCollection(type))
            {
                DiffCollection(diff3, baseNode, asset1Node, asset2Node);
            }
            else if (type.IsArray)
            {
                DiffArray(diff3, baseNode, asset1Node, asset2Node);
            }

            return(diff3);
        }
Пример #15
0
 /// <summary>
 /// Diff a collection of Guid (supposed to be unique)
 /// </summary>
 /// <param name="diff3"></param>
 /// <param name="baseNode"></param>
 /// <param name="asset1Node"></param>
 /// <param name="asset2Node"></param>
 private void DiffCollectionByGuids(Diff3Node diff3, DataVisitNode baseNode, DataVisitNode asset1Node, DataVisitNode asset2Node)
 {
     DiffCollectionByIdsGeneric(diff3, baseNode, asset1Node, asset2Node, o => (Guid)o, (a1, a2, a3) => new Diff3Node(a1, a2, a3)
     {
         InstanceType = typeof(Guid), ChangeType = Diff3ChangeType.MergeFromAsset1
     });
 }
Пример #16
0
        private void DiffCollectionByIdsGeneric(Diff3Node diff3, DataVisitNode baseNode, DataVisitNode asset1Node, DataVisitNode asset2Node, Func <object, Guid> idGetter, Func <DataVisitNode, DataVisitNode, DataVisitNode, Diff3Node> diff3Getter)
        {
            var baseItems   = baseNode != null ? baseNode.Items ?? EmptyNodes : EmptyNodes;
            var asset1Items = asset1Node != null ? asset1Node.Items ?? EmptyNodes : EmptyNodes;
            var asset2Items = asset2Node != null ? asset2Node.Items ?? EmptyNodes : EmptyNodes;

            // Pre-build dictionary
            var items = new Dictionary <Guid, Diff3CollectionByIdItem>();

            for (int i = 0; i < baseItems.Count; i++)
            {
                var item = baseItems[i];
                var id   = idGetter(item.Instance);
                Diff3CollectionByIdItem entry;
                items.TryGetValue(id, out entry);
                entry.BaseIndex = i;
                items[id]       = entry;
            }
            for (int i = 0; i < asset1Items.Count; i++)
            {
                var item = asset1Items[i];
                var id   = idGetter(item.Instance);
                Diff3CollectionByIdItem entry;
                items.TryGetValue(id, out entry);
                entry.Asset1Index = i;
                items[id]         = entry;
            }
            for (int i = 0; i < asset2Items.Count; i++)
            {
                var item = asset2Items[i];
                var id   = idGetter(item.Instance);
                Diff3CollectionByIdItem entry;
                items.TryGetValue(id, out entry);
                entry.Asset2Index = i;
                items[id]         = entry;
            }

            foreach (var idAndEntry in items.OrderBy(item => item.Value.Asset1Index ?? item.Value.Asset2Index ?? item.Value.BaseIndex ?? 0))
            {
                var entry = idAndEntry.Value;

                bool hasBase   = entry.BaseIndex.HasValue;
                bool hasAsset1 = entry.Asset1Index.HasValue;
                bool hasAsset2 = entry.Asset2Index.HasValue;

                int baseIndex   = entry.BaseIndex ?? -1;
                int asset1Index = entry.Asset1Index ?? -1;
                int asset2Index = entry.Asset2Index ?? -1;

                if (hasBase && hasAsset1 && hasAsset2)
                {
                    // If asset was already in base and is still valid for asset1 and asset2
                    var diff3Node = diff3Getter(baseNode.Items[baseIndex], asset1Node.Items[asset1Index], asset2Node.Items[asset2Index]);
                    // Use index from asset2 if baseIndex = asset1Index, else use from asset1 index
                    AddItemByPosition(diff3, diff3Node, baseIndex == asset1Index ? asset2Index : asset1Index, true);
                }
                else if (!hasBase)
                {
                    // If no base, there is at least either asset1 or asset2 but not both, as they can't have the same id
                    if (hasAsset1)
                    {
                        var diff3Node = new Diff3Node(null, asset1Node.Items[asset1Index], null)
                        {
                            ChangeType = Diff3ChangeType.MergeFromAsset1, InstanceType = asset1Node.Items[asset1Index].InstanceType
                        };
                        AddItemByPosition(diff3, diff3Node, asset1Index, true);
                    }
                    else if (hasAsset2)
                    {
                        var diff3Node = new Diff3Node(null, null, asset2Node.Items[asset2Index])
                        {
                            ChangeType = Diff3ChangeType.MergeFromAsset2, InstanceType = asset2Node.Items[asset2Index].InstanceType
                        };
                        AddItemByPosition(diff3, diff3Node, asset2Index, true);
                    }
                }
                else
                {
                    // either item was removed from asset1 or asset2, so we assume that it is completely removed
                    // (not strictly correct, because one item could change a sub-property, but we assume that when a top-level list item is removed, we remove it, even if it has changed internally)
                }
            }

            // The diff will only applied to children (we don't support members)
            diff3.ChangeType = Diff3ChangeType.Children;

            // Cleanup any hole in the list
            // If new items are found, just cleanup
            if (diff3.Items != null)
            {
                // Because in the previous loop, we can add some hole while trying to merge index (null nodes), we need to remove them from here.
                for (int i = diff3.Items.Count - 1; i >= 0; i--)
                {
                    if (diff3.Items[i] == null)
                    {
                        diff3.Items.RemoveAt(i);
                    }
                }
            }
        }