78
|
1 /*******************************************************************************
|
|
2 * Copyright (c) 2006, 2008 IBM Corporation and others.
|
|
3 * All rights reserved. This program and the accompanying materials
|
|
4 * are made available under the terms of the Eclipse Public License v1.0
|
|
5 * which accompanies this distribution, and is available at
|
|
6 * http://www.eclipse.org/legal/epl-v10.html
|
|
7 *
|
|
8 * Contributors:
|
|
9 * IBM Corporation - initial API and implementation
|
|
10 * Matthew Hall - bug 226216
|
|
11 *******************************************************************************/
|
|
12
|
|
13 module org.eclipse.core.databinding.observable.Diffs;
|
|
14
|
|
15 import java.lang.all;
|
|
16
|
|
17 import java.util.ArrayList;
|
|
18 import java.util.Collections;
|
|
19 import java.util.HashMap;
|
|
20 import java.util.HashSet;
|
|
21 import java.util.Iterator;
|
|
22 import java.util.List;
|
|
23 import java.util.Map;
|
|
24 import java.util.Set;
|
|
25 import java.util.Map.Entry;
|
|
26
|
|
27 import org.eclipse.core.databinding.observable.list.ListDiff;
|
|
28 import org.eclipse.core.databinding.observable.list.ListDiffEntry;
|
|
29 import org.eclipse.core.databinding.observable.map.MapDiff;
|
|
30 import org.eclipse.core.databinding.observable.set.SetDiff;
|
|
31 import org.eclipse.core.databinding.observable.value.ValueDiff;
|
|
32 import org.eclipse.core.internal.databinding.Util;
|
|
33
|
|
34 /**
|
|
35 * @since 1.0
|
|
36 *
|
|
37 */
|
|
38 public class Diffs {
|
|
39
|
|
40 /**
|
|
41 * @param oldList
|
|
42 * @param newList
|
|
43 * @return the differences between oldList and newList
|
|
44 */
|
|
45 public static ListDiff computeListDiff(List oldList, List newList) {
|
|
46 List diffEntries = new ArrayList();
|
|
47 createListDiffs(new ArrayList(oldList), newList, diffEntries);
|
|
48 ListDiff listDiff = createListDiff(cast(ListDiffEntry[]) diffEntries
|
|
49 .toArray(new ListDiffEntry[diffEntries.size()]));
|
|
50 return listDiff;
|
|
51 }
|
|
52
|
|
53 /**
|
|
54 * adapted from EMF's ListDifferenceAnalyzer
|
|
55 */
|
|
56 private static void createListDiffs(List oldList, List newList,
|
|
57 List listDiffs) {
|
|
58 int index = 0;
|
|
59 for (Iterator it = newList.iterator(); it.hasNext();) {
|
|
60 Object newValue = it.next();
|
|
61 if (oldList.size() <= index) {
|
|
62 // append newValue to newList
|
|
63 listDiffs.add(createListDiffEntry(index, true, newValue));
|
|
64 } else {
|
|
65 bool done;
|
|
66 do {
|
|
67 done = true;
|
|
68 Object oldValue = oldList.get(index);
|
|
69 if (oldValue is null ? newValue !is null : !oldValue.equals(newValue)) {
|
|
70 int oldIndexOfNewValue = listIndexOf(oldList, newValue, index);
|
|
71 if (oldIndexOfNewValue !is -1) {
|
|
72 int newIndexOfOldValue = listIndexOf(newList, oldValue, index);
|
|
73 if (newIndexOfOldValue is -1) {
|
|
74 // removing oldValue from list[index]
|
|
75 listDiffs.add(createListDiffEntry(index, false, oldValue));
|
|
76 oldList.remove(index);
|
|
77 done = false;
|
|
78 } else if (newIndexOfOldValue > oldIndexOfNewValue) {
|
|
79 // moving oldValue from list[index] to [newIndexOfOldValue]
|
|
80 if (oldList.size() <= newIndexOfOldValue) {
|
|
81 // The element cannot be moved to the correct index
|
|
82 // now, however later iterations will insert elements
|
|
83 // in front of it, eventually moving it into the
|
|
84 // correct spot.
|
|
85 newIndexOfOldValue = oldList.size() - 1;
|
|
86 }
|
|
87 listDiffs.add(createListDiffEntry(index, false, oldValue));
|
|
88 oldList.remove(index);
|
|
89 listDiffs.add(createListDiffEntry(newIndexOfOldValue, true, oldValue));
|
|
90 oldList.add(newIndexOfOldValue, oldValue);
|
|
91 done = false;
|
|
92 } else {
|
|
93 // move newValue from list[oldIndexOfNewValue] to [index]
|
|
94 listDiffs.add(createListDiffEntry(oldIndexOfNewValue, false, newValue));
|
|
95 oldList.remove(oldIndexOfNewValue);
|
|
96 listDiffs.add(createListDiffEntry(index, true, newValue));
|
|
97 oldList.add(index, newValue);
|
|
98 }
|
|
99 } else {
|
|
100 // add newValue at list[index]
|
|
101 oldList.add(index, newValue);
|
|
102 listDiffs.add(createListDiffEntry(index, true, newValue));
|
|
103 }
|
|
104 }
|
|
105 } while (!done);
|
|
106 }
|
|
107 ++index;
|
|
108 }
|
|
109 for (int i = oldList.size(); i > index;) {
|
|
110 // remove excess trailing elements not present in newList
|
|
111 listDiffs.add(createListDiffEntry(--i, false, oldList.get(i)));
|
|
112 }
|
|
113 }
|
|
114
|
|
115 /**
|
|
116 * @param list
|
|
117 * @param object
|
|
118 * @param index
|
|
119 * @return the index, or -1 if not found
|
|
120 */
|
|
121 private static int listIndexOf(List list, Object object, int index) {
|
|
122 int size = list.size();
|
|
123 for (int i=index; i<size;i++) {
|
|
124 Object candidate = list.get(i);
|
|
125 if (candidateisnull ? objectisnull : candidate.equals(object)) {
|
|
126 return i;
|
|
127 }
|
|
128 }
|
|
129 return -1;
|
|
130 }
|
|
131
|
|
132 /**
|
|
133 * Checks whether the two objects are <code>null</code> -- allowing for
|
|
134 * <code>null</code>.
|
|
135 *
|
|
136 * @param left
|
|
137 * The left object to compare; may be <code>null</code>.
|
|
138 * @param right
|
|
139 * The right object to compare; may be <code>null</code>.
|
|
140 * @return <code>true</code> if the two objects are equivalent;
|
|
141 * <code>false</code> otherwise.
|
|
142 */
|
|
143 public static final bool equals(Object left, Object right) {
|
|
144 return left is null ? right is null : ((right !is null) && left
|
|
145 .equals(right));
|
|
146 }
|
|
147
|
|
148 /**
|
|
149 * @param oldSet
|
|
150 * @param newSet
|
|
151 * @return a set diff
|
|
152 */
|
|
153 public static SetDiff computeSetDiff(Set oldSet, Set newSet) {
|
|
154 Set additions = new HashSet(newSet);
|
|
155 additions.removeAll(oldSet);
|
|
156 Set removals = new HashSet(oldSet);
|
|
157 removals.removeAll(newSet);
|
|
158 return createSetDiff(additions, removals);
|
|
159 }
|
|
160
|
|
161 /**
|
|
162 * Computes the difference between two maps.
|
|
163 *
|
|
164 * @param oldMap
|
|
165 * @param newMap
|
|
166 * @return a map diff representing the changes needed to turn oldMap into
|
|
167 * newMap
|
|
168 */
|
|
169 public static MapDiff computeMapDiff(Map oldMap, Map newMap) {
|
|
170 // starts out with all keys from the new map, we will remove keys from
|
|
171 // the old map as we go
|
|
172 final Set addedKeys = new HashSet(newMap.keySet());
|
|
173 final Set removedKeys = new HashSet();
|
|
174 final Set changedKeys = new HashSet();
|
|
175 final Map oldValues = new HashMap();
|
|
176 final Map newValues = new HashMap();
|
|
177 for (Iterator it = oldMap.entrySet().iterator(); it.hasNext();) {
|
|
178 Map.Entry oldEntry = cast(Entry) it.next();
|
|
179 Object oldKey = oldEntry.getKey();
|
|
180 if (addedKeys.remove(oldKey)) {
|
|
181 // potentially changed key since it is in oldMap and newMap
|
|
182 Object oldValue = oldEntry.getValue();
|
|
183 Object newValue = newMap.get(oldKey);
|
|
184 if (!Util.equals(oldValue, newValue)) {
|
|
185 changedKeys.add(oldKey);
|
|
186 oldValues.put(oldKey, oldValue);
|
|
187 newValues.put(oldKey, newValue);
|
|
188 }
|
|
189 } else {
|
|
190 removedKeys.add(oldKey);
|
|
191 oldValues.put(oldKey, oldEntry.getValue());
|
|
192 }
|
|
193 }
|
|
194 for (Iterator it = addedKeys.iterator(); it.hasNext();) {
|
|
195 Object newKey = it.next();
|
|
196 newValues.put(newKey, newMap.get(newKey));
|
|
197 }
|
|
198 return new class() MapDiff {
|
|
199 public Set getAddedKeys() {
|
|
200 return addedKeys;
|
|
201 }
|
|
202
|
|
203 public Set getChangedKeys() {
|
|
204 return changedKeys;
|
|
205 }
|
|
206
|
|
207 public Set getRemovedKeys() {
|
|
208 return removedKeys;
|
|
209 }
|
|
210
|
|
211 public Object getNewValue(Object key) {
|
|
212 return newValues.get(key);
|
|
213 }
|
|
214
|
|
215 public Object getOldValue(Object key) {
|
|
216 return oldValues.get(key);
|
|
217 }
|
|
218 };
|
|
219 }
|
|
220
|
|
221 /**
|
|
222 * @param oldValue
|
|
223 * @param newValue
|
|
224 * @return a value diff
|
|
225 */
|
|
226 public static ValueDiff createValueDiff(Object oldValue,
|
|
227 Object newValue) {
|
|
228 return new class(oldValue, newValue) ValueDiff {
|
|
229 Object oldValue_, newValue_;
|
|
230 this(Object a, Object b){
|
|
231 oldValue_=a;
|
|
232 newValue_=b;
|
|
233 }
|
|
234
|
|
235 public Object getOldValue() {
|
|
236 return oldValue_;
|
|
237 }
|
|
238
|
|
239 public Object getNewValue() {
|
|
240 return newValue_;
|
|
241 }
|
|
242 };
|
|
243 }
|
|
244
|
|
245 /**
|
|
246 * @param additions
|
|
247 * @param removals
|
|
248 * @return a set diff
|
|
249 */
|
|
250 public static SetDiff createSetDiff(Set additions, Set removals) {
|
|
251 return new class() SetDiff {
|
|
252 Set unmodifiableAdditions;
|
|
253 Set unmodifiableRemovals;
|
|
254 this(){
|
|
255 unmodifiableAdditions = Collections
|
|
256 .unmodifiableSet(additions);
|
|
257 unmodifiableRemovals = Collections.unmodifiableSet(removals);
|
|
258 }
|
|
259
|
|
260 public Set getAdditions() {
|
|
261 return unmodifiableAdditions;
|
|
262 }
|
|
263
|
|
264 public Set getRemovals() {
|
|
265 return unmodifiableRemovals;
|
|
266 }
|
|
267 };
|
|
268 }
|
|
269
|
|
270 /**
|
|
271 * @param difference
|
|
272 * @return a list diff with one differing entry
|
|
273 */
|
|
274 public static ListDiff createListDiff(ListDiffEntry difference) {
|
|
275 return createListDiff([ difference ]);
|
|
276 }
|
|
277
|
|
278 /**
|
|
279 * @param difference1
|
|
280 * @param difference2
|
|
281 * @return a list diff with two differing entries
|
|
282 */
|
|
283 public static ListDiff createListDiff(ListDiffEntry difference1,
|
|
284 ListDiffEntry difference2) {
|
|
285 return createListDiff([ difference1, difference2 ]);
|
|
286 }
|
|
287
|
|
288 /**
|
|
289 * @param differences
|
|
290 * @return a list diff with the given entries
|
|
291 */
|
|
292 public static ListDiff createListDiff(ListDiffEntry[] differences) {
|
|
293 return new class() ListDiff {
|
|
294 ListDiffEntry[] differences_;
|
|
295 this(){
|
|
296 differences_=differences;
|
|
297 }
|
|
298 public ListDiffEntry[] getDifferences() {
|
|
299 return differences_;
|
|
300 }
|
|
301 };
|
|
302 }
|
|
303
|
|
304 /**
|
|
305 * @param position
|
|
306 * @param isAddition
|
|
307 * @param element
|
|
308 * @return a list diff entry
|
|
309 */
|
|
310 public static ListDiffEntry createListDiffEntry(int position,
|
|
311 bool isAddition, Object element) {
|
|
312 return new class() ListDiffEntry {
|
|
313 int position_;
|
|
314 bool isAddition_;
|
|
315 Object element_;
|
|
316 this(){
|
|
317 position_=position;
|
|
318 isAddition_=isAddition;
|
|
319 element_=element;
|
|
320 }
|
|
321
|
|
322 public int getPosition() {
|
|
323 return position_;
|
|
324 }
|
|
325
|
|
326 public bool isAddition() {
|
|
327 return isAddition_;
|
|
328 }
|
|
329
|
|
330 public Object getElement() {
|
|
331 return element_;
|
|
332 }
|
|
333 };
|
|
334 }
|
|
335
|
|
336 /**
|
|
337 * @param addedKey
|
|
338 * @param newValue
|
|
339 * @return a map diff
|
|
340 */
|
|
341 public static MapDiff createMapDiffSingleAdd(Object addedKey,
|
|
342 Object newValue) {
|
|
343 return new class() MapDiff {
|
|
344 Object addedKey_, newValue_;
|
|
345 this(){
|
|
346 addedKey_=addedKey;
|
|
347 newValue_=newValue;
|
|
348 }
|
|
349
|
|
350 public Set getAddedKeys() {
|
|
351 return Collections.singleton(addedKey_);
|
|
352 }
|
|
353
|
|
354 public Set getChangedKeys() {
|
|
355 return Collections.EMPTY_SET;
|
|
356 }
|
|
357
|
|
358 public Object getNewValue(Object key) {
|
|
359 return newValue_;
|
|
360 }
|
|
361
|
|
362 public Object getOldValue(Object key) {
|
|
363 return null;
|
|
364 }
|
|
365
|
|
366 public Set getRemovedKeys() {
|
|
367 return Collections.EMPTY_SET;
|
|
368 }
|
|
369 };
|
|
370 }
|
|
371
|
|
372 /**
|
|
373 * @param existingKey
|
|
374 * @param oldValue
|
|
375 * @param newValue
|
|
376 * @return a map diff
|
|
377 */
|
|
378 public static MapDiff createMapDiffSingleChange(Object existingKey,
|
|
379 Object oldValue, Object newValue) {
|
|
380 return new class() MapDiff {
|
|
381 Object existingKey_;
|
|
382 Object oldValue_;
|
|
383 Object newValue_;
|
|
384 this(){
|
|
385 existingKey_=existingKey;
|
|
386 oldValue_=oldValue;
|
|
387 newValue_=newValue;
|
|
388 }
|
|
389 public Set getAddedKeys() {
|
|
390 return Collections.EMPTY_SET;
|
|
391 }
|
|
392
|
|
393 public Set getChangedKeys() {
|
|
394 return Collections.singleton(existingKey_);
|
|
395 }
|
|
396
|
|
397 public Object getNewValue(Object key) {
|
|
398 return newValue_;
|
|
399 }
|
|
400
|
|
401 public Object getOldValue(Object key) {
|
|
402 return oldValue_;
|
|
403 }
|
|
404
|
|
405 public Set getRemovedKeys() {
|
|
406 return Collections.EMPTY_SET;
|
|
407 }
|
|
408 };
|
|
409 }
|
|
410
|
|
411 /**
|
|
412 * @param removedKey
|
|
413 * @param oldValue
|
|
414 * @return a map diff
|
|
415 */
|
|
416 public static MapDiff createMapDiffSingleRemove(Object removedKey,
|
|
417 Object oldValue) {
|
|
418 return new class() MapDiff {
|
|
419 Object removedKey_;
|
|
420 Object oldValue_;
|
|
421 this(){
|
|
422 removedKey_=removedKey;
|
|
423 oldValue_=oldValue;
|
|
424 }
|
|
425
|
|
426 public Set getAddedKeys() {
|
|
427 return Collections.EMPTY_SET;
|
|
428 }
|
|
429
|
|
430 public Set getChangedKeys() {
|
|
431 return Collections.EMPTY_SET;
|
|
432 }
|
|
433
|
|
434 public Object getNewValue(Object key) {
|
|
435 return null;
|
|
436 }
|
|
437
|
|
438 public Object getOldValue(Object key) {
|
|
439 return oldValue_;
|
|
440 }
|
|
441
|
|
442 public Set getRemovedKeys() {
|
|
443 return Collections.singleton(removedKey_);
|
|
444 }
|
|
445 };
|
|
446 }
|
|
447
|
|
448 /**
|
|
449 * @param copyOfOldMap
|
|
450 * @return a map diff
|
|
451 */
|
|
452 public static MapDiff createMapDiffRemoveAll(Map copyOfOldMap) {
|
|
453 return new class() MapDiff {
|
|
454 Map copyOfOldMap_;
|
|
455 this(){
|
|
456 copyOfOldMap_=copyOfOldMap;
|
|
457 }
|
|
458
|
|
459 public Set getAddedKeys() {
|
|
460 return Collections.EMPTY_SET;
|
|
461 }
|
|
462
|
|
463 public Set getChangedKeys() {
|
|
464 return Collections.EMPTY_SET;
|
|
465 }
|
|
466
|
|
467 public Object getNewValue(Object key) {
|
|
468 return null;
|
|
469 }
|
|
470
|
|
471 public Object getOldValue(Object key) {
|
|
472 return copyOfOldMap_.get(key);
|
|
473 }
|
|
474
|
|
475 public Set getRemovedKeys() {
|
|
476 return copyOfOldMap_.keySet();
|
|
477 }
|
|
478 };
|
|
479 }
|
|
480
|
|
481 /**
|
|
482 * @param addedKeys
|
|
483 * @param removedKeys
|
|
484 * @param changedKeys
|
|
485 * @param oldValues
|
|
486 * @param newValues
|
|
487 * @return a map diff
|
|
488 */
|
|
489 public static MapDiff createMapDiff(Set addedKeys,
|
|
490 Set removedKeys, Set changedKeys, Map oldValues,
|
|
491 Map newValues) {
|
|
492 return new class() MapDiff {
|
|
493 Set addedKeys_;
|
|
494 Set removedKeys_;
|
|
495 Set changedKeys_;
|
|
496 Map oldValues_;
|
|
497 Map newValues_;
|
|
498 this(){
|
|
499 addedKeys_=addedKeys;
|
|
500 removedKeys_=removedKeys;
|
|
501 changedKeys_=changedKeys;
|
|
502 oldValues_=oldValues;
|
|
503 newValues_=newValues;
|
|
504 }
|
|
505
|
|
506 public Set getAddedKeys() {
|
|
507 return addedKeys_;
|
|
508 }
|
|
509
|
|
510 public Set getChangedKeys() {
|
|
511 return changedKeys_;
|
|
512 }
|
|
513
|
|
514 public Object getNewValue(Object key) {
|
|
515 return newValues_.get(key);
|
|
516 }
|
|
517
|
|
518 public Object getOldValue(Object key) {
|
|
519 return oldValues_.get(key);
|
|
520 }
|
|
521
|
|
522 public Set getRemovedKeys() {
|
|
523 return removedKeys_;
|
|
524 }
|
|
525 };
|
|
526 }
|
|
527 }
|