/// <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; }
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); } } } }