001 /**
002 * <copyright>
003 *
004 * Copyright (c) 2002-2007 IBM Corporation and others.
005 * All rights reserved. This program and the accompanying materials
006 * are made available under the terms of the Eclipse Public License v1.0
007 * which accompanies this distribution, and is available at
008 * http://www.eclipse.org/legal/epl-v10.html
009 *
010 * Contributors:
011 * IBM - Initial API and implementation
012 *
013 * </copyright>
014 *
015 * $Id: NotifyingListImpl.java,v 1.19 2008/05/25 16:58:59 emerks Exp $
016 */
017 package org.eclipse.emf.common.notify.impl;
018
019
020 import java.util.Arrays;
021 import java.util.Collection;
022 import java.util.Collections;
023 import java.util.Iterator;
024 import java.util.List;
025
026 import org.eclipse.emf.common.notify.Notification;
027 import org.eclipse.emf.common.notify.NotificationChain;
028 import org.eclipse.emf.common.notify.Notifier;
029 import org.eclipse.emf.common.notify.NotifyingList;
030 import org.eclipse.emf.common.util.BasicEList;
031
032
033 /**
034 * An extensible implementation of a notifying list.
035 */
036 public class NotifyingListImpl<E> extends BasicEList<E> implements NotifyingList<E>
037 {
038 private static final long serialVersionUID = 1L;
039
040 /**
041 * Creates an empty instance.
042 */
043 public NotifyingListImpl()
044 {
045 super();
046 }
047
048 /**
049 * Creates an empty instance with the given capacity.
050 * @param initialCapacity the initial capacity of the list before it must grow.
051 */
052 public NotifyingListImpl(int initialCapacity)
053 {
054 super(initialCapacity);
055 }
056
057 /**
058 * Creates an instance that is a copy of the collection.
059 * @param collection the initial contents of the list.
060 */
061 public NotifyingListImpl(Collection<? extends E> collection)
062 {
063 super(collection);
064 }
065
066 /**
067 * Returns <code>null</code>.
068 * @return <code>null</code>.
069 */
070 public Object getNotifier()
071 {
072 return null;
073 }
074
075 /**
076 * Returns <code>null</code>.
077 * @return <code>null</code>.
078 */
079 public Object getFeature()
080 {
081 return null;
082 }
083
084 /**
085 * Returns {@link org.eclipse.emf.common.notify.Notification#NO_FEATURE_ID}.
086 * @return <code>Notification.NO_FEATURE_ID</code>.
087 */
088 public int getFeatureID()
089 {
090 return Notification.NO_FEATURE_ID;
091 }
092
093 /**
094 * Returns the result of calling {@link #getFeatureID()}.
095 * @param expectedClass the class to which the ID is relative.
096 * @return <code>getFeatureID()</code>.
097 */
098 protected int getFeatureID(Class<?> expectedClass)
099 {
100 return getFeatureID();
101 }
102
103 /**
104 * Returns whether the list is considered set, i.e., whether it's not empty.
105 * A derived implementation may model this state directly.
106 * @return whether the list is considered set.
107 */
108 protected boolean isSet()
109 {
110 return !isEmpty();
111 }
112
113 /**
114 * Returns <code>false</code>.
115 * @return <code>false</code>.
116 */
117 protected boolean hasInverse()
118 {
119 return false;
120 }
121
122 /**
123 * Returns <code>!{@link #hasInverse()}</code>.
124 * @return <code>!hasInverse</code>.
125 */
126 @Override
127 protected boolean canContainNull()
128 {
129 return !hasInverse();
130 }
131
132 /**
133 * Returns <code>false</code>.
134 * @return <code>false</code>.
135 */
136 protected boolean isNotificationRequired()
137 {
138 return false;
139 }
140
141 /**
142 * Returns <code>false</code>.
143 * @return <code>false</code>.
144 */
145 protected boolean hasShadow()
146 {
147 return false;
148 }
149
150 /**
151 * Does nothing and returns the <code>notifications</code>.
152 * Clients can override this to update the inverse of a bidirectional relation.
153 * @param object the object that's been added to the list.
154 * @param notifications the chain of accumulating notifications.
155 * @return the <code>notifications</code>.
156 */
157 protected NotificationChain shadowAdd(E object, NotificationChain notifications)
158 {
159 return notifications;
160 }
161
162 /**
163 * Does nothing and returns the <code>notifications</code>.
164 * Clients can override this to update the inverse of a bidirectional relation.
165 * @param object the object that's been remove from the list.
166 * @param notifications the chain of accumulating notifications.
167 * @return the <code>notifications</code>.
168 */
169 protected NotificationChain shadowRemove(E object, NotificationChain notifications)
170 {
171 return notifications;
172 }
173
174 /**
175 * Does nothing and returns the <code>notifications</code>.
176 * Clients can override this to update the inverse of a bidirectional relation.
177 * @param oldObject the object that's been removed from the list.
178 * @param newObject the object that's been added to the list.
179 * @param notifications the chain of accumulating notifications.
180 * @return the <code>notifications</code>.
181 */
182 protected NotificationChain shadowSet(E oldObject, E newObject, NotificationChain notifications)
183 {
184 return notifications;
185 }
186
187 /**
188 * Does nothing and returns the <code>notifications</code>.
189 * Clients can override this to update the inverse of a bidirectional relation.
190 * @param object the object that's been added to the list.
191 * @param notifications the chain of accumulating notifications.
192 * @return the <code>notifications</code>.
193 */
194 protected NotificationChain inverseAdd(E object, NotificationChain notifications)
195 {
196 return notifications;
197 }
198
199 /**
200 * Does nothing and returns the <code>notifications</code>.
201 * Clients can override this to update the inverse of a bidirectional relation.
202 * @param object the object that's been remove from the list.
203 * @param notifications the chain of accumulating notifications.
204 * @return the <code>notifications</code>.
205 */
206 protected NotificationChain inverseRemove(E object, NotificationChain notifications)
207 {
208 return notifications;
209 }
210
211 /*
212 * @deprecated
213 */
214 protected NotificationImpl createNotification(int eventType, Object oldObject, Object newObject, int index)
215 {
216 throw new UnsupportedOperationException("Please change your code to call new five argument version of this method");
217 }
218
219 /**
220 * Creates a notification.
221 * @param eventType the type of change that has occurred.
222 * @param oldObject the value of the notifier's feature before the change occurred.
223 * @param newObject the value of the notifier's feature after the change occurred.
224 * @param index the position at which the change occurred.
225 * @return a new notification.
226 */
227 protected NotificationImpl createNotification(int eventType, Object oldObject, Object newObject, int index, boolean wasSet)
228 {
229 return
230 new NotificationImpl(eventType, oldObject, newObject, index, wasSet)
231 {
232 @Override
233 public Object getNotifier()
234 {
235 return NotifyingListImpl.this.getNotifier();
236 }
237
238 @Override
239 public Object getFeature()
240 {
241 return NotifyingListImpl.this.getFeature();
242 }
243
244 @Override
245 public int getFeatureID(Class<?> expectedClass)
246 {
247 return NotifyingListImpl.this.getFeatureID(expectedClass);
248 }
249 };
250 }
251
252 /**
253 * Creates a notification chain, if the expected capacity exceeds the threshold
254 * at which a list is better than chaining individual notification instances.
255 */
256 protected NotificationChain createNotificationChain(int capacity)
257 {
258 return capacity < 100 ? null: new NotificationChainImpl(capacity);
259 }
260
261 /**
262 * Dispatches a notification to the notifier of the list.
263 * @param notification the notification to dispatch.
264 */
265 protected void dispatchNotification(Notification notification)
266 {
267 ((Notifier)getNotifier()).eNotify(notification);
268 }
269
270 /**
271 * Adds the object at the end of the list;
272 * it does no uniqueness checking.
273 * In addition to the normal effects,
274 * this override implementation generates notifications as {@link #isNotificationRequired required}
275 * and delegates to {@link #inverseAdd inverseAdd} as {@link #hasInverse required}.
276 * @param object the object to be added.
277 * @see #isNotificationRequired
278 * @see #hasInverse
279 * @see #inverseAdd
280 */
281 @Override
282 public void addUnique(E object)
283 {
284 if (isNotificationRequired())
285 {
286 int index = size;
287 boolean oldIsSet = isSet();
288 doAddUnique(object);
289 NotificationImpl notification = createNotification(Notification.ADD, null, object, index, oldIsSet);
290 if (hasInverse())
291 {
292 NotificationChain notifications = inverseAdd(object, null);
293 if (hasShadow())
294 {
295 notifications = shadowAdd(object, notifications);
296 }
297
298 if (notifications == null)
299 {
300 dispatchNotification(notification);
301 }
302 else
303 {
304 notifications.add(notification);
305 notifications.dispatch();
306 }
307 }
308 else
309 {
310 dispatchNotification(notification);
311 }
312 }
313 else
314 {
315 doAddUnique(object);
316 if (hasInverse())
317 {
318 NotificationChain notifications = inverseAdd(object, null);
319 if (notifications != null) notifications.dispatch();
320 }
321 }
322 }
323
324 /**
325 * Adds the object at the end of the list;
326 * it does no uniqueness checking, inverse updating, or notification.
327 * @param object the object to be added.
328 */
329 protected void doAddUnique(E object)
330 {
331 super.addUnique(object);
332 }
333
334 /**
335 * Adds the object at the given index in the list;
336 * it does no ranging checking or uniqueness checking.
337 * In addition to the normal effects,
338 * this override implementation generates notifications as {@link #isNotificationRequired required}
339 * and delegates to {@link #inverseAdd inverseAdd} as {@link #hasInverse required}.
340 * @param object the object to be added.
341 * @see #isNotificationRequired
342 * @see #hasInverse
343 * @see #inverseAdd
344 */
345 @Override
346 public void addUnique(int index, E object)
347 {
348 if (isNotificationRequired())
349 {
350 boolean oldIsSet = isSet();
351 doAddUnique(index, object);
352 NotificationImpl notification = createNotification(Notification.ADD, null, object, index, oldIsSet);
353 if (hasInverse())
354 {
355 NotificationChain notifications = inverseAdd(object, null);
356 if (hasShadow())
357 {
358 notifications = shadowAdd(object, notifications);
359 }
360 if (notifications == null)
361 {
362 dispatchNotification(notification);
363 }
364 else
365 {
366 notifications.add(notification);
367 notifications.dispatch();
368 }
369 }
370 else
371 {
372 dispatchNotification(notification);
373 }
374 }
375 else
376 {
377 doAddUnique(index, object);
378 if (hasInverse())
379 {
380 NotificationChain notifications = inverseAdd(object, null);
381 if (notifications != null) notifications.dispatch();
382 }
383 }
384 }
385
386 /**
387 * Adds the object at the given index in the list;
388 * it does no range checking, uniqueness checking, inverse updating, or notification.
389 * @param object the object to be added.
390 */
391 protected void doAddUnique(int index, E object)
392 {
393 super.addUnique(index, object);
394 }
395
396 /**
397 * Adds each object of the collection to the end of the list;
398 * it does no uniqueness checking.
399 * This implementation delegates to {@link #addAllUnique(int, Collection) addAllUnique(int, Collection)}.
400 * @param collection the collection of objects to be added.
401 * @see #inverseAdd
402 */
403 @Override
404 public boolean addAllUnique(Collection<? extends E> collection)
405 {
406 return addAllUnique(size, collection);
407 }
408
409 /**
410 * Adds each object of the collection to the end of the list;
411 * it does no uniqueness checking, inverse updating, or notification.
412 * @param collection the collection of objects to be added.
413 */
414 protected boolean doAddAllUnique(Collection<? extends E> collection)
415 {
416 return super.addAllUnique(collection);
417 }
418
419 /**
420 * Adds each object of the collection at each successive index in the list
421 * and returns whether any objects were added;
422 * it does no ranging checking or uniqueness checking.
423 * In addition to the normal effects,
424 * this override implementation generates notifications as {@link #isNotificationRequired required}
425 * and delegates to {@link #inverseAdd inverseAdd} as {@link #hasInverse required}.
426 * @param index the index at which to add.
427 * @param collection the collection of objects to be added.
428 * @return whether any objects were added.
429 * @see #isNotificationRequired
430 * @see #hasInverse
431 * @see #inverseAdd
432 */
433 @Override
434 public boolean addAllUnique(int index, Collection<? extends E> collection)
435 {
436 int collectionSize = collection.size();
437 if (collectionSize == 0)
438 {
439 return false;
440 }
441 else
442 {
443 if (isNotificationRequired())
444 {
445 boolean oldIsSet = isSet();
446 doAddAllUnique(index, collection);
447 NotificationImpl notification =
448 collectionSize == 1 ?
449 createNotification(Notification.ADD, null, collection.iterator().next(), index, oldIsSet) :
450 createNotification(Notification.ADD_MANY, null, collection, index, oldIsSet);
451 if (hasInverse())
452 {
453 NotificationChain notifications = createNotificationChain(collectionSize);
454 int lastIndex = index + collectionSize;
455 for (int i = index; i < lastIndex; ++i)
456 {
457 @SuppressWarnings("unchecked") E value = (E)data[i];
458 notifications = inverseAdd(value, notifications);
459 notifications = shadowAdd(value, notifications);
460 }
461 if (notifications == null)
462 {
463 dispatchNotification(notification);
464 }
465 else
466 {
467 notifications.add(notification);
468 notifications.dispatch();
469 }
470 }
471 else
472 {
473 dispatchNotification(notification);
474 }
475 }
476 else
477 {
478 doAddAllUnique(index, collection);
479 if (hasInverse())
480 {
481 NotificationChain notifications = createNotificationChain(collectionSize);
482 int lastIndex = index + collectionSize;
483 for (int i = index; i < lastIndex; ++i)
484 {
485 @SuppressWarnings("unchecked") E object = (E)data[i];
486 notifications = inverseAdd(object, notifications);
487 }
488 if (notifications != null) notifications.dispatch();
489 }
490 }
491
492 return true;
493 }
494 }
495
496 /**
497 * Adds each object of the collection at each successive index in the list
498 * and returns whether any objects were added;
499 * it does no range checking, uniqueness checking, inverse updating, or notification.
500 * @param index the index at which to add.
501 * @param collection the collection of objects to be added.
502 * @return whether any objects were added.
503 */
504 protected boolean doAddAllUnique(int index, Collection<? extends E> collection)
505 {
506 return super.addAllUnique(index, collection);
507 }
508
509 /**
510 * Adds each object from start to end of the array to the end of the list
511 * and returns whether any objects were added;
512 * it does no uniqueness checking.
513 * This implementation delegates to {@link #addAllUnique(int, Object[], int, int) addAllUnique(int, Object[], int, int)}.
514 * @param objects the objects to be added.
515 * @param start the index of first object to be added.
516 * @param end the index past the last object to be added.
517 * @return whether any objects were added.
518 * @see #inverseAdd
519 */
520 @Override
521 public boolean addAllUnique(Object [] objects, int start, int end)
522 {
523 return addAllUnique(size, objects, start, end);
524 }
525
526 /**
527 * Adds each object from start to end of the array to the end of the list
528 * and returns whether any objects were added;
529 * it does no ranging checking, uniqueness checking, inverse updating, or notification.
530 * @param objects the objects to be added.
531 * @param start the index of first object to be added.
532 * @param end the index past the last object to be added.
533 * @return whether any objects were added.
534 */
535 protected boolean doAddAllUnique(Object [] objects, int start, int end)
536 {
537 return super.addAllUnique(objects, start, end);
538 }
539
540 /**
541 * Adds each object from start to end of the array at each successive index in the list
542 * and returns whether any objects were added;
543 * it does no ranging checking or uniqueness checking.
544 * This implementation delegates to {@link #assign(int, Object) assign},
545 * {@link #didAdd(int, Object) didAdd}, and {@link #didChange() didChange}.
546 * In addition to the normal effects,
547 * this override implementation generates notifications as {@link #isNotificationRequired required}
548 * and delegates to {@link #inverseAdd inverseAdd} as {@link #hasInverse required}.
549 * @param index the index at which to add.
550 * @param objects the objects to be added.
551 * @param start the index of first object to be added.
552 * @param end the index past the last object to be added.
553 * @return whether any objects were added.
554 * @see #addAllUnique(int, Collection)
555 * @see #isNotificationRequired
556 * @see #hasInverse
557 * @see #inverseAdd
558 * @see #assign(int, Object)
559 */
560 @Override
561 public boolean addAllUnique(int index, Object [] objects, int start, int end)
562 {
563 int collectionSize = end - start;
564 if (collectionSize == 0)
565 {
566 return false;
567 }
568 else
569 {
570 if (isNotificationRequired())
571 {
572 boolean oldIsSet = isSet();
573 doAddAllUnique(index, objects, start, end);
574 NotificationImpl notification;
575 if (collectionSize == 1)
576 {
577 notification = createNotification(Notification.ADD, null, objects[0], index, oldIsSet);
578 }
579 else
580 {
581 if (start != 0 || end != objects.length)
582 {
583 Object [] actualObjects = new Object [collectionSize];
584 for (int i = 0, j = start; j < end; ++i, ++j)
585 {
586 actualObjects[i] = objects[j];
587 }
588 notification = createNotification(Notification.ADD_MANY, null, Arrays.asList(actualObjects), index, oldIsSet);
589 }
590 else
591 {
592 notification = createNotification(Notification.ADD_MANY, null, Arrays.asList(objects), index, oldIsSet);
593 }
594 }
595 if (hasInverse())
596 {
597 NotificationChain notifications = createNotificationChain(collectionSize);
598 int lastIndex = index + collectionSize;
599 for (int i = index; i < lastIndex; ++i)
600 {
601 @SuppressWarnings("unchecked") E value = (E)data[i];
602 notifications = inverseAdd(value, notifications);
603 notifications = shadowAdd(value, notifications);
604 }
605 if (notifications == null)
606 {
607 dispatchNotification(notification);
608 }
609 else
610 {
611 notifications.add(notification);
612 notifications.dispatch();
613 }
614 }
615 else
616 {
617 dispatchNotification(notification);
618 }
619 }
620 else
621 {
622 doAddAllUnique(index, objects, start, end);
623 if (hasInverse())
624 {
625 NotificationChain notifications = createNotificationChain(collectionSize);
626 int lastIndex = index + collectionSize;
627 for (int i = index; i < lastIndex; ++i)
628 {
629 @SuppressWarnings("unchecked") E object = (E)data[i];
630 notifications = inverseAdd(object, notifications);
631 }
632 if (notifications != null) notifications.dispatch();
633 }
634 }
635
636 return true;
637 }
638 }
639
640 /**
641 * Adds each object from start to end of the array at each successive index in the list
642 * and returns whether any objects were added;
643 * it does no ranging checking, uniqueness checking, inverse updating, or notification.
644 * @param index the index at which to add.
645 * @param objects the objects to be added.
646 * @param start the index of first object to be added.
647 * @param end the index past the last object to be added.
648 * @return whether any objects were added.
649 */
650 protected boolean doAddAllUnique(int index, Object [] objects, int start, int end)
651 {
652 return super.addAllUnique(index, objects, start, end);
653 }
654
655 /**
656 * Adds the object at the end of the list and returns the potentially updated notification chain;
657 * it does no {@link #inverseAdd inverse} updating.
658 * This implementation generates notifications as {@link #isNotificationRequired required}.
659 * @param object the object to be added.
660 * @return the notification chain.
661 * @see #isNotificationRequired
662 * @see #hasInverse
663 * @see #inverseAdd
664 */
665 public NotificationChain basicAdd(E object, NotificationChain notifications)
666 {
667 if (isNotificationRequired())
668 {
669 int index = size;
670 boolean oldIsSet = isSet();
671 doAddUnique(index, object);
672 NotificationImpl notification = createNotification(Notification.ADD, null, object, index, oldIsSet);
673 if (notifications == null)
674 {
675 notifications = notification;
676 }
677 else
678 {
679 notifications.add(notification);
680 }
681 }
682 else
683 {
684 doAddUnique(size, object);
685 }
686 return notifications;
687 }
688
689 /**
690 * Removes the object at the index from the list and returns it.
691 * In addition to the normal effects,
692 * this override implementation generates notifications as {@link #isNotificationRequired required}
693 * and delegates to {@link #inverseRemove inverseRemove} as {@link #hasInverse required}.
694 * @param index the position of the object to remove.
695 * @return the removed object.
696 * @exception IndexOutOfBoundsException if the index isn't within the size range.
697 * @see #isNotificationRequired
698 * @see #hasInverse
699 * @see #inverseRemove
700 */
701 @Override
702 public E remove(int index)
703 {
704 if (isNotificationRequired())
705 {
706 NotificationChain notifications = null;
707 boolean oldIsSet = isSet();
708 if (hasShadow())
709 {
710 notifications = shadowRemove(basicGet(index), null);
711 }
712 E oldObject;
713 NotificationImpl notification = createNotification(Notification.REMOVE, oldObject = doRemove(index), null, index, oldIsSet);
714 if (hasInverse() && oldObject != null)
715 {
716 notifications = inverseRemove(oldObject, notifications);
717 if (notifications == null)
718 {
719 dispatchNotification(notification);
720 }
721 else
722 {
723 notifications.add(notification);
724 notifications.dispatch();
725 }
726 }
727 else
728 {
729 if (notifications == null)
730 {
731 dispatchNotification(notification);
732 }
733 else
734 {
735 notifications.add(notification);
736 notifications.dispatch();
737 }
738 }
739 return oldObject;
740 }
741 else
742 {
743 E oldObject = doRemove(index);
744 if (hasInverse() && oldObject != null)
745 {
746 NotificationChain notifications = inverseRemove(oldObject, null);
747 if (notifications != null) notifications.dispatch();
748 }
749 return oldObject;
750 }
751 }
752
753 /**
754 * Removes the object at the index from the list and returns it;
755 * it does no inverse updating, or notification.
756 * @param index the position of the object to remove.
757 * @return the removed object.
758 * @exception IndexOutOfBoundsException if the index isn't within the size range.
759 */
760 protected E doRemove(int index)
761 {
762 return super.remove(index);
763 }
764
765 /**
766 * Removes each object of the collection from the list and returns whether any object was actually contained by the list.
767 * In addition to the normal effects,
768 * this override implementation generates notifications as {@link #isNotificationRequired required}
769 * and delegates to {@link #inverseRemove inverseRemove} as {@link #hasInverse required}.
770 * @param collection the collection of objects to be removed.
771 * @return whether any object was actually contained by the list.
772 * @see #isNotificationRequired
773 * @see #hasInverse
774 * @see #inverseRemove
775 */
776 @Override
777 public boolean removeAll(Collection<?> collection)
778 {
779 boolean oldIsSet = isSet();
780
781 boolean result = false;
782 int [] positions = null;
783 if (isNotificationRequired())
784 {
785 int listSize = collection.size();
786 if (listSize > 0)
787 {
788 NotificationChain notifications = createNotificationChain(listSize);
789
790 // Copy to a list and allocate positions.
791 //
792 BasicEList<Object> list = new BasicEList<Object>(collection);
793 Object[] objects = list.data();
794 positions = new int [listSize];
795 int count = 0;
796
797 if (isUnique())
798 {
799 // Count up the objects that will be removed.
800 // The objects are exchanged to produce this list's order
801 //
802 for (int i = 0; i < size; ++i)
803 {
804 @SuppressWarnings("unchecked") E initialObject = (E)data[i];
805 E object = initialObject;
806 LOOP:
807 for (int repeat = 0; repeat < 2; ++repeat)
808 {
809 for (int j = listSize; --j >= 0; )
810 {
811 if (equalObjects(object, objects[j]))
812 {
813 if (count != j)
814 {
815 Object x = objects[count];
816 objects[count] = objects[j];
817 objects[j] = x;
818 }
819 positions[count++] = i;
820 break LOOP;
821 }
822 }
823 object = resolve(object);
824 if (object == initialObject)
825 {
826 break;
827 }
828 }
829 }
830 }
831 else
832 {
833 BasicEList<Object> resultList = new BasicEList<Object>(listSize);
834
835 // Count up the objects that will be removed.
836 // The objects are exchanged to produce this list's order
837 //
838 for (int i = 0; i < size; ++i)
839 {
840 @SuppressWarnings("unchecked") E initialObject = (E)data[i];
841 E object = initialObject;
842 LOOP:
843 for (int repeat = 0; repeat < 2; ++repeat)
844 {
845 for (int j = listSize; --j >= 0; )
846 {
847 if (equalObjects(object, objects[j]))
848 {
849 if (positions.length <= count)
850 {
851 int [] oldPositions = positions;
852 positions = new int [2 * positions.length];
853 System.arraycopy(oldPositions, 0, positions, 0, count);
854 }
855 positions[count++] = i;
856 resultList.add(objects[j]);
857 break LOOP;
858 }
859 }
860 object = resolve(object);
861 if (object == initialObject)
862 {
863 break;
864 }
865 }
866 }
867
868 list = resultList;
869 objects = resultList.data();
870 listSize = count;
871
872 if (count > positions.length)
873 {
874 int [] oldPositions = positions;
875 positions = new int [count];
876 System.arraycopy(oldPositions, 0, positions, 0, count);
877 }
878 }
879
880 // If any objects are matched.
881 //
882 if (count > 0)
883 {
884 result = true;
885
886 if (hasShadow())
887 {
888 // Remove from by position in reverse order.
889 //
890 for (int i = 0; i < count; ++i)
891 {
892 @SuppressWarnings("unchecked") E object = (E)objects[i];
893 notifications = shadowRemove(object, notifications);
894 }
895 }
896
897 // Remove from by position in reverse order.
898 //
899 for (int i = count; --i >= 0;)
900 {
901 doRemove(positions[i]);
902 }
903
904 // Compact the results to remove unmatched objects
905 //
906 if (count != listSize)
907 {
908 for (int i = listSize; --i >= count; )
909 {
910 list.remove(i);
911 }
912 int [] oldPositions = positions;
913 positions = new int [count];
914 System.arraycopy(oldPositions, 0, positions, 0, count);
915 }
916
917 collection = list;
918 }
919 }
920 }
921 else
922 {
923 collection = getDuplicates(collection);
924
925 for (int i = size; --i >= 0; )
926 {
927 if (collection.contains(data[i]))
928 {
929 doRemove(i);
930 result = true;
931 }
932 }
933 }
934
935 if (result)
936 {
937 if (positions != null)
938 {
939 int collectionSize = collection.size();
940 NotificationImpl notification =
941 (collectionSize == 1 ?
942 createNotification(Notification.REMOVE, collection.iterator().next(), null, positions[0], oldIsSet) :
943 createNotification(Notification.REMOVE_MANY, collection, positions, positions[0], oldIsSet));
944
945 NotificationChain notifications = createNotificationChain(collectionSize);
946 if (hasInverse())
947 {
948 for (Iterator<?> i = collection.iterator(); i.hasNext(); )
949 {
950 @SuppressWarnings("unchecked") E object = (E)i.next();
951 notifications = inverseRemove(object, notifications);
952 }
953 if (notifications == null)
954 {
955 dispatchNotification(notification);
956 }
957 else
958 {
959 notifications.add(notification);
960 notifications.dispatch();
961 }
962 }
963 else
964 {
965 if (notifications == null)
966 {
967 dispatchNotification(notification);
968 }
969 else
970 {
971 notifications.add(notification);
972 notifications.dispatch();
973 }
974 }
975 }
976 else if (hasInverse())
977 {
978 NotificationChain notifications = createNotificationChain(collection.size());
979 for (Iterator<?> i = collection.iterator(); i.hasNext(); )
980 {
981 @SuppressWarnings("unchecked") E object = (E)i.next();
982 notifications = inverseRemove(object, notifications);
983 }
984 if (notifications != null) notifications.dispatch();
985 }
986 return true;
987 }
988 else
989 {
990 return false;
991 }
992 }
993
994 /**
995 * Returns the resolved object from this list for the purpose of testing whether {@link #removeAll(Collection)} applies to it.
996 * @param object the object to be resolved.
997 * @return the resolved object from this list for the purpose of testing whether removeAll applies to it.
998 */
999 protected E resolve(E object)
1000 {
1001 return object;
1002 }
1003
1004 /**
1005 * Removes each object of the collection from the list and returns whether any object was actually contained by the list;
1006 * it does no inverse updating, or notification.
1007 * @param collection the collection of objects to be removed.
1008 * @return whether any object was actually contained by the list.
1009 */
1010 protected boolean doRemoveAll(Collection<?> collection)
1011 {
1012 return super.removeAll(collection);
1013 }
1014
1015 /**
1016 * Removes the object from the list and returns the potentially updated notification chain;
1017 * it does no {@link #inverseRemove inverse} updating.
1018 * This implementation generates notifications as {@link #isNotificationRequired required}.
1019 * @param object the object to be removed.
1020 * @return the notification chain.
1021 * @see #isNotificationRequired
1022 * @see #hasInverse
1023 * @see #inverseRemove
1024 */
1025 public NotificationChain basicRemove(Object object, NotificationChain notifications)
1026 {
1027 int index = indexOf(object);
1028 if (index != -1)
1029 {
1030 if (isNotificationRequired())
1031 {
1032 boolean oldIsSet = isSet();
1033 Object oldObject = doRemove(index);
1034 NotificationImpl notification = createNotification(Notification.REMOVE, oldObject, null, index, oldIsSet);
1035 if (notifications == null)
1036 {
1037 notifications = notification;
1038 }
1039 else
1040 {
1041 notifications.add(notification);
1042 }
1043 }
1044 else
1045 {
1046 doRemove(index);
1047 }
1048 }
1049 return notifications;
1050 }
1051
1052 /**
1053 * Clears the list of all objects.
1054 * In addition to the normal effects,
1055 * this override implementation generates notifications as {@link #isNotificationRequired required}
1056 * and delegates to {@link #inverseRemove inverseRemove} as {@link #hasInverse required}.
1057 * @see #isNotificationRequired
1058 * @see #hasInverse
1059 * @see #inverseRemove
1060 */
1061 @Override
1062 public void clear()
1063 {
1064 if (isNotificationRequired())
1065 {
1066 boolean oldIsSet = isSet();
1067 if (size > 0)
1068 {
1069 List<E> collection = new UnmodifiableEList<E>(size, data);
1070 int collectionSize = size;
1071
1072 NotificationChain notifications = createNotificationChain(collectionSize);
1073 if (hasShadow())
1074 {
1075 for (int i = 0; i < size; ++i)
1076 {
1077 @SuppressWarnings("unchecked") E object = (E)data[i];
1078 notifications = shadowRemove(object, notifications);
1079 }
1080 }
1081
1082 doClear();
1083 Notification notification =
1084 (collectionSize == 1 ?
1085 createNotification(Notification.REMOVE, collection.get(0), null, 0, oldIsSet) :
1086 createNotification(Notification.REMOVE_MANY, collection, null, Notification.NO_INDEX, oldIsSet));
1087
1088 if (hasInverse())
1089 {
1090 for (Iterator<E> i = collection.iterator(); i.hasNext(); )
1091 {
1092 notifications = inverseRemove(i.next(), notifications);
1093 }
1094 if (notifications == null)
1095 {
1096 dispatchNotification(notification);
1097 }
1098 else
1099 {
1100 notifications.add(notification);
1101 notifications.dispatch();
1102 }
1103 }
1104 else
1105 {
1106 if (notifications == null)
1107 {
1108 dispatchNotification(notification);
1109 }
1110 else
1111 {
1112 notifications.add(notification);
1113 notifications.dispatch();
1114 }
1115 }
1116 }
1117 else
1118 {
1119 doClear();
1120 dispatchNotification(createNotification(Notification.REMOVE_MANY, Collections.EMPTY_LIST, null, Notification.NO_INDEX, oldIsSet));
1121 }
1122 }
1123 else if (hasInverse())
1124 {
1125 if (size > 0)
1126 {
1127 Object [] oldData = data;
1128 int oldSize = size;
1129 doClear();
1130 NotificationChain notifications = createNotificationChain(oldSize);
1131 for (int i = 0; i < oldSize; ++i)
1132 {
1133 @SuppressWarnings("unchecked") E object = (E)oldData[i];
1134 notifications = inverseRemove(object, notifications);
1135 }
1136 if (notifications != null) notifications.dispatch();
1137 }
1138 else
1139 {
1140 doClear();
1141 }
1142 }
1143 else
1144 {
1145 doClear();
1146 }
1147 }
1148
1149 /**
1150 * Clears the list of all objects;
1151 * it does no {@link #inverseRemove inverse} updating.
1152 */
1153 protected void doClear()
1154 {
1155 super.clear();
1156 }
1157
1158 /**
1159 * Sets the object at the index
1160 * and returns the old object at the index;
1161 * it does no ranging checking or uniqueness checking.
1162 * In addition to the normal effects,
1163 * this override implementation generates notifications as {@link #isNotificationRequired required}
1164 * and delegates to {@link #inverseAdd inverseAdd} and {@link #inverseRemove inverseRemove} as {@link #hasInverse required}.
1165 * @param index the position in question.
1166 * @param object the object to set.
1167 * @return the old object at the index.
1168 * @see #isNotificationRequired
1169 * @see #hasInverse
1170 * @see #inverseAdd
1171 * @see #inverseRemove
1172 */
1173 @Override
1174 public E setUnique(int index, E object)
1175 {
1176 if (isNotificationRequired())
1177 {
1178 NotificationChain notifications = null;
1179 boolean oldIsSet = isSet();
1180 E oldObject;
1181 Notification notification = createNotification(Notification.SET, oldObject = doSetUnique(index, object), object, index, oldIsSet);
1182 if (hasInverse() && !equalObjects(oldObject, object))
1183 {
1184 if (oldObject != null)
1185 {
1186 notifications = inverseRemove(oldObject, notifications);
1187 }
1188
1189 notifications = inverseAdd(object, notifications);
1190
1191 if (hasShadow())
1192 {
1193 notifications = shadowSet(oldObject, object, notifications);
1194 }
1195
1196 if (notifications == null)
1197 {
1198 dispatchNotification(notification);
1199 }
1200 else
1201 {
1202 notifications.add(notification);
1203 notifications.dispatch();
1204 }
1205 }
1206 else
1207 {
1208 if (hasShadow())
1209 {
1210 notifications = shadowSet(oldObject, object, notifications);
1211 }
1212
1213 if (notifications == null)
1214 {
1215 dispatchNotification(notification);
1216 }
1217 else
1218 {
1219 notifications.add(notification);
1220 notifications.dispatch();
1221 }
1222 }
1223
1224 return oldObject;
1225 }
1226 else
1227 {
1228 E oldObject = doSetUnique(index, object);
1229 if (hasInverse() && !equalObjects(oldObject, object))
1230 {
1231 NotificationChain notifications = null;
1232 if (oldObject != null)
1233 {
1234 notifications = inverseRemove(oldObject, null);
1235 }
1236 notifications = inverseAdd(object, notifications);
1237 if (notifications != null) notifications.dispatch();
1238 }
1239 return oldObject;
1240 }
1241 }
1242
1243 /**
1244 * Sets the object at the index
1245 * and returns the old object at the index;
1246 * it does no ranging checking, uniqueness checking, inverse updating or notification.
1247 * @param index the position in question.
1248 * @param object the object to set.
1249 * @return the old object at the index.
1250 */
1251 protected E doSetUnique(int index, E object)
1252 {
1253 return super.setUnique(index, object);
1254 }
1255
1256 /**
1257 * Sets the object at the index
1258 * and returns the potentially updated notification chain;
1259 * it does no {@link #hasInverse inverse} updating.
1260 * This implementation generates notifications as {@link #isNotificationRequired required}.
1261 * @param index the position in question.
1262 * @param object the object to set.
1263 * @return the notification chain.
1264 * @see #isNotificationRequired
1265 * @see #hasInverse
1266 * @see #inverseAdd
1267 * @see #inverseRemove
1268 */
1269 public NotificationChain basicSet(int index, E object, NotificationChain notifications)
1270 {
1271 if (isNotificationRequired())
1272 {
1273 boolean oldIsSet = isSet();
1274 NotificationImpl notification = createNotification(Notification.SET, doSetUnique(index, object), object, index, oldIsSet);
1275 if (notifications == null)
1276 {
1277 notifications = notification;
1278 }
1279 else
1280 {
1281 notifications.add(notification);
1282 }
1283 }
1284 else
1285 {
1286 doSetUnique(index, object);
1287 }
1288 return notifications;
1289 }
1290
1291 /**
1292 * Moves the object at the source index of the list to the target index of the list
1293 * and returns the moved object.
1294 * In addition to the normal effects,
1295 * this override implementation generates notifications as {@link #isNotificationRequired required}.
1296 * @param targetIndex the new position for the object in the list.
1297 * @param sourceIndex the old position of the object in the list.
1298 * @return the moved object.
1299 * @exception IndexOutOfBoundsException if either index isn't within the size range.
1300 * @see #isNotificationRequired
1301 */
1302 @Override
1303 public E move(int targetIndex, int sourceIndex)
1304 {
1305 if (isNotificationRequired())
1306 {
1307 boolean oldIsSet = isSet();
1308 E object = doMove(targetIndex, sourceIndex);
1309 dispatchNotification
1310 (createNotification
1311 (Notification.MOVE,
1312 sourceIndex,
1313 object,
1314 targetIndex,
1315 oldIsSet));
1316 return object;
1317 }
1318 else
1319 {
1320 return doMove(targetIndex, sourceIndex);
1321 }
1322 }
1323
1324 /**
1325 * Moves the object at the source index of the list to the target index of the list
1326 * and returns the moved object;
1327 * it does no notification.
1328 * @param targetIndex the new position for the object in the list.
1329 * @param sourceIndex the old position of the object in the list.
1330 * @return the moved object.
1331 * @exception IndexOutOfBoundsException if either index isn't within the size range.
1332 */
1333 protected E doMove(int targetIndex, int sourceIndex)
1334 {
1335 return super.move(targetIndex, sourceIndex);
1336 }
1337 }