diff options
author | Eike Stepper | 2016-09-03 08:28:27 +0000 |
---|---|---|
committer | Eike Stepper | 2016-09-03 08:28:27 +0000 |
commit | dae48587ae2860f74865536953122a28247d4a44 (patch) | |
tree | cd0d6d0404979420b35afceef9087952b1db2064 | |
parent | 2bf20add550a863a153f8e3a64737a4c7337c0ce (diff) | |
download | cdo-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
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); + } + } +} |