/**
* 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 static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
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;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class ECrossReferenceAdapterTest
{
private EPackage testPackage;
private EAttribute mapAttribute;
private ECrossReferenceAdapterFixture fixture;
/**
* Tests the filtered intrinsic cross-reference iterator for a resolving cross-referencer.
*/
@Test
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.
*/
@Test
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.
*/
@Test
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.
*/
@Test
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.
*/
@Test
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
*/
@Test
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
//
@Before
public 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);
}
@After
public 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());
}
}
}