Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEike Stepper2016-09-03 08:28:27 +0000
committerEike Stepper2016-09-03 08:28:27 +0000
commitdae48587ae2860f74865536953122a28247d4a44 (patch)
treecd0d6d0404979420b35afceef9087952b1db2064
parent2bf20add550a863a153f8e3a64737a4c7337c0ce (diff)
downloadcdo-dae48587ae2860f74865536953122a28247d4a44.tar.gz
cdo-dae48587ae2860f74865536953122a28247d4a44.tar.xz
cdo-dae48587ae2860f74865536953122a28247d4a44.zip
[467075] Provide ECrossReferenceAdapter that does not recreate removed CDOResource
https://bugs.eclipse.org/bugs/show_bug.cgi?id=467075
-rw-r--r--plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_467075_Test.java77
-rw-r--r--plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/util/CDOCrossReferenceAdapter.java217
2 files changed, 294 insertions, 0 deletions
diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_467075_Test.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_467075_Test.java
new file mode 100644
index 0000000000..a8ee01f521
--- /dev/null
+++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_467075_Test.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2016 Eike Stepper (Berlin, Germany) 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:
+ * Eike Stepper - initial API and implementation
+ */
+package org.eclipse.emf.cdo.tests.bugzilla;
+
+import org.eclipse.emf.cdo.eresource.CDOResource;
+import org.eclipse.emf.cdo.session.CDOSession;
+import org.eclipse.emf.cdo.tests.AbstractCDOTest;
+import org.eclipse.emf.cdo.tests.model1.Company;
+import org.eclipse.emf.cdo.transaction.CDOTransaction;
+import org.eclipse.emf.cdo.util.CDOCrossReferenceAdapter;
+
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.ecore.resource.ResourceSet;
+import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
+import org.eclipse.emf.ecore.util.ECrossReferenceAdapter;
+import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl;
+
+/**
+ * Bug 467075 - Provide ECrossReferenceAdapter that does not recreate removed CDOResource
+ *
+ * @author Eike Stepper
+ */
+public class Bugzilla_467075_Test extends AbstractCDOTest
+{
+ public void testResourceSetRemoveCDOResource() throws Exception
+ {
+ ECrossReferenceAdapter adapter = createAdapter();
+ Company company = getModel1Factory().createCompany();
+
+ CDOSession session = openSession();
+ CDOTransaction transaction = session.openTransaction();
+ CDOResource resource = transaction.createResource(getResourcePath("test1.model1"));
+ resource.getContents().add(company);
+ transaction.commit();
+
+ doTest(transaction.getResourceSet(), resource, adapter);
+ }
+
+ public void testResourceSetRemoveXMIResource() throws Exception
+ {
+ Company company = getModel1Factory().createCompany();
+ ECrossReferenceAdapter adapter = createAdapter();
+
+ Resource.Factory.Registry registry = Resource.Factory.Registry.INSTANCE;
+ registry.getExtensionToFactoryMap().put("model1", new XMIResourceFactoryImpl());
+ ResourceSet resourceSet = new ResourceSetImpl();
+
+ URI uri = URI.createFileURI(createTempFile("resource", ".model1").getCanonicalPath());
+ Resource resource = resourceSet.createResource(uri);
+ resource.getContents().add(company);
+ resource.save(null);
+
+ doTest(resourceSet, resource, adapter);
+ }
+
+ private ECrossReferenceAdapter createAdapter()
+ {
+ return new CDOCrossReferenceAdapter();
+ }
+
+ private void doTest(ResourceSet resourceSet, Resource resource, ECrossReferenceAdapter adapter)
+ {
+ resource.eAdapters().add(adapter);
+ resourceSet.getResources().remove(resource);
+ resource.eAdapters().remove(adapter);
+ assertEquals(0, resourceSet.getResources().size());
+ }
+}
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/util/CDOCrossReferenceAdapter.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/util/CDOCrossReferenceAdapter.java
new file mode 100644
index 0000000000..04199a5534
--- /dev/null
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/util/CDOCrossReferenceAdapter.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 2016 Eike Stepper (Berlin, Germany) 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:
+ * Eike Stepper - initial API and implementation
+ */
+package org.eclipse.emf.cdo.util;
+
+import org.eclipse.emf.common.notify.Notification;
+import org.eclipse.emf.common.notify.Notifier;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.EReference;
+import org.eclipse.emf.ecore.EStructuralFeature;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.ecore.resource.ResourceSet;
+import org.eclipse.emf.ecore.util.ECrossReferenceAdapter;
+import org.eclipse.emf.ecore.util.EcoreUtil;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * An {@link ECrossReferenceAdapter} that does instanceof checks of {@link Notifier} instances
+ * in the order {@link Resource}, {@link EObject}, and {@link ResourceSet}.
+ * <p>
+ * <b>Background:</b>
+ * For performance reasons (assuming that there are typically more <code>EObject</code> instances than <code>Resource</code> instances)
+ * EMF does instanceof checks of <code>Notifier</code> instances in the order <code>EObject</code>, <code>Resource</code>, and <code>ResourceSet</code>.
+ * That is problematic with CDOResources because they implement both <code>Resource</code> and <code>EObject</code>.
+ *
+ * @author Eike Stepper
+ * @since 4.6
+ */
+public final class CDOCrossReferenceAdapter extends ECrossReferenceAdapter
+{
+ @Override
+ public void setTarget(Notifier target)
+ {
+ if (target instanceof Resource)
+ {
+ super.setTarget((Resource)target);
+ }
+ else if (target instanceof EObject)
+ {
+ super.setTarget((EObject)target);
+ }
+ else if (target instanceof ResourceSet)
+ {
+ super.setTarget((ResourceSet)target);
+ }
+ }
+
+ @Override
+ public void unsetTarget(Notifier target)
+ {
+ if (target instanceof Resource)
+ {
+ super.unsetTarget((Resource)target);
+ }
+ else if (target instanceof EObject)
+ {
+ super.unsetTarget((EObject)target);
+ }
+ else if (target instanceof ResourceSet)
+ {
+ super.unsetTarget((ResourceSet)target);
+ }
+ }
+
+ @Override
+ protected void selfAdapt(Notification notification)
+ {
+ Object notifier = notification.getNotifier();
+ if (notifier instanceof Resource)
+ {
+ switch (notification.getFeatureID(Resource.class))
+ {
+ case Resource.RESOURCE__CONTENTS:
+ {
+ if (!unloadedResources.contains(notifier))
+ {
+ switch (notification.getEventType())
+ {
+ case Notification.REMOVE:
+ {
+ Resource resource = (Resource)notifier;
+ if (!resource.isLoaded())
+ {
+ EObject eObject = (EObject)notification.getOldValue();
+ unloadedEObjects.put(eObject, resource);
+ for (Iterator<EObject> i = EcoreUtil.getAllProperContents(eObject, false); i.hasNext();)
+ {
+ unloadedEObjects.put(i.next(), resource);
+ }
+ }
+ break;
+ }
+ case Notification.REMOVE_MANY:
+ {
+ Resource resource = (Resource)notifier;
+ if (!resource.isLoaded())
+ {
+ @SuppressWarnings("unchecked")
+ List<EObject> eObjects = (List<EObject>)notification.getOldValue();
+ for (Iterator<EObject> i = EcoreUtil.getAllProperContents(eObjects, false); i.hasNext();)
+ {
+ unloadedEObjects.put(i.next(), resource);
+ }
+ }
+ break;
+ }
+ default:
+ {
+ handleContainment(notification);
+ break;
+ }
+ }
+ }
+ break;
+ }
+ case Resource.RESOURCE__IS_LOADED:
+ {
+ if (notification.getNewBooleanValue())
+ {
+ unloadedResources.remove(notifier);
+ for (Notifier child : ((Resource)notifier).getContents())
+ {
+ addAdapter(child);
+ }
+ }
+ else
+ {
+ unloadedResources.add((Resource)notifier);
+ for (Iterator<Map.Entry<EObject, Resource>> i = unloadedEObjects.entrySet().iterator(); i.hasNext();)
+ {
+ Map.Entry<EObject, Resource> entry = i.next();
+ if (entry.getValue() == notifier)
+ {
+ i.remove();
+ if (!resolve())
+ {
+ EObject eObject = entry.getKey();
+ Collection<EStructuralFeature.Setting> settings = inverseCrossReferencer.get(eObject);
+ if (settings != null)
+ {
+ for (EStructuralFeature.Setting setting : settings)
+ {
+ getInverseCrossReferencer().addProxy(eObject, setting.getEObject());
+ }
+ }
+ }
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+ else if (notifier instanceof EObject)
+ {
+ Object feature = notification.getFeature();
+ if (feature instanceof EReference)
+ {
+ EReference reference = (EReference)feature;
+ if (reference.isContainment())
+ {
+ handleContainment(notification);
+ }
+ else if (isIncluded(reference))
+ {
+ handleCrossReference(reference, notification);
+ }
+ }
+ }
+ else if (notifier instanceof ResourceSet)
+ {
+ if (notification.getFeatureID(ResourceSet.class) == ResourceSet.RESOURCE_SET__RESOURCES)
+ {
+ handleContainment(notification);
+ }
+ }
+ }
+
+ @Override
+ protected CDOInverseCrossReferencer createInverseCrossReferencer()
+ {
+ return new CDOInverseCrossReferencer();
+ }
+
+ protected CDOInverseCrossReferencer getInverseCrossReferencer()
+ {
+ return (CDOInverseCrossReferencer)inverseCrossReferencer;
+ }
+
+ /**
+ * An {@link org.eclipse.emf.ecore.util.ECrossReferenceAdapter.InverseCrossReferencer InverseCrossReferencer} with an
+ * {@link #addProxy(EObject, EObject)} method that is visible to {@link CDOCrossReferenceAdapter}.
+ *
+ * @author Eike Stepper
+ */
+ protected class CDOInverseCrossReferencer extends InverseCrossReferencer
+ {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void addProxy(EObject proxy, EObject context)
+ {
+ super.addProxy(proxy, context);
+ }
+ }
+}

Back to the top