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    }