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