/* * 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); } } }