blob: f4c9302320b4d8fe8aa537e3a420d93a4b35f82c [file] [log] [blame]
/*
* Copyright (c) 2005, 2018 IBM Corporation, Embarcadero Technologies, CEA, and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* IBM - initial API and implementation
* Kenn Hussey (Embarcadero Technologies) - 204200
* Kenn Hussey (CEA) - 414745
* Kenn Hussey - 535301
*
*/
package org.eclipse.uml2.common.util;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.NoSuchElementException;
import java.util.RandomAccess;
import java.util.Set;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.impl.NotificationImpl;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.UniqueEList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.impl.ENotificationImpl;
import org.eclipse.emf.ecore.util.AbstractSequentialInternalEList;
import org.eclipse.emf.ecore.util.FeatureMap;
import org.eclipse.emf.ecore.util.FeatureMapUtil;
import org.eclipse.emf.ecore.util.InternalEList;
/**
* A list whose contents are derived (dynamically using a "smart" iterator) from
* the values of other features in a trivial way (e.g. by type). The default
* implementation does not support modification.
*
* @since 1.2
*/
public class DerivedEObjectEList<E>
extends AbstractSequentialInternalEList<E>
implements EStructuralFeature.Setting, InternalEList.Unsettable<E> {
protected class DerivedListIterator
implements ListIterator<E> {
protected int index = 0;
protected int featureIndex = 0;
protected ListIterator<Object> valuesIterator = null;
protected EStructuralFeature preparedFeature = null;
protected EList<Object> preparedValues = new UniqueEList.FastCompare<Object>();
protected int prepared = 0;
protected boolean scanNext(EStructuralFeature nextFeature,
ListIterator<Object> nextValuesIterator) {
boolean isFeatureMap = FeatureMapUtil.isFeatureMap(nextFeature);
while (nextValuesIterator.hasNext()) {
Object nextValue = nextValuesIterator.next();
if (isFeatureMap) {
FeatureMap.Entry entry = (FeatureMap.Entry) nextValue;
nextFeature = entry.getEStructuralFeature();
nextValue = entry.getValue();
}
if ((isIncluded(nextFeature)
? nextValue != null
: isIncluded(nextValue))
&& ((index < preparedValues.size() && nextValue == preparedValues
.get(index)) || preparedValues.add(nextValue))) {
valuesIterator = nextValuesIterator;
preparedFeature = nextFeature;
return true;
}
}
return false;
}
protected boolean prepareNext() {
if (valuesIterator == null
|| !scanNext(preparedFeature, valuesIterator)) {
while (featureIndex < sourceFeatureIDs.length) {
int sourceFeatureID = sourceFeatureIDs[featureIndex++];
if (owner.eIsSet(sourceFeatureID)) {
EStructuralFeature sourceFeature = getEStructuralFeature(sourceFeatureID);
Object value = owner.eGet(sourceFeatureID, resolve(),
true);
if (sourceFeature.isMany()
|| FeatureMapUtil.isFeatureMap(sourceFeature)) {
@SuppressWarnings("unchecked")
InternalEList<Object> valuesList = (InternalEList<Object>) value;
if (scanNext(sourceFeature, resolve()
? valuesList.listIterator()
: valuesList.basicListIterator())) {
prepared = 3;
return true;
}
} else if ((isIncluded(sourceFeature)
? value != null
: isIncluded(value))
&& ((index < preparedValues.size() && value == preparedValues
.get(index)) || preparedValues.add(value))) {
valuesIterator = null;
preparedFeature = sourceFeature;
prepared = 2;
return true;
}
}
}
prepared = 1;
return false;
} else {
prepared = 3;
return true;
}
}
public boolean hasNext() {
switch (prepared) {
case 3 :
case 2 :
return true;
case 1 :
return false;
case -3 :
valuesIterator.next();
default :
return prepareNext();
}
}
public E next() {
if (hasNext()) {
prepared = 0;
Object next = preparedValues.get(index++);
hasNext();
return derive(next);
} else {
throw new NoSuchElementException();
}
}
protected boolean scanPrevious(EStructuralFeature previousFeature,
ListIterator<Object> previousValuesIterator) {
boolean isFeatureMap = FeatureMapUtil.isFeatureMap(previousFeature);
while (previousValuesIterator.hasPrevious()) {
Object previousValue = previousValuesIterator.previous();
if (isFeatureMap) {
FeatureMap.Entry entry = (FeatureMap.Entry) previousValue;
previousFeature = entry.getEStructuralFeature();
previousValue = entry.getValue();
}
if (index > 0 && previousValue == preparedValues.get(index - 1)) {
valuesIterator = previousValuesIterator;
preparedFeature = previousFeature;
return true;
}
}
return false;
}
protected boolean preparePrevious() {
if (valuesIterator == null
|| !scanPrevious(preparedFeature, valuesIterator)) {
while (featureIndex > 0) {
int sourceFeatureID = sourceFeatureIDs[--featureIndex];
if (owner.eIsSet(sourceFeatureID)) {
EStructuralFeature sourceFeature = getEStructuralFeature(sourceFeatureID);
Object value = owner.eGet(sourceFeatureID, resolve(),
true);
if (sourceFeature.isMany()
|| FeatureMapUtil.isFeatureMap(sourceFeature)) {
@SuppressWarnings("unchecked")
InternalEList<Object> valuesList = (InternalEList<Object>) value;
int valuesListSize = valuesList.size();
if (scanPrevious(sourceFeature, resolve()
? valuesList.listIterator(valuesListSize)
: valuesList.basicListIterator(valuesListSize))) {
prepared = -3;
return true;
}
} else if (index > 0
&& value == preparedValues.get(index - 1)) {
valuesIterator = null;
preparedFeature = sourceFeature;
prepared = -2;
return true;
}
}
}
prepared = -1;
return false;
} else {
prepared = -3;
return true;
}
}
public boolean hasPrevious() {
switch (prepared) {
case -3 :
case -2 :
return true;
case -1 :
return false;
case 3 :
valuesIterator.previous();
default :
return preparePrevious();
}
}
public E previous() {
if (prepared < -1 || hasPrevious()) {
prepared = 0;
Object previous = preparedValues.remove(--index);
hasPrevious();
return derive(previous);
} else {
throw new NoSuchElementException();
}
}
public int nextIndex() {
return index;
}
public int previousIndex() {
return index - 1;
}
public void remove() {
throw new UnsupportedOperationException();
}
public void set(Object element) {
throw new UnsupportedOperationException();
}
public void add(Object element) {
throw new UnsupportedOperationException();
}
protected boolean resolve() {
return false;
}
}
protected class EmptyDerivedListIterator
extends DerivedListIterator {
@Override
public boolean hasNext() {
return false;
}
@Override
public boolean hasPrevious() {
return false;
}
}
protected class ResolvingDerivedListIterator
extends DerivedListIterator {
@Override
protected boolean resolve() {
return true;
}
}
protected final Class<?> dataClass;
protected final InternalEObject owner;
protected final int featureID;
protected final int[] sourceFeatureIDs;
public DerivedEObjectEList(Class<?> dataClass, InternalEObject owner,
int featureID, int[] sourceFeatureIDs) {
super();
this.dataClass = dataClass;
this.owner = owner;
this.featureID = featureID;
this.sourceFeatureIDs = sourceFeatureIDs;
}
public Object get(boolean resolve) {
return this;
}
public EObject getEObject() {
return owner;
}
public EStructuralFeature getEStructuralFeature() {
return getEStructuralFeature(featureID);
}
public EStructuralFeature getEStructuralFeature(int featureID) {
return owner.eClass().getEStructuralFeature(featureID);
}
public boolean isSet() {
return !isEmpty();
}
@SuppressWarnings("unchecked")
public void set(Object newValue) {
clear();
addAll((List<? extends E>) newValue);
}
public void unset() {
clear();
}
@Override
public ListIterator<E> listIterator(int index) {
return listIterator(index, true);
}
@Override
public int size() {
if (sourceFeatureIDs != null) {
Set<Object> values = new HashSet<Object>();
for (int i = 0; i < sourceFeatureIDs.length; i++) {
int sourceFeatureID = sourceFeatureIDs[i];
if (owner.eIsSet(sourceFeatureID)) {
EStructuralFeature sourceFeature = getEStructuralFeature(sourceFeatureID);
Object value = owner.eGet(sourceFeatureID, false, true);
if (FeatureMapUtil.isFeatureMap(sourceFeature)) {
FeatureMap featureMap = (FeatureMap) value;
for (int j = 0, size = featureMap.size(); j < size; j++) {
value = featureMap.getValue(j);
if (isIncluded(featureMap.getEStructuralFeature(j))
? value != null
: isIncluded(value)) {
values.add(value);
}
}
} else if (isIncluded(sourceFeature)) {
if (sourceFeature.isMany()) {
InternalEList<?> valuesList = (InternalEList<?>) value;
if (valuesList instanceof RandomAccess) {
for (int j = 0, size = valuesList.size(); j < size; j++) {
values.add(valuesList.basicGet(j));
}
} else {
for (Iterator<?> v = valuesList.basicIterator(); v
.hasNext();) {
values.add(v.next());
}
}
} else if (value != null) {
values.add(value);
}
} else {
if (sourceFeature.isMany()) {
InternalEList<?> valuesList = (InternalEList<?>) value;
if (valuesList instanceof RandomAccess) {
for (int j = 0, size = valuesList.size(); j < size; j++) {
value = valuesList.basicGet(j);
if (isIncluded(value)) {
values.add(value);
}
}
} else {
for (Iterator<?> v = valuesList.basicIterator(); v
.hasNext();) {
value = v.next();
if (isIncluded(value)) {
values.add(value);
}
}
}
} else if (isIncluded(value)) {
values.add(value);
}
}
}
}
return values.size();
}
return 0;
}
@Override
public boolean isEmpty() {
if (sourceFeatureIDs != null) {
for (int i = 0; i < sourceFeatureIDs.length; i++) {
int sourceFeatureID = sourceFeatureIDs[i];
if (owner.eIsSet(sourceFeatureID)) {
EStructuralFeature sourceFeature = getEStructuralFeature(sourceFeatureID);
Object value = owner.eGet(sourceFeatureID, false, true);
if (FeatureMapUtil.isFeatureMap(sourceFeature)) {
FeatureMap featureMap = (FeatureMap) value;
for (int j = 0, size = featureMap.size(); j < size; j++) {
if (isIncluded(featureMap.getEStructuralFeature(j))
? featureMap.getValue(j) != null
: isIncluded(featureMap.getValue(j))) {
return false;
}
}
} else if (isIncluded(sourceFeature)) {
if (sourceFeature.isMany()
? ((List<?>) value).size() > 0
: value != null) {
return false;
}
} else {
if (sourceFeature.isMany()) {
InternalEList<?> valuesList = (InternalEList<?>) value;
if (valuesList instanceof RandomAccess) {
for (int j = 0, size = valuesList.size(); j < size; j++) {
if (isIncluded(valuesList.basicGet(j))) {
return false;
}
}
} else {
for (Iterator<?> v = valuesList.basicIterator(); v
.hasNext();) {
if (isIncluded(v.next())) {
return false;
}
}
}
} else if (isIncluded(value)) {
return false;
}
}
}
}
}
return true;
}
@Override
public boolean contains(Object object) {
if (sourceFeatureIDs != null) {
for (int i = 0; i < sourceFeatureIDs.length; i++) {
int sourceFeatureID = sourceFeatureIDs[i];
if (owner.eIsSet(sourceFeatureID)) {
EStructuralFeature sourceFeature = getEStructuralFeature(sourceFeatureID);
Object value = owner.eGet(sourceFeatureID, true, true);
if (FeatureMapUtil.isFeatureMap(sourceFeature)) {
FeatureMap featureMap = (FeatureMap) value;
for (int j = 0, size = featureMap.size(); j < size; j++) {
value = featureMap.getValue(j);
if (isIncluded(featureMap.getEStructuralFeature(j))
? value == object
: isIncluded(value) && derive(value) == object) {
return true;
}
}
} else if (isIncluded(sourceFeature)) {
if (sourceFeature.isMany()
? ((List<?>) value).contains(object)
: value == object) {
return true;
}
} else {
if (sourceFeature.isMany()) {
InternalEList<?> valuesList = (InternalEList<?>) value;
if (valuesList instanceof RandomAccess) {
for (int j = 0, size = valuesList.size(); j < size; j++) {
value = valuesList.basicGet(j);
if (isIncluded(value)
&& derive(value) == object) {
return true;
}
}
} else {
for (Iterator<?> v = valuesList.basicIterator(); v
.hasNext();) {
value = v.next();
if (isIncluded(value)
&& derive(value) == object) {
return true;
}
}
}
} else if (isIncluded(value) && derive(value) == object) {
return true;
}
}
}
}
}
return false;
}
@Override
public List<E> basicList() {
return new DerivedEObjectEList<E>(dataClass, owner, featureID,
sourceFeatureIDs) {
@Override
public ListIterator<E> listIterator(int index) {
return basicListIterator(index);
}
};
}
@Override
public ListIterator<E> basicListIterator(int index) {
return listIterator(index, false);
}
protected boolean isNotificationRequired() {
return false;
}
protected NotificationImpl createNotification(int eventType,
Object oldObject, Object newObject, int index, boolean wasSet) {
return new ENotificationImpl(owner, eventType, featureID, oldObject,
newObject, index, wasSet);
}
protected void dispatchNotification(Notification notification) {
owner.eNotify(notification);
}
@Override
public void add(int index, E object) {
addUnique(index, object);
}
@Override
public void addUnique(int index, E object) {
if (isNotificationRequired()) {
boolean oldIsSet = isSet();
super.add(index, validate(index, object));
NotificationImpl notification = createNotification(
Notification.ADD, null, object, index, oldIsSet);
dispatchNotification(notification);
} else {
super.add(index, validate(index, object));
}
}
@Override
public boolean addAll(int index, Collection<? extends E> objects) {
return addAllUnique(index, objects);
}
@Override
public boolean addAllUnique(int index, Collection<? extends E> objects) {
int size = objects.size();
if (size > 0) {
if (isNotificationRequired()) {
boolean oldIsSet = isSet();
if (doAddAllUnique(index, objects)) {
NotificationImpl notification = size == 1
? createNotification(Notification.ADD, null, objects
.iterator().next(), index, oldIsSet)
: createNotification(Notification.ADD_MANY, null,
objects, index, oldIsSet);
dispatchNotification(notification);
return true;
}
} else {
return doAddAllUnique(index, objects);
}
}
return false;
}
protected boolean doAddAllUnique(int index, Collection<? extends E> objects) {
boolean modified = false;
ListIterator<E> listIterator = listIterator(index);
for (Iterator<? extends E> o = objects.iterator(); o.hasNext();) {
listIterator.add(validate(index, o.next()));
modified = true;
}
return modified;
}
@Override
public E remove(int index) {
if (isNotificationRequired()) {
boolean oldIsSet = isSet();
NotificationImpl notification = createNotification(
Notification.REMOVE, super.remove(index), null, index, oldIsSet);
dispatchNotification(notification);
@SuppressWarnings("unchecked")
E oldValue = (E) notification.getOldValue();
return oldValue;
} else {
return super.remove(index);
}
}
@Override
public E set(int index, E object) {
return setUnique(index, object);
}
@Override
public E setUnique(int index, E object) {
if (isNotificationRequired()) {
boolean oldIsSet = isSet();
Notification notification = createNotification(Notification.SET,
super.set(index, validate(index, object)), object, index,
oldIsSet);
dispatchNotification(notification);
@SuppressWarnings("unchecked")
E oldValue = (E) notification.getOldValue();
return oldValue;
} else {
return super.set(index, validate(index, object));
}
}
/**
* Indicates whether all elements from the specified source feature are
* included in this list.
*
* @param feature
* A source feature.
* @return <code>true</code> if the elements are included;
* <code>false</code> otherwise.
*/
protected boolean isIncluded(EStructuralFeature feature) {
return dataClass
.isAssignableFrom(feature.getEType().getInstanceClass());
}
/**
* Indicates whether the specified element from a source feature is included
* in this list.
*
* @param object
* An element from a source feature.
* @return <code>true</code> if the element is included;
* <code>false</code> otherwise.
*/
protected boolean isIncluded(Object object) {
return dataClass.isInstance(derive(object));
}
/**
* Derives a value for this list from the specified element in a source
* feature.
*
* @param object
* An element from a source feature.
* @return The "derived" value of the specified element.
*/
@SuppressWarnings("unchecked")
protected E derive(Object object) {
return (E) object;
}
protected E validate(int index, E object) {
if (!dataClass.isInstance(object)) {
throw new IllegalArgumentException(String.valueOf(object));
}
return object;
}
protected ListIterator<E> newListIterator() {
return new DerivedListIterator();
}
protected ListIterator<E> newResolvingListIterator() {
return new ResolvingDerivedListIterator();
}
protected ListIterator<E> newEmptyListIterator() {
return new EmptyDerivedListIterator();
}
protected ListIterator<E> listIterator(int index, boolean resolve) {
if (sourceFeatureIDs == null || sourceFeatureIDs.length == 0) {
if (index != 0) {
throw new IndexOutOfBoundsException("index = " + index //$NON-NLS-1$
+ ", size = 0"); //$NON-NLS-1$
}
return newEmptyListIterator();
}
ListIterator<E> listIterator = resolve
? newResolvingListIterator()
: newListIterator();
for (int i = 0; i < index; i++) {
listIterator.next();
}
return listIterator;
}
}