Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
ThreeWayMergeOrdered.cs
Go to the documentation of this file.
1 // Copyright (c) 2014 Silicon Studio Corp. (http://siliconstudio.co.jp)
2 // This file is distributed under GPL v3. See LICENSE.md for details.
3 using System;
4 using System.Collections.Generic;
5 using System.Linq;
6 
7 namespace SiliconStudio.Paradox.Diffs
8 {
9  internal static class ThreeWayMergeOrdered
10  {
11  public static void Merge<T, TKey>(IList<T> result, IList<T> listBase, IList<T> list1, IList<T> list2, Func<T, TKey> keyExtractor, Func<T, T, bool> contentComparer, Action<ThreeWayConflictType, IList<T>[], int[], IList<T>> conflictResolver)
12  {
13  var keyComparer = Comparer<TKey>.Default;
14 
15  if (contentComparer == null)
16  contentComparer = EqualityComparer<T>.Default.Equals;
17  // For now, to simplify some code, just create an empty base list if there is none.
18  if (listBase == null)
19  listBase = new T[0];
20 
21  var lists = new[] { listBase, list1, list2 };
22  var indices = new[] { 0, 0, 0 }; // current indices
23  var reachedEnd = new bool[3]; // is enumeration finished?
24  var keys = new TKey[3]; // extracted key
25  var isMinKey = new bool[3]; // is it equal to minimum key this iteration? (=> needs to be processed)
26 
27  while (indices[0] < lists[0].Count || indices[1] < lists[1].Count || indices[2] < lists[2].Count)
28  {
29  bool minKeyDefined = false;
30  TKey minKey = default(TKey);
31 
32  // Find minimum key this iteration
33  for (int i = 0; i < 3; ++i)
34  {
35  reachedEnd[i] = indices[i] >= lists[i].Count;
36  if (!reachedEnd[i])
37  {
38  keys[i] = keyExtractor(lists[i][indices[i]]);
39  if (!minKeyDefined || keyComparer.Compare(minKey, keys[i]) > 0)
40  {
41  minKeyDefined = true;
42  minKey = keys[i];
43  }
44  }
45  }
46 
47  // Find which list has this minimum key in this iteration
48  for (int i = 0; i < 3; ++i)
49  {
50  isMinKey[i] = (!reachedEnd[i]) && (keyComparer.Compare(minKey, keys[i]) == 0);
51  }
52 
53  if (!minKeyDefined || (!isMinKey[0] && !isMinKey[1] && !isMinKey[2]))
54  throw new InvalidOperationException();
55 
56  if (!isMinKey[0])
57  {
58  // New content from either (or both) list1 and list2
59  if (isMinKey[1] && !isMinKey[2])
60  {
61  // Insertion from list1
62  result.Add(lists[1][indices[1]]);
63  }
64  else if (!isMinKey[1] && isMinKey[2])
65  {
66  // Insertion from list2
67  result.Add(lists[2][indices[2]]);
68  }
69  else if (isMinKey[1] && isMinKey[2])
70  {
71  // Insertion from both list1 and list 2, compare content
72  if (!contentComparer(lists[1][indices[1]], lists[2][indices[2]]))
73  {
74  // Different adds, CONFLICT!
75  conflictResolver(ThreeWayConflictType.Insertion1And2, lists.ToArray(), indices.ToArray(), result);
76  //throw new NotImplementedException();
77  }
78  else
79  {
80  result.Add(lists[1][indices[1]]);
81  }
82  }
83  else
84  {
85  throw new InvalidOperationException();
86  }
87  }
88  else
89  {
90  if (!isMinKey[1] && !isMinKey[2])
91  {
92  // Deleted from both sides
93  conflictResolver(ThreeWayConflictType.Deleted1And2, lists.ToArray(), indices.ToArray(), result);
94  }
95  else if (isMinKey[1] && isMinKey[2])
96  {
97  // Present everywhere, compare content
98  bool compare01 = contentComparer(lists[0][indices[0]], lists[1][indices[1]]);
99  bool compare02 = contentComparer(lists[0][indices[0]], lists[2][indices[2]]);
100  bool compare12 = contentComparer(lists[1][indices[1]], lists[2][indices[2]]);
101 
102  if (compare01 && compare02)
103  {
104  // No changes
105  result.Add(lists[0][indices[0]]);
106  }
107  else if (compare12)
108  {
109  // Same changes
110  result.Add(lists[1][indices[1]]);
111  }
112  else if (!compare01 && !compare02)
113  {
114  // Both side diverged, CONFLICT!
115  conflictResolver(ThreeWayConflictType.Modified1And2, lists.ToArray(), indices.ToArray(), result);
116  //throw new NotImplementedException();
117  }
118  else if (!compare01 || !compare02)
119  {
120  // Only one side changed, take it
121  if (!compare01)
122  result.Add(lists[1][indices[1]]);
123  else if (!compare02)
124  result.Add(lists[2][indices[2]]);
125  }
126  }
127  else if (isMinKey[1] && !isMinKey[2])
128  {
129  // Present in list1 but not list2 anymore, compare content
130  if (!contentComparer(lists[0][indices[0]], lists[1][indices[1]]))
131  {
132  // Modifed on one side, deleted on the other, CONFLICT!
133  conflictResolver(ThreeWayConflictType.Modified1Deleted2, lists.ToArray(), indices.ToArray(), result);
134  //throw new NotImplementedException();
135  }
136  }
137  else if (!isMinKey[1] && isMinKey[2])
138  {
139  // Present in list2 but not list1 anymore, compare content
140  if (!contentComparer(lists[0][indices[0]], lists[2][indices[2]]))
141  {
142  // Modifed on one side, deleted on the other, CONFLICT!
143  conflictResolver(ThreeWayConflictType.Modified2Deleted1, lists.ToArray(), indices.ToArray(), result);
144  //throw new NotImplementedException();
145  }
146  }
147  }
148 
149  // Advance iterator
150  for (int i = 0; i < 3; ++i)
151  {
152  if (isMinKey[i])
153  indices[i]++;
154  }
155  }
156  }
157  }
158 }