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