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    }