Exemple #1
0
        private static void DiffValue(Diff3Node diff3, ref NodeDescription baseNodeDesc, ref NodeDescription asset1NodeDesc, ref NodeDescription asset2NodeDesc)
        {
            var node            = diff3.Asset1Node ?? diff3.Asset2Node ?? diff3.BaseNode;
            var dataVisitMember = node as DataVisitMember;

            if (dataVisitMember != null)
            {
                var specificAssetAttribute = dataVisitMember.MemberDescriptor.GetCustomAttributes <DiffUseSpecificAssetAttribute>(true).FirstOrDefault();
                if (specificAssetAttribute != null)
                {
                    if (specificAssetAttribute is DiffUseAsset1Attribute)
                    {
                        diff3.ChangeType = Diff3ChangeType.MergeFromAsset1;
                    }
                    else if (specificAssetAttribute is DiffUseAsset2Attribute)
                    {
                        diff3.ChangeType = Diff3ChangeType.MergeFromAsset2;
                    }
                    else
                    {
                        throw new InvalidOperationException();
                    }
                    return;
                }
            }

            var baseAsset1Equals = Equals(baseNodeDesc.Instance, asset1NodeDesc.Instance);
            var baseAsset2Equals = Equals(baseNodeDesc.Instance, asset2NodeDesc.Instance);
            var asset1And2Equals = Equals(asset1NodeDesc.Instance, asset2NodeDesc.Instance);

            diff3.ChangeType = baseAsset1Equals && baseAsset2Equals
                ? Diff3ChangeType.None
                : baseAsset2Equals ? Diff3ChangeType.MergeFromAsset1 : baseAsset1Equals ? Diff3ChangeType.MergeFromAsset2 : asset1And2Equals ? Diff3ChangeType.MergeFromAsset1And2 : Diff3ChangeType.Conflict;
        }
Exemple #2
0
        private static void ApplyOverrideOnValue(Diff3Node diff3, bool isClassType = false)
        {
            var memberBase      = (diff3.BaseNode) as DataVisitMember;
            var memberAsset1    = (diff3.Asset1Node) as DataVisitMember;
            var memberAsset2    = (diff3.Asset2Node) as DataVisitMember;
            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;

            if (member2Override.IsSealed())
            {
                diff3.ChangeType = Diff3ChangeType.MergeFromAsset2;
                // Force asset1 override to be base|sealed
                diff3.FinalOverride = OverrideType.Base | OverrideType.Sealed;
            }
            else if (member1Override.IsBase())
            {
                diff3.ChangeType    = Diff3ChangeType.MergeFromAsset2;
                diff3.FinalOverride = member1Override;
            }
            else
            {
                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;
            }
        }
Exemple #3
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]));
            }
        }
Exemple #4
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
     });
 }
Exemple #5
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);

            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);
        }
Exemple #6
0
        /// <summary>
        /// Adds an item (array, list or dictionary item) to this 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(Diff3Node thisObject, Diff3Node item, bool hasChildrenChanged = false)
        {
            if (thisObject.Items == null)
            {
                thisObject.Items = new List <Diff3Node>();
            }

            // Add at the end of the list
            AddItemByPosition(thisObject, item, thisObject.Items.Count, hasChildrenChanged);
        }
Exemple #7
0
        private static void DiffValue(Diff3Node diff3, ref NodeDescription baseNodeDesc, ref NodeDescription asset1NodeDesc, ref NodeDescription asset2NodeDesc)
        {
            var baseAsset1Equals = Equals(baseNodeDesc.Instance, asset1NodeDesc.Instance);
            var baseAsset2Equals = Equals(baseNodeDesc.Instance, asset2NodeDesc.Instance);
            var asset1And2Equals = Equals(asset1NodeDesc.Instance, asset2NodeDesc.Instance);

            diff3.ChangeType = baseAsset1Equals && baseAsset2Equals
                ? Diff3ChangeType.None
                : baseAsset2Equals ? Diff3ChangeType.MergeFromAsset1 : baseAsset1Equals ? Diff3ChangeType.MergeFromAsset2 : asset1And2Equals ? Diff3ChangeType.MergeFromAsset1And2 : Diff3ChangeType.Conflict;
        }
        /// <summary>
        /// The merge policy is expecting <c>asset2</c> to be the new base of <c>asset1</c>, and <c>base</c> the current base of <c>asset1</c>.
        /// In case of conflicts:
        /// <ul>
        /// <li>If there is a type conflict, leave as-is</li>
        /// <li>If there is a <see cref="Diff3ChangeType.Conflict"/>:
        ///     <ul>
        ///      <li>If it is on a member, and both <c>asset1</c> and <c>asset2</c> are not null and changes were done
        ///      in <c>asset2</c> but not in <c>asset1</c>, select <c>asset2</c> else select <c>asset1</c></li>
        ///      <li>If it is on a list item, we can't resolve it and leave the conflict as-is</li>
        ///      <li>If it is on a array item, we select <c>asset2</c></li>
        ///     </ul>
        /// </li>
        /// </ul>
        /// </summary>
        /// <param name="diff3Node">The diff3 node.</param>
        /// <returns>.</returns>
        public static Diff3ChangeType MergePolicyAsset2AsNewBaseOfAsset1(Diff3Node diff3Node)
        {
            var hasConflicts = diff3Node.ChangeType > Diff3ChangeType.Conflict;

            if (hasConflicts)
            {
                return(diff3Node.ChangeType);
            }

            switch (diff3Node.ChangeType)
            {
            case Diff3ChangeType.Conflict:
            case Diff3ChangeType.MergeFromAsset2:

                var dataNode = diff3Node.Asset2Node ?? diff3Node.Asset1Node ?? diff3Node.BaseNode;

                if (dataNode is DataVisitMember)
                {
                    if (diff3Node.Asset1Node != null && diff3Node.Asset2Node != null && diff3Node.ChangeType == Diff3ChangeType.MergeFromAsset2)
                    {
                        return(Diff3ChangeType.MergeFromAsset2);
                    }

                    return(Diff3ChangeType.MergeFromAsset1);
                }

                if (dataNode is DataVisitListItem)
                {
                    // If we have a conflict in a list, we can't really resolve it here, so we are breaking out of the loop
                    if (diff3Node.ChangeType == Diff3ChangeType.Conflict)
                    {
                        return(Diff3ChangeType.Conflict);
                    }
                    return(Diff3ChangeType.MergeFromAsset2);
                }

                if (dataNode is DataVisitDictionaryItem)
                {
                    return(Diff3ChangeType.MergeFromAsset2);
                }

                if (dataNode is DataVisitArrayItem)
                {
                    if (diff3Node.Asset1Node != null && ((DataVisitArrayItem)diff3Node.Asset1Node).Index == ((DataVisitArrayItem)dataNode).Index)
                    {
                        return(Diff3ChangeType.MergeFromAsset2);
                    }
                }
                break;
            }

            return(diff3Node.ChangeType);
        }
Exemple #9
0
        /// <summary>
        /// Computes the diff3 between <see cref="BaseAsset" />, <see cref="Asset1" /> and <see cref="Asset2" />.
        /// </summary>
        /// <param name="forceRecompute">if set to <c>true</c> force to recompute the diff.</param>
        /// <returns>The result of the diff. This result is cached so next call will return it directly.</returns>
        public Diff3Node Compute(bool forceRecompute = false)
        {
            if (computed != null && !forceRecompute)
            {
                return(computed);
            }
            var baseNodes   = DataVisitNodeBuilder.Run(TypeDescriptorFactory.Default, baseAsset);
            var asset1Nodes = DataVisitNodeBuilder.Run(TypeDescriptorFactory.Default, asset1);
            var asset2Nodes = DataVisitNodeBuilder.Run(TypeDescriptorFactory.Default, asset2);

            computed = DiffNode(baseNodes, asset1Nodes, asset2Nodes);
            return(computed);
        }
Exemple #10
0
        /// <summary>
        /// Adds a member to this 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(Diff3Node thisObject, Diff3Node member)
        {
            if (member == null)
            {
                throw new ArgumentNullException("member");
            }
            if (thisObject.Members == null)
            {
                thisObject.Members = new List <Diff3Node>();
            }

            member.Parent = thisObject;
            if (member.ChangeType != Diff3ChangeType.None)
            {
                thisObject.ChangeType = Diff3ChangeType.Children;
            }
            thisObject.Members.Add(member);
        }
Exemple #11
0
        /// <summary>
        /// Adds an item (array, list or dictionary item) to this 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(Diff3Node thisObject, Diff3Node item)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }
            if (thisObject.Items == null)
            {
                thisObject.Items = new List <Diff3Node>();
            }

            item.Parent = thisObject;
            if (item.ChangeType != Diff3ChangeType.None)
            {
                thisObject.ChangeType = Diff3ChangeType.Children;
            }
            thisObject.Items.Add(item);
        }
Exemple #12
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);
        }
Exemple #13
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]));
            }

            // TODO: Add diff by ids on array
        }
Exemple #14
0
        /// <summary>
        /// Adds an item (array, list or dictionary item) to this 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(Diff3Node thisObject, Diff3Node item, bool hasChildrenChanged = false)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }
            if (thisObject.Items == null)
            {
                thisObject.Items = new List <Diff3Node>();
            }

            item.Parent = thisObject;
            if (item.ChangeType != Diff3ChangeType.None || hasChildrenChanged)
            {
                thisObject.ChangeType = Diff3ChangeType.Children;
            }
            item.Index = thisObject.Items.Count;
            thisObject.Items.Add(item);
        }
Exemple #15
0
        /// <summary>
        /// Computes the diff3 between <see cref="BaseAsset" />, <see cref="Asset1" /> and <see cref="Asset2" />.
        /// </summary>
        /// <param name="forceRecompute">if set to <c>true</c> force to recompute the diff.</param>
        /// <returns>The result of the diff. This result is cached so next call will return it directly.</returns>
        public Diff3Node Compute(bool forceRecompute = false)
        {
            if (computed != null && !forceRecompute)
            {
                return(computed);
            }

            // If asset implement IDiffResolver, run callback
            if (baseAsset is IDiffResolver)
            {
                ((IDiffResolver)baseAsset).BeforeDiff(baseAsset, asset1, asset2);
            }

            var baseNodes   = DataVisitNodeBuilder.Run(TypeDescriptorFactory.Default, baseAsset);
            var asset1Nodes = DataVisitNodeBuilder.Run(TypeDescriptorFactory.Default, asset1);
            var asset2Nodes = DataVisitNodeBuilder.Run(TypeDescriptorFactory.Default, asset2);

            computed = DiffNode(baseNodes, asset1Nodes, asset2Nodes);
            return(computed);
        }
Exemple #16
0
        private void DiffValue(Diff3Node diff3, ref NodeDescription baseNodeDesc, ref NodeDescription asset1NodeDesc, ref NodeDescription asset2NodeDesc)
        {
            var node            = diff3.Asset1Node ?? diff3.Asset2Node ?? diff3.BaseNode;
            var dataVisitMember = node as DataVisitMember;

            if (dataVisitMember != null)
            {
                var diffMember = dataVisitMember.MemberDescriptor.GetCustomAttributes <DiffMemberAttribute>(true).FirstOrDefault();
                if (diffMember != null)
                {
                    if (diffMember.PreferredChange.HasValue)
                    {
                        diff3.ChangeType = diffMember.PreferredChange.Value;
                    }

                    diff3.Weight = diffMember.Weight;
                }
            }

            var instanceType = asset1NodeDesc.Instance?.GetType() ?? asset2NodeDesc.Instance?.GetType();

            object baseInstance   = baseNodeDesc.Instance;
            object asset1Instance = asset1NodeDesc.Instance;
            object asset2Instance = asset2NodeDesc.Instance;

            // If this is an identifiable type (but we are for example not visiting its member), compare only the Ids instead
            if (UseOverrideMode && instanceType != null && IdentifiableHelper.IsIdentifiable(instanceType))
            {
                baseInstance   = IdentifiableHelper.GetId(baseInstance);
                asset1Instance = IdentifiableHelper.GetId(asset1Instance);
                asset2Instance = IdentifiableHelper.GetId(asset2Instance);
            }

            var baseAsset1Equals = Equals(baseInstance, asset1Instance);
            var baseAsset2Equals = Equals(baseInstance, asset2Instance);
            var asset1And2Equals = Equals(asset1Instance, asset2Instance);

            diff3.ChangeType = baseAsset1Equals && baseAsset2Equals
                ? Diff3ChangeType.None
                : baseAsset2Equals ? Diff3ChangeType.MergeFromAsset1 : baseAsset1Equals ? Diff3ChangeType.MergeFromAsset2 : asset1And2Equals ? Diff3ChangeType.MergeFromAsset1And2 : Diff3ChangeType.Conflict;
        }
Exemple #17
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 (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);
        }
Exemple #18
0
        private static void AddItemByPosition(Diff3Node thisObject, Diff3Node item, int position, bool hasChildrenChanged = false)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }
            if (thisObject.Items == null)
            {
                thisObject.Items = new List <Diff3Node>();
            }

            item.Parent = thisObject;
            if (item.ChangeType != Diff3ChangeType.None || hasChildrenChanged)
            {
                thisObject.ChangeType = Diff3ChangeType.Children;
            }

            if (position >= thisObject.Items.Count)
            {
                int count = (position - thisObject.Items.Count + 1);
                for (int i = 0; i < count; i++)
                {
                    thisObject.Items.Add(null);
                }
            }

            if (thisObject.Items[position] == null)
            {
                item.Index = position;
                thisObject.Items[position] = item;
            }
            else
            {
                item.Index = position + 1;
                thisObject.Items.Insert(item.Index, item);
            }
        }
Exemple #19
0
        private static void DiffValue(Diff3Node diff3, ref NodeDescription baseNodeDesc, ref NodeDescription asset1NodeDesc, ref NodeDescription asset2NodeDesc)
        {
            var node            = diff3.Asset1Node ?? diff3.Asset2Node ?? diff3.BaseNode;
            var dataVisitMember = node as DataVisitMember;
            var diffMember      = dataVisitMember?.MemberDescriptor.GetCustomAttributes <DiffMemberAttribute>(true).FirstOrDefault();

            if (diffMember != null)
            {
                if (diffMember.PreferredChange.HasValue)
                {
                    diff3.ChangeType = diffMember.PreferredChange.Value;
                }

                diff3.Weight = diffMember.Weight;
            }

            var baseAsset1Equals = Equals(baseNodeDesc.Instance, asset1NodeDesc.Instance);
            var baseAsset2Equals = Equals(baseNodeDesc.Instance, asset2NodeDesc.Instance);
            var asset1And2Equals = Equals(asset1NodeDesc.Instance, asset2NodeDesc.Instance);

            diff3.ChangeType = baseAsset1Equals && baseAsset2Equals
                ? Diff3ChangeType.None
                : baseAsset2Equals ? Diff3ChangeType.MergeFromAsset1 : baseAsset1Equals ? Diff3ChangeType.MergeFromAsset2 : asset1And2Equals ? Diff3ChangeType.MergeFromAsset1And2 : Diff3ChangeType.Conflict;
        }
Exemple #20
0
 public void Reset()
 {
     computed = null;
 }
Exemple #21
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;

            // A comparable type doesn't have any members, is not a collection or dictionary or array.
            bool isComparableType = !hasMembers && !CollectionDescriptor.IsCollection(type) && !DictionaryDescriptor.IsDictionary(type) && !type.IsArray;

            if (isComparableType)
            {
                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);
        }
Exemple #22
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);
            }
        }
Exemple #23
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() ?? asset1Items.FirstOrDefault() ?? asset2Items.FirstOrDefault();

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

            if (specificAssetAttribute != null)
            {
                var isFromAsset2 = specificAssetAttribute is DiffUseAsset2Attribute;
                var diffChange   = isFromAsset2
                    ? 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;
            }
        }
Exemple #24
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);
        }
Exemple #25
0
        private static void MergeContainer(Diff3Node diff3Node, out object dataInstanceOut)
        {
            dataInstanceOut = null;

            // We don't have a valid parent (probably removed), so skip this node
            if (diff3Node.Parent != null && diff3Node.Parent.Asset1Node.Instance == null)
            {
                return;
            }

            if (diff3Node.Asset1Node == null)
            {
                diff3Node.Asset1Node = (diff3Node.Asset2Node ?? diff3Node.BaseNode).CreateWithEmptyInstance();
                if (diff3Node.Parent != null)
                {
                    diff3Node.Asset1Node.Parent = diff3Node.Parent.Asset1Node;
                }
            }

            object dataInstance = diff3Node.Asset1Node.Instance;

            // If a node has children, since DiffCollection/DiffDictionary takes null as empty arrays,
            // we should now create this array for it to be properly merged
            if (dataInstance == null)
            {
                dataInstanceOut = dataInstance = Activator.CreateInstance(diff3Node.InstanceType);
            }

            // If it's a collection, clear it and reconstruct it from DiffCollection result (stored in Diff3Node.Items)
            // TODO: Various optimizations to avoid removing and reinserting items that were already here before (would need to diff Asset1 and Diff3Node...)
            var collectionDescriptor = diff3Node.Asset1Node.InstanceDescriptor as CollectionDescriptor;

            if (collectionDescriptor != null && diff3Node.Type == Diff3NodeType.Collection)
            {
                collectionDescriptor.Clear(dataInstance);
                if (diff3Node.Items != null)
                {
                    foreach (var item in diff3Node.Items)
                    {
                        object itemInstance;
                        switch (item.ChangeType)
                        {
                        case Diff3ChangeType.Children:
                        case Diff3ChangeType.Conflict:
                        case Diff3ChangeType.ConflictType:
                            // Use any valid object, it will be replaced later
                            itemInstance = SafeNodeInstance(item.Asset1Node) ?? SafeNodeInstance(item.Asset2Node) ?? SafeNodeInstance(item.BaseNode);
                            break;

                        case Diff3ChangeType.None:
                        case Diff3ChangeType.MergeFromAsset1:
                        case Diff3ChangeType.MergeFromAsset1And2:
                            itemInstance = item.Asset1Node.Instance;
                            break;

                        case Diff3ChangeType.MergeFromAsset2:
                            itemInstance = item.Asset2Node.Instance;
                            break;

                        default:
                            throw new InvalidOperationException();
                        }
                        collectionDescriptor.Add(dataInstance, itemInstance);
                    }
                }
            }
        }
Exemple #26
0
 private void DiffCollectionByIds(Diff3Node diff3, DataVisitNode baseNode, DataVisitNode asset1Node, DataVisitNode asset2Node)
 {
     DiffCollectionByIdsGeneric(diff3, baseNode, asset1Node, asset2Node, IdentifiableHelper.GetId, DiffNode);
 }
Exemple #27
0
        private void DiffCollection(Diff3Node diff3, DataVisitNode baseNode, DataVisitNode asset1Node, DataVisitNode asset2Node)
        {
            var baseItems   = baseNode != null ? baseNode.Items ?? EmptyNodes : EmptyNodes;
            var asset1Items = asset1Node != null ? asset1Node.Items ?? EmptyNodes : EmptyNodes;
            var asset2Items = asset2Node != null ? asset2Node.Items ?? EmptyNodes : EmptyNodes;

            equalityComparer.Reset();
            var changes = Diff3.Compare(baseItems, asset1Items, asset2Items, equalityComparer);

            foreach (var change in changes)
            {
                switch (change.ChangeType)
                {
                case SharpDiff.Diff3ChangeType.Equal:
                    for (int i = 0; i < change.Base.Length; i++)
                    {
                        var diff3Node = new Diff3Node(baseItems[change.Base.From + i], asset1Items[change.From1.From + i], asset2Items[change.From2.From + i])
                        {
                            ChangeType = Diff3ChangeType.None
                        };
                        AddItem(diff3, diff3Node);
                    }
                    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);
                    }
                    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);
                    }
                    break;

                case SharpDiff.Diff3ChangeType.MergeFrom1And2:
                    for (int i = 0; i < change.From2.Length; i++)
                    {
                        var diff3Node = new Diff3Node(null, asset1Items[change.From1.From + i], asset2Items[change.From2.From + i])
                        {
                            ChangeType = Diff3ChangeType.MergeFromAsset1And2
                        };
                        AddItem(diff3, diff3Node);
                    }
                    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 ?
                                        DiffNode(baseItem, asset1Item, asset2Item) :
                                        new Diff3Node(baseItem, asset1Item, asset2Item)
                        {
                            ChangeType = Diff3ChangeType.Conflict
                        };
                        AddItem(diff3, diff3Node);
                    }
                    break;
                }
            }

            // Order by descending index
            if (diff3.Items != null)
            {
                diff3.Items.Sort((left, right) =>
                {
                    int leftAsset1Index  = left.Asset1Node != null ? ((DataVisitListItem)left.Asset1Node).Index : -1;
                    int rightAsset1Index = right.Asset1Node != null ? ((DataVisitListItem)right.Asset1Node).Index : -1;

                    return(rightAsset1Index.CompareTo(leftAsset1Index));
                });
            }
        }
Exemple #28
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   = GetSafeGuidForCollectionItem(item.Instance, i, idGetter);
                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   = GetSafeGuidForCollectionItem(item.Instance, i, idGetter);
                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   = GetSafeGuidForCollectionItem(item.Instance, i, idGetter);
                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);
                    }
                }
            }
        }