/* * grEMF * * Copyright (C) 2006-2012 Institute for Software Technology * University of Koblenz-Landau, Germany * ist@uni-koblenz.de * * For bug reports, documentation and further information, visit * * https://github.com/jgralab/gremf * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General * Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, see <http://www.gnu.org/licenses>. * * Additional permission under GNU GPL version 3 section 7 * * If you modify this Program, or any covered work, by linking or combining * it with Eclipse (or a modified version of that program or an Eclipse * plugin), containing parts covered by the terms of the Eclipse Public * License (EPL), the licensors of this Program grant you additional * permission to convey the resulting work. Corresponding Source for a * non-source form of such a combination shall include the source code for * the parts of JGraLab used as well as that of the covered work. */ package de.uni_koblenz.gremf.collection; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import org.eclipse.emf.common.notify.Notification; import org.eclipse.emf.common.notify.NotificationChain; import org.eclipse.emf.common.util.ECollections; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.ecore.impl.ENotificationImpl; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.util.InternalEList; import de.uni_koblenz.gremf.GrEMFInstanceType; import de.uni_koblenz.gremf.impl.GrEMFEdgeImpl; import de.uni_koblenz.gremf.impl.GrEMFGraphImpl; import de.uni_koblenz.gremf.impl.GrEMFVertexImpl; import de.uni_koblenz.jgralab.Edge; import de.uni_koblenz.jgralab.Vertex; public class GrEMFGraphObjectListProxy implements EList<EObject>, InternalEList<EObject> { private enum ObjectKind { VERTICES, EDGES } private EReference feature; private GrEMFGraphImpl g; private List<EObject> objectList; private ObjectKind kind; public GrEMFGraphObjectListProxy(GrEMFGraphImpl g, EReference f) { this.g = g; this.feature = f; this.kind = f.getName().equals("vertices") ? ObjectKind.VERTICES : ObjectKind.EDGES; } private List<EObject> getObjectList() { if (this.objectList == null) { if (this.kind == ObjectKind.VERTICES) { this.objectList = new ArrayList<EObject>(this.g.getVCount()); for (Vertex v : this.g.vertices()) { if (v instanceof GrEMFInstanceType) { this.objectList.add((GrEMFVertexImpl) v); } } } else { this.objectList = new ArrayList<EObject>(this.g.getECount()); for (Edge e : this.g.edges()) { if (e instanceof GrEMFInstanceType) { this.objectList.add((GrEMFEdgeImpl) e); } } } } return this.objectList; } private void resetObjectList() { this.objectList = null; } private GrEMFGraphImpl getGraph(EObject obj) { if (this.kind == ObjectKind.VERTICES) { return (GrEMFGraphImpl) ((GrEMFVertexImpl) obj).getGraph(); } else if (this.kind == ObjectKind.EDGES) { return (GrEMFGraphImpl) ((GrEMFEdgeImpl) obj).getGraph(); } else { return null; } } private int getId(EObject obj) { if (this.kind == ObjectKind.VERTICES) { return ((GrEMFVertexImpl) obj).getId(); } else if (this.kind == ObjectKind.EDGES) { return ((GrEMFEdgeImpl) obj).getId(); } else { return -1; } } private void addObject(EObject obj) { if (this.kind == ObjectKind.VERTICES) { this.g.addVertex((GrEMFVertexImpl) obj); } else { GrEMFEdgeImpl e = (GrEMFEdgeImpl) obj; this.g.addEdge(e, e.getAlpha(), e.getOmega()); } } private void putBefore(EObject target, EObject moved) { if (this.kind == ObjectKind.VERTICES) { this.g.putVertexBefore((GrEMFVertexImpl) target, (GrEMFVertexImpl) moved); } else { this.g.putEdgeBeforeInGraph((GrEMFEdgeImpl) target, (GrEMFEdgeImpl) moved); } } private void putAfter(EObject target, EObject moved) { if (this.kind == ObjectKind.VERTICES) { this.g.putVertexAfter((GrEMFVertexImpl) target, (GrEMFVertexImpl) moved); } else { this.g.putEdgeAfterInGraph((GrEMFEdgeImpl) target, (GrEMFEdgeImpl) moved); } } private boolean containsObject(EObject obj) { if (this.kind == ObjectKind.VERTICES) { return this.g.containsVertex((GrEMFVertexImpl) obj); } else { return this.g.containsEdge((GrEMFEdgeImpl) obj); } } private void delete(EObject obj) { if (this.kind == ObjectKind.VERTICES) { ((GrEMFVertexImpl) obj).delete(); } else { ((GrEMFEdgeImpl) obj).delete(); } } private boolean handable(EObject obj) { if (this.kind == ObjectKind.VERTICES) { return obj instanceof GrEMFVertexImpl; } else { return obj instanceof GrEMFEdgeImpl; } } // -------------------------------------------------------------------------- // Methods from interface // java.util.List // -------------------------------------------------------------------------- @Override public boolean add(EObject obj) { boolean addable = this.handable(obj) && !this.contains(obj) && this.getGraph(obj).equals(this.g); if (addable) { this.disableECANotification(); this.addObject(obj); if (this.g.eNotificationRequired()) { this.g.eNotify(new ENotificationImpl(this.g, Notification.ADD, this.feature, null, obj, this.size() - 1)); } this.enableECANotification(); } this.resetObjectList(); return addable; } @Override public void add(int index, EObject obj) { int size = this.size(); boolean addable = this.handable(obj) && !this.contains(obj) && this.getGraph(obj).equals(this.g) && (index <= size); if (addable) { this.disableECANotification(); if (index != (size - 1)) { EObject objAtIndex = this.get(index); this.putBefore(objAtIndex, obj); } else { this.add(obj); } if (this.g.eNotificationRequired()) { this.g.eNotify(new ENotificationImpl(this.g, Notification.ADD, this.feature, null, obj, index)); } this.enableECANotification(); } this.resetObjectList(); if (index > size) { throw new IndexOutOfBoundsException(); } } @Override public boolean addAll(Collection<? extends EObject> collection) { boolean addable = true; for (EObject obj : collection) { if (!this.handable(obj) || this.contains(obj) || !this.getGraph(obj).equals(this.g)) { addable = false; break; } } if (addable) { this.disableECANotification(); for (EObject obj : collection) { this.addObject(obj); } if (this.g.eNotificationRequired()) { int collectionSize = collection.size(); if (collectionSize > 1) { this.g.eNotify(new ENotificationImpl(this.g, Notification.ADD_MANY, this.feature, null, collection, this.size() - collectionSize)); } else if (collectionSize == 1) { this.g.eNotify(new ENotificationImpl(this.g, Notification.ADD, this.feature, null, collection .iterator().next(), this.size() - collectionSize)); } } this.enableECANotification(); } this.resetObjectList(); return addable; } @Override public boolean addAll(int index, Collection<? extends EObject> collection) { int size = this.size(); boolean addable = index <= size; for (EObject obj : collection) { if (!this.handable(obj) || this.contains(obj) || !this.getGraph(obj).equals(this.g)) { addable = false; break; } } if (addable) { this.disableECANotification(); int i = index; for (EObject obj : collection) { if (i != (size - 1)) { EObject objAtIndex = this.get(i); this.putBefore(objAtIndex, obj); } else { this.add(obj); } i++; } if (this.g.eNotificationRequired()) { int collectionSize = collection.size(); if (collectionSize > 1) { this.g.eNotify(new ENotificationImpl(this.g, Notification.ADD_MANY, this.feature, null, collection, index)); } else if (collectionSize == 1) { this.g.eNotify(new ENotificationImpl(this.g, Notification.ADD, this.feature, null, collection .iterator().next(), index)); } } this.enableECANotification(); } this.resetObjectList(); if (index > size) { throw new IndexOutOfBoundsException(); } return addable; } @Override public void clear() { List<EObject> clear = this.getObjectList(); this.disableECANotification(); for (EObject obj : clear) { // not deleted through cascading remove if (this.containsObject(obj)) { this.remove(obj); } } if (this.g.eNotificationRequired()) { int size = clear.size(); if (size > 1) { this.g.eNotify(new ENotificationImpl(this.g, Notification.REMOVE_MANY, this.feature, clear, null, 0)); } else if (size == 1) { this.g.eNotify(new ENotificationImpl(this.g, Notification.REMOVE, this.feature, clear.get(0), null, 0)); } } this.enableECANotification(); this.resetObjectList(); } @Override public boolean contains(Object o) { return this.indexOf(o) > -1; } @Override public boolean containsAll(Collection<?> collection) { boolean contains = true; for (Object o : collection) { if (!this.contains(o)) { contains = false; } } this.resetObjectList(); return contains; } @Override public EObject get(int index) { boolean gettable = (index > -1) && (index < this.size()); EObject get = null; if (gettable) { get = this.getObjectList().get(index); } if (get == null) { throw new IndexOutOfBoundsException(); } return get; } @Override public int indexOf(Object o) { int index = -1; if (o instanceof EObject) { int id = this.getId((EObject) o); for (int i = id - 1; i >= 0; i--) { try { Object compare = this.get(i); if (o.equals(compare)) { index = i; break; } } catch (IndexOutOfBoundsException e) { } } } return index; } @Override public int lastIndexOf(Object o) { return this.indexOf(o); } @Override public boolean isEmpty() { return this.getObjectList().isEmpty(); } @Override public Iterator<EObject> iterator() { return this.getObjectList().iterator(); } @Override public ListIterator<EObject> listIterator() { return this.getObjectList().listIterator(); } @Override public ListIterator<EObject> listIterator(int index) { return this.getObjectList().listIterator(index); } @Override public boolean remove(Object o) { int index = this.indexOf(o); boolean removable = index > -1; if (removable) { this.disableECANotification(); EObject remove = this.get(index); this.delete(remove); Resource r = remove.eResource(); if (r != null) { r.getContents().remove(remove); } if (this.g.eNotificationRequired()) { this.g.eNotify(new ENotificationImpl(this.g, Notification.REMOVE, this.feature, remove, null, index)); } this.enableECANotification(); } this.resetObjectList(); return removable; } @Override public EObject remove(int index) { boolean removable = (index > -1) && (index < this.size()); EObject remove = null; if (removable) { this.disableECANotification(); remove = this.get(index); this.delete(remove); Resource r = remove.eResource(); if (r != null) { r.getContents().remove(remove); } if (this.g.eNotificationRequired()) { this.g.eNotify(new ENotificationImpl(this.g, Notification.REMOVE, this.feature, remove, null, index)); } this.enableECANotification(); } this.resetObjectList(); if (remove == null) { throw new IndexOutOfBoundsException(); } return remove; } @Override public boolean removeAll(Collection<?> collection) { boolean removable = true; int minIndex = -1; List<EObject> remove = new ArrayList<EObject>(collection.size()); for (Object obj : collection) { int index = this.indexOf(obj); if (index < 0) { removable = false; break; } else { remove.add((EObject) obj); } if (minIndex > index) { minIndex = index; } } if (removable) { this.disableECANotification(); for (EObject obj : remove) { // not deleted through cascading remove if (this.containsObject(obj)) { this.remove(obj); } } if (this.g.eNotificationRequired()) { int size = remove.size(); if (size > 1) { this.g.eNotify(new ENotificationImpl(this.g, Notification.REMOVE_MANY, this.feature, remove, null, minIndex)); } else if (size == 1) { this.g.eNotify(new ENotificationImpl(this.g, Notification.REMOVE, this.feature, remove.get(0), null, minIndex)); } } this.enableECANotification(); } this.resetObjectList(); return removable; } @Override public boolean retainAll(Collection<?> collection) { boolean retainable = true; int minIndex = -1; List<EObject> remove = new ArrayList<EObject>(); for (EObject obj : this.getObjectList()) { if (!collection.contains(obj)) { remove.add(obj); int index = this.indexOf(obj); if (minIndex > index) { minIndex = index; } } } retainable = !remove.isEmpty(); if (retainable) { this.disableECANotification(); for (EObject obj : remove) { // not deleted through cascading remove if (this.containsObject(obj)) { this.remove(obj); } } if (this.g.eNotificationRequired()) { int size = remove.size(); if (size > 1) { this.g.eNotify(new ENotificationImpl(this.g, Notification.REMOVE_MANY, this.feature, remove, null, minIndex)); } else if (size == 1) { this.g.eNotify(new ENotificationImpl(this.g, Notification.REMOVE, this.feature, remove.get(0), null, minIndex)); } } this.enableECANotification(); } this.resetObjectList(); return retainable; } @Override public EObject set(int index, EObject obj) { boolean settable = this.handable(obj) && !this.contains(obj) && this.getGraph(obj).equals(this.g) && (index > -1) && (index < this.size()); EObject set = null; if (settable) { this.disableECANotification(); set = this.get(index); this.putAfter(set, obj); this.remove(index); if (this.g.eNotificationRequired()) { this.g.eNotify(new ENotificationImpl(this.g, Notification.SET, this.feature, set, obj, index)); } this.enableECANotification(); } if ((index < 0) || (index >= this.size())) { throw new IndexOutOfBoundsException(); } return set; } @Override public int size() { return this.getObjectList().size(); } @Override public List<EObject> subList(int fromIndex, int toIndex) { return this.getObjectList().subList(fromIndex, toIndex); } @Override public EObject[] toArray() { return this.getObjectList().toArray(new EObject[this.size()]); } @SuppressWarnings("unchecked") @Override public Object[] toArray(Object[] a) { return this.getObjectList().toArray(a); } // -------------------------------------------------------------------------- // Methods from interface // org.eclipse.emf.ecore.util.EList // -------------------------------------------------------------------------- @Override public void move(int newPosition, EObject obj) { int index = this.indexOf(obj); if (index < 0) { throw new ArrayIndexOutOfBoundsException(-1); } this.move(newPosition, index); } @Override public EObject move(int newPosition, int oldPosition) { int illegalIndex = 0; int size = this.size(); if ((newPosition < 0) || (newPosition >= size)) { illegalIndex = newPosition; } if ((oldPosition < 0) || (oldPosition >= size)) { illegalIndex = oldPosition; } if ((illegalIndex != 0) || ((illegalIndex == 0) && (size == 0))) { throw new ArrayIndexOutOfBoundsException(illegalIndex); } EObject old = this.get(newPosition); EObject move = this.get(oldPosition); this.putAfter(old, move); if (this.g.eNotificationRequired()) { this.g.eNotify(new ENotificationImpl(this.g, Notification.MOVE, this.feature, oldPosition, move, newPosition)); } this.resetObjectList(); return move; } // -------------------------------------------------------------------------- // Additional Methods for convenience // to support others // -------------------------------------------------------------------------- private void disableECANotification() { this.g.getVertexClassCondition().setCondition(false); } private void enableECANotification() { this.g.getVertexClassCondition().setCondition(true); } // -------------------------------------------------------------------------- // Methods from interface // java.lang.Object // -------------------------------------------------------------------------- @Override public String toString() { StringBuilder rep = new StringBuilder().append('['); for (EObject obj : this.getObjectList()) { rep.append(obj).append(',').append(' '); } return rep.append(']').toString(); } // -------------------------------------------------------------------------- // Methods from interface // org.eclipse.emf.ecore.util.InternalEList // -------------------------------------------------------------------------- @Override public EObject basicGet(int index) { return this.get(index); } @Override public List<EObject> basicList() { return ECollections.unmodifiableEList(this); } @Override public Iterator<EObject> basicIterator() { return this.iterator(); } @Override public ListIterator<EObject> basicListIterator() { return this.listIterator(); } @Override public ListIterator<EObject> basicListIterator(int index) { return this.listIterator(index); } @Override public Object[] basicToArray() { return this.toArray(); } @SuppressWarnings("unchecked") @Override public <T> T[] basicToArray(T[] array) { return (T[]) this.toArray(array); } @Override public int basicIndexOf(Object object) { return this.indexOf(object); } @Override public int basicLastIndexOf(Object object) { return this.lastIndexOf(object); } @Override public boolean basicContains(Object object) { return this.contains(object); } @Override public boolean basicContainsAll(Collection<?> collection) { return this.containsAll(collection); } @Override public NotificationChain basicRemove(Object object, NotificationChain notifications) { this.remove(object); return notifications; } @Override public NotificationChain basicAdd(EObject object, NotificationChain notifications) { this.add(object); return notifications; } @Override public void addUnique(EObject object) { this.add(object); } @Override public void addUnique(int index, EObject object) { this.add(index, object); } @Override public boolean addAllUnique(Collection<? extends EObject> collection) { return this.addAll(collection); } @Override public boolean addAllUnique(int index, Collection<? extends EObject> collection) { return this.addAll(index, collection); } @Override public EObject setUnique(int index, EObject object) { return this.set(index, object); } }