//------------------------------------------------------------------------------
// 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.impl;

import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.emf.common.CommonPlugin;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.NotificationChain;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.Resource.Internal;
import org.eclipse.emf.ecore.sdo.impl.EDataObjectImpl;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.util.InternalEList;
import org.eclipse.epf.uma.ecore.IModelObject;
import org.eclipse.epf.uma.ecore.IProxyResolutionListener;
import org.eclipse.epf.uma.ecore.IUmaResourceSet;
import org.eclipse.epf.uma.ecore.ResolveException;
import org.eclipse.epf.uma.ecore.util.OppositeFeature;
import org.eclipse.epf.uma.ecore.util.OppositeFeatureResolvingEList;

/**
 * The base class for all UMA model objects.
 * <p>
 * By default, EMF stores model objects related via a containment relationship
 * in a single XMI file. This class extends the default EMF persistence behavior
 * by providing a mechanism for the containing and contained objects to be
 * stored in separate XMI files. Contained objects are lazy loaded, when
 * required, similar to the proxy object behavior of EMF's standard resource
 * implementation.
 * 
 * @author Phong Nguyen Le
 * @since 1.0
 */
public class MultiResourceEObject extends EDataObjectImpl implements
		IModelObject {

	private static final long serialVersionUID = 3258126947153097273L;

	private static final Map featureToDefaultValueMap = new HashMap();
	
	/**
	 * Sets the default value for a feature.
	 * <p>
	 * Note: This method is designed for migration use only. Do not call this
	 * method to override the default value of a feature since it will slow down
	 * the loading of a method library.
	 * 
	 * @param feature
	 *            a feature
	 * @param defaultValue
	 *            the default value for the feature
	 */
	public static final void setDefaultValue(EStructuralFeature feature,
			Object defaultValue) {
		featureToDefaultValueMap.put(feature, defaultValue);
	}
	
	/**
	 * Removes the default value for a feature.
	 * <p>
	 * Note: This method is designed for migration use only. Do not call this
	 * method to override the default value of a feature since it will slow down
	 * the loading of a method library.
	 * 
	 * @param feature
	 *            a feature
	 */
	public static final void removeDefaultValue(EStructuralFeature feature) {
		featureToDefaultValueMap.remove(feature);
	}
	
	/**
	 * A map of entries of OppositeFeature / OppositeFeature's value
	 */
	private Map oppositeFeatureMap;

	private boolean hasOppositeFeature = true;

	private Boolean notifyOpposite = null;

	private boolean valid = true;

	private List featuresWithOverridenDefaultValue;

	/**
	 * Checks the validity of this object.
	 * 
	 * @return <code>true</code> if this object is valid
	 */
	public boolean isValid() {
		return valid;
	}

	private void oppositeAdd(OppositeFeature oppositeFeature, Object object) {
		List list = (List) getOppositeFeatureMap().get(oppositeFeature);
		if (list == null) {
			list = new OppositeFeatureResolvingEList(this, oppositeFeature);
			getOppositeFeatureMap().put(oppositeFeature, list);
		}
		if (!list.contains(object)) {
			list.add(object);
		}
	}

	private void oppositeRemove(OppositeFeature oppositeFeature, Object object) {
		List list = (List) getOppositeFeatureMap().get(oppositeFeature);
		if (list == null) {
			list = new OppositeFeatureResolvingEList(this, oppositeFeature);
			getOppositeFeatureMap().put(oppositeFeature, list);
		}
		list.remove(object);
	}

	/**
	 * Resolves the given proxy object.
	 * 
	 * @param object
	 *            a proxy object to resolve
	 * @return the resolved object
	 */
	public Object resolve(Object object) {
		if (object instanceof InternalEObject
				&& ((InternalEObject) object).eIsProxy()) {
			return eResolveProxy((InternalEObject) object);
		}
		return object;
	}

	private void replace(EStructuralFeature feature, Object oldValue,
			InternalEObject newValue) {
		if (newValue != null && !newValue.eIsProxy() && newValue != oldValue) {
			boolean notify = eDeliver();
			try {
				eSetDeliver(false);
				EcoreUtil.replace(this, feature, oldValue, newValue);
			} catch (Exception e) {
				CommonPlugin.INSTANCE.log(e);
				e.printStackTrace();
				System.out.println("MultiResourceEObject.replace():");
				System.out.println("  object: " + this);
				System.out.println("  feature: " + feature);
				System.out.println("  proxy: " + oldValue);
				System.out.println("  resolved: " + newValue);
			} finally {
				eSetDeliver(notify);
			}
		}

	}

	/**
	 * @see org.eclipse.emf.common.notify.impl.BasicNotifierImpl#eNotify(org.eclipse.emf.common.notify.Notification)
	 */
	public void eNotify(Notification msg) {
		/*
		 * if(!OppositeFeature.isDeliverOnResolve() && msg.getEventType() ==
		 * Notification.RESOLVE) { return; }
		 */

		if (msg.getEventType() == Notification.RESOLVE) {
			return;
		}
		
		Object f = msg.getFeature();
		if (f instanceof EStructuralFeature) {
			EStructuralFeature feature = (EStructuralFeature) f;
			OppositeFeature oppositeFeature = OppositeFeature
					.getOppositeFeature(feature);
			if (oppositeFeature != null) {
				MultiResourceEObject oldOtherEnd;
				MultiResourceEObject otherEnd;
				if (oppositeFeature.isMany()) {
					switch (msg.getEventType()) {
					case Notification.SET:
						oldOtherEnd = (MultiResourceEObject) msg.getOldValue();
						if (oppositeFeature.resolveOwner()) {
							oldOtherEnd = (MultiResourceEObject) resolve(oldOtherEnd);
						}
						if (oldOtherEnd != null) {
							oldOtherEnd.oppositeRemove(oppositeFeature, msg
									.getNotifier());
						}
					case Notification.ADD:
						otherEnd = (MultiResourceEObject) msg.getNewValue();
						if (oppositeFeature.resolveOwner()) {
							otherEnd = (MultiResourceEObject) resolve(otherEnd);
							replace(feature, msg.getNewValue(), otherEnd);
						}
						if (otherEnd != null) {
							otherEnd.oppositeAdd(oppositeFeature, msg
									.getNotifier());
						}
						break;
					case Notification.ADD_MANY:
						for (Iterator iter = ((Collection) msg.getNewValue())
								.iterator(); iter.hasNext();) {
							Object obj = iter.next();
							otherEnd = (MultiResourceEObject) obj;
							if (oppositeFeature.resolveOwner()) {
								otherEnd = (MultiResourceEObject) resolve(otherEnd);
								replace(feature, obj, otherEnd);
							}
							otherEnd.oppositeAdd(oppositeFeature, msg
									.getNotifier());
						}
						break;
					case Notification.REMOVE:
						otherEnd = (MultiResourceEObject) msg.getOldValue();
						if (oppositeFeature.resolveOwner()) {
							otherEnd = (MultiResourceEObject) resolve(otherEnd);
						}
						if (otherEnd != null)
							otherEnd.oppositeRemove(oppositeFeature, msg
									.getNotifier());
						break;
					case Notification.REMOVE_MANY:
						for (Iterator iter = ((Collection) msg.getOldValue())
								.iterator(); iter.hasNext();) {
							otherEnd = (MultiResourceEObject) iter.next();
							if (oppositeFeature.resolveOwner()) {
								otherEnd = (MultiResourceEObject) resolve(otherEnd);
							}
							otherEnd.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 = (MultiResourceEObject) obj;
							if (oppositeFeature.resolveOwner()) {
								otherEnd = (MultiResourceEObject) resolve(otherEnd);
								replace(feature, obj, otherEnd);
							}
							if (otherEnd != null) {
								EObject oldValue = (EObject) otherEnd
										.getOppositeFeatureMap().get(
												oppositeFeature);
								if (oldValue != null) {
									// remove otherEnd from target feature of
									// oldValue
									((Collection) oldValue
											.eGet((EStructuralFeature) f))
											.remove(otherEnd);
								}
								otherEnd.getOppositeFeatureMap().put(
										oppositeFeature, msg.getNotifier());
							}
						}
						break;
					case Notification.REMOVE_MANY:
						for (Iterator iter = ((Collection) msg.getOldValue())
								.iterator(); iter.hasNext();) {
							otherEnd = (MultiResourceEObject) iter.next();
							if (oppositeFeature.resolveOwner()) {
								otherEnd = (MultiResourceEObject) resolve(otherEnd);
							}
							otherEnd.getOppositeFeatureMap().put(
									oppositeFeature, null);
						}
						break;
					case Notification.ADD:
						otherEnd = (MultiResourceEObject) msg.getNewValue();
						if (oppositeFeature.resolveOwner()) {
							otherEnd = (MultiResourceEObject) resolve(otherEnd);
							replace(feature, msg.getNewValue(), otherEnd);
						}
						if (otherEnd != null) {
							EObject oldValue = (EObject) otherEnd
									.getOppositeFeatureMap().get(
											oppositeFeature);
							if (oldValue != null) {
								// remove otherEnd from target feature of
								// oldValue
								((Collection) oldValue
										.eGet((EStructuralFeature) f))
										.remove(otherEnd);
							}
							otherEnd.getOppositeFeatureMap().put(
									oppositeFeature, msg.getNotifier());
						}
						break;
					case Notification.SET:
						otherEnd = (MultiResourceEObject) msg.getNewValue();
						if (oppositeFeature.resolveOwner()) {
							otherEnd = (MultiResourceEObject) resolve(otherEnd);
							replace(feature, msg.getNewValue(), otherEnd);
						}
						if (otherEnd != null) {
							EObject oldValue = (EObject) otherEnd
									.getOppositeFeatureMap().get(
											oppositeFeature);
							if (oldValue != null) {
								// set the target feature of oldValue to null
								oldValue.eSet((EStructuralFeature) f, null);
							}
							otherEnd.getOppositeFeatureMap().put(
									oppositeFeature, msg.getNotifier());
						}
						else {
							EStructuralFeature targetFeature = (EStructuralFeature) f;
							if(!targetFeature.isMany()) {
								oldOtherEnd = (MultiResourceEObject) msg.getOldValue();
								if(oldOtherEnd != null) {
									oldOtherEnd.getOppositeFeatureMap().put(oppositeFeature, null);
								}
							}
						}
						break;
					case Notification.REMOVE:
						// case Notification.UNSET:
						otherEnd = (MultiResourceEObject) msg.getOldValue();
						if (oppositeFeature.resolveOwner()) {
							otherEnd = (MultiResourceEObject) resolve(otherEnd);
						}
						if (otherEnd != null)
							otherEnd.getOppositeFeatureMap().put(
									oppositeFeature, null);
						break;

					}
				}
			}
		}

		super.eNotify(msg);
	}

	/**
	 * @see org.eclipse.emf.common.notify.impl.BasicNotifierImpl#eNotificationRequired()
	 */
	public boolean eNotificationRequired() {
		if (!eDeliver())
			return false;

		Resource resource = eResource();
		if (resource != null && !resource.eDeliver())
			return false;

		if (notifyOpposite == null) {
			if (OppositeFeature.featureOppositeFeatureMap.isEmpty()) {
				notifyOpposite = Boolean.FALSE;
			} else {
				Collection features = OppositeFeature.featureOppositeFeatureMap
						.keySet();
				for (Iterator iter = eClass().getEAllReferences().iterator(); iter
						.hasNext();) {
					if (features.contains(iter.next())) {
						notifyOpposite = Boolean.TRUE;
						break;
					}
				}
				if (notifyOpposite == null) {
					notifyOpposite = Boolean.FALSE;
				}
			}
		}
		if (notifyOpposite.booleanValue())
			return true;
		return super.eNotificationRequired();
	}

	/*
	 * private void removeChildResources() { List children = eContents(); int
	 * size = children.size(); for (int i = 0; i < size; i++) {
	 * MultiResourceEObject child = (MultiResourceEObject) children.get(i);
	 * Resource resource = child.eResource(); if (resource != null && resource !=
	 * eResource()) { child.removeChildResources(); } ((InternalEList)
	 * resource.getContents()).basicRemove(this, null);
	 * child.eProperties().setEResource(null); } }
	*/

	/**
	 * @see org.eclipse.emf.ecore.InternalEObject#eBasicSetContainer(org.eclipse.emf.ecore.InternalEObject,
	 *      int, org.eclipse.emf.common.notify.NotificationChain)
	 */
	public NotificationChain eBasicSetContainer(InternalEObject newContainer,
			int newContainerFeatureID, NotificationChain msgs) {
		Resource.Internal oldResource = this.eDirectResource();
		Resource.Internal newResource = newContainer == null ? null
				: newContainer.eInternalResource();
		// if (oldResource != newResource && oldResource != null) {
		// removeChildResources();
		// }

		int oldIndex = -1;
		int oldSize = -1;
		if (oldResource != null) {
			oldIndex = oldResource.getContents().indexOf(this);
			oldSize = oldResource.getContents().size();
		}

		NotificationChain messages = super.eBasicSetContainer(newContainer,
				newContainerFeatureID, msgs);
		if (oldResource != newResource && oldResource != null) {
			// remove any newly added ModificationTrackingAdapters from this
			// object's adapter list
			//
			if (newResource != null)
				newResource.detached(this);

			// Override the default semantic: MultiResourceEObject is allowed to
			// be owned by both container and resource.
			// Add this object back to the old resource.
			//
			BasicEList contents = ((BasicEList) oldResource.getContents());
			if (oldSize != contents.size()) {
				// this object has been removed from the resource's contents
				// add it back
				//
				if (contents.isEmpty()) {
					// this will flag resource as loaded
					//
					contents.clear();
					contents.setData(1, new Object[] { this });
				} else {
					Object[] data = contents.toArray();
					Object[] newData = new Object[data.length + 1];
					if (oldIndex > 0) {
						System.arraycopy(data, 0, newData, 0, oldIndex);
					}
					newData[oldIndex] = this;
					if (oldIndex < data.length) {
						System.arraycopy(data, oldIndex, newData, oldIndex + 1,
								data.length - oldIndex);
					}
					contents.setData(newData.length, newData);
				}
			}
			eSetResource(oldResource);
			// don't have to re-attach this object to oldResource since it was
			// not dettached in super method
			//
			// oldResource.attached(this);

			// if(newResource != null) newResource.attached(this);
		}
		return messages;
	}

	/**
	 * Sets the container and container feature ID for this object only if it is
	 * not contained by any container.
	 * 
	 * @see org.eclipse.emf.ecore.impl.BasicEObjectImpl#eBasicSetContainer(org.eclipse.emf.ecore.InternalEObject,
	 *      int)
	 */
	public void eSetContainer(InternalEObject newContainer,
			int newContainerFeatureID) {
		if (eContainer() != null)
			return;
		super.eBasicSetContainer(newContainer, newContainerFeatureID);
	}

	/**
	 * @see org.eclipse.emf.ecore.impl.BasicEObjectImpl#eBasicSetContainer(org.eclipse.emf.ecore.InternalEObject,
	 *      int)
	 */
	public void eBasicSetContainer(InternalEObject newContainer,
			int newContainerFeatureID) {
		super.eBasicSetContainer(newContainer, newContainerFeatureID);
	}

	/**
	 * Sets the containing resource for this object.
	 * 
	 * @param res
	 *            a resource
	 */
	public void eSetResource(Resource.Internal res) {
		eProperties().setEResource(res);
	}

	/**
	 * @see org.eclipse.emf.ecore.impl.BasicEObjectImpl#eDirectResource()
	 */
	public Internal eDirectResource() {
		return super.eDirectResource();
	}

	/**
	 * Returns the resolved object represented by proxy. Proxy chains are
	 * followed. If resourceSet is null, the global package registry is
	 * consulted to obtain a package registered against the proxy URI, less its
	 * fragment, in the same manner as the default resource set implementation's
	 * fallback behaviour.
	 * 
	 * @param proxy
	 *            the proxy to be resolved.
	 * @param resourceSet
	 *            the resource set in which to resolve.
	 * @return the resolved object, or the proxy if unable to resolve.
	 * @see org.eclipse.emf.ecore.util.EcoreUtil#resolve(org.eclipse.emf.ecore.EObject,
	 *      org.eclipse.emf.ecore.resource.ResourceSet)
	 */
	private EObject resolveProxy(EObject proxy) {
		ResourceSet resourceSet = eResource() != null ? eResource()
				.getResourceSet() : null;

		URI proxyURI = ((InternalEObject) proxy).eProxyURI();
		if (proxyURI != null) {
			try {
				EObject resolvedObject = null;

				if (resourceSet != null) {
					if (resourceSet instanceof IUmaResourceSet) {
						resolvedObject = ((IUmaResourceSet) resourceSet)
								.getEObject(this, proxyURI, true);
					} else {
						resolvedObject = resourceSet.getEObject(proxyURI, true);
					}
				} else {
					EPackage ePackage = EPackage.Registry.INSTANCE
							.getEPackage(proxyURI.trimFragment().toString());
					if (ePackage != null) {
						Resource resource = ePackage.eResource();
						if (resource != null) {
							resolvedObject = resource.getEObject(proxyURI
									.fragment().toString());
						}
					}
				}

				if (resolvedObject != null) {
					if(resolvedObject != proxy) {
//						return resolveProxy(resolvedObject);
						return resolvedObject;
					}
				} else {
					if (resourceSet instanceof IProxyResolutionListener) {
						((IProxyResolutionListener) resourceSet)
								.notifyException(new ResolveException(proxy,
										(String) null, this));
					}
				}
			} catch (RuntimeException exception) {
				if (resourceSet instanceof IProxyResolutionListener) {
					((IProxyResolutionListener) resourceSet)
							.notifyException(new ResolveException(proxy,
									exception, this));
				}
			}
		}
		return proxy;
	}

	/**
	 * @see org.eclipse.emf.ecore.InternalEObject#eResolveProxy(org.eclipse.emf.ecore.InternalEObject)
	 */
	public EObject eResolveProxy(InternalEObject proxy) {
		EObject container = proxy.eContainer();
		int featureID = proxy.eContainerFeatureID();
		EObject result = null;

		result = resolveProxy(proxy);

		if (result != null && result instanceof MultiResourceEObject) {
			if (proxy.eIsProxy() && result == proxy) {
				// proxy could not be resolved
				//
				((MultiResourceEObject) result).valid = false;
			} else {
				((MultiResourceEObject) result).valid = true;

				if (container != null) {
					((MultiResourceEObject) result).eSetContainer(
							(InternalEObject) container, featureID);
				}

				// merge the opposite feature map
				//
				if (result != proxy) {
					if(proxy instanceof MultiResourceEObject) {
						MultiResourceEObject myObj = (MultiResourceEObject) proxy;

						if (myObj.oppositeFeatureMap != null
								&& !myObj.oppositeFeatureMap.isEmpty()) {
							Map newMap = ((MultiResourceEObject) result)
							.getOppositeFeatureMap();
							for (Iterator iter = myObj.getOppositeFeatureMap()
									.entrySet().iterator(); iter.hasNext();) {
								Map.Entry entry = (Map.Entry) iter.next();
								if (entry.getValue() != null) {
									OppositeFeature oppositeFeature = (OppositeFeature) entry
									.getKey();
									if (oppositeFeature.isMany()) {
										List values = (List) newMap
										.get(oppositeFeature);
										if (values == null) {
											newMap.put(oppositeFeature, entry
													.getValue());
										} else {
											values.addAll((Collection) entry
													.getValue());
										}
									} else {
										newMap.put(oppositeFeature, entry
												.getValue());
									}
								}
							}
							myObj.getOppositeFeatureMap().clear();
						}
					}
					
					ResourceSet resourceSet = eResource() != null ? eResource()
							.getResourceSet() : null;
					if (resourceSet instanceof IProxyResolutionListener) {
						((IProxyResolutionListener) resourceSet).proxyResolved(
								proxy, result);
					}
				}
			}

		}

		return result;
	}

	private Map createOppositeFeatureMap() {
		Map map = new HashMap();
		for (Iterator iter = OppositeFeature.classOppositeFeaturesMap
				.entrySet().iterator(); iter.hasNext();) {
			Map.Entry entry = (Map.Entry) iter.next();
			Class cls = (Class) entry.getKey();
			if (cls.isInstance(this)) {
				for (Iterator iterator = ((Collection) entry.getValue())
						.iterator(); iterator.hasNext();) {
					map.put(iterator.next(), null);
				}
			}
		}
		if (map.isEmpty()) {
			hasOppositeFeature = false;
			return Collections.EMPTY_MAP;
		}
		return map;
	}

	/**
	 * Gets the opposite feature map.
	 * 
	 * @return a map containing the opposite features mapped to their values
	 */
	public Map getOppositeFeatureMap() {
		if (oppositeFeatureMap == null && hasOppositeFeature) {
			oppositeFeatureMap = createOppositeFeatureMap();
		}
		if(oppositeFeatureMap == null) {
			return Collections.EMPTY_MAP;
		}
		return oppositeFeatureMap;
	}

	/**
	 * Gets the opposite feature map.
	 * 
	 * @return a map containing the opposite features mapped to their values or
	 *         <code>null</code> if this object does not have any opposite
	 *         feature or the map has not been created
	 */
	public Map basicGetOppositeFeatureMap() {
		return oppositeFeatureMap;
	}

	/**
	 * Gets the value of an opposite feature.
	 * 
	 * @param feature
	 *            an opposite feature
	 * @return the value for the opposite feature
	 */
	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;
		}

		if (value instanceof InternalEObject
				&& ((InternalEObject) value).eIsProxy()) {
			EObject resolved = eResolveProxy((InternalEObject) value);
			if (resolved != value) {
				getOppositeFeatureMap().put(feature, resolved);
				value = resolved;
			}
		}
		return value;
	}

	/**
	 * Gets the resource at a given location.
	 * 
	 * @param location
	 *            the resource location
	 * @return a resource in the workspace
	 */
	public static IResource getResourceForLocation(String location) {
		IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
		IPath path = new Path(location);
		IResource resource;
		File file =  new File(location);
		if (file.isFile()) {
			resource = workspaceRoot.getFileForLocation(path);
			if(resource == null) {
				IResource parentResource = getResourceForLocation(file
						.getParent());
				if(parentResource != null) {
					try {
						parentResource.refreshLocal(IResource.DEPTH_ONE, null);
					} catch (CoreException e) {
						CommonPlugin.INSTANCE.log(e);
					}
					resource = workspaceRoot.getFileForLocation(path);
				}
			}
		} else {
			resource = workspaceRoot.getContainerForLocation(path);
		}
		return resource;
	}

//	/* (non-Javadoc)
//	 * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
//	 */
//	public Object getAdapter(Class adapter) {
//		if(adapter == IResource.class) {
//			Resource resource = eResource();
//			if(resource != null) {
//				return getResourceForLocation(resource.getURI().toFileString());
//			}
//		}
//		return null;
//	}
	
	/**
	 * Removes all opposite features registered with the system.
	 */
	public void removeFromAllOppositeFeatures() {
		// 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 = eClass().getEAllReferences().iterator(); iter
				.hasNext();) {
			EReference ref = (EReference) iter.next();
			OppositeFeature oppositeFeature = OppositeFeature.getOppositeFeature(ref);
			if(oppositeFeature != null) {
				if(ref.isMany()) {
					List list = (List) 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) eGet(ref, false);
					if(e != null && !e.eIsProxy()) {
						eSet(ref, null);
					}
				}
			}
		}

	}

	/**
	 * @see org.eclipse.epf.uma.ecore.IModelObject#getOppositeFeatures()
	 */
	public Collection getOppositeFeatures() {
		return getOppositeFeatureMap().keySet();
	}

	/**
	 * @see org.eclipse.epf.uma.ecore.IModelObject#getDefaultValue(org.eclipse.emf.ecore.EStructuralFeature)
	 */
	public Object getDefaultValue(EStructuralFeature feature) {
		return featureToDefaultValueMap.get(feature);
	}
	
	protected Collection getFeaturesWithOverridenDefaultValue() {
		if (featureToDefaultValueMap.isEmpty())
			return Collections.EMPTY_LIST;
		
		if(featuresWithOverridenDefaultValue == null) {
			featuresWithOverridenDefaultValue = new ArrayList();
			for (Iterator iter = featureToDefaultValueMap.keySet().iterator(); iter
					.hasNext();) {
				EStructuralFeature feature = (EStructuralFeature) iter.next();
				if(eClass().getFeatureID(feature) != -1) {
					featuresWithOverridenDefaultValue.add(feature);
				}
			}
			if(featuresWithOverridenDefaultValue.isEmpty()) {
				featuresWithOverridenDefaultValue = Collections.EMPTY_LIST;
			}
		}
		return featuresWithOverridenDefaultValue;
	}
	
	/**
	 * Unregisters a feature whose default value has been overriden.
	 * 
	 * @param feature
	 *            a feature
	 */
	public void removeFeatureWithOverridenDefaultValue(
			EStructuralFeature feature) {
		if(featuresWithOverridenDefaultValue != null) {
			try {
				featuresWithOverridenDefaultValue.remove(feature);
			} catch (Exception e) {
				//
			}
		}
	}

	/**
	 * Check all features if 
	 *
	 */
	protected void reassignDefaultValues() {
		Collection features = getFeaturesWithOverridenDefaultValue();
		if(features.isEmpty()) {
			return;
		}
		
		for (Iterator iter = features.iterator(); iter.hasNext();) {
			EStructuralFeature feature = (EStructuralFeature) iter.next();
			eSet(feature, getDefaultValue(feature));
		}
	}
	
	protected EStructuralFeature getFeatureWithOverridenDefaultValue(
			int featureID) {
		Collection features = getFeaturesWithOverridenDefaultValue();
		if(!features.isEmpty()) {
			EStructuralFeature feature = eClass().getEStructuralFeature(
					featureID);
			if(feature != null && features.contains(feature)) {
				return feature;
			}
		}
		return null;
	}
	
	protected boolean isFeatureWithOverridenDefaultValueSet(
			EStructuralFeature feature) {
		Object defaultValue = getDefaultValue(feature);
		Object value = eGet(feature, false);
		if(feature.isMany()) {
			return value != null && !((Collection) value).isEmpty()
					&& value != defaultValue;
		} else {
			return defaultValue == null ? value != null : !defaultValue
					.equals(value);
		}
		}
	}