/*******************************************************************************
* Copyright (c) 2009, 2010 Fraunhofer IWU 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:
* Fraunhofer IWU - initial API and implementation
*******************************************************************************/
package net.enilink.komma.common.notify;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import net.enilink.komma.common.util.ExtensibleList;
/**
* An extensible implementation of a notifying list.
*/
public class NotifyingList<E> extends ExtensibleList<E>implements INotifyingList<E> {
private static final long serialVersionUID = 1L;
/**
* Creates an empty instance.
*/
public NotifyingList() {
super();
}
/**
* Creates an empty instance with the given capacity.
*
* @param initialCapacity
* the initial capacity of the list before it must grow.
*/
public NotifyingList(int initialCapacity) {
super(initialCapacity);
}
/**
* Creates an instance that is a copy of the collection.
*
* @param collection
* the initial contents of the list.
*/
public NotifyingList(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 getProperty() {
return null;
}
/**
* 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 INotificationChain shadowAdd(E object, INotificationChain 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 INotificationChain shadowRemove(E object, INotificationChain 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 INotificationChain shadowSet(E oldObject, E newObject, INotificationChain 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 INotificationChain inverseAdd(E object, INotificationChain 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 INotificationChain inverseRemove(E object, INotificationChain notifications) {
return notifications;
}
/**
* 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 PropertyNotification createNotification(int eventType, Object oldObject, Object newObject, int index,
boolean wasSet) {
return new PropertyNotification(eventType, oldObject, newObject, index, wasSet) {
@Override
public Object getSubject() {
return NotifyingList.this.getNotifier();
}
@Override
public Object getProperty() {
return NotifyingList.this.getProperty();
}
};
}
/**
* Creates a notification chain, if the expected capacity exceeds the
* threshold at which a list is better than chaining individual notification
* instances.
*/
@SuppressWarnings("unchecked")
protected INotificationChain createNotificationChain(int capacity) {
return capacity < 100 ? null : new NotificationChain((INotificationBroadcaster) getNotifier(), capacity);
}
/**
* Dispatches a notification to the notifier of the list.
*
* @param notification
* the notification to dispatch.
*/
@SuppressWarnings("unchecked")
protected void dispatchNotification(IPropertyNotification notification) {
((INotificationBroadcaster) getNotifier()).fireNotifications(Arrays.asList(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);
PropertyNotification notification = createNotification(IPropertyNotification.ADD, null, object, index,
oldIsSet);
if (hasInverse()) {
INotificationChain 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()) {
INotificationChain 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);
PropertyNotification notification = createNotification(IPropertyNotification.ADD, null, object, index,
oldIsSet);
if (hasInverse()) {
INotificationChain 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()) {
INotificationChain 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);
PropertyNotification notification = collectionSize == 1
? createNotification(IPropertyNotification.ADD, null, collection.iterator().next(), index,
oldIsSet)
: createNotification(IPropertyNotification.ADD_MANY, null, collection, index, oldIsSet);
if (hasInverse()) {
INotificationChain 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()) {
INotificationChain 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);
PropertyNotification notification;
if (collectionSize == 1) {
notification = createNotification(IPropertyNotification.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(IPropertyNotification.ADD_MANY, null,
Arrays.asList(actualObjects), index, oldIsSet);
} else {
notification = createNotification(IPropertyNotification.ADD_MANY, null, Arrays.asList(objects),
index, oldIsSet);
}
}
if (hasInverse()) {
INotificationChain 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()) {
INotificationChain 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 INotificationChain basicAdd(E object, INotificationChain notifications) {
if (isNotificationRequired()) {
int index = size;
boolean oldIsSet = isSet();
doAddUnique(index, object);
PropertyNotification notification = createNotification(IPropertyNotification.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()) {
INotificationChain notifications = null;
boolean oldIsSet = isSet();
if (hasShadow()) {
notifications = shadowRemove(basicGet(index), null);
}
E oldObject;
PropertyNotification notification = createNotification(IPropertyNotification.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) {
INotificationChain 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) {
INotificationChain notifications = createNotificationChain(listSize);
// Copy to a list and allocate positions.
//
ExtensibleList<Object> list = new ExtensibleList<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 {
ExtensibleList<Object> resultList = new ExtensibleList<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();
PropertyNotification notification = (collectionSize == 1
? createNotification(IPropertyNotification.REMOVE, collection.iterator().next(), null,
positions[0], oldIsSet)
: createNotification(IPropertyNotification.REMOVE_MANY, collection, positions, positions[0],
oldIsSet));
INotificationChain 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()) {
INotificationChain 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 INotificationChain basicRemove(Object object, INotificationChain notifications) {
int index = indexOf(object);
if (index != -1) {
if (isNotificationRequired()) {
boolean oldIsSet = isSet();
Object oldObject = doRemove(index);
PropertyNotification notification = createNotification(IPropertyNotification.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;
INotificationChain notifications = createNotificationChain(collectionSize);
if (hasShadow()) {
for (int i = 0; i < size; ++i) {
@SuppressWarnings("unchecked")
E object = (E) data[i];
notifications = shadowRemove(object, notifications);
}
}
doClear();
PropertyNotification notification = (collectionSize == 1
? createNotification(IPropertyNotification.REMOVE, collection.get(0), null, 0, oldIsSet)
: createNotification(IPropertyNotification.REMOVE_MANY, collection, null,
IPropertyNotification.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(IPropertyNotification.REMOVE_MANY, Collections.EMPTY_LIST, null,
IPropertyNotification.NO_INDEX, oldIsSet));
}
} else if (hasInverse()) {
if (size > 0) {
Object[] oldData = data;
int oldSize = size;
doClear();
INotificationChain 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()) {
INotificationChain notifications = null;
boolean oldIsSet = isSet();
E oldObject;
PropertyNotification notification = createNotification(IPropertyNotification.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)) {
INotificationChain 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 INotificationChain basicSet(int index, E object, INotificationChain notifications) {
if (isNotificationRequired()) {
boolean oldIsSet = isSet();
PropertyNotification notification = createNotification(IPropertyNotification.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(IPropertyNotification.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);
}
}