/** | |
* <copyright> | |
* | |
* Copyright (c) 2014 itemis 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: | |
* itemis - Initial API and implementation | |
* | |
* </copyright> | |
*/ | |
package org.eclipse.sphinx.emf.util; | |
import java.io.Serializable; | |
import java.util.ArrayList; | |
import java.util.Collection; | |
import java.util.List; | |
import java.util.ListIterator; | |
import org.eclipse.core.runtime.Assert; | |
import org.eclipse.core.runtime.CoreException; | |
import org.eclipse.emf.common.util.AbstractEList; | |
import org.eclipse.emf.common.util.ECollections; | |
import org.eclipse.sphinx.emf.Activator; | |
import org.eclipse.sphinx.platform.util.PlatformLogUtil; | |
/** | |
* An extensible abstract unwrapping delegating list implementation. | |
*/ | |
public abstract class AbstractUnwrappingEList<W, T> extends AbstractEList<T> implements Cloneable, Serializable { | |
private static final long serialVersionUID = 1L; | |
protected Class<T> targetType; | |
protected Class<W> wrapperType; | |
private List<W> delegateList; | |
/** | |
* Wraps the given object. | |
* | |
* @param object | |
* object to be wrapped. | |
* @return the wrapped object. | |
* @throws CoreException | |
*/ | |
protected abstract W wrap(T object) throws CoreException; | |
/** | |
* Unwraps the given object. | |
* | |
* @param object | |
* object to be unwrapped. | |
* @return the unwrapped object. | |
*/ | |
protected abstract T unwrap(W object); | |
/** | |
* Creates an instance of the AbstractUnwrappingEList delegating list. | |
* | |
* @param wrapperType | |
* the wrapper type | |
* @param targetType | |
* the target object type | |
*/ | |
public AbstractUnwrappingEList(List<W> delegateList, Class<W> wrapperType, Class<T> targetType) { | |
super(); | |
Assert.isNotNull(delegateList); | |
Assert.isNotNull(wrapperType); | |
Assert.isNotNull(targetType); | |
this.delegateList = delegateList; | |
this.wrapperType = wrapperType; | |
this.targetType = targetType; | |
} | |
/** | |
* Returns the list that acts as the backing store. | |
* | |
* @return the list that acts as the backing store. | |
*/ | |
protected List<W> delegateList() { | |
return delegateList; | |
} | |
/** | |
* Returns the number of objects in the list. | |
* | |
* @return the number of objects in the list. | |
*/ | |
@Override | |
public int size() { | |
return delegateSize(); | |
} | |
/** | |
* Returns the number of objects in the backing store list. | |
* | |
* @return the number of objects in the backing store list. | |
*/ | |
protected int delegateSize() { | |
return delegateList().size(); | |
} | |
/** | |
* Returns whether the list has zero size. | |
* | |
* @return whether the list has zero size. | |
*/ | |
@Override | |
public boolean isEmpty() { | |
return delegateIsEmpty(); | |
} | |
/** | |
* Returns whether the backing store list has zero size. | |
* | |
* @return whether the backing store list has zero size. | |
*/ | |
protected boolean delegateIsEmpty() { | |
return delegateList().isEmpty(); | |
} | |
/** | |
* Returns whether the list contains the object. | |
* | |
* @param object | |
* the object in question. | |
* @return whether the list contains the object. | |
*/ | |
@Override | |
public boolean contains(Object object) { | |
return delegateContains(object); | |
} | |
/** | |
* Returns whether the backing store list contains the object. | |
* | |
* @param object | |
* the object in question. | |
* @return whether the backing store list contains the object. | |
*/ | |
protected boolean delegateContains(Object object) { | |
return delegateList().contains(object); | |
} | |
/** | |
* Returns whether the list contains each object in the collection. | |
* | |
* @return whether the list contains each object in the collection. | |
* @see #contains | |
* @see #useEquals | |
*/ | |
@Override | |
public boolean containsAll(Collection<?> collection) { | |
return delegateContainsAll(collection); | |
} | |
/** | |
* Returns whether the backing store list contains each object in the collection. | |
* | |
* @return whether the backing store list contains each object in the collection. | |
* @see #contains | |
* @see #useEquals | |
*/ | |
protected boolean delegateContainsAll(Collection<?> collection) { | |
return delegateList().containsAll(collection); | |
} | |
/** | |
* Returns the position of the first occurrence of the object in the list. | |
* | |
* @param object | |
* the object in question. | |
* @return the position of the first occurrence of the object in the list. | |
*/ | |
@Override | |
public int indexOf(Object object) { | |
return delegateIndexOf(object); | |
} | |
/** | |
* Returns the position of the first occurrence of the object in the backing store list. | |
* | |
* @param object | |
* the object in question. | |
* @return the position of the first occurrence of the object in the backing store list. | |
*/ | |
protected int delegateIndexOf(Object object) { | |
return delegateList().indexOf(object); | |
} | |
/** | |
* Returns the position of the last occurrence of the object in the list. | |
* | |
* @param object | |
* the object in question. | |
* @return the position of the last occurrence of the object in the list. | |
*/ | |
@Override | |
public int lastIndexOf(Object object) { | |
return delegateLastIndexOf(object); | |
} | |
/** | |
* Returns the position of the last occurrence of the object in the backing store list. | |
* | |
* @param object | |
* the object in question. | |
* @return the position of the last occurrence of the object in the backing store list. | |
*/ | |
protected int delegateLastIndexOf(Object object) { | |
return delegateList().lastIndexOf(object); | |
} | |
/** | |
* Returns an array containing all the objects in sequence. | |
* | |
* @return an array containing all the objects in sequence. | |
*/ | |
@Override | |
public Object[] toArray() { | |
return delegateToArray(); | |
} | |
/** | |
* Returns an array containing all the objects in the backing store list in sequence. | |
* | |
* @return an array containing all the objects in the backing store list in sequence. | |
*/ | |
protected Object[] delegateToArray() { | |
List<T> result = new ArrayList<T>(size()); | |
for (W object : delegateList()) { | |
try { | |
result.add(unwrap(object)); | |
} catch (Exception ex) { | |
PlatformLogUtil.logAsError(Activator.getPlugin(), ex); | |
} | |
} | |
return result.toArray(); | |
} | |
/** | |
* Returns an array containing all the objects in sequence. | |
* | |
* @param array | |
* the array that will be filled and returned, if it's big enough; otherwise, a suitably large array of | |
* the same type will be allocated and used instead. | |
* @return an array containing all the objects in sequence. | |
*/ | |
@Override | |
public <T1> T1[] toArray(T1[] array) { | |
return delegateToArray(array); | |
} | |
/** | |
* Returns an array containing all the objects in the backing store list in sequence. | |
* | |
* @param array | |
* the array that will be filled and returned, if it's big enough; otherwise, a suitably large array of | |
* the same type will be allocated and used instead. | |
* @return an array containing all the objects in sequence. | |
*/ | |
protected <T1> T1[] delegateToArray(T1[] array) { | |
List<T> result = new ArrayList<T>(size()); | |
for (W object : delegateList()) { | |
try { | |
result.add(unwrap(object)); | |
} catch (Exception ex) { | |
PlatformLogUtil.logAsError(Activator.getPlugin(), ex); | |
} | |
} | |
return result.toArray(array); | |
} | |
/** | |
* Returns the object at the index. This implementation delegates to {@link #resolve resolve} so that clients may | |
* transform the fetched object. | |
* | |
* @param index | |
* the position in question. | |
* @return the object at the index. | |
* @exception IndexOutOfBoundsException | |
* if the index isn't within the size range. | |
* @see #resolve | |
* @see #basicGet | |
*/ | |
@Override | |
public T get(int index) { | |
return resolve(index, delegateGet(index)); | |
} | |
/** | |
* Returns the unwrapped object at the index in the backing store list. | |
* | |
* @param index | |
* the position in question. | |
* @return the object at the index. | |
* @exception IndexOutOfBoundsException | |
* if the index isn't within the size range. | |
*/ | |
protected T delegateGet(int index) { | |
W object = delegateList().get(index); | |
try { | |
return unwrap(object); | |
} catch (Exception ex) { | |
PlatformLogUtil.logAsError(Activator.getPlugin(), ex); | |
return null; | |
} | |
} | |
/** | |
* Returns the object at the index without {@link #resolve resolving} it. | |
* | |
* @param index | |
* the position in question. | |
* @return the object at the index. | |
* @exception IndexOutOfBoundsException | |
* if the index isn't within the size range. | |
* @see #resolve | |
* @see #get | |
*/ | |
@Override | |
protected T basicGet(int index) { | |
return delegateGet(index); | |
} | |
@Override | |
protected T primitiveGet(int index) { | |
return delegateGet(index); | |
} | |
/** | |
* Sets the object at the index and returns the old object at the index; it does no ranging checking or uniqueness | |
* checking. This implementation delegates to {@link #didSet didSet} and {@link #didChange didChange}. | |
* | |
* @param index | |
* the position in question. | |
* @param object | |
* the object to set. | |
* @return the old object at the index. | |
* @see #set | |
*/ | |
@Override | |
public T setUnique(int index, T object) { | |
T oldObject = delegateSet(index, validate(index, object)); | |
didSet(index, object, oldObject); | |
didChange(); | |
return oldObject; | |
} | |
/** | |
* Sets the wrapped object at the index in the backing store list and returns the unwrapped object at the index. | |
* | |
* @param object | |
* the object to set. | |
* @return the old object at the index. | |
*/ | |
protected T delegateSet(int index, T object) { | |
try { | |
return unwrap(delegateList().set(index, wrap(object))); | |
} catch (Exception ex) { | |
PlatformLogUtil.logAsError(Activator.getPlugin(), ex); | |
return null; | |
} | |
} | |
/** | |
* Adds the object at the end of the list; it does no uniqueness checking. This implementation delegates to | |
* {@link #didAdd didAdd} and {@link #didChange didChange}. after uniqueness checking. | |
* | |
* @param object | |
* the object to be added. | |
* @see #add(Object) | |
*/ | |
@Override | |
public void addUnique(T object) { | |
++modCount; | |
int size = size(); | |
delegateAdd(validate(size, object)); | |
didAdd(size, object); | |
didChange(); | |
} | |
/** | |
* Adds the wrapped object at the end of the backing store list. | |
* | |
* @param object | |
* the object to be added. | |
*/ | |
protected void delegateAdd(T object) { | |
try { | |
W target = wrap(object); | |
delegateList().add(target); | |
} catch (Exception ex) { | |
PlatformLogUtil.logAsError(Activator.getPlugin(), ex); | |
} | |
} | |
/** | |
* Adds the object at the given index in the list; it does no ranging checking or uniqueness checking. This | |
* implementation delegates to {@link #didAdd didAdd} and {@link #didChange didChange}. | |
* | |
* @param object | |
* the object to be added. | |
* @see #add(int, Object) | |
*/ | |
@Override | |
public void addUnique(int index, T object) { | |
++modCount; | |
delegateAdd(index, validate(index, object)); | |
didAdd(index, object); | |
didChange(); | |
} | |
/** | |
* Adds the wrapped object at the given index in the backing store list. | |
* | |
* @param object | |
* the object to be added. | |
*/ | |
protected void delegateAdd(int index, T object) { | |
try { | |
delegateList().add(index, wrap(object)); | |
} catch (Exception ex) { | |
PlatformLogUtil.logAsError(Activator.getPlugin(), ex); | |
} | |
} | |
/** | |
* Adds each object of the collection to the end of the list; it does no uniqueness checking. This implementation | |
* delegates to {@link #didAdd didAdd} and {@link #didChange didChange}. | |
* | |
* @param collection | |
* the collection of objects to be added. | |
* @see #addAll(Collection) | |
*/ | |
@Override | |
public boolean addAllUnique(Collection<? extends T> collection) { | |
++modCount; | |
if (collection.isEmpty()) { | |
return false; | |
} else { | |
int i = size(); | |
for (T object : collection) { | |
delegateAdd(validate(i, object)); | |
didAdd(i, object); | |
didChange(); | |
i++; | |
} | |
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 ranging checking or uniqueness checking. This implementation delegates to {@link #didAdd | |
* didAdd} and {@link #didChange didChange}. | |
* | |
* @param index | |
* the index at which to add. | |
* @param collection | |
* the collection of objects to be added. | |
* @return whether any objects were added. | |
* @see #addAll(int, Collection) | |
*/ | |
@Override | |
public boolean addAllUnique(int index, Collection<? extends T> collection) { | |
++modCount; | |
if (collection.isEmpty()) { | |
return false; | |
} else { | |
for (T object : collection) { | |
delegateAdd(index, validate(index, object)); | |
didAdd(index, object); | |
didChange(); | |
index++; | |
} | |
return true; | |
} | |
} | |
/** | |
* Adds each object from start to end of the array at the index of list and returns whether any objects were added; | |
* it does no ranging checking or uniqueness checking. This implementation delegates to {@link #delegateAdd(Object) | |
* delegatedAdd}, {@link #didAdd didAdd}, and {@link #didChange didChange}. | |
* | |
* @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, Object[], int, int) | |
*/ | |
@Override | |
public boolean addAllUnique(Object[] objects, int start, int end) { | |
int growth = end - start; | |
++modCount; | |
if (growth == 0) { | |
return false; | |
} else { | |
int index = size(); | |
for (int i = start; i < end; ++i, ++index) { | |
@SuppressWarnings("unchecked") | |
T object = (T) objects[i]; | |
delegateAdd(validate(index, object)); | |
didAdd(index, object); | |
didChange(); | |
} | |
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 or uniqueness checking. This implementation delegates to | |
* {@link #delegateAdd(int, Object) delegatedAdd}, {@link #didAdd didAdd}, and {@link #didChange didChange}. | |
* | |
* @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(Object[], int, int) | |
*/ | |
@Override | |
public boolean addAllUnique(int index, Object[] objects, int start, int end) { | |
int growth = end - start; | |
++modCount; | |
if (growth == 0) { | |
return false; | |
} else { | |
for (int i = start; i < end; ++i, ++index) { | |
@SuppressWarnings("unchecked") | |
T object = (T) objects[i]; | |
delegateAdd(validate(index, object)); | |
didAdd(index, object); | |
didChange(); | |
} | |
return true; | |
} | |
} | |
/** | |
* Removes the object from the list and returns whether the object was actually contained by the list. This | |
* implementation uses {@link #indexOf indexOf} to find the object and delegates to {@link #remove(int) remove(int)} | |
* in the case that it finds the object. | |
* | |
* @param object | |
* the object to be removed. | |
* @return whether the object was actually contained by the list. | |
*/ | |
@Override | |
public boolean remove(Object object) { | |
int index = indexOf(object); | |
if (index >= 0) { | |
remove(index); | |
return true; | |
} else { | |
return false; | |
} | |
} | |
/** | |
* Removes each object of the collection from the list and returns whether any object was actually contained by the | |
* list. | |
* | |
* @param collection | |
* the collection of objects to be removed. | |
* @return whether any object was actually contained by the list. | |
*/ | |
@Override | |
public boolean removeAll(Collection<?> collection) { | |
boolean modified = false; | |
for (ListIterator<?> i = listIterator(); i.hasNext();) { | |
if (collection.contains(i.next())) { | |
i.remove(); | |
modified = true; | |
} | |
} | |
return modified; | |
} | |
/** | |
* Removes the object at the index from the list and returns it. This implementation delegates to {@link #didRemove | |
* didRemove} and {@link #didChange didChange}. | |
* | |
* @param index | |
* the position of the object to remove. | |
* @return the removed object. | |
* @exception IndexOutOfBoundsException | |
* if the index isn't within the size range. | |
*/ | |
@Override | |
public T remove(int index) { | |
++modCount; | |
T oldObject = delegateRemove(index); | |
didRemove(index, oldObject); | |
didChange(); | |
return oldObject; | |
} | |
/** | |
* Removes the object at the index from the backing store list and returns the unwrapped object. | |
* | |
* @return the removed object. | |
* @exception IndexOutOfBoundsException | |
* if the index isn't within the size range. | |
*/ | |
protected T delegateRemove(int index) { | |
try { | |
return unwrap(delegateList().remove(index)); | |
} catch (Exception ex) { | |
PlatformLogUtil.logAsError(Activator.getPlugin(), ex); | |
return null; | |
} | |
} | |
/** | |
* Removes from the list each object not contained by the collection and returns whether any object was actually | |
* removed. This delegates to {@link #remove(int) remove(int)} in the case that it finds an object that isn't | |
* retained. | |
* | |
* @param collection | |
* the collection of objects to be retained. | |
* @return whether any object was actually removed. | |
*/ | |
@Override | |
public boolean retainAll(Collection<?> collection) { | |
boolean modified = false; | |
for (ListIterator<?> i = listIterator(); i.hasNext();) { | |
if (!collection.contains(i.next())) { | |
i.remove(); | |
modified = true; | |
} | |
} | |
return modified; | |
} | |
/** | |
* Clears the list of all objects. | |
*/ | |
@Override | |
public void clear() { | |
doClear(size(), delegateToArray()); | |
} | |
/** | |
* Does the actual job of clearing all the objects. | |
* | |
* @param oldSize | |
* the size of the list before it is cleared. | |
* @param oldData | |
* old values of the list before it is cleared. | |
*/ | |
protected void doClear(int oldSize, Object[] oldData) { | |
++modCount; | |
delegateClear(); | |
didClear(oldSize, oldData); | |
didChange(); | |
} | |
/** | |
* Clears the backing store list of all objects. | |
*/ | |
protected void delegateClear() { | |
delegateList().clear(); | |
} | |
/** | |
* Moves the object at the source index of the list to the target index of the list and returns the moved object. | |
* This implementation delegates to {@link #didMove didMove} and {@link #didChange didChange}. | |
* | |
* @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. | |
*/ | |
@Override | |
public T move(int targetIndex, int sourceIndex) { | |
++modCount; | |
int size = size(); | |
if (targetIndex >= size || targetIndex < 0) { | |
throw new IndexOutOfBoundsException("targetIndex=" + targetIndex + ", size=" + size); //$NON-NLS-1$ //$NON-NLS-2$ | |
} | |
if (sourceIndex >= size || sourceIndex < 0) { | |
throw new IndexOutOfBoundsException("sourceIndex=" + sourceIndex + ", size=" + size); //$NON-NLS-1$ //$NON-NLS-2$ | |
} | |
T object; | |
if (targetIndex != sourceIndex) { | |
object = delegateMove(targetIndex, sourceIndex); | |
didMove(targetIndex, object, sourceIndex); | |
didChange(); | |
} else { | |
object = delegateGet(sourceIndex); | |
} | |
return object; | |
} | |
/** | |
* Moves the object at the source index in the backing store list by removing it and adding it at the new target | |
* index. | |
* | |
* @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. | |
* @since 2.3 | |
*/ | |
protected T delegateMove(int targetIndex, int sourceIndex) { | |
T result = delegateRemove(sourceIndex); | |
delegateAdd(targetIndex, result); | |
return result; | |
} | |
/** | |
* Returns whether the object is a list with corresponding equal objects. This implementation uses either | |
* <code>equals</code> or <code>"=="</code> depending on {@link #useEquals useEquals}. | |
* | |
* @return whether the object is a list with corresponding equal objects. | |
* @see #useEquals | |
*/ | |
@Override | |
public boolean equals(Object object) { | |
return delegateEquals(object); | |
} | |
/** | |
* Returns whether the object is a list with corresponding equal objects to those in the backing store list. | |
* | |
* @return whether the object is a list with corresponding equal objects. | |
*/ | |
protected boolean delegateEquals(Object object) { | |
return delegateList().equals(object); | |
} | |
/** | |
* Returns a hash code computed from each object's hash code. | |
* | |
* @return a hash code. | |
*/ | |
@Override | |
public int hashCode() { | |
return delegateHashCode(); | |
} | |
/** | |
* Returns the hash code of the backing store list. | |
* | |
* @return a hash code. | |
*/ | |
protected int delegateHashCode() { | |
return delegateList().hashCode(); | |
} | |
/** | |
* Returns a string of the form <code>"[object1, object2]"</code>. | |
* | |
* @return a string of the form <code>"[object1, object2]"</code>. | |
*/ | |
@Override | |
public String toString() { | |
return delegateToString(); | |
} | |
/** | |
* Returns a the string form of the backing store list. | |
* | |
* @return a the string form of the backing store list. | |
*/ | |
protected String delegateToString() { | |
return delegateList().toString(); | |
} | |
/** | |
* Returns an <b>unsafe</b> list that provides a {@link #resolve non-resolving} view of the underlying data storage. | |
* | |
* @return an <b>unsafe</b> list that provides a non-resolving view of the underlying data storage. | |
*/ | |
@Override | |
protected List<T> basicList() { | |
if (delegateSize() == 0) { | |
return ECollections.emptyEList(); | |
} else { | |
return ECollections.unmodifiableEList(this); | |
} | |
} | |
} |