/****************************************************************************** * Copyright (c) 2006, 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 Corporation - initial API and implementation ****************************************************************************/ package org.eclipse.gmf.runtime.emf.core.util; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.emf.common.notify.Adapter; import org.eclipse.emf.common.notify.Notification; import org.eclipse.emf.common.notify.Notifier; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.InternalEObject; import org.eclipse.emf.ecore.EStructuralFeature.Setting; import org.eclipse.emf.ecore.impl.EClassImpl; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.emf.ecore.util.EContentsEList; import org.eclipse.emf.ecore.util.ECrossReferenceAdapter; import org.eclipse.emf.ecore.util.ECrossReferenceEList; import org.eclipse.emf.ecore.util.ExtendedMetaData; import org.eclipse.emf.ecore.util.FeatureMapUtil; import org.eclipse.emf.ecore.util.InternalEList; /** * An adapter that maintains itself as an adapter for all contained objects. It * can be installed for an {@link EObject}, a {@link Resource}, or a * {@link ResourceSet}. * <p> * This adapter maintain information on inverse references, resource imports, * and resource exports. * * @author Christian Vogt (cvogt) * @author Christian W. Damus (cdamus) */ public class CrossReferenceAdapter extends ECrossReferenceAdapter { private Map imports = new HashMap(); private Map exports = new HashMap(); private boolean resolve = true; private Map eClassToChangeableFeatures = new HashMap(); private static List nullList = new ArrayList(1); /** * Initializes me. */ public CrossReferenceAdapter() { this(true); } /** * Initializes me. * * @param resolve * flag to determine if the proxies need to be resolved */ public CrossReferenceAdapter(boolean resolve) { super(); this.resolve = resolve; } /** * Updates imports and exports maps. * * @param notification * the event notification */ public void selfAdapt(Notification notification) { super.selfAdapt(notification); Object notifier = notification.getNotifier(); Object feature = notification.getFeature(); // update import / export information when a resource // is unloaded or loaded if (notifier instanceof Resource) { if (notification.getFeatureID(Resource.class) == Resource.RESOURCE__IS_LOADED) { if (!notification.getNewBooleanValue()) { deregisterReferences((Resource) notifier); } else { for (Iterator i = ((Resource) notifier).getContents() .iterator(); i.hasNext();) { EObject child = (EObject) i.next(); if (child != null) { updateImportsAndExports((Resource) notifier, child, true); } } } } return; } // interested in maintaining import / export information // when the notifier is an EObject and the feature is a // non-containment EReference if (!(notifier instanceof EObject) || !(feature instanceof EReference)) { return; } EReference reference = (EReference) feature; if (!isImportExportCapable(reference, (EObject) notifier)) { return; } switch (notification.getEventType()) { case Notification.RESOLVE: case Notification.SET: case Notification.UNSET: { if (!reference.isMany() || notification.getPosition() != Notification.NO_INDEX) { EObject oldValue = (EObject) notification.getOldValue(); if (oldValue != null) { deregisterReference(((EObject) notification.getNotifier()) .eResource(), oldValue.eResource()); } EObject newValue = (EObject) notification.getNewValue(); if (newValue != null) { registerReference(((EObject) notification.getNotifier()) .eResource(), newValue.eResource()); } } break; } case Notification.ADD: { EObject newValue = (EObject) notification.getNewValue(); if (newValue != null) { registerReference(((EObject) notification.getNotifier()) .eResource(), newValue.eResource()); } break; } case Notification.ADD_MANY: { Collection newValues = (Collection) notification.getNewValue(); for (Iterator i = newValues.iterator(); i.hasNext();) { EObject newValue = (EObject) i.next(); registerReference(((EObject) notification.getNotifier()) .eResource(), newValue.eResource()); } break; } case Notification.REMOVE: { EObject oldValue = (EObject) notification.getOldValue(); if (oldValue != null) { deregisterReference(((EObject) notification.getNotifier()) .eResource(), oldValue.eResource()); } break; } case Notification.REMOVE_MANY: { Collection oldValues = (Collection) notification.getOldValue(); for (Iterator i = oldValues.iterator(); i.hasNext();) { EObject oldValue = (EObject) i.next(); deregisterReference(((EObject) notification.getNotifier()) .eResource(), oldValue.eResource()); } break; } } } /** * Extends the superclass method to handle the removal cases of containment, * to tear down aggregate (resource-level) cross-references. */ protected void handleContainment(Notification notification) { super.handleContainment(notification); Object notifier = notification.getNotifier(); if (notifier instanceof ResourceSet) { // not interested in removal of resources from the resource set return; } switch (notification.getEventType()) { case Notification.ADD: { EObject newValue = (EObject) notification.getNewValue(); if (newValue != null) { Resource resource; if (notifier instanceof Resource) { resource = (Resource) notifier; } else { resource = ((EObject) notification.getNotifier()) .eResource(); } // handle processing of the new value that has been added updateImportsAndExports(resource, newValue, true); } break; } case Notification.ADD_MANY: { Resource resource; if (notifier instanceof Resource) { resource = (Resource) notifier; } else { resource = ((EObject) notification.getNotifier()).eResource(); } Collection newValues = (Collection) notification.getNewValue(); for (Iterator iter = newValues.iterator(); iter.hasNext();) { EObject next = (EObject) iter.next(); if (next != null) { // handle processing of the new value that has been added updateImportsAndExports(resource, next, true); } } break; } case Notification.REMOVE: { EObject oldValue = (EObject) notification.getOldValue(); if (oldValue != null) { Resource resource; if (notifier instanceof Resource) { resource = (Resource) notifier; } else { resource = ((EObject) notification.getNotifier()) .eResource(); } // handle processing of the old value that has been removed updateImportsAndExports(resource, oldValue, false); } break; } case Notification.REMOVE_MANY: { Resource resource; if (notifier instanceof Resource) { resource = (Resource) notifier; if (!resource.isLoaded()) { // purge the resource from the imports/exports map deregisterReferences(resource); return; } } else { resource = ((EObject) notification.getNotifier()).eResource(); } Collection oldValues = (Collection) notification.getOldValue(); for (Iterator iter = oldValues.iterator(); iter.hasNext();) { EObject next = (EObject) iter.next(); if (next != null) { // handle processing of the old value that has been removed updateImportsAndExports(resource, next, false); } } break; } } } /** * Updates the imports and exports map for the specified eObject * * @param resource * a resource * @param eObject * the specified eObject * @param register * boolean flag to indicate whether to register imports or * unregister imports */ public void updateImportsAndExports(Resource resource, EObject value, boolean register) { CrossReferenceAdapter adapter = getExistingCrossReferenceAdapter(value); if (register) { if (adapter != null) { // now, register incoming unidirectional references and // opposites for (Iterator iter = adapter.getInverseReferences(value).iterator(); iter.hasNext();) { EStructuralFeature.Setting next = (EStructuralFeature.Setting) iter.next(); EReference ref = (EReference) next.getEStructuralFeature(); EObject owner = next.getEObject(); if (isImportExportCapable(ref, owner)) { registerReference(owner.eResource(), resource); } } } } else { // deregister the outgoing references and incoming bidirectionals EContentsEList.FeatureIterator crossReferences = getOptimizedCrossReferenceIterator( value); while (crossReferences.hasNext()) { EObject referent = (EObject) crossReferences.next(); if (referent != null) { EReference eReference = (EReference) crossReferences .feature(); if (isImportExportCapable(eReference, referent)) { Resource referencedResource = referent.eResource(); deregisterReference(resource, referencedResource); } } } // now, deregister incoming unidirectional references and opposites if (adapter != null) { for (Iterator iter = adapter.getInverseReferences(value).iterator(); iter.hasNext();) { EStructuralFeature.Setting next = (EStructuralFeature.Setting) iter.next(); EReference ref = (EReference) next.getEStructuralFeature(); EObject owner = next.getEObject(); if (isImportExportCapable(ref, owner)) { deregisterReference(owner.eResource(), resource); } } } } // process contents if (adapter != null) { adapter.updateImportsAndExportsForContents(resource, value, register); } } /** * Updates the imports and exports map for the contents of the specified * eObject * * @param resource * a resource * @param eObject * the specified eObject * @param register * boolean flag to indicate whether to register imports or * unregister imports */ public void updateImportsAndExportsForContents(Resource resource, EObject value, boolean register) { // go through the children of the eObject for (Iterator i = resolve() ? value.eContents().iterator() : ((InternalEList) value.eContents()).basicIterator(); i .hasNext();) { updateImportsAndExports(resource, (EObject) i.next(), register); } } /** * @see org.eclipse.emf.ecore.util.ECrossReferenceAdapter#setTarget(org.eclipse.emf.common.notify.Notifier) */ public void setTarget(Notifier target) { super.setTarget(target); if (target instanceof EObject) { EObject eObject = (EObject) target; Resource resource = eObject.eResource(); // register the outgoing references and incoming bidirectionals EContentsEList.FeatureIterator crossReferences = getOptimizedCrossReferenceIterator( eObject); while (crossReferences.hasNext()) { EObject referent = (EObject) crossReferences.next(); if (referent != null) { EReference eReference = (EReference) crossReferences .feature(); if (isImportExportCapable(eReference, referent)) { Resource referencedResource = referent.eResource(); registerReference(resource, referencedResource); } } } } } /** * @see org.eclipse.emf.ecore.util.ECrossReferenceAdapter#unsetTarget(org.eclipse.emf.common.notify.Notifier) */ public void unsetTarget(Notifier notifier) { super.unsetTarget(notifier); if (notifier instanceof Resource) { deregisterReferences((Resource) notifier); } } /** * Gets the imports of a resource. * * @param referencer * the resource to retrieve imports for * @return a Set of resource imports */ public Set getImports(Resource referencer) { Map importsMap = getImportsMap(referencer); if (importsMap != null) { return Collections.unmodifiableSet(importsMap.keySet()); } else { return Collections.EMPTY_SET; } } /** * Gets the exports of a resource. * * @param referenced * the resource to retrieve exports for * @return a Set of resource exports */ public Set getExports(Resource referenced) { Map exportsMap = getExportsMap(referenced); if (exportsMap != null) { return Collections.unmodifiableSet(exportsMap.keySet()); } else { return Collections.EMPTY_SET; } } /** * Returns the imports map of the given resource. * * @param resource * @return imports map of the given resource */ private Map getImportsMap(Resource resource) { return (Map) imports.get(resource); } /** * Returns the exports map of the given resource. * * @param resource * @return exports map of the given resource */ private Map getExportsMap(Resource resource) { return (Map) exports.get(resource); } /** * Registers a reference updating the imports and exports maps accordingly. * * @param referencer * the referencing resource * @param referenced * the referenced resouce */ private void registerReference(final Resource referencer, final Resource referenced) { if ((referencer != null) && (referenced != null) && (referencer != referenced)) { Map importsMap = getImportsMap(referencer); if (importsMap == null) { importsMap = new HashMap(); imports.put(referencer, importsMap); } Counter importsCount = (Counter) importsMap.get(referenced); if (importsCount == null) { importsCount = new Counter(); importsMap.put(referenced, importsCount); importAdded(referencer, referenced); } else { importsCount.inc(); } Map exportsMap = getExportsMap(referenced); if (exportsMap == null) { exportsMap = new HashMap(); exports.put(referenced, exportsMap); } Counter exportsCount = (Counter) exportsMap.get(referencer); if (exportsCount == null) { exportsCount = new Counter(); exportsMap.put(referencer, exportsCount); exportAdded(referenced, referencer); } else { exportsCount.inc(); } } } /** * Hook to be implemented by subclasses upon the establishment of a new * import of the <code>referenced</code> resource by the * <code>referencer</code>. This implementation does nothing; subclasses * need not call <code>super</code>. * * @param referencer * the referencing resource (doing the importing) * @param referenced * the resource that it references */ protected void importAdded(Resource referencer, Resource referenced) { // subclass hook } /** * Hook to be implemented by subclasses upon the elimination of an import of * the <code>referenced</code> resource by the <code>referencer</code>. * This implementation does nothing; subclasses need not call * <code>super</code>. * * @param referencer * the formerly referencing resource (doing the importing) * @param referenced * the resource that it had referenced */ protected void importRemoved(Resource referencer, Resource referenced) { // subclass hook } /** * Hook to be implemented by subclasses upon the establishment of a new * export of the <code>referenced</code> resource to the * <code>referencer</code>. This implementation does nothing; subclasses * need not call <code>super</code>. * * @param referenced * the resource being referenced (doing the exporting) * @param referencer * the referencing resource */ protected void exportAdded(Resource referenced, Resource referencer) { // subclass hook } /** * Hook to be implemented by subclasses upon the elimination of an export of * the <code>referenced</code> resource to the <code>referencer</code>. * This implementation does nothing; subclasses need not call * <code>super</code>. * * @param referenced * the resource formerly being referenced (doing the exporting) * @param referencer * the formerly referencing resource */ protected void exportRemoved(Resource referenced, Resource referencer) { // subclass hook } /** * Deregisters a reference updating the imports and exports maps * accordingly. * * @param referencer * the referencing resource * @param referenced * the referenced resource */ private void deregisterReference(final Resource referencer, final Resource referenced) { if ((referencer != null) && (referenced != null) && (referencer != referenced)) { Map importsMap = getImportsMap(referencer); if (importsMap != null) { Counter importsCount = (Counter) importsMap.get(referenced); if ((importsCount != null) && importsCount.dec()) { importsMap.remove(referenced); importRemoved(referencer, referenced); if (importsMap.isEmpty()) { imports.remove(referencer); } } } Map exportsMap = getExportsMap(referenced); if (exportsMap != null) { Counter exportsCount = (Counter) exportsMap.get(referencer); if ((exportsCount != null) && exportsCount.dec()) { exportsMap.remove(referencer); exportRemoved(referenced, referencer); if (exportsMap.isEmpty()) { exports.remove(referenced); } } } } } /** * Cleans up a resource from the imports and exports maps. * * @param referencer * the referencing resource */ private void deregisterReferences(final Resource referencer) { Object[] resImports = getImports(referencer).toArray(); for (int i = 0; i < resImports.length; i++) { final Resource referenced = (Resource) resImports[i]; Map importsMap = getImportsMap(referencer); if (importsMap != null) { importsMap.remove(referenced); importRemoved(referencer, referenced); if (importsMap.isEmpty()) { imports.remove(referencer); } } Map exportsMap = getExportsMap(referenced); if (exportsMap != null) { exportsMap.remove(referencer); exportRemoved(referenced, referencer); if (exportsMap.isEmpty()) { exports.remove(referenced); } } } } /** * Returns a Set of EObjects that reference the given EObject. If an * EReference is specified, the scope of the search is limited only to that * EReference. To include all references specify a value of null. If an * EClass type is specified, the returned Set will only include those * referencers that match the given type. To include all types specify a * value of null. * * @param referenced * the referenced EObject * @param reference * the reference to find referencers on, null for any reference * @param type * the type of the referencers, use null for any type * @return a Set of referencers */ public Set getInverseReferencers(EObject referenced, EReference reference, EClass type) { return getReferencers(getInverseReferences(referenced), reference, type); } /** * Like the {@link #getInverseReferencers(EObject, EReference, EClass)} method, * obtains referencing objects (optionally filtered by reference and type), * except that it additionally only considers references that are * {@linkplain EStructuralFeature#isChangeable() changeable} and can * {@linkplain EReference#isResolveProxies() reference other resources}. * * @param referenced * the referenced EObject * @param reference * the reference to find referencers on, null for any reference * @param type * the type of the referencers, use null for any type * @return a Set of referencers on potentially cross-resource references */ public Set getInverseReferencersCrossResource(EObject referenced, EReference reference, EClass type) { return getReferencers(getInverseReferencesCrossResource(referenced), reference, type); } /** * Like the {@link #getInverseReferences(EObject)} method, * obtains settings implementing references to the specified object, * except that it only considers references that are * {@linkplain EStructuralFeature#isChangeable() changeable} and can * {@linkplain EReference#isResolveProxies() reference other resources}. * * @param eObject the referenced EObject * * @return a collection of {@link EStructuralFeature.Setting}s on * potentially cross-resource references */ public Collection getInverseReferencesCrossResource(EObject eObject) { return getInverseReferencesCrossResource(eObject, !resolve()); } /** * Returns a Set of EObjects that reference the given EObject through a uni * directional EReferences. If an EReference is specified, the scope of the * search is limited only to that EReference. To include all references * specify a value of null. If an EClass type is specified, the returned Set * will only include those referencers that match the given type. To include * all types specify a value of null. * * @param referenced * the referenced EObject * @param reference * the reference to find referencers on, null for any reference * @param type * the type of the referencers, use null for any type * @return a Set of referencers */ public Set getNonNavigableInverseReferencers(EObject referenced, EReference reference, EClass type) { return getReferencers(getNonNavigableInverseReferences(referenced), reference, type); } /** * Extracts the EObjects from the EStructuralFeature.Setting references and * returns a filtered Set based on the given reference and type. * * @param references * a collection of EStructuralFeature.Setting * @param reference * the reference to find referencers on, null for any reference * @param type * the type of the referencers, use null for any type * @return a Set of referencers */ private Set getReferencers(Collection references, EReference reference, EClass type) { Set set = new HashSet(); if (!references.isEmpty()) { for (Iterator iter = references.iterator(); iter.hasNext();) { Setting setting = (Setting) iter.next(); if (reference == null || reference == setting.getEStructuralFeature()) { EObject referencer = setting.getEObject(); if (referencer != null && (type == null || type.isInstance(referencer))) { set.add(referencer); } } } } return set; } /** * Searches the adapter list of the given Notifier for a * CrossReferenceAdapter. If not found, returns null. * * @param notifier * the notifier to search * @return the CrossReferenceAdapter if found, otherwise null */ public static CrossReferenceAdapter getExistingCrossReferenceAdapter( Notifier notifier) { if (notifier == null) { return null; } List adapters = notifier.eAdapters(); for (int i = 0, size = adapters.size(); i < size; ++i) { Adapter adapter = (Adapter) adapters.get(i); if (adapter instanceof CrossReferenceAdapter) { return (CrossReferenceAdapter) adapter; } } return null; } /** * Obtains the cross-reference adapter for the specified resource set, if * necessary creating it and attaching it. * * @param resourceSet * the resource set * * @return the resourceSet's cross-reference adapter */ public static CrossReferenceAdapter getCrossReferenceAdapter( ResourceSet resourceSet) { if (resourceSet == null) { return null; } CrossReferenceAdapter result = getExistingCrossReferenceAdapter(resourceSet); if (result == null) { result = new CrossReferenceAdapter(); resourceSet.eAdapters().add(result); } return result; } /** * A mutable integer used to count number of object-level references between * two resources. * * @author Christian W. Damus (cdamus) */ private static final class Counter { private int value = 1; Counter() { super(); } /** * Increments me. */ void inc() { value++; } /** * Decrements me. * * @return <code>true</code> if I am now zero; <code>false</code>, * otherwise */ boolean dec() { return --value <= 0; } } protected boolean resolve() { return this.resolve; } public Collection getInverseReferences(EObject eObject, boolean _resolve) { Collection result = new ArrayList(); if (_resolve) { resolveAll(eObject); } EObject eContainer = eObject.eContainer(); if (eContainer != null) { result.add(((InternalEObject) eContainer).eSetting(eObject .eContainmentFeature())); } Collection nonNavigableInverseReferences = (Collection) inverseCrossReferencer .get(eObject); if (nonNavigableInverseReferences != null) { result.addAll(nonNavigableInverseReferences); } for (Iterator i = eObject.eClass().getEAllReferences().iterator(); i .hasNext();) { EReference eReference = (EReference) i.next(); EReference eOpposite = eReference.getEOpposite(); if (eOpposite != null && !eReference.isContainer() && !eReference.isContainment() && eObject.eIsSet(eReference)) { if (FeatureMapUtil.isMany(eObject, eReference)) { Object collection = eObject.eGet(eReference); for (Iterator j = resolve() ? ((Collection) collection) .iterator() : ((InternalEList) collection) .basicIterator(); j.hasNext();) { InternalEObject referencingEObject = (InternalEObject) j .next(); result.add(referencingEObject.eSetting(eOpposite)); } } else { // although the reference is set, the value could be null InternalEObject referencingEObject = ((InternalEObject) eObject .eGet(eReference, resolve())); if (referencingEObject != null) { result.add(referencingEObject.eSetting(eOpposite)); } } } } return result; } /** * Computes the references defined by the specified EClass that are * {@linkplain EStructuralFeature#isChangeable() changeable}. * * @param eCls an EClass * @return a list of its {@link EReference}s that are changeable */ private List getCrossReferencesChangeableFeatures(EClass eCls) { List features = (List) eClassToChangeableFeatures.get(eCls); if (features == null) { features = nullList; EStructuralFeature[] crossReferenceFeatures = ((EClassImpl.FeatureSubsetSupplier) eCls .getEAllStructuralFeatures()).crossReferences(); if (crossReferenceFeatures != null) { features = new ArrayList(crossReferenceFeatures.length); for (int i = 0; i < crossReferenceFeatures.length; i++) { EStructuralFeature feature = crossReferenceFeatures[i]; if (isMutable(feature)) features.add(feature); } } eClassToChangeableFeatures.put(eCls, features); } return features != nullList ? features : null; } /** * Queries whether a feature is mutable. A feature is considered * mutable if and only if it is changeable and it is either not derived * or it is a member of a feature map (though not itself a feature map). * * @param feature the feature to test * * @return <code>true</code> if the reference is mutable; * <code>false</code>, otherwise */ static boolean isMutable(EStructuralFeature feature) { boolean result = feature.isChangeable(); if (result) { if (feature.isDerived()) { // check whether it is a feature-map member that is not, itself, // a feature map EStructuralFeature group = ExtendedMetaData.INSTANCE.getGroup(feature); result = (group != null) && !FeatureMapUtil.isFeatureMap(feature); } } return result; } /** * An iterator over the references defined by the specified EObject that * are {@linkplain EStructuralFeature#isChangeable() changeable}. * * @param eObj an EObject * @return an iterator over its {@link EReference}s that are changeable */ private EContentsEList.FeatureIterator getOptimizedCrossReferenceIterator( EObject eObj) { List features = getCrossReferencesChangeableFeatures(eObj.eClass()); if (features != null) { EContentsEList list = null; if (features.size() > 0) { list = new ECrossReferenceEList(eObj, (EStructuralFeature[]) features .toArray(new EStructuralFeature[features.size()])) { // to get to the protected constructor }; } else { list = ECrossReferenceEList.EMPTY_CROSS_REFERENCE_ELIST; } return (EContentsEList.FeatureIterator) (resolve() ? list .iterator() : ((InternalEList) list).basicIterator()); } return (EContentsEList.FeatureIterator) ECrossReferenceEList.EMPTY_CROSS_REFERENCE_ELIST .iterator(); } /** * Like the {@link #getInverseReferences(EObject, boolean)} method, * obtains settings implementing references to the specified object, * except that it only considers references that are * {@linkplain EStructuralFeature#isChangeable() changeable} and can * {@linkplain EReference#isResolveProxies() reference other resources}. * * @param eObject the referenced EObject * @param resolve whether to resolve proxies or not * * @return a collection of {@link EStructuralFeature.Setting}s on * potentially cross-resource references */ public Collection getInverseReferencesCrossResource(EObject eObject, boolean resolve) { Collection result = new ArrayList(); if (resolve) { resolveAll(eObject); } EObject eContainer = eObject.eContainer(); if (eContainer != null) { result.add(((InternalEObject) eContainer).eSetting(eObject .eContainmentFeature())); } Collection nonNavigableInverseReferences = (Collection) inverseCrossReferencer .get(eObject); if (nonNavigableInverseReferences != null) { result.addAll(nonNavigableInverseReferences); } for (Iterator i = eObject.eClass().getEAllReferences().iterator(); i .hasNext();) { EReference eReference = (EReference) i.next(); EReference eOpposite = eReference.getEOpposite(); if (eOpposite != null && isImportExportCapable(eReference, eObject) && eObject.eIsSet(eReference)) { if (FeatureMapUtil.isMany(eObject, eReference)) { Object collection = eObject.eGet(eReference); for (Iterator j = resolve() ? ((Collection) collection) .iterator() : ((InternalEList) collection) .basicIterator(); j.hasNext();) { InternalEObject referencingEObject = (InternalEObject) j .next(); result.add(referencingEObject.eSetting(eOpposite)); } } else { // although the reference is set, the value could be null InternalEObject referencingEObject = ((InternalEObject) eObject .eGet(eReference, resolve())); if (referencingEObject != null) { result.add(referencingEObject.eSetting(eOpposite)); } } } } return result; } /** * Queries whether the specified reference, applied to the given owner * object, is capable of establishing a resource import or export by * virtue of being a mutable cross-resource reference. * <p> * A reference is considered to support resource imports and exports if * all of the following apply: * </p> * <ul> * <li>the reference is not a container or containment reference. Note * that this excludes cross-resource containment from registering * as an import/export dependency</li> * <li>the reference resolves proxies</li> * <li>the reference is changeable</li> * </ul> * * @param reference a reference feature * @param owner an object under consideration that defines this reference. * Subclasses may need to introspect the object or its EClass to further * refine their criteria * * @return <code>true</code> if this reference in the context of this * owner should be counted for resource imports and exports; * false, otherwise */ protected boolean isImportExportCapable(EReference reference, EObject owner) { return !reference.isContainer() && !reference.isContainment() && reference.isResolveProxies() // can be cross-resource && reference.isChangeable(); // not computed } }