blob: 281977f4c4533a730f9eaca6df5dc59a9c1c1c2b [file] [log] [blame]
//------------------------------------------------------------------------------
// 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;
}
}
}