/**
* Copyright (c) 2002-2010 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM - Initial API and implementation
*/
package org.eclipse.emf.common.notify.impl;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.NotificationChain;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.common.notify.NotifyingList;
import org.eclipse.emf.common.util.BasicEList;
/**
* An extensible implementation of a notifying list.
*/
public class NotifyingListImpl<E> extends BasicEList<E> implements NotifyingList<E>
{
private static final long serialVersionUID = 1L;
/**
* Creates an empty instance.
*/
public NotifyingListImpl()
{
super();
}
/**
* Creates an empty instance with the given capacity.
* @param initialCapacity the initial capacity of the list before it must grow.
*/
public NotifyingListImpl(int initialCapacity)
{
super(initialCapacity);
}
/**
* Creates an instance that is a copy of the collection.
* @param collection the initial contents of the list.
*/
public NotifyingListImpl(Collection<? extends E> collection)
{
super(collection);
}
/**
* Returns <code>null</code>.
* @return <code>null</code>.
*/
public Object getNotifier()
{
return null;
}
/**
* Returns <code>null</code>.
* @return <code>null</code>.
*/
public Object getFeature()
{
return null;
}
/**
* Returns {@link org.eclipse.emf.common.notify.Notification#NO_FEATURE_ID}.
* @return <code>Notification.NO_FEATURE_ID</code>.
*/
public int getFeatureID()
{
return Notification.NO_FEATURE_ID;
}
/**
* Returns the result of calling {@link #getFeatureID()}.
* @param expectedClass the class to which the ID is relative.
* @return <code>getFeatureID()</code>.
*/
protected int getFeatureID(Class<?> expectedClass)
{
return getFeatureID();
}
/**
* Returns whether the list is considered set, i.e., whether it's not empty.
* A derived implementation may model this state directly.
* @return whether the list is considered set.
*/
protected boolean isSet()
{
return !isEmpty();
}
/**
* Returns <code>false</code>.
* @return <code>false</code>.
*/
protected boolean hasInverse()
{
return false;
}
/**
* Returns <code>!{@link #hasInverse()}</code>.
* @return <code>!hasInverse</code>.
*/
@Override
protected boolean canContainNull()
{
return !hasInverse();
}
/**
* Returns <code>false</code>.
* @return <code>false</code>.
*/
protected boolean isNotificationRequired()
{
return false;
}
/**
* Returns <code>false</code>.
* @return <code>false</code>.
*/
protected boolean hasShadow()
{
return false;
}
/**
* Does nothing and returns the <code>notifications</code>.
* Clients can override this to update the inverse of a bidirectional relation.
* @param object the object that's been added to the list.
* @param notifications the chain of accumulating notifications.
* @return the <code>notifications</code>.
*/
protected NotificationChain shadowAdd(E object, NotificationChain notifications)
{
return notifications;
}
/**
* Does nothing and returns the <code>notifications</code>.
* Clients can override this to update the inverse of a bidirectional relation.
* @param object the object that's been remove from the list.
* @param notifications the chain of accumulating notifications.
* @return the <code>notifications</code>.
*/
protected NotificationChain shadowRemove(E object, NotificationChain notifications)
{
return notifications;
}
/**
* Does nothing and returns the <code>notifications</code>.
* Clients can override this to update the inverse of a bidirectional relation.
* @param oldObject the object that's been removed from the list.
* @param newObject the object that's been added to the list.
* @param notifications the chain of accumulating notifications.
* @return the <code>notifications</code>.
*/
protected NotificationChain shadowSet(E oldObject, E newObject, NotificationChain notifications)
{
return notifications;
}
/**
* Does nothing and returns the <code>notifications</code>.
* Clients can override this to update the inverse of a bidirectional relation.
* @param object the object that's been added to the list.
* @param notifications the chain of accumulating notifications.
* @return the <code>notifications</code>.
*/
protected NotificationChain inverseAdd(E object, NotificationChain notifications)
{
return notifications;
}
/**
* Does nothing and returns the <code>notifications</code>.
* Clients can override this to update the inverse of a bidirectional relation.
* @param object the object that's been remove from the list.
* @param notifications the chain of accumulating notifications.
* @return the <code>notifications</code>.
*/
protected NotificationChain inverseRemove(E object, NotificationChain notifications)
{
return notifications;
}
/*
* @deprecated
*/
protected NotificationImpl createNotification(int eventType, Object oldObject, Object newObject, int index)
{
throw new UnsupportedOperationException("Please change your code to call new five argument version of this method");
}
/**
* Creates a notification.
* @param eventType the type of change that has occurred.
* @param oldObject the value of the notifier's feature before the change occurred.
* @param newObject the value of the notifier's feature after the change occurred.
* @param index the position at which the change occurred.
* @return a new notification.
*/
protected NotificationImpl createNotification(int eventType, Object oldObject, Object newObject, int index, boolean wasSet)
{
return
new NotificationImpl(eventType, oldObject, newObject, index, wasSet)
{
@Override
public Object getNotifier()
{
return NotifyingListImpl.this.getNotifier();
}
@Override
public Object getFeature()
{
return NotifyingListImpl.this.getFeature();
}
@Override
public int getFeatureID(Class<?> expectedClass)
{
return NotifyingListImpl.this.getFeatureID(expectedClass);
}
};
}
/**
* Creates a notification chain, if the expected capacity exceeds the threshold
* at which a list is better than chaining individual notification instances.
*/
protected NotificationChain createNotificationChain(int capacity)
{
return capacity < 100 ? null: new NotificationChainImpl(capacity);
}
/**
* Dispatches a notification to the notifier of the list.
* @param notification the notification to dispatch.
*/
protected void dispatchNotification(Notification notification)
{
((Notifier)getNotifier()).eNotify(notification);
}
/**
* Adds the object at the end of the list;
* it does no uniqueness checking.
* In addition to the normal effects,
* this override implementation generates notifications as {@link #isNotificationRequired required}
* and delegates to {@link #inverseAdd inverseAdd} as {@link #hasInverse required}.
* @param object the object to be added.
* @see #isNotificationRequired
* @see #hasInverse
* @see #inverseAdd
*/
@Override
public void addUnique(E object)
{
if (isNotificationRequired())
{
int index = size;
boolean oldIsSet = isSet();
doAddUnique(object);
NotificationImpl notification = createNotification(Notification.ADD, null, object, index, oldIsSet);
if (hasInverse())
{
NotificationChain notifications = inverseAdd(object, null);
if (hasShadow())
{
notifications = shadowAdd(object, notifications);
}
if (notifications == null)
{
dispatchNotification(notification);
}
else
{
notifications.add(notification);
notifications.dispatch();
}
}
else
{
dispatchNotification(notification);
}
}
else
{
doAddUnique(object);
if (hasInverse())
{
NotificationChain notifications = inverseAdd(object, null);
if (notifications != null) notifications.dispatch();
}
}
}
/**
* Adds the object at the end of the list;
* it does no uniqueness checking, inverse updating, or notification.
* @param object the object to be added.
*/
protected void doAddUnique(E object)
{
super.addUnique(object);
}
/**
* Adds the object at the given index in the list;
* it does no ranging checking or uniqueness checking.
* In addition to the normal effects,
* this override implementation generates notifications as {@link #isNotificationRequired required}
* and delegates to {@link #inverseAdd inverseAdd} as {@link #hasInverse required}.
* @param object the object to be added.
* @see #isNotificationRequired
* @see #hasInverse
* @see #inverseAdd
*/
@Override
public void addUnique(int index, E object)
{
if (isNotificationRequired())
{
boolean oldIsSet = isSet();
doAddUnique(index, object);
NotificationImpl notification = createNotification(Notification.ADD, null, object, index, oldIsSet);
if (hasInverse())
{
NotificationChain notifications = inverseAdd(object, null);
if (hasShadow())
{
notifications = shadowAdd(object, notifications);
}
if (notifications == null)
{
dispatchNotification(notification);
}
else
{
notifications.add(notification);
notifications.dispatch();
}
}
else
{
dispatchNotification(notification);
}
}
else
{
doAddUnique(index, object);
if (hasInverse())
{
NotificationChain notifications = inverseAdd(object, null);
if (notifications != null) notifications.dispatch();
}
}
}
/**
* Adds the object at the given index in the list;
* it does no range checking, uniqueness checking, inverse updating, or notification.
* @param object the object to be added.
*/
protected void doAddUnique(int index, E object)
{
super.addUnique(index, object);
}
/**
* Adds each object of the collection to the end of the list;
* it does no uniqueness checking.
* This implementation delegates to {@link #addAllUnique(int, Collection) addAllUnique(int, Collection)}.
* @param collection the collection of objects to be added.
* @see #inverseAdd
*/
@Override
public boolean addAllUnique(Collection<? extends E> collection)
{
return addAllUnique(size, collection);
}
/**
* Adds each object of the collection to the end of the list;
* it does no uniqueness checking, inverse updating, or notification.
* @param collection the collection of objects to be added.
*/
protected boolean doAddAllUnique(Collection<? extends E> collection)
{
return super.addAllUnique(collection);
}
/**
* Adds each object of the collection at each successive index in the list
* and returns whether any objects were added;
* it does no ranging checking or uniqueness checking.
* In addition to the normal effects,
* this override implementation generates notifications as {@link #isNotificationRequired required}
* and delegates to {@link #inverseAdd inverseAdd} as {@link #hasInverse required}.
* @param index the index at which to add.
* @param collection the collection of objects to be added.
* @return whether any objects were added.
* @see #isNotificationRequired
* @see #hasInverse
* @see #inverseAdd
*/
@Override
public boolean addAllUnique(int index, Collection<? extends E> collection)
{
int collectionSize = collection.size();
if (collectionSize == 0)
{
return false;
}
else
{
if (isNotificationRequired())
{
boolean oldIsSet = isSet();
doAddAllUnique(index, collection);
NotificationImpl notification =
collectionSize == 1 ?
createNotification(Notification.ADD, null, collection.iterator().next(), index, oldIsSet) :
createNotification(Notification.ADD_MANY, null, collection, index, oldIsSet);
if (hasInverse())
{
NotificationChain notifications = createNotificationChain(collectionSize);
int lastIndex = index + collectionSize;
for (int i = index; i < lastIndex; ++i)
{
@SuppressWarnings("unchecked") E value = (E)data[i];
notifications = inverseAdd(value, notifications);
notifications = shadowAdd(value, notifications);
}
if (notifications == null)
{
dispatchNotification(notification);
}
else
{
notifications.add(notification);
notifications.dispatch();
}
}
else
{
dispatchNotification(notification);
}
}
else
{
doAddAllUnique(index, collection);
if (hasInverse())
{
NotificationChain notifications = createNotificationChain(collectionSize);
int lastIndex = index + collectionSize;
for (int i = index; i < lastIndex; ++i)
{
@SuppressWarnings("unchecked") E object = (E)data[i];
notifications = inverseAdd(object, notifications);
}
if (notifications != null) notifications.dispatch();
}
}
return true;
}
}
/**
* Adds each object of the collection at each successive index in the list
* and returns whether any objects were added;
* it does no range checking, uniqueness checking, inverse updating, or notification.
* @param index the index at which to add.
* @param collection the collection of objects to be added.
* @return whether any objects were added.
*/
protected boolean doAddAllUnique(int index, Collection<? extends E> collection)
{
return super.addAllUnique(index, collection);
}
/**
* Adds each object from start to end of the array to the end of the list
* and returns whether any objects were added;
* it does no uniqueness checking.
* This implementation delegates to {@link #addAllUnique(int, Object[], int, int) addAllUnique(int, Object[], int, int)}.
* @param objects the objects to be added.
* @param start the index of first object to be added.
* @param end the index past the last object to be added.
* @return whether any objects were added.
* @see #inverseAdd
*/
@Override
public boolean addAllUnique(Object [] objects, int start, int end)
{
return addAllUnique(size, objects, start, end);
}
/**
* Adds each object from start to end of the array to the end of the list
* and returns whether any objects were added;
* it does no ranging checking, uniqueness checking, inverse updating, or notification.
* @param objects the objects to be added.
* @param start the index of first object to be added.
* @param end the index past the last object to be added.
* @return whether any objects were added.
*/
protected boolean doAddAllUnique(Object [] objects, int start, int end)
{
return super.addAllUnique(objects, start, end);
}
/**
* Adds each object from start to end of the array at each successive index in the list
* and returns whether any objects were added;
* it does no ranging checking or uniqueness checking.
* This implementation delegates to {@link #assign(int, Object) assign},
* {@link #didAdd(int, Object) didAdd}, and {@link #didChange() didChange}.
* In addition to the normal effects,
* this override implementation generates notifications as {@link #isNotificationRequired required}
* and delegates to {@link #inverseAdd inverseAdd} as {@link #hasInverse required}.
* @param index the index at which to add.
* @param objects the objects to be added.
* @param start the index of first object to be added.
* @param end the index past the last object to be added.
* @return whether any objects were added.
* @see #addAllUnique(int, Collection)
* @see #isNotificationRequired
* @see #hasInverse
* @see #inverseAdd
* @see #assign(int, Object)
*/
@Override
public boolean addAllUnique(int index, Object [] objects, int start, int end)
{
int collectionSize = end - start;
if (collectionSize == 0)
{
return false;
}
else
{
if (isNotificationRequired())
{
boolean oldIsSet = isSet();
doAddAllUnique(index, objects, start, end);
NotificationImpl notification;
if (collectionSize == 1)
{
notification = createNotification(Notification.ADD, null, objects[0], index, oldIsSet);
}
else
{
if (start != 0 || end != objects.length)
{
Object [] actualObjects = new Object [collectionSize];
for (int i = 0, j = start; j < end; ++i, ++j)
{
actualObjects[i] = objects[j];
}
notification = createNotification(Notification.ADD_MANY, null, Arrays.asList(actualObjects), index, oldIsSet);
}
else
{
notification = createNotification(Notification.ADD_MANY, null, Arrays.asList(objects), index, oldIsSet);
}
}
if (hasInverse())
{
NotificationChain notifications = createNotificationChain(collectionSize);
int lastIndex = index + collectionSize;
for (int i = index; i < lastIndex; ++i)
{
@SuppressWarnings("unchecked") E value = (E)data[i];
notifications = inverseAdd(value, notifications);
notifications = shadowAdd(value, notifications);
}
if (notifications == null)
{
dispatchNotification(notification);
}
else
{
notifications.add(notification);
notifications.dispatch();
}
}
else
{
dispatchNotification(notification);
}
}
else
{
doAddAllUnique(index, objects, start, end);
if (hasInverse())
{
NotificationChain notifications = createNotificationChain(collectionSize);
int lastIndex = index + collectionSize;
for (int i = index; i < lastIndex; ++i)
{
@SuppressWarnings("unchecked") E object = (E)data[i];
notifications = inverseAdd(object, notifications);
}
if (notifications != null) notifications.dispatch();
}
}
return true;
}
}
/**
* Adds each object from start to end of the array at each successive index in the list
* and returns whether any objects were added;
* it does no ranging checking, uniqueness checking, inverse updating, or notification.
* @param index the index at which to add.
* @param objects the objects to be added.
* @param start the index of first object to be added.
* @param end the index past the last object to be added.
* @return whether any objects were added.
*/
protected boolean doAddAllUnique(int index, Object [] objects, int start, int end)
{
return super.addAllUnique(index, objects, start, end);
}
/**
* Adds the object at the end of the list and returns the potentially updated notification chain;
* it does no {@link #inverseAdd inverse} updating.
* This implementation generates notifications as {@link #isNotificationRequired required}.
* @param object the object to be added.
* @return the notification chain.
* @see #isNotificationRequired
* @see #hasInverse
* @see #inverseAdd
*/
public NotificationChain basicAdd(E object, NotificationChain notifications)
{
if (isNotificationRequired())
{
int index = size;
boolean oldIsSet = isSet();
doAddUnique(index, object);
NotificationImpl notification = createNotification(Notification.ADD, null, object, index, oldIsSet);
if (notifications == null)
{
notifications = notification;
}
else
{
notifications.add(notification);
}
}
else
{
doAddUnique(size, object);
}
return notifications;
}
/**
* Removes the object at the index from the list and returns it.
* In addition to the normal effects,
* this override implementation generates notifications as {@link #isNotificationRequired required}
* and delegates to {@link #inverseRemove inverseRemove} as {@link #hasInverse required}.
* @param index the position of the object to remove.
* @return the removed object.
* @exception IndexOutOfBoundsException if the index isn't within the size range.
* @see #isNotificationRequired
* @see #hasInverse
* @see #inverseRemove
*/
@Override
public E remove(int index)
{
if (isNotificationRequired())
{
NotificationChain notifications = null;
boolean oldIsSet = isSet();
if (hasShadow())
{
notifications = shadowRemove(basicGet(index), null);
}
E oldObject;
NotificationImpl notification = createNotification(Notification.REMOVE, oldObject = doRemove(index), null, index, oldIsSet);
if (hasInverse() && oldObject != null)
{
notifications = inverseRemove(oldObject, notifications);
if (notifications == null)
{
dispatchNotification(notification);
}
else
{
notifications.add(notification);
notifications.dispatch();
}
}
else
{
if (notifications == null)
{
dispatchNotification(notification);
}
else
{
notifications.add(notification);
notifications.dispatch();
}
}
return oldObject;
}
else
{
E oldObject = doRemove(index);
if (hasInverse() && oldObject != null)
{
NotificationChain notifications = inverseRemove(oldObject, null);
if (notifications != null) notifications.dispatch();
}
return oldObject;
}
}
/**
* Removes the object at the index from the list and returns it;
* it does no inverse updating, or notification.
* @param index the position of the object to remove.
* @return the removed object.
* @exception IndexOutOfBoundsException if the index isn't within the size range.
*/
protected E doRemove(int index)
{
return super.remove(index);
}
/**
* Removes each object of the collection from the list and returns whether any object was actually contained by the list.
* In addition to the normal effects,
* this override implementation generates notifications as {@link #isNotificationRequired required}
* and delegates to {@link #inverseRemove inverseRemove} as {@link #hasInverse required}.
* @param collection the collection of objects to be removed.
* @return whether any object was actually contained by the list.
* @see #isNotificationRequired
* @see #hasInverse
* @see #inverseRemove
*/
@Override
public boolean removeAll(Collection<?> collection)
{
boolean oldIsSet = isSet();
boolean result = false;
int [] positions = null;
if (isNotificationRequired())
{
int listSize = collection.size();
if (listSize > 0)
{
NotificationChain notifications = createNotificationChain(listSize);
// Copy to a list and allocate positions.
//
BasicEList<Object> list = new BasicEList<Object>(collection);
Object[] objects = list.data();
positions = new int [listSize];
int count = 0;
if (isUnique())
{
// Count up the objects that will be removed.
// The objects are exchanged to produce this list's order
//
for (int i = 0; i < size; ++i)
{
@SuppressWarnings("unchecked") E initialObject = (E)data[i];
E object = initialObject;
LOOP:
for (int repeat = 0; repeat < 2; ++repeat)
{
for (int j = listSize; --j >= 0; )
{
if (equalObjects(object, objects[j]))
{
if (count != j)
{
Object x = objects[count];
objects[count] = objects[j];
objects[j] = x;
}
positions[count++] = i;
break LOOP;
}
}
object = resolve(object);
if (object == initialObject)
{
break;
}
}
}
}
else
{
BasicEList<Object> resultList = new BasicEList<Object>(listSize);
// Count up the objects that will be removed.
// The objects are exchanged to produce this list's order
//
for (int i = 0; i < size; ++i)
{
@SuppressWarnings("unchecked") E initialObject = (E)data[i];
E object = initialObject;
LOOP:
for (int repeat = 0; repeat < 2; ++repeat)
{
for (int j = listSize; --j >= 0; )
{
if (equalObjects(object, objects[j]))
{
if (positions.length <= count)
{
int [] oldPositions = positions;
positions = new int [2 * positions.length];
System.arraycopy(oldPositions, 0, positions, 0, count);
}
positions[count++] = i;
resultList.add(objects[j]);
break LOOP;
}
}
object = resolve(object);
if (object == initialObject)
{
break;
}
}
}
list = resultList;
objects = resultList.data();
listSize = count;
if (count > positions.length)
{
int [] oldPositions = positions;
positions = new int [count];
System.arraycopy(oldPositions, 0, positions, 0, count);
}
}
// If any objects are matched.
//
if (count > 0)
{
result = true;
if (hasShadow())
{
// Remove from by position in reverse order.
//
for (int i = 0; i < count; ++i)
{
@SuppressWarnings("unchecked") E object = (E)objects[i];
notifications = shadowRemove(object, notifications);
}
}
// Remove from by position in reverse order.
//
for (int i = count; --i >= 0;)
{
doRemove(positions[i]);
}
// Compact the results to remove unmatched objects
//
if (count != listSize)
{
for (int i = listSize; --i >= count; )
{
list.remove(i);
}
int [] oldPositions = positions;
positions = new int [count];
System.arraycopy(oldPositions, 0, positions, 0, count);
}
collection = list;
}
}
}
else
{
collection = getDuplicates(collection);
for (int i = size; --i >= 0; )
{
if (collection.contains(data[i]))
{
doRemove(i);
result = true;
}
}
}
if (result)
{
if (positions != null)
{
int collectionSize = collection.size();
NotificationImpl notification =
(collectionSize == 1 ?
createNotification(Notification.REMOVE, collection.iterator().next(), null, positions[0], oldIsSet) :
createNotification(Notification.REMOVE_MANY, collection, positions, positions[0], oldIsSet));
NotificationChain notifications = createNotificationChain(collectionSize);
if (hasInverse())
{
for (Iterator<?> i = collection.iterator(); i.hasNext(); )
{
@SuppressWarnings("unchecked") E object = (E)i.next();
notifications = inverseRemove(object, notifications);
}
if (notifications == null)
{
dispatchNotification(notification);
}
else
{
notifications.add(notification);
notifications.dispatch();
}
}
else
{
if (notifications == null)
{
dispatchNotification(notification);
}
else
{
notifications.add(notification);
notifications.dispatch();
}
}
}
else if (hasInverse())
{
NotificationChain notifications = createNotificationChain(collection.size());
for (Iterator<?> i = collection.iterator(); i.hasNext(); )
{
@SuppressWarnings("unchecked") E object = (E)i.next();
notifications = inverseRemove(object, notifications);
}
if (notifications != null) notifications.dispatch();
}
return true;
}
else
{
return false;
}
}
/**
* Returns the resolved object from this list for the purpose of testing whether {@link #removeAll(Collection)} applies to it.
* @param object the object to be resolved.
* @return the resolved object from this list for the purpose of testing whether removeAll applies to it.
*/
protected E resolve(E object)
{
return object;
}
/**
* Removes each object of the collection from the list and returns whether any object was actually contained by the list;
* it does no inverse updating, or notification.
* @param collection the collection of objects to be removed.
* @return whether any object was actually contained by the list.
*/
protected boolean doRemoveAll(Collection<?> collection)
{
return super.removeAll(collection);
}
/**
* Removes the object from the list and returns the potentially updated notification chain;
* it does no {@link #inverseRemove inverse} updating.
* This implementation generates notifications as {@link #isNotificationRequired required}.
* @param object the object to be removed.
* @return the notification chain.
* @see #isNotificationRequired
* @see #hasInverse
* @see #inverseRemove
*/
public NotificationChain basicRemove(Object object, NotificationChain notifications)
{
int index = indexOf(object);
if (index != -1)
{
if (isNotificationRequired())
{
boolean oldIsSet = isSet();
Object oldObject = doRemove(index);
NotificationImpl notification = createNotification(Notification.REMOVE, oldObject, null, index, oldIsSet);
if (notifications == null)
{
notifications = notification;
}
else
{
notifications.add(notification);
}
}
else
{
doRemove(index);
}
}
return notifications;
}
/**
* Clears the list of all objects.
* In addition to the normal effects,
* this override implementation generates notifications as {@link #isNotificationRequired required}
* and delegates to {@link #inverseRemove inverseRemove} as {@link #hasInverse required}.
* @see #isNotificationRequired
* @see #hasInverse
* @see #inverseRemove
*/
@Override
public void clear()
{
if (isNotificationRequired())
{
boolean oldIsSet = isSet();
if (size > 0)
{
List<E> collection = new UnmodifiableEList<E>(size, data);
int collectionSize = size;
NotificationChain notifications = createNotificationChain(collectionSize);
if (hasShadow())
{
for (int i = 0; i < size; ++i)
{
@SuppressWarnings("unchecked") E object = (E)data[i];
notifications = shadowRemove(object, notifications);
}
}
doClear();
Notification notification =
(collectionSize == 1 ?
createNotification(Notification.REMOVE, collection.get(0), null, 0, oldIsSet) :
createNotification(Notification.REMOVE_MANY, collection, null, Notification.NO_INDEX, oldIsSet));
if (hasInverse())
{
for (Iterator<E> i = collection.iterator(); i.hasNext(); )
{
notifications = inverseRemove(i.next(), notifications);
}
if (notifications == null)
{
dispatchNotification(notification);
}
else
{
notifications.add(notification);
notifications.dispatch();
}
}
else
{
if (notifications == null)
{
dispatchNotification(notification);
}
else
{
notifications.add(notification);
notifications.dispatch();
}
}
}
else
{
doClear();
dispatchNotification(createNotification(Notification.REMOVE_MANY, Collections.EMPTY_LIST, null, Notification.NO_INDEX, oldIsSet));
}
}
else if (hasInverse())
{
if (size > 0)
{
Object [] oldData = data;
int oldSize = size;
doClear();
NotificationChain notifications = createNotificationChain(oldSize);
for (int i = 0; i < oldSize; ++i)
{
@SuppressWarnings("unchecked") E object = (E)oldData[i];
notifications = inverseRemove(object, notifications);
}
if (notifications != null) notifications.dispatch();
}
else
{
doClear();
}
}
else
{
doClear();
}
}
/**
* Clears the list of all objects;
* it does no {@link #inverseRemove inverse} updating.
*/
protected void doClear()
{
super.clear();
}
/**
* Sets the object at the index
* and returns the old object at the index;
* it does no ranging checking or uniqueness checking.
* In addition to the normal effects,
* this override implementation generates notifications as {@link #isNotificationRequired required}
* and delegates to {@link #inverseAdd inverseAdd} and {@link #inverseRemove inverseRemove} as {@link #hasInverse required}.
* @param index the position in question.
* @param object the object to set.
* @return the old object at the index.
* @see #isNotificationRequired
* @see #hasInverse
* @see #inverseAdd
* @see #inverseRemove
*/
@Override
public E setUnique(int index, E object)
{
if (isNotificationRequired())
{
NotificationChain notifications = null;
boolean oldIsSet = isSet();
E oldObject;
Notification notification = createNotification(Notification.SET, oldObject = doSetUnique(index, object), object, index, oldIsSet);
if (hasInverse() && !equalObjects(oldObject, object))
{
if (oldObject != null)
{
notifications = inverseRemove(oldObject, notifications);
}
notifications = inverseAdd(object, notifications);
if (hasShadow())
{
notifications = shadowSet(oldObject, object, notifications);
}
if (notifications == null)
{
dispatchNotification(notification);
}
else
{
notifications.add(notification);
notifications.dispatch();
}
}
else
{
if (hasShadow())
{
notifications = shadowSet(oldObject, object, notifications);
}
if (notifications == null)
{
dispatchNotification(notification);
}
else
{
notifications.add(notification);
notifications.dispatch();
}
}
return oldObject;
}
else
{
E oldObject = doSetUnique(index, object);
if (hasInverse() && !equalObjects(oldObject, object))
{
NotificationChain notifications = null;
if (oldObject != null)
{
notifications = inverseRemove(oldObject, null);
}
notifications = inverseAdd(object, notifications);
if (notifications != null) notifications.dispatch();
}
return oldObject;
}
}
/**
* Sets the object at the index
* and returns the old object at the index;
* it does no ranging checking, uniqueness checking, inverse updating or notification.
* @param index the position in question.
* @param object the object to set.
* @return the old object at the index.
*/
protected E doSetUnique(int index, E object)
{
return super.setUnique(index, object);
}
/**
* Sets the object at the index
* and returns the potentially updated notification chain;
* it does no {@link #hasInverse inverse} updating.
* This implementation generates notifications as {@link #isNotificationRequired required}.
* @param index the position in question.
* @param object the object to set.
* @return the notification chain.
* @see #isNotificationRequired
* @see #hasInverse
* @see #inverseAdd
* @see #inverseRemove
*/
public NotificationChain basicSet(int index, E object, NotificationChain notifications)
{
if (isNotificationRequired())
{
boolean oldIsSet = isSet();
NotificationImpl notification = createNotification(Notification.SET, doSetUnique(index, object), object, index, oldIsSet);
if (notifications == null)
{
notifications = notification;
}
else
{
notifications.add(notification);
}
}
else
{
doSetUnique(index, object);
}
return notifications;
}
/**
* Moves the object at the source index of the list to the target index of the list
* and returns the moved object.
* In addition to the normal effects,
* this override implementation generates notifications as {@link #isNotificationRequired required}.
* @param targetIndex the new position for the object in the list.
* @param sourceIndex the old position of the object in the list.
* @return the moved object.
* @exception IndexOutOfBoundsException if either index isn't within the size range.
* @see #isNotificationRequired
*/
@Override
public E move(int targetIndex, int sourceIndex)
{
if (isNotificationRequired())
{
boolean oldIsSet = isSet();
E object = doMove(targetIndex, sourceIndex);
dispatchNotification
(createNotification
(Notification.MOVE,
sourceIndex,
object,
targetIndex,
oldIsSet));
return object;
}
else
{
return doMove(targetIndex, sourceIndex);
}
}
/**
* Moves the object at the source index of the list to the target index of the list
* and returns the moved object;
* it does no notification.
* @param targetIndex the new position for the object in the list.
* @param sourceIndex the old position of the object in the list.
* @return the moved object.
* @exception IndexOutOfBoundsException if either index isn't within the size range.
*/
protected E doMove(int targetIndex, int sourceIndex)
{
return super.move(targetIndex, sourceIndex);
}
}