//------------------------------------------------------------------------------ // Copyright (c) 2005, 2006 IBM Corporation 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: // IBM Corporation - initial implementation //------------------------------------------------------------------------------ package org.eclipse.epf.uma.ecore.util; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.emf.common.CommonPlugin; import org.eclipse.emf.common.notify.Adapter; import org.eclipse.emf.common.notify.Notification; import org.eclipse.emf.common.notify.Notifier; import org.eclipse.emf.common.notify.impl.AdapterImpl; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.InternalEObject; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.emf.ecore.util.InternalEList; /** * * @author Phong Nguyen Le * @since 1.5 * */ public abstract class AbstractOppositeFeatureManager { protected Map<Class<?>, Set<OppositeFeature>> classOppositeFeaturesMap = new HashMap<Class<?>, Set<OppositeFeature>>(); /** * Maps EStructuralFeature objects to OppositeFeature objects. */ protected Map<EStructuralFeature, OppositeFeature> featureOppositeFeatureMap = new HashMap<EStructuralFeature, OppositeFeature>(); /** * Gets the opposite feature for the given feature. * * @param feature * an EMF <code>EStructuralFeature</code> feature * @return an opposite feature that is based on the given feature */ public OppositeFeature getOppositeFeature( EStructuralFeature feature) { return (OppositeFeature) featureOppositeFeatureMap.get(feature); } /** * Registers the given opposite feature. * * @param oppositeFeature * the opposite feature to register */ public void registerOppositeFeature( OppositeFeature oppositeFeature) { Class<?> cls = oppositeFeature.getOwnerClass(); Set<OppositeFeature> features = classOppositeFeaturesMap.get(cls); if (features == null) { features = new HashSet<OppositeFeature>(); classOppositeFeaturesMap.put(cls, features); } features.add(oppositeFeature); featureOppositeFeatureMap.put(oppositeFeature.getTargetFeature(), oppositeFeature); } private ArrayList<OppositeFeature> predefinedOppositeFeatures; protected AbstractOppositeFeatureManager() { registerPredefinedOppositeFeatures(); } /** * Registers the predefined opposite features. */ private void registerPredefinedOppositeFeatures() { predefinedOppositeFeatures = new ArrayList<OppositeFeature>(); Field[] fields = getClass().getFields(); for (int i = 0; i < fields.length; i++) { Field field = fields[i]; int mod = field.getModifiers(); if (Modifier.isPublic(mod) && Modifier.isStatic(mod) && Modifier.isFinal(mod) && field.getType() == OppositeFeature.class) { try { predefinedOppositeFeatures.add((OppositeFeature) field.get(this)); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } } for (OppositeFeature feature : predefinedOppositeFeatures) { registerOppositeFeature(feature); } } /** * Gets the predefined opposite features. * * @return the predefined opposite features */ public Collection<OppositeFeature> getPredefinedOppositeFeatures() { return predefinedOppositeFeatures; } /** * Gets the value of an opposite feature. * * @param feature * an opposite feature * @return the value for the specified opposite feature */ public Object getOppositeFeatureValue(EObject eObject, OppositeFeature feature) { OppositeFeatureAdapter adapter = getOppositeFeatureAdapter(eObject); if(adapter == null) { throw new IllegalArgumentException("Object is not managed by this manager."); } return adapter.getOppositeFeatureValue(feature); } /** * Gets all the opposite features associated with this model object. * * @return a collection of opposite features */ public Collection<OppositeFeature> getOppositeFeatures(EObject eObject) { OppositeFeatureAdapter adapter = getOppositeFeatureAdapter(eObject); if(adapter == null) { throw new IllegalArgumentException("Object is not managed by this manager."); } return adapter.getOppositeFeatures(); } public void manage(EObject eObject) { OppositeFeatureAdapter adapter = getOppositeFeatureAdapter(eObject); if(adapter == null) { adapter = createOppositeFeatureAdapter(); eObject.eAdapters().add(adapter); } } protected OppositeFeatureAdapter createOppositeFeatureAdapter() { return new OppositeFeatureAdapter(); } public void release(EObject eObject) { OppositeFeatureAdapter adapter = getOppositeFeatureAdapter(eObject); if(adapter != null) { eObject.eAdapters().remove(adapter); } } private OppositeFeatureAdapter getOppositeFeatureAdapter(EObject eObject) { for (Adapter adapter : new ArrayList<Adapter>(eObject.eAdapters())) { if(adapter instanceof OppositeFeatureAdapter) { return (OppositeFeatureAdapter) adapter; } } return null; } /** * Removes all opposite features registered with the system. */ public void removeFromAllOppositeFeatures(EObject eObject) { // find all features that have opposite features and clear those // features. This will remove the references to // unloaded object by those opposite features // for (Iterator iter = eObject.eClass().getEAllReferences().iterator(); iter .hasNext();) { EReference ref = (EReference) iter.next(); OppositeFeature oppositeFeature = OppositeFeature.getOppositeFeature(ref); if(oppositeFeature != null) { if(ref.isMany()) { List list = (List) eObject.eGet(ref, false); if(!list.isEmpty()) { if(!oppositeFeature.resolveOwner()) { list.clear(); } else if(list instanceof InternalEList) { List basicList = ((InternalEList)list).basicList(); for(int i = basicList.size() - 1; i > -1; i--) { EObject e = (EObject) basicList.get(i); if(!e.eIsProxy()) { list.remove(e); } } } } } else { EObject e = (EObject) eObject.eGet(ref, false); if(e != null && !e.eIsProxy()) { eObject.eSet(ref, null); } } } } } protected class OppositeFeatureAdapter extends AdapterImpl { private static final boolean DEBUG = false; /** * A map of entries of OppositeFeature / OppositeFeature's value */ private Map<OppositeFeature, Object> oppositeFeatureMap; private boolean hasOppositeFeature = true; protected OppositeFeatureAdapter() { } @Override public void notifyChanged(Notification msg) { if (msg.getEventType() == Notification.RESOLVE) { return; } Object f = msg.getFeature(); if (f instanceof EStructuralFeature) { EStructuralFeature feature = (EStructuralFeature) f; OppositeFeature oppositeFeature = getOppositeFeature(feature); if (oppositeFeature != null) { EObject oldOtherEnd; EObject otherEnd; if (oppositeFeature.isMany()) { switch (msg.getEventType()) { case Notification.SET: oldOtherEnd = (EObject) msg.getOldValue(); if (oppositeFeature.resolveOwner()) { oldOtherEnd = (EObject) resolve(oldOtherEnd); } if (oldOtherEnd != null) { OppositeFeatureAdapter adapter = getOppositeFeatureAdapter(oldOtherEnd); adapter.oppositeRemove(oppositeFeature, msg .getNotifier()); } case Notification.ADD: otherEnd = (EObject) msg.getNewValue(); if (oppositeFeature.resolveOwner()) { otherEnd = (EObject) resolve(otherEnd); replace(feature, msg.getNewValue(), otherEnd); } if (otherEnd != null) { OppositeFeatureAdapter adapter = getOppositeFeatureAdapter(otherEnd); adapter.oppositeAdd(oppositeFeature, msg .getNotifier()); } break; case Notification.ADD_MANY: for (Iterator iter = ((Collection) msg.getNewValue()) .iterator(); iter.hasNext();) { Object obj = iter.next(); otherEnd = (EObject) obj; if (oppositeFeature.resolveOwner()) { otherEnd = (EObject) resolve(otherEnd); replace(feature, obj, otherEnd); } OppositeFeatureAdapter adapter = getOppositeFeatureAdapter(otherEnd); adapter.oppositeAdd(oppositeFeature, msg .getNotifier()); } break; case Notification.REMOVE: otherEnd = (EObject) msg.getOldValue(); if (oppositeFeature.resolveOwner()) { otherEnd = (EObject) resolve(otherEnd); } if (otherEnd != null) { OppositeFeatureAdapter adapter = getOppositeFeatureAdapter(otherEnd); adapter.oppositeRemove(oppositeFeature, msg .getNotifier()); } break; case Notification.REMOVE_MANY: for (Iterator<?> iter = ((Collection) msg.getOldValue()) .iterator(); iter.hasNext();) { otherEnd = (EObject) iter.next(); if (oppositeFeature.resolveOwner()) { otherEnd = (EObject) resolve(otherEnd); } OppositeFeatureAdapter adapter = getOppositeFeatureAdapter(otherEnd); adapter.oppositeRemove(oppositeFeature, msg .getNotifier()); } break; } } else { switch (msg.getEventType()) { case Notification.ADD_MANY: for (Iterator<?> iter = ((Collection) msg.getNewValue()) .iterator(); iter.hasNext();) { Object obj = iter.next(); otherEnd = (EObject) obj; if (oppositeFeature.resolveOwner()) { otherEnd = (EObject) resolve(otherEnd); replace(feature, obj, otherEnd); } if (otherEnd != null) { OppositeFeatureAdapter adapter = getOppositeFeatureAdapter(otherEnd); EObject oldValue = (EObject) adapter .getOppositeFeatureMap().get( oppositeFeature); if (oldValue != null) { // remove otherEnd from target feature of // oldValue ((Collection) oldValue .eGet((EStructuralFeature) f)) .remove(otherEnd); } adapter.getOppositeFeatureMap().put( oppositeFeature, msg.getNotifier()); } } break; case Notification.REMOVE_MANY: for (Iterator<?> iter = ((Collection) msg.getOldValue()) .iterator(); iter.hasNext();) { otherEnd = (EObject) iter.next(); if (oppositeFeature.resolveOwner()) { otherEnd = (EObject) resolve(otherEnd); } OppositeFeatureAdapter adapter = getOppositeFeatureAdapter(otherEnd); adapter.getOppositeFeatureMap().put( oppositeFeature, null); } break; case Notification.ADD: otherEnd = (EObject) msg.getNewValue(); if (oppositeFeature.resolveOwner()) { otherEnd = (EObject) resolve(otherEnd); replace(feature, msg.getNewValue(), otherEnd); } if (otherEnd != null) { OppositeFeatureAdapter adapter = getOppositeFeatureAdapter(otherEnd); EObject oldValue = (EObject) adapter .getOppositeFeatureMap().get( oppositeFeature); if (oldValue != null) { // remove otherEnd from target feature of // oldValue ((Collection) oldValue .eGet((EStructuralFeature) f)) .remove(otherEnd); } adapter.getOppositeFeatureMap().put( oppositeFeature, msg.getNotifier()); } break; case Notification.SET: otherEnd = (EObject) msg.getNewValue(); if (oppositeFeature.resolveOwner()) { otherEnd = (EObject) resolve(otherEnd); replace(feature, msg.getNewValue(), otherEnd); } if (otherEnd != null) { OppositeFeatureAdapter adapter = getOppositeFeatureAdapter(otherEnd); EObject oldValue = (EObject) adapter .getOppositeFeatureMap().get( oppositeFeature); if (oldValue != null) { // set the target feature of oldValue to null oldValue.eSet((EStructuralFeature) f, null); } adapter.getOppositeFeatureMap().put( oppositeFeature, msg.getNotifier()); } else { EStructuralFeature targetFeature = (EStructuralFeature) f; if(!targetFeature.isMany()) { oldOtherEnd = (EObject) msg.getOldValue(); if(oldOtherEnd != null) { OppositeFeatureAdapter adapter = getOppositeFeatureAdapter(oldOtherEnd); adapter.getOppositeFeatureMap().put(oppositeFeature, null); } } } break; case Notification.REMOVE: // case Notification.UNSET: otherEnd = (EObject) msg.getOldValue(); if (oppositeFeature.resolveOwner()) { otherEnd = (EObject) resolve(otherEnd); } if (otherEnd != null) { OppositeFeatureAdapter adapter = getOppositeFeatureAdapter(otherEnd); adapter.getOppositeFeatureMap().put( oppositeFeature, null); } break; } } } } } /** * Resolves the given proxy object. * * @param object * a proxy object to resolve * @return the resolved object */ protected Object resolve(Object object) { if (object instanceof InternalEObject && ((InternalEObject) object).eIsProxy()) { return ((InternalEObject) getTarget()).eResolveProxy((InternalEObject) object); } return object; } @Override public void setTarget(Notifier newTarget) { if(target != null && newTarget != null) { throw new IllegalArgumentException("An OppositeFeatureAdapter cannot be associated with 2 targets at the same time."); } super.setTarget(newTarget); } private Map<OppositeFeature, Object> createOppositeFeatureMap() { Map<OppositeFeature, Object> map = new HashMap<OppositeFeature, Object>(); for (Map.Entry<Class<?>, Set<OppositeFeature>> entry : classOppositeFeaturesMap.entrySet()) { Class<?> cls = entry.getKey(); if (cls.isInstance(getTarget())) { for (OppositeFeature oppositeFeature : entry.getValue()) { map.put(oppositeFeature, null); } } } if (map.isEmpty()) { hasOppositeFeature = false; return Collections.emptyMap(); } return map; } /** * Gets the opposite feature map. * * @return a map containing the opposite features mapped to their values */ protected Map<OppositeFeature, Object> getOppositeFeatureMap() { if (oppositeFeatureMap == null && hasOppositeFeature) { oppositeFeatureMap = createOppositeFeatureMap(); } if(oppositeFeatureMap == null) { return Collections.emptyMap(); } return oppositeFeatureMap; } protected List<?> createOppositeFeatureValueList(EObject eObject, OppositeFeature oppositeFeature) { return new OppositeFeatureResolvingEList(eObject, oppositeFeature); } protected void oppositeAdd(OppositeFeature oppositeFeature, Object object) { List list = (List) getOppositeFeatureMap().get(oppositeFeature); if (list == null) { list = createOppositeFeatureValueList((EObject) getTarget(), oppositeFeature); getOppositeFeatureMap().put(oppositeFeature, list); } if (!list.contains(object)) { list.add(object); } } protected void oppositeRemove(OppositeFeature oppositeFeature, Object object) { List list = (List) getOppositeFeatureMap().get(oppositeFeature); if (list == null) { list = createOppositeFeatureValueList((EObject) getTarget(), oppositeFeature); getOppositeFeatureMap().put(oppositeFeature, list); } list.remove(object); } private void replace(EStructuralFeature feature, Object oldValue, EObject newValue) { if (newValue != null && !newValue.eIsProxy() && newValue != oldValue) { EObject eObject = (EObject) getTarget(); boolean notify = eObject.eDeliver(); try { eObject.eSetDeliver(false); EcoreUtil.replace(eObject, feature, oldValue, newValue); } catch (Exception e) { if (DEBUG) { CommonPlugin.INSTANCE.log(e); e.printStackTrace(); System.out.println("OppositeFeatureAdapter.replace():"); //$NON-NLS-1$ System.out.println(" object: " + eObject); //$NON-NLS-1$ System.out.println(" feature: " + feature); //$NON-NLS-1$ System.out.println(" proxy: " + oldValue); //$NON-NLS-1$ System.out.println(" resolved: " + newValue); //$NON-NLS-1$ } } finally { eObject.eSetDeliver(notify); } } } public Collection<OppositeFeature> getOppositeFeatures() { return getOppositeFeatureMap().keySet(); } public Object getOppositeFeatureValue(OppositeFeature feature) { Object value = getOppositeFeatureMap().get(feature); // System.out.println("MultiResourceEObject.getOppositeFeatureValue():"); // System.out.println(" feature: " + feature); // System.out.println(" value: " + value); // System.out.println(" this: " + this); if (feature.isMany()) { if (value == null) { return Collections.EMPTY_LIST; } return ((OppositeFeatureResolvingEList) value) .getUnmodifiableList(); } else if (value instanceof EObject && ((EObject) value).eResource() == null) { getOppositeFeatureMap().put(feature, null); return null; } Object resolved = resolve(value); if (resolved != value) { getOppositeFeatureMap().put(feature, resolved); value = resolved; } return value; } } }