diff options
author | Christian W. Damus | 2014-04-17 16:51:25 +0000 |
---|---|---|
committer | Ed Merks | 2014-05-05 13:08:04 +0000 |
commit | beac8a81689e9a2b745078425914501ab764d39c (patch) | |
tree | be22d94bbb694127dd11f29910d7ad9f9c279aa8 | |
parent | 5101bb8dbce61f37339e2a44afa2640f441d7e71 (diff) | |
download | org.eclipse.emf-beac8a81689e9a2b745078425914501ab764d39c.tar.gz org.eclipse.emf-beac8a81689e9a2b745078425914501ab764d39c.tar.xz org.eclipse.emf-beac8a81689e9a2b745078425914501ab764d39c.zip |
[433027] ECrossReferenceAdapter leaks cross-references on ETypedElement::eGenericType
https://bugs.eclipse.org/bugs/show_bug.cgi?id=433027
Fix memory leaks in ECrossReferenceAdapters that index the cross-references between ETypedElements and their types via contained EGenericTypes.
Includes comprehensive JUnit tests for the intrinsic and extrinsic cross-reference iterator filtering and for the original memory-leak scenario.
Signed-off-by: Christian W. Damus <give.a.damus@gmail.com>
Change-Id: Ie6c5f0b6d5c32d8451b6dd96acad6a431be0c027
18 files changed, 2201 insertions, 21 deletions
diff --git a/plugins/org.eclipse.emf.ecore/src/org/eclipse/emf/ecore/util/EContentsEList.java b/plugins/org.eclipse.emf.ecore/src/org/eclipse/emf/ecore/util/EContentsEList.java index b4637f965..107568a0b 100644 --- a/plugins/org.eclipse.emf.ecore/src/org/eclipse/emf/ecore/util/EContentsEList.java +++ b/plugins/org.eclipse.emf.ecore/src/org/eclipse/emf/ecore/util/EContentsEList.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2002-2007 IBM Corporation and others. + * Copyright (c) 2002-2014 IBM Corporation, itemis AG, CEA, 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 @@ -7,6 +7,8 @@ * * Contributors: * IBM - Initial API and implementation + * Ed Merks (itemis AG) - bug 433027 + * Christian W. Damus (CEA) - bug 433027 */ package org.eclipse.emf.ecore.util; @@ -311,7 +313,23 @@ public class EContentsEList<E> extends AbstractSequentialInternalEList<E> implem // No new methods. } - public static class FeatureIteratorImpl<E> implements FeatureListIterator<E> + /** + * @since 2.10 + */ + public interface FeatureFilter + { + boolean isIncluded(EStructuralFeature eStructuralFeature); + } + + /** + * @since 2.10 + */ + public interface Filterable + { + void filter(FeatureFilter featureFilter); + } + + public static class FeatureIteratorImpl<E> implements FeatureListIterator<E>, Filterable { protected final EObject eObject; protected final EStructuralFeature [] eStructuralFeatures; @@ -328,6 +346,11 @@ public class EContentsEList<E> extends AbstractSequentialInternalEList<E> implem protected int valueListSize; protected int valueListIndex; + /** + * @since 2.10 + */ + protected FeatureFilter featureFilter; + public FeatureIteratorImpl(EObject eObject, List<? extends EStructuralFeature> eStructuralFeatures) { this.eObject = eObject; @@ -361,6 +384,19 @@ public class EContentsEList<E> extends AbstractSequentialInternalEList<E> implem return eStructuralFeature instanceof EReference && ((EReference)eStructuralFeature).isContainment(); } + /** + * @since 2.10 + */ + public void filter(FeatureFilter featureFilter) + { + if (prepared != 0 || this.featureFilter != null) + { + throw new IllegalStateException("Iterator already in use or already filtered"); //$NON-NLS-1$ + } + + this.featureFilter = featureFilter; + } + public EStructuralFeature feature() { return feature; @@ -398,7 +434,7 @@ public class EContentsEList<E> extends AbstractSequentialInternalEList<E> implem while (featureCursor < eStructuralFeatures.length) { EStructuralFeature feature = eStructuralFeatures[featureCursor++]; - if (isIncluded(feature) && (!useIsSet() || eObject.eIsSet(feature))) + if (isIncluded(feature) && (featureFilter == null || featureFilter.isIncluded(feature)) && (!useIsSet() || eObject.eIsSet(feature))) { Object value = eObject.eGet(feature, resolve()); isHandlingFeatureMap = FeatureMapUtil.isFeatureMap(feature); @@ -505,7 +541,7 @@ public class EContentsEList<E> extends AbstractSequentialInternalEList<E> implem { FeatureMap.Entry entry = (FeatureMap.Entry)values.next(); EStructuralFeature entryFeature = entry.getEStructuralFeature(); - if (isIncludedEntry(entryFeature) && entry.getValue() != null) + if (isIncludedEntry(entryFeature) && (featureFilter == null || featureFilter.isIncluded(entryFeature)) && entry.getValue() != null) { values.previous(); return true; @@ -531,7 +567,7 @@ public class EContentsEList<E> extends AbstractSequentialInternalEList<E> implem valueList.get(valueListIndex) : valueInternalEList.basicGet(valueListIndex)); EStructuralFeature entryFeature = entry.getEStructuralFeature(); - if (isIncludedEntry(entryFeature) && entry.getValue() != null) + if (isIncludedEntry(entryFeature) && (featureFilter == null || featureFilter.isIncluded(entryFeature)) && entry.getValue() != null) { return true; } @@ -602,7 +638,7 @@ public class EContentsEList<E> extends AbstractSequentialInternalEList<E> implem while (featureCursor > 0) { EStructuralFeature feature = eStructuralFeatures[--featureCursor]; - if (isIncluded(feature) && (!useIsSet() || eObject.eIsSet(feature))) + if (isIncluded(feature) && (featureFilter == null || featureFilter.isIncluded(feature)) && (!useIsSet() || eObject.eIsSet(feature))) { Object value = eObject.eGet(feature, resolve()); isHandlingFeatureMap = FeatureMapUtil.isFeatureMap(feature); @@ -707,7 +743,7 @@ public class EContentsEList<E> extends AbstractSequentialInternalEList<E> implem { FeatureMap.Entry entry = (FeatureMap.Entry)values.previous(); EStructuralFeature entryFeature = entry.getEStructuralFeature(); - if (isIncludedEntry(entryFeature) && entry.getValue() != null) + if (isIncludedEntry(entryFeature) && (featureFilter == null || featureFilter.isIncluded(entryFeature)) && entry.getValue() != null) { values.next(); return true; @@ -729,7 +765,7 @@ public class EContentsEList<E> extends AbstractSequentialInternalEList<E> implem { FeatureMap.Entry entry = (FeatureMap.Entry)valueList.get(valueListIndex - 1); EStructuralFeature entryFeature = entry.getEStructuralFeature(); - if (isIncludedEntry(entryFeature) && entry.getValue() != null) + if (isIncludedEntry(entryFeature) && (featureFilter == null || featureFilter.isIncluded(entryFeature)) && entry.getValue() != null) { return true; } @@ -787,6 +823,12 @@ public class EContentsEList<E> extends AbstractSequentialInternalEList<E> implem new FeatureIteratorImpl<Object>(null, (EStructuralFeature [] )null) { @Override + public void filter(EContentsEList.FeatureFilter featureFilter) + { + // I'm empty, so there's nothing to filter + } + + @Override public boolean hasNext() { return false; @@ -824,4 +866,4 @@ public class EContentsEList<E> extends AbstractSequentialInternalEList<E> implem return true; } } -} +}
\ No newline at end of file diff --git a/plugins/org.eclipse.emf.ecore/src/org/eclipse/emf/ecore/util/ECrossReferenceAdapter.java b/plugins/org.eclipse.emf.ecore/src/org/eclipse/emf/ecore/util/ECrossReferenceAdapter.java index b242b2f13..af97bc352 100644 --- a/plugins/org.eclipse.emf.ecore/src/org/eclipse/emf/ecore/util/ECrossReferenceAdapter.java +++ b/plugins/org.eclipse.emf.ecore/src/org/eclipse/emf/ecore/util/ECrossReferenceAdapter.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2005-2012 IBM Corporation and others. + * Copyright (c) 2005-2014 IBM Corporation, CEA, 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 @@ -7,6 +7,7 @@ * * Contributors: * IBM - Initial API and implementation + * Christian W. Damus (CEA) - bug 433027 */ package org.eclipse.emf.ecore.util; @@ -19,6 +20,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.NoSuchElementException; import java.util.Set; import org.eclipse.emf.common.notify.Adapter; @@ -32,6 +34,7 @@ import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.InternalEObject; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.ResourceSet; +import org.eclipse.emf.ecore.util.EContentsEList.FeatureIterator; /** @@ -70,27 +73,97 @@ public class ECrossReferenceAdapter implements Adapter.Internal protected Map<URI, List<EObject>> proxyMap; + /** + * @since 2.10 + */ + protected EContentsEList.FeatureFilter crossReferenceFilter; + protected InverseCrossReferencer() { super((Collection<Notifier>)null); + + crossReferenceFilter = createCrossReferenceFilter(); + } + + /** + * @since 2.10 + */ + protected EContentsEList.FeatureFilter createCrossReferenceFilter() + { + return new EContentsEList.FeatureFilter() + { + public boolean isIncluded(EStructuralFeature eStructuralFeature) + { + return FeatureMapUtil.isFeatureMap(eStructuralFeature) || ECrossReferenceAdapter.this.isIncluded((EReference)eStructuralFeature); + } + }; } @Override protected EContentsEList.FeatureIterator<EObject> getCrossReferences(EObject eObject) { - return - new ECrossReferenceEList.FeatureIteratorImpl<EObject>(eObject) + InternalEList<EObject> eCrossReferences = (InternalEList<EObject>)eObject.eCrossReferences(); + + final EContentsEList.FeatureIterator<EObject> underlyingIterator = (FeatureIterator<EObject>)(resolve() ? eCrossReferences.iterator() : eCrossReferences.basicIterator()); + + if (underlyingIterator instanceof EContentsEList.Filterable) + { + // Simply filter the model-provided iterator to skip the references that we don't need to index + ((EContentsEList.Filterable)underlyingIterator).filter(crossReferenceFilter); + return underlyingIterator; + } + + // Otherwise, we'll post-filter the iterator, ourselves, but this does mean that we may compute derived features unnecessarily + return new EContentsEList.FeatureIterator<EObject>() { - @Override - protected boolean isIncluded(EStructuralFeature eStructuralFeature) + private boolean prepared; + + private EObject preparedNext; + + private EStructuralFeature preparedFeature; + + private EStructuralFeature feature; + + public boolean hasNext() { - return FeatureMapUtil.isFeatureMap(eStructuralFeature) || ECrossReferenceAdapter.this.isIncluded((EReference)eStructuralFeature); + if (!prepared) + { + while (underlyingIterator.hasNext()) + { + preparedNext = underlyingIterator.next(); + preparedFeature = underlyingIterator.feature(); + if (crossReferenceFilter.isIncluded(preparedFeature)) + { + prepared = true; + break; + } + } + } + + return prepared; } - @Override - protected boolean resolve() + public EObject next() + { + if (!prepared && !hasNext()) + { + throw new NoSuchElementException(); + } + + feature = preparedFeature; + prepared = false; + return preparedNext; + } + + public void remove() + { + // Must not attempt to remove cross-references in indexing them + throw new UnsupportedOperationException(); + } + + public EStructuralFeature feature() { - return InverseCrossReferencer.this.resolve(); + return feature; } }; } diff --git a/tests/org.eclipse.emf.test.core/META-INF/MANIFEST.MF b/tests/org.eclipse.emf.test.core/META-INF/MANIFEST.MF index 195b8670a..3e27c759d 100644 --- a/tests/org.eclipse.emf.test.core/META-INF/MANIFEST.MF +++ b/tests/org.eclipse.emf.test.core/META-INF/MANIFEST.MF @@ -1,7 +1,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName -Bundle-SymbolicName: org.eclipse.emf.test.core; singleton:=true +Bundle-SymbolicName: org.eclipse.emf.test.core;singleton:=true Bundle-Version: 2.10.0.qualifier Bundle-ClassPath: test.core.jar Bundle-Vendor: %providerName @@ -19,7 +19,10 @@ Export-Package: org.eclipse.emf.test.core, org.eclipse.emf.test.core.featuremap, org.eclipse.emf.test.core.featuremap.supplier, org.eclipse.emf.test.core.featuremap.supplier.impl, - org.eclipse.emf.test.core.featuremap.supplier.util + org.eclipse.emf.test.core.featuremap.supplier.util, + org.eclipse.emf.test.core.xrefsmodel, + org.eclipse.emf.test.core.xrefsmodel.impl, + org.eclipse.emf.test.core.xrefsmodel.util Require-Bundle: org.eclipse.core.runtime, org.eclipse.core.resources, org.eclipse.emf.ecore, diff --git a/tests/org.eclipse.emf.test.core/data/xrefs0.xmi b/tests/org.eclipse.emf.test.core/data/xrefs0.xmi new file mode 100644 index 000000000..522ed4514 --- /dev/null +++ b/tests/org.eclipse.emf.test.core/data/xrefs0.xmi @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="ASCII"?> +<xmi:XMI xmi:version="2.0" + xmlns:xmi="http://www.omg.org/XMI" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:xrefs="http://www.eclipse.org/EMF/2014/test/xrefsmodel"> + <xrefs:A + name="a0"> + <others href="xrefs1.xmi#/0"/> + <others href="xrefs1.xmi#/1"/> + <nonOthers href="xrefs1.xmi#/2"/> + </xrefs:A> + <xrefs:A + name="a5"> + </xrefs:A> + <xrefs:A + name="a6"> + <others href="xrefs1.xmi#/0"/> + </xrefs:A> +</xmi:XMI> diff --git a/tests/org.eclipse.emf.test.core/data/xrefs1.xmi b/tests/org.eclipse.emf.test.core/data/xrefs1.xmi new file mode 100644 index 000000000..1a557027d --- /dev/null +++ b/tests/org.eclipse.emf.test.core/data/xrefs1.xmi @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="ASCII"?> +<xmi:XMI xmi:version="2.0" + xmlns:xmi="http://www.omg.org/XMI" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:xrefs="http://www.eclipse.org/EMF/2014/test/xrefsmodel"> + <xrefs:A + name="a1" + others="/2 /3"> + </xrefs:A> + <xrefs:A + name="a2"> + <others href="xrefs0.xmi#/1"/> + <others href="xrefs0.xmi#/2"/> + </xrefs:A> + <xrefs:A + name="a3"> + </xrefs:A> + <xrefs:A + name="a4"> + </xrefs:A> +</xmi:XMI> diff --git a/tests/org.eclipse.emf.test.core/src/org/eclipse/emf/test/core/AllSuites.java b/tests/org.eclipse.emf.test.core/src/org/eclipse/emf/test/core/AllSuites.java index 93b3cc9d2..65c285675 100644 --- a/tests/org.eclipse.emf.test.core/src/org/eclipse/emf/test/core/AllSuites.java +++ b/tests/org.eclipse.emf.test.core/src/org/eclipse/emf/test/core/AllSuites.java @@ -7,7 +7,7 @@ * * Contributors: * IBM - Initial API and implementation - * Christian W. Damus (CEA) - 433108 + * Christian W. Damus (CEA) - 433108, 433027 */ package org.eclipse.emf.test.core; @@ -52,6 +52,7 @@ public class AllSuites extends TestSuite org.eclipse.emf.test.core.ecore.SwitchTest.suite(), org.eclipse.emf.test.core.ecore.ResourceSetMappedResourceLocatorTest.suite(), org.eclipse.emf.test.core.ecore.ReificationTest.suite(), + org.eclipse.emf.test.core.ecore.ECrossReferenceAdapterTest.suite(), org.eclipse.emf.test.core.common.util.WeakInterningHashSetTest.suite(), org.eclipse.emf.test.core.common.util.PoolTest.suite(), org.eclipse.emf.test.core.common.util.StringPoolTest.suite(), diff --git a/tests/org.eclipse.emf.test.core/src/org/eclipse/emf/test/core/ecore/ECrossReferenceAdapterTest.java b/tests/org.eclipse.emf.test.core/src/org/eclipse/emf/test/core/ecore/ECrossReferenceAdapterTest.java new file mode 100644 index 000000000..969b9843e --- /dev/null +++ b/tests/org.eclipse.emf.test.core/src/org/eclipse/emf/test/core/ecore/ECrossReferenceAdapterTest.java @@ -0,0 +1,376 @@ +/** + * Copyright (c) 2014 CEA 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: + * CEA - Initial API and implementation + */ +package org.eclipse.emf.test.core.ecore; + + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.EAttribute; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EClassifier; +import org.eclipse.emf.ecore.EGenericType; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.ETypedElement; +import org.eclipse.emf.ecore.EcoreFactory; +import org.eclipse.emf.ecore.EcorePackage; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.emf.ecore.resource.ResourceSet; +import org.eclipse.emf.ecore.resource.impl.ResourceImpl; +import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; +import org.eclipse.emf.ecore.util.EContentsEList; +import org.eclipse.emf.ecore.util.ECrossReferenceAdapter; +import org.eclipse.emf.ecore.util.EcoreUtil; +import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl; +import org.eclipse.emf.test.common.TestUtil; +import org.eclipse.emf.test.core.AllSuites; +import org.eclipse.emf.test.core.xrefsmodel.A; +import org.eclipse.emf.test.core.xrefsmodel.XRefsModelPackage; +import org.eclipse.emf.test.core.xrefsmodel.util.XRefsModelUtil; + + +public class ECrossReferenceAdapterTest extends TestCase +{ + private EPackage testPackage; + + private EAttribute mapAttribute; + + private ECrossReferenceAdapterFixture fixture; + + public ECrossReferenceAdapterTest(String name) + { + super(name); + } + + public static Test suite() + { + TestSuite ts = new TestSuite("ECrossReferenceAdapterTest"); + ts.addTest(new ECrossReferenceAdapterTest("testCrossReferenceIterator_resolving")); + ts.addTest(new ECrossReferenceAdapterTest("testCrossReferenceIterator_nonResolving")); + ts.addTest(new ECrossReferenceAdapterTest("testCrossReferenceIterator_resolving_wrapper")); + ts.addTest(new ECrossReferenceAdapterTest("testCrossReferenceIterator_nonResolving_wrapper")); + ts.addTest(new ECrossReferenceAdapterTest("testSimpleTypeTypeReferencesDoNotLeak")); + ts.addTest(new ECrossReferenceAdapterTest("testGenericTypeTypeReferencesDoNotLeak")); + return ts; + } + + /** + * Tests the filtered intrinsic cross-reference iterator for a resolving cross-referencer. + */ + public void testCrossReferenceIterator_resolving() + { + A a0 = loadXRefsInstance(); + + List<EStructuralFeature> expectedEntries = new ArrayList<EStructuralFeature>(Arrays.asList( + XRefsModelPackage.Literals.A__OTHERS, + XRefsModelPackage.Literals.A__OTHERS, + XRefsModelPackage.Literals.A__NON_OTHERS)); + List<String> expectedNames = new ArrayList<String>(Arrays.asList("a1", "a2", "a3")); + for (EContentsEList.FeatureIterator<EObject> iter = fixture.getCrossReferences(a0); iter.hasNext();) + { + A next = (A)iter.next(); + EStructuralFeature feature = iter.feature(); + + // Assert that we don't get an unexpected entry + assertTrue(expectedEntries.remove(feature)); + + // Assert that we get the expected object + assertFalse(next.eIsProxy()); + assertEquals(expectedNames.remove(0), next.getName()); + } + + // Assert that all of the expected entries were found + assertTrue(expectedEntries.isEmpty()); + } + + /** + * Tests the filtered intrinsic cross-reference iterator for a non-resolving cross-referencer. + */ + public void testCrossReferenceIterator_nonResolving() + { + // Don't resolve cross-references + fixture.setResolve(false); + + A a0 = loadXRefsInstance(); + + List<EStructuralFeature> expectedEntries = new ArrayList<EStructuralFeature>(Arrays.asList( + XRefsModelPackage.Literals.A__OTHERS, + XRefsModelPackage.Literals.A__OTHERS, + XRefsModelPackage.Literals.A__NON_OTHERS)); + List<String> expectedURIs = new ArrayList<String>(Arrays.asList("xrefs1.xmi#/0", "xrefs1.xmi#/1", "xrefs1.xmi#/2")); + for (EContentsEList.FeatureIterator<EObject> iter = fixture.getCrossReferences(a0); iter.hasNext();) + { + A next = (A)iter.next(); + EStructuralFeature feature = iter.feature(); + + // Assert that we don't get an unexpected entry + assertTrue(expectedEntries.remove(feature)); + + // Assert that we get the expected object + assertTrue(next.eIsProxy()); + assertEquals(expectedURIs.remove(0), EcoreUtil.getURI(next).deresolve(a0.eResource().getURI()).toString()); + } + + // Assert that all of the expected entries were found + assertTrue(expectedEntries.isEmpty()); + } + + /** + * Tests the wrapped intrinsic cross-reference iterator for a resolving cross-referencer. This also tests that + * its implementation of {@link EContentsEList.FeatureIterator#feature()} is consistent. + */ + public void testCrossReferenceIterator_resolving_wrapper() + { + XRefsModelUtil.setWrapCrossReferenceIterators(true); + + A a0 = loadXRefsInstance(); + + List<EStructuralFeature> expectedEntries = new ArrayList<EStructuralFeature>(Arrays.asList( + XRefsModelPackage.Literals.A__OTHERS, + XRefsModelPackage.Literals.A__OTHERS, + XRefsModelPackage.Literals.A__NON_OTHERS)); + List<String> expectedNames = new ArrayList<String>(Arrays.asList("a1", "a2", "a3")); + for (EContentsEList.FeatureIterator<EObject> iter = fixture.getCrossReferences(a0); iter.hasNext();) + { + A next = (A)iter.next(); + EStructuralFeature feature = iter.feature(); + + // Assert that we don't get an unexpected entry + assertTrue(expectedEntries.remove(feature)); + + // Assert that we get the expected object + assertFalse(next.eIsProxy()); + assertEquals(expectedNames.remove(0), next.getName()); + } + + // Assert that all of the expected entries were found + assertTrue(expectedEntries.isEmpty()); + + // And that we couldn't avoid calling the derived A::getAllOthers() exactly twice: + // once for eIsSet() and once for eGet() + assertEquals(2, XRefsModelUtil.getAllOthersCallCount()); + } + + /** + * Tests the wrapped intrinsic cross-reference iterator for a non-resolving cross-referencer. This also tests that + * its implementation of {@link EContentsEList.FeatureIterator#feature()} is consistent. + */ + public void testCrossReferenceIterator_nonResolving_wrapper() + { + XRefsModelUtil.setWrapCrossReferenceIterators(true); + + // Don't resolve cross-references + fixture.setResolve(false); + + A a0 = loadXRefsInstance(); + + List<EStructuralFeature> expectedEntries = new ArrayList<EStructuralFeature>(Arrays.asList( + XRefsModelPackage.Literals.A__OTHERS, + XRefsModelPackage.Literals.A__OTHERS, + XRefsModelPackage.Literals.A__NON_OTHERS)); + List<String> expectedURIs = new ArrayList<String>(Arrays.asList("xrefs1.xmi#/0", "xrefs1.xmi#/1", "xrefs1.xmi#/2")); + for (EContentsEList.FeatureIterator<EObject> iter = fixture.getCrossReferences(a0); iter.hasNext();) + { + A next = (A)iter.next(); + EStructuralFeature feature = iter.feature(); + + // Assert that we don't get an unexpected entry + assertTrue(expectedEntries.remove(feature)); + + // Assert that we get the expected object + assertTrue(next.eIsProxy()); + assertEquals(expectedURIs.remove(0), EcoreUtil.getURI(next).deresolve(a0.eResource().getURI()).toString()); + } + + // Assert that all of the expected entries were found + assertTrue(expectedEntries.isEmpty()); + + // And that we couldn't avoid calling the derived A::getAllOthers() exactly twice: + // once for eIsSet() and once for eGet() + assertEquals(2, XRefsModelUtil.getAllOthersCallCount()); + } + + /** + * Control test case: ETypedElements referencing types simply by eType. + */ + public void testSimpleTypeTypeReferencesDoNotLeak() + { + Resource resource = new ResourceImpl(URI.createURI("http:///bogus/testpackage.ecore")); + resource.getContents().add(testPackage); + resource.eAdapters().add(fixture); + EcoreUtil.resolveAll(resource); + + // sanity check + assertSame(EcorePackage.Literals.ESTRING, mapAttribute.getEType()); + + fixture.assertCrossReferenceMapNotEmpty(); + fixture.assertCrossReferenced(mapAttribute, EcorePackage.Literals.ESTRING); + + resource.unload(); + + fixture.assertCrossReferenceMapEmpty(); + } + + /** + * Memory leak scenario: ETypedElements referencing types via eGenericType + * + * @see https://bugs.eclipse.org/bugs/show_bug.cgi?id=433027 + */ + public void testGenericTypeTypeReferencesDoNotLeak() + { + // change the map attribute to a generic type + mapAttribute.setEType(null); + EGenericType genType = EcoreFactory.eINSTANCE.createEGenericType(); + mapAttribute.setEGenericType(genType); + genType.setEClassifier(EcorePackage.Literals.EMAP); + EGenericType k = EcoreFactory.eINSTANCE.createEGenericType(); + genType.getETypeArguments().add(k); + k.setEClassifier(EcorePackage.Literals.EJAVA_OBJECT); + EGenericType t = EcoreFactory.eINSTANCE.createEGenericType(); + genType.getETypeArguments().add(t); + t.setEClassifier(EcorePackage.Literals.EJAVA_OBJECT); + + Resource resource = new ResourceImpl(URI.createURI("http:///bogus/testpackage.ecore")); + resource.getContents().add(testPackage); + resource.eAdapters().add(fixture); + EcoreUtil.resolveAll(resource); + + // sanity check + assertSame(EcorePackage.Literals.EMAP, mapAttribute.getEType()); + + fixture.assertCrossReferenceMapNotEmpty(); + fixture.assertCrossReferenced(mapAttribute, EcorePackage.Literals.EMAP); + + resource.unload(); + + fixture.assertCrossReferenceMapEmpty(); + } + + // + // Test framework + // + + @Override + protected void setUp() + { + // We must never call the A::getAllOthers() derived reference accessor when cross-referencing + XRefsModelUtil.assertNoAllOthersCalls(true); + + fixture = new ECrossReferenceAdapterFixture(); + + testPackage = EcoreFactory.eINSTANCE.createEPackage(); + testPackage.setName("test"); + testPackage.setNsPrefix("test"); + testPackage.setNsURI("http://testpackage"); + + EClass foo = EcoreFactory.eINSTANCE.createEClass(); + testPackage.getEClassifiers().add(foo); + foo.setName("Foo"); + + mapAttribute = EcoreFactory.eINSTANCE.createEAttribute(); + foo.getEStructuralFeatures().add(mapAttribute); + mapAttribute.setName("map"); + mapAttribute.setEType(EcorePackage.Literals.ESTRING); + } + + @Override + protected void tearDown() + { + XRefsModelUtil.assertNoAllOthersCalls(false); + + // In case it was set by a test case + XRefsModelUtil.setWrapCrossReferenceIterators(false); + + testPackage = null; + mapAttribute = null; + fixture = null; + } + + A loadXRefsInstance() + { + ResourceSet rset = new ResourceSetImpl(); + rset.getResourceFactoryRegistry().getExtensionToFactoryMap().put("xmi", new XMIResourceFactoryImpl()); + rset.getPackageRegistry().put(XRefsModelPackage.eNS_URI, XRefsModelPackage.eINSTANCE); + Resource resource = rset.getResource(URI.createFileURI(TestUtil.getPluginDirectory(AllSuites.PLUGIN_ID) + "/data/xrefs0.xmi"), true); + return (A)resource.getContents().get(0); + } + + static class ECrossReferenceAdapterFixture extends ECrossReferenceAdapter + { + private boolean resolve = true; + + class InverseCrossReferencerFixture extends InverseCrossReferencer + { + private static final long serialVersionUID = 1L; + + @Override + protected EContentsEList.FeatureIterator<EObject> getCrossReferences(EObject eObject) + { + return super.getCrossReferences(eObject); + } + } + + @Override + protected InverseCrossReferencer createInverseCrossReferencer() + { + return new InverseCrossReferencerFixture(); + } + + EContentsEList.FeatureIterator<EObject> getCrossReferences(EObject eObject) + { + return ((InverseCrossReferencerFixture)inverseCrossReferencer).getCrossReferences(eObject); + } + + void setResolve(boolean resolve) + { + this.resolve = resolve; + } + + @Override + protected boolean resolve() + { + return resolve; + } + + void assertCrossReferenced(ETypedElement typedElement, EClassifier type) + { + Collection<EStructuralFeature.Setting> settings = getInverseReferences(type); + for (EStructuralFeature.Setting next : settings) + { + if ((next.getEObject() == typedElement) && (next.getEStructuralFeature() == EcorePackage.Literals.ETYPED_ELEMENT__ETYPE)) + { + return; + } + } + + fail("Cross-reference not found for ETypedElement::eType"); + } + + void assertCrossReferenceMapNotEmpty() + { + assertFalse("Cross-reference map is empty", inverseCrossReferencer.isEmpty()); + } + + void assertCrossReferenceMapEmpty() + { + assertTrue("Cross-reference map is not empty", inverseCrossReferencer.isEmpty()); + } + } +} diff --git a/tests/org.eclipse.emf.test.core/src/org/eclipse/emf/test/core/xrefsmodel/A.java b/tests/org.eclipse.emf.test.core/src/org/eclipse/emf/test/core/xrefsmodel/A.java new file mode 100644 index 000000000..c69f28a7a --- /dev/null +++ b/tests/org.eclipse.emf.test.core/src/org/eclipse/emf/test/core/xrefsmodel/A.java @@ -0,0 +1,113 @@ +/** + * Copyright (c) 2014 CEA 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: + * CEA - Initial API and implementation + * + */ +package org.eclipse.emf.test.core.xrefsmodel; + +import org.eclipse.emf.common.util.EList; + +import org.eclipse.emf.ecore.EObject; + +/** + * <!-- begin-user-doc --> + * A representation of the model object '<em><b>A</b></em>'. + * <!-- end-user-doc --> + * + * <p> + * The following features are supported: + * <ul> + * <li>{@link org.eclipse.emf.test.core.xrefsmodel.A#getName <em>Name</em>}</li> + * <li>{@link org.eclipse.emf.test.core.xrefsmodel.A#getOthers <em>Others</em>}</li> + * <li>{@link org.eclipse.emf.test.core.xrefsmodel.A#getAllOthers <em>All Others</em>}</li> + * <li>{@link org.eclipse.emf.test.core.xrefsmodel.A#getNonOthers <em>Non Others</em>}</li> + * </ul> + * </p> + * + * @see org.eclipse.emf.test.core.xrefsmodel.XRefsModelPackage#getA() + * @model + * @generated + */ +public interface A extends EObject +{ + /** + * Returns the value of the '<em><b>Name</b></em>' attribute. + * <!-- begin-user-doc --> + * <p> + * If the meaning of the '<em>Name</em>' attribute isn't clear, + * there really should be more of a description here... + * </p> + * <!-- end-user-doc --> + * @return the value of the '<em>Name</em>' attribute. + * @see #setName(String) + * @see org.eclipse.emf.test.core.xrefsmodel.XRefsModelPackage#getA_Name() + * @model required="true" + * @generated + */ + String getName(); + + /** + * Sets the value of the '{@link org.eclipse.emf.test.core.xrefsmodel.A#getName <em>Name</em>}' attribute. + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @param value the new value of the '<em>Name</em>' attribute. + * @see #getName() + * @generated + */ + void setName(String value); + + /** + * Returns the value of the '<em><b>Others</b></em>' reference list. + * The list contents are of type {@link org.eclipse.emf.test.core.xrefsmodel.A}. + * <!-- begin-user-doc --> + * <p> + * If the meaning of the '<em>Others</em>' reference list isn't clear, + * there really should be more of a description here... + * </p> + * <!-- end-user-doc --> + * @return the value of the '<em>Others</em>' reference list. + * @see org.eclipse.emf.test.core.xrefsmodel.XRefsModelPackage#getA_Others() + * @model + * @generated + */ + EList<A> getOthers(); + + /** + * Returns the value of the '<em><b>All Others</b></em>' reference list. + * The list contents are of type {@link org.eclipse.emf.test.core.xrefsmodel.A}. + * <!-- begin-user-doc --> + * <p> + * If the meaning of the '<em>All Others</em>' reference list isn't clear, + * there really should be more of a description here... + * </p> + * <!-- end-user-doc --> + * @return the value of the '<em>All Others</em>' reference list. + * @see org.eclipse.emf.test.core.xrefsmodel.XRefsModelPackage#getA_AllOthers() + * @model transient="true" changeable="false" volatile="true" derived="true" + * @generated + */ + EList<A> getAllOthers(); + + /** + * Returns the value of the '<em><b>Non Others</b></em>' reference list. + * The list contents are of type {@link org.eclipse.emf.test.core.xrefsmodel.A}. + * <!-- begin-user-doc --> + * <p> + * If the meaning of the '<em>Non Others</em>' reference list isn't clear, + * there really should be more of a description here... + * </p> + * <!-- end-user-doc --> + * @return the value of the '<em>Non Others</em>' reference list. + * @see org.eclipse.emf.test.core.xrefsmodel.XRefsModelPackage#getA_NonOthers() + * @model + * @generated + */ + EList<A> getNonOthers(); + +} // A diff --git a/tests/org.eclipse.emf.test.core/src/org/eclipse/emf/test/core/xrefsmodel/XRefsModelFactory.java b/tests/org.eclipse.emf.test.core/src/org/eclipse/emf/test/core/xrefsmodel/XRefsModelFactory.java new file mode 100644 index 000000000..d51e63a46 --- /dev/null +++ b/tests/org.eclipse.emf.test.core/src/org/eclipse/emf/test/core/xrefsmodel/XRefsModelFactory.java @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2014 CEA 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: + * CEA - Initial API and implementation + * + */ +package org.eclipse.emf.test.core.xrefsmodel; + +import org.eclipse.emf.ecore.EFactory; + +/** + * <!-- begin-user-doc --> + * The <b>Factory</b> for the model. + * It provides a create method for each non-abstract class of the model. + * <!-- end-user-doc --> + * @see org.eclipse.emf.test.core.xrefsmodel.XRefsModelPackage + * @generated + */ +public interface XRefsModelFactory extends EFactory +{ + /** + * The singleton instance of the factory. + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated + */ + XRefsModelFactory eINSTANCE = org.eclipse.emf.test.core.xrefsmodel.impl.XRefsModelFactoryImpl.init(); + + /** + * Returns a new object of class '<em>A</em>'. + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @return a new object of class '<em>A</em>'. + * @generated + */ + A createA(); + + /** + * Returns the package supported by this factory. + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @return the package supported by this factory. + * @generated + */ + XRefsModelPackage getXRefsModelPackage(); + +} //XRefsModelFactory diff --git a/tests/org.eclipse.emf.test.core/src/org/eclipse/emf/test/core/xrefsmodel/XRefsModelPackage.java b/tests/org.eclipse.emf.test.core/src/org/eclipse/emf/test/core/xrefsmodel/XRefsModelPackage.java new file mode 100644 index 000000000..b11b62ee2 --- /dev/null +++ b/tests/org.eclipse.emf.test.core/src/org/eclipse/emf/test/core/xrefsmodel/XRefsModelPackage.java @@ -0,0 +1,256 @@ +/** + * Copyright (c) 2014 CEA 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: + * CEA - Initial API and implementation + * + */ +package org.eclipse.emf.test.core.xrefsmodel; + +import org.eclipse.emf.ecore.EAttribute; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.EReference; + +/** + * <!-- begin-user-doc --> + * The <b>Package</b> for the model. + * It contains accessors for the meta objects to represent + * <ul> + * <li>each class,</li> + * <li>each feature of each class,</li> + * <li>each operation of each class,</li> + * <li>each enum,</li> + * <li>and each data type</li> + * </ul> + * <!-- end-user-doc --> + * @see org.eclipse.emf.test.core.xrefsmodel.XRefsModelFactory + * @model kind="package" + * @generated + */ +public interface XRefsModelPackage extends EPackage +{ + /** + * The package name. + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated + */ + String eNAME = "xrefsmodel"; + + /** + * The package namespace URI. + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated + */ + String eNS_URI = "http://www.eclipse.org/EMF/2014/test/xrefsmodel"; + + /** + * The package namespace name. + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated + */ + String eNS_PREFIX = "xrefs"; + + /** + * The singleton instance of the package. + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated + */ + XRefsModelPackage eINSTANCE = org.eclipse.emf.test.core.xrefsmodel.impl.XRefsModelPackageImpl.init(); + + /** + * The meta object id for the '{@link org.eclipse.emf.test.core.xrefsmodel.impl.AImpl <em>A</em>}' class. + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @see org.eclipse.emf.test.core.xrefsmodel.impl.AImpl + * @see org.eclipse.emf.test.core.xrefsmodel.impl.XRefsModelPackageImpl#getA() + * @generated + */ + int A = 0; + + /** + * The feature id for the '<em><b>Name</b></em>' attribute. + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated + * @ordered + */ + int A__NAME = 0; + + /** + * The feature id for the '<em><b>Others</b></em>' reference list. + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated + * @ordered + */ + int A__OTHERS = 1; + + /** + * The feature id for the '<em><b>All Others</b></em>' reference list. + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated + * @ordered + */ + int A__ALL_OTHERS = 2; + + /** + * The feature id for the '<em><b>Non Others</b></em>' reference list. + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated + * @ordered + */ + int A__NON_OTHERS = 3; + + /** + * The number of structural features of the '<em>A</em>' class. + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated + * @ordered + */ + int A_FEATURE_COUNT = 4; + + /** + * The number of operations of the '<em>A</em>' class. + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated + * @ordered + */ + int A_OPERATION_COUNT = 0; + + + /** + * Returns the meta object for class '{@link org.eclipse.emf.test.core.xrefsmodel.A <em>A</em>}'. + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @return the meta object for class '<em>A</em>'. + * @see org.eclipse.emf.test.core.xrefsmodel.A + * @generated + */ + EClass getA(); + + /** + * Returns the meta object for the attribute '{@link org.eclipse.emf.test.core.xrefsmodel.A#getName <em>Name</em>}'. + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @return the meta object for the attribute '<em>Name</em>'. + * @see org.eclipse.emf.test.core.xrefsmodel.A#getName() + * @see #getA() + * @generated + */ + EAttribute getA_Name(); + + /** + * Returns the meta object for the reference list '{@link org.eclipse.emf.test.core.xrefsmodel.A#getOthers <em>Others</em>}'. + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @return the meta object for the reference list '<em>Others</em>'. + * @see org.eclipse.emf.test.core.xrefsmodel.A#getOthers() + * @see #getA() + * @generated + */ + EReference getA_Others(); + + /** + * Returns the meta object for the reference list '{@link org.eclipse.emf.test.core.xrefsmodel.A#getAllOthers <em>All Others</em>}'. + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @return the meta object for the reference list '<em>All Others</em>'. + * @see org.eclipse.emf.test.core.xrefsmodel.A#getAllOthers() + * @see #getA() + * @generated + */ + EReference getA_AllOthers(); + + /** + * Returns the meta object for the reference list '{@link org.eclipse.emf.test.core.xrefsmodel.A#getNonOthers <em>Non Others</em>}'. + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @return the meta object for the reference list '<em>Non Others</em>'. + * @see org.eclipse.emf.test.core.xrefsmodel.A#getNonOthers() + * @see #getA() + * @generated + */ + EReference getA_NonOthers(); + + /** + * Returns the factory that creates the instances of the model. + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @return the factory that creates the instances of the model. + * @generated + */ + XRefsModelFactory getXRefsModelFactory(); + + /** + * <!-- begin-user-doc --> + * Defines literals for the meta objects that represent + * <ul> + * <li>each class,</li> + * <li>each feature of each class,</li> + * <li>each operation of each class,</li> + * <li>each enum,</li> + * <li>and each data type</li> + * </ul> + * <!-- end-user-doc --> + * @generated + */ + interface Literals + { + /** + * The meta object literal for the '{@link org.eclipse.emf.test.core.xrefsmodel.impl.AImpl <em>A</em>}' class. + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @see org.eclipse.emf.test.core.xrefsmodel.impl.AImpl + * @see org.eclipse.emf.test.core.xrefsmodel.impl.XRefsModelPackageImpl#getA() + * @generated + */ + EClass A = eINSTANCE.getA(); + + /** + * The meta object literal for the '<em><b>Name</b></em>' attribute feature. + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated + */ + EAttribute A__NAME = eINSTANCE.getA_Name(); + + /** + * The meta object literal for the '<em><b>Others</b></em>' reference list feature. + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated + */ + EReference A__OTHERS = eINSTANCE.getA_Others(); + + /** + * The meta object literal for the '<em><b>All Others</b></em>' reference list feature. + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated + */ + EReference A__ALL_OTHERS = eINSTANCE.getA_AllOthers(); + + /** + * The meta object literal for the '<em><b>Non Others</b></em>' reference list feature. + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated + */ + EReference A__NON_OTHERS = eINSTANCE.getA_NonOthers(); + + } + +} //XRefsModelPackage diff --git a/tests/org.eclipse.emf.test.core/src/org/eclipse/emf/test/core/xrefsmodel/impl/AImpl.java b/tests/org.eclipse.emf.test.core/src/org/eclipse/emf/test/core/xrefsmodel/impl/AImpl.java new file mode 100644 index 000000000..d0afa3646 --- /dev/null +++ b/tests/org.eclipse.emf.test.core/src/org/eclipse/emf/test/core/xrefsmodel/impl/AImpl.java @@ -0,0 +1,282 @@ +/** + * Copyright (c) 2014 CEA 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: + * CEA - Initial API and implementation + * + */ +package org.eclipse.emf.test.core.xrefsmodel.impl; + +import java.util.Collection; +import org.eclipse.emf.common.notify.Notification; +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.impl.ENotificationImpl; +import org.eclipse.emf.ecore.impl.MinimalEObjectImpl; +import org.eclipse.emf.ecore.util.EObjectResolvingEList; +import org.eclipse.emf.test.core.xrefsmodel.A; +import org.eclipse.emf.test.core.xrefsmodel.XRefsModelPackage; +import org.eclipse.emf.test.core.xrefsmodel.util.XRefsModelUtil; + +/** + * <!-- begin-user-doc --> + * An implementation of the model object '<em><b>A</b></em>'. + * <!-- end-user-doc --> + * <p> + * The following features are implemented: + * <ul> + * <li>{@link org.eclipse.emf.test.core.xrefsmodel.impl.AImpl#getName <em>Name</em>}</li> + * <li>{@link org.eclipse.emf.test.core.xrefsmodel.impl.AImpl#getOthers <em>Others</em>}</li> + * <li>{@link org.eclipse.emf.test.core.xrefsmodel.impl.AImpl#getAllOthers <em>All Others</em>}</li> + * <li>{@link org.eclipse.emf.test.core.xrefsmodel.impl.AImpl#getNonOthers <em>Non Others</em>}</li> + * </ul> + * </p> + * + * @generated + */ +public class AImpl extends MinimalEObjectImpl.Container implements A +{ + /** + * The default value of the '{@link #getName() <em>Name</em>}' attribute. + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @see #getName() + * @generated + * @ordered + */ + protected static final String NAME_EDEFAULT = null; + + /** + * The cached value of the '{@link #getName() <em>Name</em>}' attribute. + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @see #getName() + * @generated + * @ordered + */ + protected String name = NAME_EDEFAULT; + + /** + * The cached value of the '{@link #getOthers() <em>Others</em>}' reference list. + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @see #getOthers() + * @generated + * @ordered + */ + protected EList<A> others; + + /** + * The cached value of the '{@link #getNonOthers() <em>Non Others</em>}' reference list. + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @see #getNonOthers() + * @generated + * @ordered + */ + protected EList<A> nonOthers; + + /** + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated + */ + protected AImpl() + { + super(); + } + + @Override + public EList<EObject> eCrossReferences() + { + return XRefsModelUtil.getCrossReferences(super.eCrossReferences()); + } + + /** + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated + */ + @Override + protected EClass eStaticClass() + { + return XRefsModelPackage.Literals.A; + } + + /** + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated + */ + public String getName() + { + return name; + } + + /** + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated + */ + public void setName(String newName) + { + String oldName = name; + name = newName; + if (eNotificationRequired()) + eNotify(new ENotificationImpl(this, Notification.SET, XRefsModelPackage.A__NAME, oldName, name)); + } + + /** + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated + */ + public EList<A> getOthers() + { + if (others == null) + { + others = new EObjectResolvingEList<A>(A.class, this, XRefsModelPackage.A__OTHERS); + } + return others; + } + + /** + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated NOT + */ + public EList<A> getAllOthers() + { + return XRefsModelUtil.getAllOthers(this); + } + + /** + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated + */ + public EList<A> getNonOthers() + { + if (nonOthers == null) + { + nonOthers = new EObjectResolvingEList<A>(A.class, this, XRefsModelPackage.A__NON_OTHERS); + } + return nonOthers; + } + + /** + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated + */ + @Override + public Object eGet(int featureID, boolean resolve, boolean coreType) + { + switch (featureID) + { + case XRefsModelPackage.A__NAME: + return getName(); + case XRefsModelPackage.A__OTHERS: + return getOthers(); + case XRefsModelPackage.A__ALL_OTHERS: + return getAllOthers(); + case XRefsModelPackage.A__NON_OTHERS: + return getNonOthers(); + } + return super.eGet(featureID, resolve, coreType); + } + + /** + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated + */ + @SuppressWarnings("unchecked") + @Override + public void eSet(int featureID, Object newValue) + { + switch (featureID) + { + case XRefsModelPackage.A__NAME: + setName((String)newValue); + return; + case XRefsModelPackage.A__OTHERS: + getOthers().clear(); + getOthers().addAll((Collection<? extends A>)newValue); + return; + case XRefsModelPackage.A__NON_OTHERS: + getNonOthers().clear(); + getNonOthers().addAll((Collection<? extends A>)newValue); + return; + } + super.eSet(featureID, newValue); + } + + /** + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated + */ + @Override + public void eUnset(int featureID) + { + switch (featureID) + { + case XRefsModelPackage.A__NAME: + setName(NAME_EDEFAULT); + return; + case XRefsModelPackage.A__OTHERS: + getOthers().clear(); + return; + case XRefsModelPackage.A__NON_OTHERS: + getNonOthers().clear(); + return; + } + super.eUnset(featureID); + } + + /** + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated + */ + @Override + public boolean eIsSet(int featureID) + { + switch (featureID) + { + case XRefsModelPackage.A__NAME: + return NAME_EDEFAULT == null ? name != null : !NAME_EDEFAULT.equals(name); + case XRefsModelPackage.A__OTHERS: + return others != null && !others.isEmpty(); + case XRefsModelPackage.A__ALL_OTHERS: + return !getAllOthers().isEmpty(); + case XRefsModelPackage.A__NON_OTHERS: + return nonOthers != null && !nonOthers.isEmpty(); + } + return super.eIsSet(featureID); + } + + /** + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated + */ + @Override + public String toString() + { + if (eIsProxy()) return super.toString(); + + StringBuffer result = new StringBuffer(super.toString()); + result.append(" (name: "); + result.append(name); + result.append(')'); + return result.toString(); + } + +} //AImpl diff --git a/tests/org.eclipse.emf.test.core/src/org/eclipse/emf/test/core/xrefsmodel/impl/XRefsModelFactoryImpl.java b/tests/org.eclipse.emf.test.core/src/org/eclipse/emf/test/core/xrefsmodel/impl/XRefsModelFactoryImpl.java new file mode 100644 index 000000000..8fdda4883 --- /dev/null +++ b/tests/org.eclipse.emf.test.core/src/org/eclipse/emf/test/core/xrefsmodel/impl/XRefsModelFactoryImpl.java @@ -0,0 +1,115 @@ +/** + * Copyright (c) 2014 CEA 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: + * CEA - Initial API and implementation + * + */ +package org.eclipse.emf.test.core.xrefsmodel.impl; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EPackage; + +import org.eclipse.emf.ecore.impl.EFactoryImpl; + +import org.eclipse.emf.ecore.plugin.EcorePlugin; + +import org.eclipse.emf.test.core.xrefsmodel.*; + +/** + * <!-- begin-user-doc --> + * An implementation of the model <b>Factory</b>. + * <!-- end-user-doc --> + * @generated + */ +public class XRefsModelFactoryImpl extends EFactoryImpl implements XRefsModelFactory +{ + /** + * Creates the default factory implementation. + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated + */ + public static XRefsModelFactory init() + { + try + { + XRefsModelFactory theXRefsModelFactory = (XRefsModelFactory)EPackage.Registry.INSTANCE.getEFactory(XRefsModelPackage.eNS_URI); + if (theXRefsModelFactory != null) + { + return theXRefsModelFactory; + } + } + catch (Exception exception) + { + EcorePlugin.INSTANCE.log(exception); + } + return new XRefsModelFactoryImpl(); + } + + /** + * Creates an instance of the factory. + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated + */ + public XRefsModelFactoryImpl() + { + super(); + } + + /** + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated + */ + @Override + public EObject create(EClass eClass) + { + switch (eClass.getClassifierID()) + { + case XRefsModelPackage.A: return createA(); + default: + throw new IllegalArgumentException("The class '" + eClass.getName() + "' is not a valid classifier"); + } + } + + /** + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated + */ + public A createA() + { + AImpl a = new AImpl(); + return a; + } + + /** + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated + */ + public XRefsModelPackage getXRefsModelPackage() + { + return (XRefsModelPackage)getEPackage(); + } + + /** + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @deprecated + * @generated + */ + @Deprecated + public static XRefsModelPackage getPackage() + { + return XRefsModelPackage.eINSTANCE; + } + +} //XRefsModelFactoryImpl diff --git a/tests/org.eclipse.emf.test.core/src/org/eclipse/emf/test/core/xrefsmodel/impl/XRefsModelPackageImpl.java b/tests/org.eclipse.emf.test.core/src/org/eclipse/emf/test/core/xrefsmodel/impl/XRefsModelPackageImpl.java new file mode 100644 index 000000000..3918b01bd --- /dev/null +++ b/tests/org.eclipse.emf.test.core/src/org/eclipse/emf/test/core/xrefsmodel/impl/XRefsModelPackageImpl.java @@ -0,0 +1,230 @@ +/** + * Copyright (c) 2014 CEA 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: + * CEA - Initial API and implementation + * + */ +package org.eclipse.emf.test.core.xrefsmodel.impl; + +import org.eclipse.emf.ecore.EAttribute; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.EReference; + +import org.eclipse.emf.ecore.impl.EPackageImpl; + +import org.eclipse.emf.test.core.xrefsmodel.XRefsModelFactory; +import org.eclipse.emf.test.core.xrefsmodel.XRefsModelPackage; + +/** + * <!-- begin-user-doc --> + * An implementation of the model <b>Package</b>. + * <!-- end-user-doc --> + * @generated + */ +public class XRefsModelPackageImpl extends EPackageImpl implements XRefsModelPackage +{ + /** + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated + */ + private EClass aEClass = null; + + /** + * Creates an instance of the model <b>Package</b>, registered with + * {@link org.eclipse.emf.ecore.EPackage.Registry EPackage.Registry} by the package + * package URI value. + * <p>Note: the correct way to create the package is via the static + * factory method {@link #init init()}, which also performs + * initialization of the package, or returns the registered package, + * if one already exists. + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @see org.eclipse.emf.ecore.EPackage.Registry + * @see org.eclipse.emf.test.core.xrefsmodel.XRefsModelPackage#eNS_URI + * @see #init() + * @generated + */ + private XRefsModelPackageImpl() + { + super(eNS_URI, XRefsModelFactory.eINSTANCE); + } + + /** + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated + */ + private static boolean isInited = false; + + /** + * Creates, registers, and initializes the <b>Package</b> for this model, and for any others upon which it depends. + * + * <p>This method is used to initialize {@link XRefsModelPackage#eINSTANCE} when that field is accessed. + * Clients should not invoke it directly. Instead, they should simply access that field to obtain the package. + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @see #eNS_URI + * @see #createPackageContents() + * @see #initializePackageContents() + * @generated + */ + public static XRefsModelPackage init() + { + if (isInited) return (XRefsModelPackage)EPackage.Registry.INSTANCE.getEPackage(XRefsModelPackage.eNS_URI); + + // Obtain or create and register package + XRefsModelPackageImpl theXRefsModelPackage = (XRefsModelPackageImpl)(EPackage.Registry.INSTANCE.get(eNS_URI) instanceof XRefsModelPackageImpl ? EPackage.Registry.INSTANCE.get(eNS_URI) : new XRefsModelPackageImpl()); + + isInited = true; + + // Create package meta-data objects + theXRefsModelPackage.createPackageContents(); + + // Initialize created meta-data + theXRefsModelPackage.initializePackageContents(); + + // Mark meta-data to indicate it can't be changed + theXRefsModelPackage.freeze(); + + + // Update the registry and return the package + EPackage.Registry.INSTANCE.put(XRefsModelPackage.eNS_URI, theXRefsModelPackage); + return theXRefsModelPackage; + } + + /** + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated + */ + public EClass getA() + { + return aEClass; + } + + /** + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated + */ + public EAttribute getA_Name() + { + return (EAttribute)aEClass.getEStructuralFeatures().get(0); + } + + /** + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated + */ + public EReference getA_Others() + { + return (EReference)aEClass.getEStructuralFeatures().get(1); + } + + /** + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated + */ + public EReference getA_AllOthers() + { + return (EReference)aEClass.getEStructuralFeatures().get(2); + } + + /** + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated + */ + public EReference getA_NonOthers() + { + return (EReference)aEClass.getEStructuralFeatures().get(3); + } + + /** + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated + */ + public XRefsModelFactory getXRefsModelFactory() + { + return (XRefsModelFactory)getEFactoryInstance(); + } + + /** + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated + */ + private boolean isCreated = false; + + /** + * Creates the meta-model objects for the package. This method is + * guarded to have no affect on any invocation but its first. + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated + */ + public void createPackageContents() + { + if (isCreated) return; + isCreated = true; + + // Create classes and their features + aEClass = createEClass(A); + createEAttribute(aEClass, A__NAME); + createEReference(aEClass, A__OTHERS); + createEReference(aEClass, A__ALL_OTHERS); + createEReference(aEClass, A__NON_OTHERS); + } + + /** + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated + */ + private boolean isInitialized = false; + + /** + * Complete the initialization of the package and its meta-model. This + * method is guarded to have no affect on any invocation but its first. + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated + */ + public void initializePackageContents() + { + if (isInitialized) return; + isInitialized = true; + + // Initialize package + setName(eNAME); + setNsPrefix(eNS_PREFIX); + setNsURI(eNS_URI); + + // Create type parameters + + // Set bounds for type parameters + + // Add supertypes to classes + + // Initialize classes, features, and operations; add parameters + initEClass(aEClass, org.eclipse.emf.test.core.xrefsmodel.A.class, "A", !IS_ABSTRACT, !IS_INTERFACE, IS_GENERATED_INSTANCE_CLASS); + initEAttribute(getA_Name(), ecorePackage.getEString(), "name", null, 1, 1, org.eclipse.emf.test.core.xrefsmodel.A.class, !IS_TRANSIENT, !IS_VOLATILE, IS_CHANGEABLE, !IS_UNSETTABLE, !IS_ID, IS_UNIQUE, !IS_DERIVED, IS_ORDERED); + initEReference(getA_Others(), this.getA(), null, "others", null, 0, -1, org.eclipse.emf.test.core.xrefsmodel.A.class, !IS_TRANSIENT, !IS_VOLATILE, IS_CHANGEABLE, !IS_COMPOSITE, IS_RESOLVE_PROXIES, !IS_UNSETTABLE, IS_UNIQUE, !IS_DERIVED, IS_ORDERED); + initEReference(getA_AllOthers(), this.getA(), null, "allOthers", null, 0, -1, org.eclipse.emf.test.core.xrefsmodel.A.class, IS_TRANSIENT, IS_VOLATILE, !IS_CHANGEABLE, !IS_COMPOSITE, IS_RESOLVE_PROXIES, !IS_UNSETTABLE, IS_UNIQUE, IS_DERIVED, IS_ORDERED); + initEReference(getA_NonOthers(), this.getA(), null, "nonOthers", null, 0, -1, org.eclipse.emf.test.core.xrefsmodel.A.class, !IS_TRANSIENT, !IS_VOLATILE, IS_CHANGEABLE, !IS_COMPOSITE, IS_RESOLVE_PROXIES, !IS_UNSETTABLE, IS_UNIQUE, !IS_DERIVED, IS_ORDERED); + + // Create resource + createResource(eNS_URI); + } + +} //XRefsModelPackageImpl diff --git a/tests/org.eclipse.emf.test.core/src/org/eclipse/emf/test/core/xrefsmodel/util/XRefsModelAdapterFactory.java b/tests/org.eclipse.emf.test.core/src/org/eclipse/emf/test/core/xrefsmodel/util/XRefsModelAdapterFactory.java new file mode 100644 index 000000000..b444b77aa --- /dev/null +++ b/tests/org.eclipse.emf.test.core/src/org/eclipse/emf/test/core/xrefsmodel/util/XRefsModelAdapterFactory.java @@ -0,0 +1,141 @@ +/** + * Copyright (c) 2014 CEA 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: + * CEA - Initial API and implementation + * + */ +package org.eclipse.emf.test.core.xrefsmodel.util; + +import org.eclipse.emf.common.notify.Adapter; +import org.eclipse.emf.common.notify.Notifier; + +import org.eclipse.emf.common.notify.impl.AdapterFactoryImpl; + +import org.eclipse.emf.ecore.EObject; + +import org.eclipse.emf.test.core.xrefsmodel.*; + +/** + * <!-- begin-user-doc --> + * The <b>Adapter Factory</b> for the model. + * It provides an adapter <code>createXXX</code> method for each class of the model. + * <!-- end-user-doc --> + * @see org.eclipse.emf.test.core.xrefsmodel.XRefsModelPackage + * @generated + */ +public class XRefsModelAdapterFactory extends AdapterFactoryImpl +{ + /** + * The cached model package. + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated + */ + protected static XRefsModelPackage modelPackage; + + /** + * Creates an instance of the adapter factory. + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated + */ + public XRefsModelAdapterFactory() + { + if (modelPackage == null) + { + modelPackage = XRefsModelPackage.eINSTANCE; + } + } + + /** + * Returns whether this factory is applicable for the type of the object. + * <!-- begin-user-doc --> + * This implementation returns <code>true</code> if the object is either the model's package or is an instance object of the model. + * <!-- end-user-doc --> + * @return whether this factory is applicable for the type of the object. + * @generated + */ + @Override + public boolean isFactoryForType(Object object) + { + if (object == modelPackage) + { + return true; + } + if (object instanceof EObject) + { + return ((EObject)object).eClass().getEPackage() == modelPackage; + } + return false; + } + + /** + * The switch that delegates to the <code>createXXX</code> methods. + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated + */ + protected XRefsModelSwitch<Adapter> modelSwitch = + new XRefsModelSwitch<Adapter>() + { + @Override + public Adapter caseA(A object) + { + return createAAdapter(); + } + @Override + public Adapter defaultCase(EObject object) + { + return createEObjectAdapter(); + } + }; + + /** + * Creates an adapter for the <code>target</code>. + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @param target the object to adapt. + * @return the adapter for the <code>target</code>. + * @generated + */ + @Override + public Adapter createAdapter(Notifier target) + { + return modelSwitch.doSwitch((EObject)target); + } + + + /** + * Creates a new adapter for an object of class '{@link org.eclipse.emf.test.core.xrefsmodel.A <em>A</em>}'. + * <!-- begin-user-doc --> + * This default implementation returns null so that we can easily ignore cases; + * it's useful to ignore a case when inheritance will catch all the cases anyway. + * <!-- end-user-doc --> + * @return the new adapter. + * @see org.eclipse.emf.test.core.xrefsmodel.A + * @generated + */ + public Adapter createAAdapter() + { + return null; + } + + /** + * Creates a new adapter for the default case. + * <!-- begin-user-doc --> + * This default implementation returns null. + * <!-- end-user-doc --> + * @return the new adapter. + * @generated + */ + public Adapter createEObjectAdapter() + { + return null; + } + +} //XRefsModelAdapterFactory diff --git a/tests/org.eclipse.emf.test.core/src/org/eclipse/emf/test/core/xrefsmodel/util/XRefsModelSwitch.java b/tests/org.eclipse.emf.test.core/src/org/eclipse/emf/test/core/xrefsmodel/util/XRefsModelSwitch.java new file mode 100644 index 000000000..ea91bfdde --- /dev/null +++ b/tests/org.eclipse.emf.test.core/src/org/eclipse/emf/test/core/xrefsmodel/util/XRefsModelSwitch.java @@ -0,0 +1,128 @@ +/** + * Copyright (c) 2014 CEA 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: + * CEA - Initial API and implementation + * + */ +package org.eclipse.emf.test.core.xrefsmodel.util; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EPackage; + +import org.eclipse.emf.ecore.util.Switch; + +import org.eclipse.emf.test.core.xrefsmodel.*; + +/** + * <!-- begin-user-doc --> + * The <b>Switch</b> for the model's inheritance hierarchy. + * It supports the call {@link #doSwitch(EObject) doSwitch(object)} + * to invoke the <code>caseXXX</code> method for each class of the model, + * starting with the actual class of the object + * and proceeding up the inheritance hierarchy + * until a non-null result is returned, + * which is the result of the switch. + * <!-- end-user-doc --> + * @see org.eclipse.emf.test.core.xrefsmodel.XRefsModelPackage + * @generated + */ +public class XRefsModelSwitch<T> extends Switch<T> +{ + /** + * The cached model package + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated + */ + protected static XRefsModelPackage modelPackage; + + /** + * Creates an instance of the switch. + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated + */ + public XRefsModelSwitch() + { + if (modelPackage == null) + { + modelPackage = XRefsModelPackage.eINSTANCE; + } + } + + /** + * Checks whether this is a switch for the given package. + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @parameter ePackage the package in question. + * @return whether this is a switch for the given package. + * @generated + */ + @Override + protected boolean isSwitchFor(EPackage ePackage) + { + return ePackage == modelPackage; + } + + /** + * Calls <code>caseXXX</code> for each class of the model until one returns a non null result; it yields that result. + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @return the first non-null result returned by a <code>caseXXX</code> call. + * @generated + */ + @Override + protected T doSwitch(int classifierID, EObject theEObject) + { + switch (classifierID) + { + case XRefsModelPackage.A: + { + A a = (A)theEObject; + T result = caseA(a); + if (result == null) result = defaultCase(theEObject); + return result; + } + default: return defaultCase(theEObject); + } + } + + /** + * Returns the result of interpreting the object as an instance of '<em>A</em>'. + * <!-- begin-user-doc --> + * This implementation returns null; + * returning a non-null result will terminate the switch. + * <!-- end-user-doc --> + * @param object the target of the switch. + * @return the result of interpreting the object as an instance of '<em>A</em>'. + * @see #doSwitch(org.eclipse.emf.ecore.EObject) doSwitch(EObject) + * @generated + */ + public T caseA(A object) + { + return null; + } + + /** + * Returns the result of interpreting the object as an instance of '<em>EObject</em>'. + * <!-- begin-user-doc --> + * This implementation returns null; + * returning a non-null result will terminate the switch, but this is the last case anyway. + * <!-- end-user-doc --> + * @param object the target of the switch. + * @return the result of interpreting the object as an instance of '<em>EObject</em>'. + * @see #doSwitch(org.eclipse.emf.ecore.EObject) + * @generated + */ + @Override + public T defaultCase(EObject object) + { + return null; + } + +} //XRefsModelSwitch diff --git a/tests/org.eclipse.emf.test.core/src/org/eclipse/emf/test/core/xrefsmodel/util/XRefsModelUtil.java b/tests/org.eclipse.emf.test.core/src/org/eclipse/emf/test/core/xrefsmodel/util/XRefsModelUtil.java new file mode 100644 index 000000000..34bfaeece --- /dev/null +++ b/tests/org.eclipse.emf.test.core/src/org/eclipse/emf/test/core/xrefsmodel/util/XRefsModelUtil.java @@ -0,0 +1,296 @@ +/** + * Copyright (c) 2014 CEA 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: + * CEA - Initial API and implementation + * + */ +package org.eclipse.emf.test.core.xrefsmodel.util; + + +import java.util.Collection; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; +import java.util.Queue; +import java.util.Set; + +import junit.framework.Assert; + +import org.eclipse.emf.common.notify.NotificationChain; +import org.eclipse.emf.common.util.DelegatingEList; +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.InternalEObject; +import org.eclipse.emf.ecore.util.EContentsEList; +import org.eclipse.emf.ecore.util.EcoreEList; +import org.eclipse.emf.ecore.util.InternalEList; +import org.eclipse.emf.test.core.xrefsmodel.A; +import org.eclipse.emf.test.core.xrefsmodel.XRefsModelPackage; + + +public class XRefsModelUtil +{ + private static boolean wrapCrossReferenceIterators; + + private static boolean assertNoAllOthersCalls; + + private static int allOthersCallCount; + + public static EList<A> getAllOthers(A self) + { + Assert.assertFalse("A::allOthers() was called", assertNoAllOthersCalls); + allOthersCallCount++; + + Set<A> result = new LinkedHashSet<A>(); + Queue<A> queue = new LinkedList<A>(); + queue.add(self); + + for (A next = queue.poll(); next != null; next = queue.poll()) + { + for (A other : next.getOthers()) + { + if (result.add(other)) + { + queue.offer(other); + } + } + } + + return new EcoreEList.UnmodifiableEList.FastCompare<A>((InternalEObject)self, XRefsModelPackage.Literals.A__ALL_OTHERS, result.size(), result.toArray()); + } + + public static void assertNoAllOthersCalls(boolean assertNoAllOthersCalls) + { + XRefsModelUtil.assertNoAllOthersCalls = assertNoAllOthersCalls; + XRefsModelUtil.allOthersCallCount = 0; + } + + public static int getAllOthersCallCount() + { + return allOthersCallCount; + } + + /** + * Sets whether {@link EObject#eCrossReferences()} iterators will be wrapped so that they do not implement the + * {@link EContentsEList.Filterable} interface. + */ + public static void setWrapCrossReferenceIterators(boolean wrap) + { + wrapCrossReferenceIterators = wrap; + + if (wrap) + { + // The wrapper will cause A::getAllOthers() to be called + assertNoAllOthersCalls(false); + } + } + + public static boolean isWrapCrossReferenceIterators() + { + return wrapCrossReferenceIterators; + } + + /** + * Obtains a list for {@link EObject#eCrossReferences()} which may provide iterators that do not implement + * the {@link EContentsEList.Filterable} interface. + * + * @see #setWrapCrossReferenceIterators(boolean) + */ + public static EList<EObject> getCrossReferences(EList<EObject> crossReferences) + { + return isWrapCrossReferenceIterators() ? wrapCrossReferences(crossReferences) : crossReferences; + } + + private static EList<EObject> wrapCrossReferences(final EList<EObject> crossReferences) + { + class Wrapper extends DelegatingEList.UnmodifiableEList<EObject> implements InternalEList<EObject> + { + + private static final long serialVersionUID = 1L; + + Wrapper(EList<EObject> delegate) + { + super(delegate); + } + + @Override + protected InternalEList<EObject> delegateBasicList() + { + return (InternalEList<EObject>)super.delegateBasicList(); + } + + protected EContentsEList.FeatureIterator<EObject> wrap(Iterator<EObject> iterator) + { + final EContentsEList.FeatureIterator<EObject> delegate = (EContentsEList.FeatureIterator<EObject>)iterator; + return new EContentsEList.FeatureIterator<EObject>() + { + public boolean hasNext() + { + return delegate.hasNext(); + } + + public EObject next() + { + return delegate.next(); + } + + public void remove() + { + delegate.remove(); + } + + public EStructuralFeature feature() + { + return delegate.feature(); + } + + }; + } + + protected EContentsEList.FeatureListIterator<EObject> wrap(ListIterator<EObject> iterator) + { + final EContentsEList.FeatureListIterator<EObject> delegate = (EContentsEList.FeatureListIterator<EObject>)iterator; + return new EContentsEList.FeatureListIterator<EObject>() + { + + public EStructuralFeature feature() + { + return delegate.feature(); + } + + public boolean hasNext() + { + return delegate.hasNext(); + } + + public EObject next() + { + return delegate.next(); + } + + public void remove() + { + delegate.remove(); + } + + public void add(EObject e) + { + delegate.add(e); + } + + public boolean hasPrevious() + { + return delegate.hasPrevious(); + } + + public int nextIndex() + { + return delegate.nextIndex(); + } + + public EObject previous() + { + return delegate.previous(); + } + + public int previousIndex() + { + return delegate.previousIndex(); + } + + public void set(EObject e) + { + delegate.set(e); + } + + }; + } + + @Override + public Iterator<EObject> iterator() + { + return wrap(delegateList().iterator()); + } + + @Override + public Iterator<EObject> basicIterator() + { + return wrap(delegateBasicList().basicIterator()); + } + + @Override + public ListIterator<EObject> basicListIterator(final int index) + { + return wrap(delegateBasicList().basicListIterator(index)); + } + + @Override + public ListIterator<EObject> basicListIterator() + { + return basicListIterator(0); + } + + @Override + public List<EObject> basicList() + { + return super.basicList(); + } + + @Override + public EObject basicGet(int index) + { + return super.basicGet(index); + } + + public Object[] basicToArray() + { + return delegateBasicList().basicToArray(); + } + + public <T> T[] basicToArray(T[] array) + { + return delegateBasicList().basicToArray(array); + } + + public int basicIndexOf(Object object) + { + return delegateBasicList().basicIndexOf(object); + } + + public int basicLastIndexOf(Object object) + { + return delegateBasicList().basicLastIndexOf(object); + } + + public boolean basicContains(Object object) + { + return delegateBasicList().basicContains(object); + } + + public boolean basicContainsAll(Collection<?> collection) + { + return delegateBasicList().basicContainsAll(collection); + } + + public NotificationChain basicRemove(Object object, NotificationChain notifications) + { + return delegateBasicList().basicRemove(object, notifications); + } + + public NotificationChain basicAdd(EObject object, NotificationChain notifications) + { + return delegateBasicList().basicAdd(object, notifications); + } + }; + + return new Wrapper(crossReferences); + } +} diff --git a/tests/org.eclipse.emf.test.core/src/org/eclipse/emf/test/core/xrefsmodel/xrefsmodel.ecore b/tests/org.eclipse.emf.test.core/src/org/eclipse/emf/test/core/xrefsmodel/xrefsmodel.ecore new file mode 100644 index 000000000..5871fbe24 --- /dev/null +++ b/tests/org.eclipse.emf.test.core/src/org/eclipse/emf/test/core/xrefsmodel/xrefsmodel.ecore @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" name="xrefsmodel" nsURI="http://www.eclipse.org/EMF/2014/test/xrefsmodel" + nsPrefix="xrefs"> + <eClassifiers xsi:type="ecore:EClass" name="A"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="name" lowerBound="1" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="others" upperBound="-1" + eType="#//A"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="allOthers" upperBound="-1" + eType="#//A" changeable="false" volatile="true" transient="true" derived="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="nonOthers" upperBound="-1" + eType="#//A"/> + </eClassifiers> +</ecore:EPackage> diff --git a/tests/org.eclipse.emf.test.core/src/org/eclipse/emf/test/core/xrefsmodel/xrefsmodel.genmodel b/tests/org.eclipse.emf.test.core/src/org/eclipse/emf/test/core/xrefsmodel/xrefsmodel.genmodel new file mode 100644 index 000000000..2c5db420d --- /dev/null +++ b/tests/org.eclipse.emf.test.core/src/org/eclipse/emf/test/core/xrefsmodel/xrefsmodel.genmodel @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<genmodel:GenModel xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" + xmlns:genmodel="http://www.eclipse.org/emf/2002/GenModel" copyrightText="Copyright (c) 2014 CEA 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:
 CEA - Initial API and implementation
" + modelDirectory="/org.eclipse.emf.test.core/src" modelPluginID="org.eclipse.emf.test.core" + modelName="Xrefsmodel" rootExtendsClass="org.eclipse.emf.ecore.impl.MinimalEObjectImpl$Container" + importerID="org.eclipse.emf.importer.ecore" complianceLevel="5.0" copyrightFields="false" + language="" operationReflection="true" importOrganizing="true"> + <foreignModel>xrefsmodel.ecore</foreignModel> + <genPackages prefix="XRefsModel" basePackage="org.eclipse.emf.test.core" disposableProviderFactory="true" + ecorePackage="xrefsmodel.ecore#/"> + <genClasses ecoreClass="xrefsmodel.ecore#//A"> + <genFeatures createChild="false" ecoreFeature="ecore:EAttribute xrefsmodel.ecore#//A/name"/> + <genFeatures notify="false" createChild="false" propertySortChoices="true" ecoreFeature="ecore:EReference xrefsmodel.ecore#//A/others"/> + <genFeatures property="Readonly" notify="false" createChild="false" ecoreFeature="ecore:EReference xrefsmodel.ecore#//A/allOthers"/> + <genFeatures notify="false" createChild="false" propertySortChoices="true" ecoreFeature="ecore:EReference xrefsmodel.ecore#//A/nonOthers"/> + </genClasses> + </genPackages> +</genmodel:GenModel> |