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

import java.io.File;
import java.io.FileFilter;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
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.core.resources.IResource;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
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.impl.NotificationImpl;
import org.eclipse.emf.common.util.AbstractTreeIterator;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.common.util.UniqueEList;
import org.eclipse.emf.common.util.WrappedException;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EObject;
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.URIConverter;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.util.InternalEList;
import org.eclipse.emf.ecore.xmi.XMLResource;
import org.eclipse.emf.ecore.xmi.impl.XMLInfoImpl;
import org.eclipse.emf.ecore.xmi.impl.XMLMapImpl;
import org.eclipse.epf.common.utils.FileUtil;
import org.eclipse.epf.persistence.refresh.IRefreshEvent;
import org.eclipse.epf.persistence.refresh.IRefreshListener;
import org.eclipse.epf.persistence.refresh.RefreshJob;
import org.eclipse.epf.persistence.refresh.internal.RefreshEvent;
import org.eclipse.epf.persistence.util.PersistenceResources;
import org.eclipse.epf.persistence.util.UnresolvedProxyMarkerManager;
import org.eclipse.epf.resourcemanager.ResourceDescriptor;
import org.eclipse.epf.resourcemanager.ResourceManager;
import org.eclipse.epf.resourcemanager.ResourcemanagerFactory;
import org.eclipse.epf.uma.Activity;
import org.eclipse.epf.uma.BreakdownElement;
import org.eclipse.epf.uma.Descriptor;
import org.eclipse.epf.uma.Diagram;
import org.eclipse.epf.uma.MethodConfiguration;
import org.eclipse.epf.uma.MethodElement;
import org.eclipse.epf.uma.MethodLibrary;
import org.eclipse.epf.uma.MethodPlugin;
import org.eclipse.epf.uma.Process;
import org.eclipse.epf.uma.ProcessComponent;
import org.eclipse.epf.uma.ProcessElement;
import org.eclipse.epf.uma.ProcessPackage;
import org.eclipse.epf.uma.RoleDescriptor;
import org.eclipse.epf.uma.SemanticModelBridge;
import org.eclipse.epf.uma.TeamProfile;
import org.eclipse.epf.uma.UMASemanticModelBridge;
import org.eclipse.epf.uma.UmaFactory;
import org.eclipse.epf.uma.UmaPackage;
import org.eclipse.epf.uma.WorkOrder;
import org.eclipse.epf.uma.WorkProductDescriptor;
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.EventTypes;
import org.eclipse.epf.uma.ecore.util.OppositeFeature;
import org.eclipse.epf.uma.util.AssociationHelper;
import org.eclipse.epf.uma.util.ContentDescriptionFactory;
import org.eclipse.epf.uma.util.UmaUtil;
import org.eclipse.osgi.util.NLS;

import com.ibm.icu.text.DateFormat;
import com.ibm.icu.text.SimpleDateFormat;
import com.ibm.icu.util.Calendar;

/**
 * @author Phong Nguyen Le
 * @since 1.0
 */
public class MultiFileResourceSetImpl extends ResourceSetImpl implements
		IProxyResolutionListener, IUmaResourceSet {

	private static final String[] DEFAULT_DELIVERY_PROCESS_PATH = { "DeliveryProcesses" }; //$NON-NLS-1$

	private static final String[] DEFAULT_CAPABILITY_PATTERN_PATH = {
			"Content", "CapabilityPatterns" }; //$NON-NLS-1$ //$NON-NLS-2$

	private static final String[] DEFAULT_PROCESS_CONTRIBUTION_PATH = { "ProcessContributions" }; //$NON-NLS-1$		

	public static final String[][] PROCESS_PACKAGE_PATHS = {
			DEFAULT_DELIVERY_PROCESS_PATH, DEFAULT_CAPABILITY_PATTERN_PATH,
			DEFAULT_PROCESS_CONTRIBUTION_PATH };

	public static boolean REPORT_ERROR = true;

	static final Map DEFAULT_SAVE_OPTIONS = new HashMap();

	static final Set DEFAULT_SAVE_SEPARATELY_CLASS_SET = new HashSet();

	static {
		DEFAULT_SAVE_SEPARATELY_CLASS_SET.add(UmaPackage.eINSTANCE
				.getMethodPlugin());
		DEFAULT_SAVE_SEPARATELY_CLASS_SET.add(UmaPackage.eINSTANCE
				.getProcessComponent());
		DEFAULT_SAVE_SEPARATELY_CLASS_SET.add(UmaPackage.eINSTANCE
				.getContentDescription());
		// eClasses.add(ResourcemanagerPackage.eINSTANCE.getResourceManager());
		DEFAULT_SAVE_SEPARATELY_CLASS_SET.add(UmaPackage.eINSTANCE
				.getMethodConfiguration());

		XMLResource.XMLMap xmlMap = createSaveXMLMap();

		HashSet saveTogether = new HashSet();
		saveTogether.add(UmaPackage.eINSTANCE.getBreakdownElementDescription());

		DEFAULT_SAVE_OPTIONS.put(
				MultiFileXMISaveImpl.SAVE_SEPARATELY_CLASS_SET,
				DEFAULT_SAVE_SEPARATELY_CLASS_SET);
		DEFAULT_SAVE_OPTIONS.put(MultiFileXMISaveImpl.SAVE_ALL, "false"); //$NON-NLS-1$
		DEFAULT_SAVE_OPTIONS.put(MultiFileXMISaveImpl.SAVE_TOGETHER_CLASS_SET,
				saveTogether);
		DEFAULT_SAVE_OPTIONS.put(MultiFileXMISaveImpl.REFRESH_NEW_RESOURCE,
				"true"); //$NON-NLS-1$
		DEFAULT_SAVE_OPTIONS.put(MultiFileXMISaveImpl.CHECK_MODIFY, "true"); //$NON-NLS-1$
		DEFAULT_SAVE_OPTIONS.put(XMLResource.OPTION_ENCODING, "UTF-8"); //$NON-NLS-1$
		DEFAULT_SAVE_OPTIONS.put(XMLResource.OPTION_PROCESS_DANGLING_HREF,
				XMLResource.OPTION_PROCESS_DANGLING_HREF_DISCARD);
		DEFAULT_SAVE_OPTIONS.put(XMLResource.OPTION_XML_MAP, xmlMap);

		AssociationHelper.init();
	}

	public static final String RESMGR_XMI = "resmgr.xmi"; //$NON-NLS-1$

	private HashMap defaultSaveOptions;

	private HashMap guidToMethodElementMap;

	private boolean loading;

	private boolean loadingResourceManagerTree;

	private Map URIToTempURIMap;

	private UnresolvedProxyMarkerManager markerMananger;

	private UniqueEList refreshListeners;

	private IURIProvider uriProvider;

	private Path configFolderPath;

	private Path libFolderPath;

	private static XMLResource.XMLMap createLoadXMLMap() {
		XMLResource.XMLMap xmlMap = new XMLMapImpl();
		Set excludedAttributes = new HashSet();
		excludedAttributes.add(UmaPackage.eINSTANCE.getMethodElement_Guid());
		excludedAttributes.add(UmaPackage.eINSTANCE.getNamedElement_Name());

		for (Iterator iter = UmaPackage.eINSTANCE.getEClassifiers().iterator(); iter
				.hasNext();) {
			EClassifier eClassifier = (EClassifier) iter.next();
			if (eClassifier instanceof EClass) {
				EClass eClass = (EClass) eClassifier;

				for (Iterator iterator = eClass.getEAttributes().iterator(); iterator
						.hasNext();) {
					EAttribute attrib = (EAttribute) iterator.next();
					if (!excludedAttributes.contains(attrib)) {
						XMLResource.XMLInfo xmlInfo = new XMLInfoImpl();
						xmlInfo.setName(attrib.getName());
						xmlInfo
								.setXMLRepresentation(XMLResource.XMLInfo.ELEMENT);
						xmlMap.add(attrib, xmlInfo);
					}
				}
			}
		}

		return xmlMap;
	}

	private static boolean saveAttributeAsElement(EClass eClass,
			Collection selectedEClasses) {
		for (Iterator iter = selectedEClasses.iterator(); iter.hasNext();) {
			EClass base = (EClass) iter.next();
			if (base.isSuperTypeOf(eClass)) {
				return true;
			}
		}
		return false;
	}

	private static XMLResource.XMLMap createSaveXMLMap() {
		XMLResource.XMLMap xmlMap = new XMLMapImpl();
		Set excludedAttributes = new HashSet();
		excludedAttributes.add(UmaPackage.eINSTANCE.getMethodElement_Guid());
		excludedAttributes.add(UmaPackage.eINSTANCE.getNamedElement_Name());

		// select only ContentDescription and Section
		Set selectedEClasses = new HashSet();
		selectedEClasses.add(UmaPackage.eINSTANCE.getContentDescription());
		selectedEClasses.add(UmaPackage.eINSTANCE.getSection());

		for (Iterator iter = UmaPackage.eINSTANCE.getEClassifiers().iterator(); iter
				.hasNext();) {
			EClassifier eClassifier = (EClassifier) iter.next();
			if (eClassifier instanceof EClass) {
				EClass eClass = (EClass) eClassifier;

				if (saveAttributeAsElement(eClass, selectedEClasses)) {
					for (Iterator iterator = eClass.getEAttributes().iterator(); iterator
							.hasNext();) {
						EAttribute attrib = (EAttribute) iterator.next();
						if (!excludedAttributes.contains(attrib)) {
							XMLResource.XMLInfo xmlInfo = new XMLInfoImpl();
							xmlInfo.setName(attrib.getName());
							xmlInfo
									.setXMLRepresentation(XMLResource.XMLInfo.ELEMENT);
							xmlMap.add(attrib, xmlInfo);
						}
					}
				}
			}
		}

		return xmlMap;
	}

	public MultiFileResourceSetImpl() {
		super();
		markerMananger = new UnresolvedProxyMarkerManager(this);
	}

	public MultiFileResourceSetImpl(boolean reportError) {
		this();
		markerMananger.setEnabled(reportError);
	}

	public UnresolvedProxyMarkerManager getUnresolvedProxyMarkerManager() {
		return markerMananger;		
	}
	
	protected IURIProvider getURIProvider() {
		if(uriProvider == null) {
			uriProvider = new FilePathProvider();
		}
		return uriProvider;
	}
	
	private void handleResourceChange(IResource wsRes) {
		System.out
				.println("MultiFileResourceSetImpl.handleResourceChange(): " + wsRes.toString()); //$NON-NLS-1$

		MultiFileXMIResourceImpl resource = (MultiFileXMIResourceImpl) getResource(
				URI.createFileURI(wsRes.getLocation().toString()), false);
		if (resource != null) {
			try {
				ArrayList proxies = new ArrayList();
				resource.reload(proxies);

				// notify MAKE_PROXY event for all proxies
				//
				Notification msg = new NotificationImpl(EventTypes.MAKE_PROXY,
						false, true);
				for (Iterator iter = proxies.iterator(); iter.hasNext();) {
					EObject o = (EObject) iter.next();
					o.eNotify(msg);
				}

			} catch (IOException e) {
				CommonPlugin.INSTANCE.log(e);
			}
		}

	}

	public Map getLoadOptions() {
		Map options = super.getLoadOptions();
		options.put(XMLResource.OPTION_ENCODING, "UTF-8"); //$NON-NLS-1$
		options.put(XMLResource.OPTION_XML_MAP, createLoadXMLMap());
		return options;
	}

	public Map getDefaultSaveOptions() {
		if (defaultSaveOptions == null) {
			defaultSaveOptions = new HashMap();
			defaultSaveOptions.putAll(DEFAULT_SAVE_OPTIONS);
		}

		return defaultSaveOptions;
	}

	private Resource getMethodLibraryResource() {
		if (getResources().isEmpty())
			return null;
		return (Resource) getResources().get(0);
	}

	public MethodLibrary getMethodLibrary() {
		Resource resource = getMethodLibraryResource();
		return resource == null ? null : (MethodLibrary) MultiFileSaveUtil
				.getMethodElement(resource);
	}

	ResourceManager getRootResourceManager() {
		Resource resource = getMethodLibraryResource();
		return resource == null ? null : MultiFileSaveUtil
				.getResourceManager(resource);
	}

	/**
	 * @see org.eclipse.emf.ecore.resource.impl.ResourceSetImpl#getURIConverter()
	 */
	public URIConverter getURIConverter() {
		if (uriConverter == null) {
			uriConverter = new MultiFileURIConverter(this);
		}
		return uriConverter;
	}

	/**
	 * @param uri
	 * @return null if the resource with the given URI could not be found
	 */
	public Resource getResource(URI uri) {
		if (getURIConverter().normalize(uri) == null)
			return null;
		Resource res = null;
		try {
			res = super.getResource(uri, false);
			if (res == null) {
				Map map = getURIResourceMap();
				res = createResource(uri);

				try {
					demandLoadHelper(res);
				} catch (RuntimeException e) {
					// remove the resource from resource set if it could not be
					// loaded
					//
					getResources().remove(res);
					throw e;
				}

				if (map != null) {
					map.put(uri, res);
				}
			}
		} catch (WrappedException e) {
			if (e.exception() instanceof FileNotFoundException) {
				return null;
			}
			throw e;
		}
		return res;
	}

	/**
	 * @see org.eclipse.emf.ecore.resource.impl.ResourceSetImpl#getResource(org.eclipse.emf.common.util.URI,
	 *      boolean)
	 */
	public Resource getResource(URI uri, boolean loadOnDemand) {
		if (getURIConverter().normalize(uri) == null)
			return null;
		Resource res = null;
		try {
			res = super.getResource(uri, loadOnDemand);
		} catch (RuntimeException e) {
			String msg = null;
			if (e.getMessage() != null)
				msg = NLS.bind(
						PersistenceResources.loadResourceErrorWithReason_msg,
						(new Object[] { uri, e.getMessage() }));
			else
				msg = NLS.bind(PersistenceResources.loadResourceError_msg, uri);
			handleException(msg);
			CommonPlugin.INSTANCE.log(e);
			throw e;
		}
		return res;
	}

	public void addRefreshListener(IRefreshListener listener) {
		if (refreshListeners == null) {
			refreshListeners = new UniqueEList();
		}
		refreshListeners.add(listener);
	}

	public void removeRefreshListenter(IRefreshListener listener) {
		if (refreshListeners != null) {
			refreshListeners.remove(listener);
		}
	}

	/**
	 * Reloads the given resources
	 * 
	 * @param resources
	 * @return resources that had been reloaded
	 */
	public synchronized Collection reloadResources(Collection resources) {
		// save the ResourceDescriptor for the resources to create URI for their
		// proxies after unload
		//
		for (Iterator iter = resources.iterator(); iter.hasNext();) {
			MultiFileXMIResourceImpl resource = (MultiFileXMIResourceImpl) iter
					.next();
			ResourceDescriptor resDesc = MultiFileSaveUtil
					.getResourceDescriptor(resource);
			if (resDesc != null) {
				resource.setResourceDescriptor(resDesc);
			}
		}

		ArrayList reloadedResources = new ArrayList();
		HashSet proxies = new HashSet();
		ArrayList resourceList = new ArrayList(resources);

		Resource libResource = getMethodLibraryResource();
		boolean reloadAllConfigs = false;
		if (resourceList.remove(libResource)) {
			// MethodLibrary resource is to reload, it must be reloaded first
			//			
			MultiFileXMIResourceImpl resource = (MultiFileXMIResourceImpl) libResource;

			// clear markers for this resource
			//
			markerMananger.clearMarkers(resource);

			try {
				if (resource.reload(proxies)) {
					reloadedResources.add(resource);

					// reset the resource manager reference in URIConverter of
					// this resource set
					// since the resource manager is reloaded
					//
					MultiFileURIConverter uriConverter = (MultiFileURIConverter) getURIConverter();
					uriConverter.resMgr = null;
					
					reloadAllConfigs = true;
				}
			} catch (IOException e) {
				CommonPlugin.INSTANCE.log(e);
			}

		}
		// reload the resources
		//
		for (Iterator iter = resourceList.iterator(); iter.hasNext();) {
			MultiFileXMIResourceImpl resource = (MultiFileXMIResourceImpl) iter
					.next();

			// clear markers for this resource
			//
			markerMananger.clearMarkers(resource);

			try {
				if (resource.reload(proxies)) {
					reloadedResources.add(resource);
				}
			} catch (IOException e) {
				CommonPlugin.INSTANCE.log(e);
			}
		}

		if (!reloadedResources.isEmpty()) {
			// reinitialize the resource set
			//
			initialize(reloadAllConfigs);

			// notify the listeners
			//
			if (refreshListeners != null) {
				IRefreshEvent event = new RefreshEvent(reloadedResources,
						proxies);
				for (Iterator iter = new ArrayList(refreshListeners).iterator(); iter
						.hasNext();) {
					IRefreshListener listener = (IRefreshListener) iter.next();
					listener.notifyRefreshed(event);
				}
			}
		}

		return reloadedResources;
	}

	public void initialize(boolean loadConfigurations) {
		// load the ResourceManager tree and opposite features
		//
		loadResourceManagerTree();

		if(loadConfigurations) {
			loadConfigurations();
		}

		loadOppositeFeatures();

		// reset the resource manager reference in URIConverter of this resource
		// set
		//
		MultiFileURIConverter uriConverter = (MultiFileURIConverter) getURIConverter();
		uriConverter.resMgr = null;
	}
	
	/**
	 * Checks if the given workspace resource represents a library resource
	 * 
	 * @param wsResource
	 * @return
	 */
	public boolean isLibraryResource(IResource wsResource) {
		return libFolderPath != null && libFolderPath.isPrefixOf(wsResource.getLocation());
	}
	
	/**
	 * Loads new resources specified by the given collection of {@link IResource work space resources}
	 * into this resource set
	 * 
	 * @param wsResources collection of {@link IResource} objects
	 */
	public void loadNewResources(Collection wsResources) {
		ArrayList newConfigFiles = new ArrayList();
		for (Iterator iter = wsResources.iterator(); iter.hasNext();) {
			Object wsResource = (Object) iter.next();
			if(wsResource instanceof IResource) {
				IResource wsRes = (IResource) wsResource;
				IPath loc = wsRes.getLocation();
				String configFile;
				if(MultiFileSaveUtil.DEFAULT_FILE_EXTENSION.equalsIgnoreCase("." + loc.getFileExtension()) //$NON-NLS-1$
					&& configFolderPath.isPrefixOf(loc)
						&& !FileManager.getInstance().isTeamPrivate(configFile = loc.toString())) {
					newConfigFiles.add(configFile);
				}
			}
		}
		
		// load new configurations
		//
		for (Iterator iter = newConfigFiles.iterator(); iter.hasNext();) {
			String configFile = (String) iter.next();
			loadConfiguration(new File(configFile));
		}
	}

	/**
	 * Special handling for MethodConfiguration since MethodLibrary will no
	 * longer keeps the references to MethodConfigurations and their files. Any
	 * XMI file under folder ./configurations and its subfolders will be treated
	 * as a configuration file. They will be loaded and added to MethodLibrary
	 * as its configurations.
	 */
	private void loadConfigurations() {
		MethodLibrary lib = getMethodLibrary();
		File configDir = new File(new File(lib.eResource().getURI()
				.toFileString()).getParent(),
				MultiFileSaveUtil.METHOD_CONFIGURATION_FOLDER_NAME);
		Iterator dirIter = new AbstractTreeIterator(configDir, false) {
			/**
			 * Comment for <code>serialVersionUID</code>
			 */
			private static final long serialVersionUID = 2758436193000640907L;

			FileFilter filter = new FileFilter() {

				public boolean accept(File pathname) {
					return !FileManager.getInstance().isTeamPrivate(pathname.getAbsolutePath()) &&
							(pathname.isDirectory()
							|| pathname.getName().endsWith(
									MultiFileSaveUtil.DEFAULT_FILE_EXTENSION));
				}

			};

			protected Iterator getChildren(Object object) {
				File[] files = ((File) object).listFiles(filter);
				if (files != null && files.length > 0) {
					return Arrays.asList(files).iterator();
				}
				return Collections.EMPTY_LIST.iterator();
			}

		};
		HashSet configFiles = new HashSet();
		while (dirIter.hasNext()) {
			File configFile = (File) dirIter.next();
			if(configFile.isFile()) {
				configFiles.add(configFile);
			}
		}

		for (Iterator iter = lib.getPredefinedConfigurations().iterator(); iter
				.hasNext();) {
			MethodConfiguration config = (MethodConfiguration) iter.next();
			File file = new File(config.eResource().getURI().toFileString());
			if (configFiles.contains(file)) {
				configFiles.remove(file);
			}
		}

		for (Iterator iter = configFiles.iterator(); iter.hasNext();) {
			File file = (File) iter.next();
			loadConfiguration(file);
		}
	}
	
	private void loadConfiguration(File configFile) {
		try {
			MethodLibrary lib = getMethodLibrary();
			URI uri = URI.createFileURI(configFile.getCanonicalPath());
			Resource resource = super.getResource(uri, true);
			if (resource != null) {
				MethodElement e = MultiFileSaveUtil
						.getMethodElement(resource);
				if (e instanceof MethodConfiguration) {
					lib.getPredefinedConfigurations().add(e);
				} else {
					// TODO: must log error into problem view
				}
			}
		} catch (Exception e) {
			// TODO: must log error into problem view
			CommonPlugin.INSTANCE.log(e);
		}
	}

	/**
	 * @param string
	 */
	protected void handleException(Object err) {
		CommonPlugin.INSTANCE.log(err);
		if (err instanceof Exception) {
			((Exception) err).printStackTrace();
		} else {
			System.err.println(err.toString());
		}
	}

	public void reset() {
		if (uriConverter instanceof MultiFileURIConverter) {
			((MultiFileURIConverter) uriConverter).dispose();
		}
		uriConverter = null;
		boolean notify = eDeliver();
		boolean reportError = REPORT_ERROR;
		boolean refresh = RefreshJob.getInstance().isEnabled();
		try {
			RefreshJob.getInstance().setEnabled(false);
			eSetDeliver(false);
			REPORT_ERROR = false;
			markerMananger.setEnabled(false);
			markerMananger.clearAll();

			for (Iterator iter = new ArrayList(getResources()).iterator(); iter
					.hasNext();) {
				Resource resource = (Resource) iter.next();
				try {
					resource.unload();
				} catch (Exception e) {
					// System.err.println("Error unloading resource: " +
					// resource.getURI());
					// e.printStackTrace();
				}
			}

			clearErrors();
		} finally {
			eSetDeliver(notify);
			REPORT_ERROR = reportError;
			markerMananger.setEnabled(true);
			RefreshJob.getInstance().setEnabled(refresh);
		}

		if (guidToMethodElementMap != null) {
			guidToMethodElementMap.clear();
		}

		getResources().clear();

		if (URIToTempURIMap != null) {
			URIToTempURIMap.clear();
		}

		MultiFileXMIResourceImpl.clearDetachedEObjectToIDMap();
	}

	private void clearErrors() {
		// clear all markers
		markerMananger.clearAll();
	}

	public MethodLibrary loadLibrary(String path) throws Exception {
		long begin = System.currentTimeMillis();

		reset();

		long afterReset = System.currentTimeMillis();
		System.out.println("Reset time: " + (afterReset - begin)); //$NON-NLS-1$

		MethodLibrary lib = loadLibraryWithoutReset(path);

		System.out
				.println("Load time: " + (System.currentTimeMillis() - begin)); //$NON-NLS-1$

		if (MultiFileSaveUtil.DEBUG) {
			// check if any resource is marked as modified after loading
			//
			System.out.println("Modified resources after loading:");
			for (Iterator iter = getResources().iterator(); iter.hasNext();) {
				Resource resource = (Resource) iter.next();
				if (resource.isModified()) {
					System.out.println("  " + resource.getURI());
				}
			}
		}

		return lib;
	}

	public MethodLibrary loadLibraryWithoutReset(String path) throws Exception {
		loading = true;

		// no refresh during loading
		//
		boolean b = RefreshJob.getInstance().isEnabled();
		RefreshJob.getInstance().setEnabled(false);

		try {		
			File libFile = new File(path);
			configFolderPath = new Path(new File(libFile.getParentFile(),
					MultiFileSaveUtil.METHOD_CONFIGURATION_FOLDER_NAME).getCanonicalPath());
			libFolderPath = new Path(libFile.getParentFile().getCanonicalPath());
			URI uri = URI.createFileURI(libFile.getCanonicalPath());
			Resource res = super.getResource(uri, true);
			if (!res.getErrors().isEmpty()) {
				StringBuffer strBuf = new StringBuffer();
				strBuf.append(PersistenceResources.loadLibraryError_msg);
				for (Iterator iter = res.getErrors().iterator(); iter.hasNext();) {
					Resource.Diagnostic error = (Resource.Diagnostic) iter
							.next();
					strBuf.append(NLS.bind(
							PersistenceResources.loadLibraryError_details,
							(new Object[] { String.valueOf(error.getLine()),
									String.valueOf(error.getColumn()),
									error.getMessage() })));
				}
				String msg = strBuf.toString();
				CommonPlugin.INSTANCE.log(msg);
				System.err.println(msg);
			}
			try {
				initialize(true);

				return getMethodLibrary();
			} catch (Exception e) {
				CommonPlugin.INSTANCE.log(e);
				throw new Exception(NLS.bind(
						PersistenceResources.invalidLibraryFileError_msg, path));
			}
		} finally {
			RefreshJob.getInstance().setEnabled(b);

			loading = false;
		}
	}

	/**
	 * Loads opposite features for all loaded objects
	 */
	private void loadOppositeFeatures() {
		ArrayList oppositeFeatures = new ArrayList();
		for (Iterator iter = AssociationHelper.getPredefinedOppositeFeatures()
				.iterator(); iter.hasNext();) {
			OppositeFeature oppositeFeature = (OppositeFeature) iter.next();
			if (oppositeFeature.resolveOwner()) {
				oppositeFeatures.add(oppositeFeature);
			}
		}
		loadOppositeFeatures(oppositeFeatures);
	}

	/**
	 * Loads ResourceManager tree of the open library and connect any loaded
	 * MethodUnit to the library tree as well.
	 */
	private void loadResourceManagerTree() {
		long begin = System.currentTimeMillis();

		// disable logging unresolved proxies during loading resource manager
		// tree
		//
		markerMananger.setEnabled(false);

		MultiFileURIConverter uriConverter = (MultiFileURIConverter) getURIConverter();
		try {
			// this prevents uriConverter from loading resources while
			// normalizing URI
			//
			uriConverter.setResolveProxy(false);

			ResourceManager resMgr = getRootResourceManager();
			Iterator iterator = new AbstractTreeIterator(resMgr, false) {

				/**
				 * Comment for <code>serialVersionUID</code>
				 */
				private static final long serialVersionUID = 2172691017987702506L;

				protected Iterator getChildren(Object object) {
					ResourceManager resMgr = (ResourceManager) object;
					Collection GUIDs = new HashSet();
					List subMgrs = new ArrayList(resMgr.getSubManagers());
					for (Iterator iter = subMgrs.iterator(); iter.hasNext();) {
						try {
							InternalEObject subMgr = (InternalEObject) iter
									.next();
							if (subMgr != null && !subMgr.eIsProxy()) {
								MethodElement e = MultiFileSaveUtil
										.getMethodElement(subMgr.eResource());
								GUIDs.add(e.getGuid());
							}
						} catch (RuntimeException e) {
							throw e;
						}
					}
					MethodElement parent = MultiFileSaveUtil
							.getMethodElement(resMgr.eResource());

					// resolve the proxy of this element in the parent
					//
					MultiFileSaveUtil.resolveProxies(GUIDs, parent);

					return resMgr.getSubManagers().iterator();
				}

			};
			loadingResourceManagerTree = true;
			for (; iterator.hasNext(); iterator.next())
				;
		} finally {
			markerMananger.setEnabled(true);
			uriConverter.setResolveProxy(true);
			loadingResourceManagerTree = false;

			// Reporting of unnormalized URIs was disabled while loading
			// resource manager tree. Now we need
			// to go thru all the loaded objects to find proxies with
			// unnormalized URIs and report them
			//
			for (Iterator iter = getGuidToMethodElementMap().values()
					.iterator(); iter.hasNext();) {
				InternalEObject o = (InternalEObject) iter.next();
				if (o.eIsProxy() && o.eContainer() != null) {
					URI uri = o.eProxyURI();
					URI normalizedURI = getURIConverter().normalize(uri);
					if (normalizedURI == null) {
						// String msg =
						// NLS.bind(PersistenceResources.normalizeURIError_msg,
						// uri);
						// addMarker(new ResolveException(o, msg,
						// o.eContainer()));

						// this call will log error about unresolved proxy
						//
						((InternalEObject) o.eContainer()).eResolveProxy(o);
					}
				}
			}

			System.out
					.println("MultiFileResourceSetImpl.loadResourceManagerTree(): " + (System.currentTimeMillis() - begin)); //$NON-NLS-1$
		}
	}

	protected ResourceManager getResourceManager() {
		return ((MultiFileURIConverter) getURIConverter()).getResourceManager();
	}

	protected EObject findEObjectInUnloadedResources(String id) {
		return findEObjectInUnloadedResources(getResourceManager(), id);
	}

	private EObject findEObjectInUnloadedResources(ResourceManager resMgr,
			String id) {
		for (Iterator iter = resMgr.getResourceDescriptors().iterator(); iter
				.hasNext();) {
			ResourceDescriptor desc = (ResourceDescriptor) iter.next();
			Resource resource = super.getResource(desc.getResolvedURI(), false);
			if (resource == null || !resource.isLoaded()) {
				try {
					resource = super.getResource(desc.getResolvedURI(), true);
				} catch (Exception e) {
					//
				}
				if (resource != null) {
					EObject eObject = resource.getEObject(id);
					if (eObject != null) {
						return eObject;
					}
				}
			}
		}
		for (Iterator iter = resMgr.getSubManagers().iterator(); iter.hasNext();) {
			EObject eObject = findEObjectInUnloadedResources(
					(ResourceManager) iter.next(), id);
			if (eObject != null)
				return eObject;
		}
		return null;
	}

	/**
	 * Gets object with the given id
	 */
	public EObject getEObject(String id) {
		// first, try to get the object from the cache guidToMethodElement
		//
		EObject eObject = (EObject) getGuidToMethodElementMap().get(id);
		if (eObject != null)
			return eObject;

		URI uri = MultiFileURIConverter.createURI(id).appendFragment(id);
		URI normalized = getURIConverter().normalize(uri);
		if (normalized == null) {
			// the object with the given id might not have direct resource,
			// hence, it does not have
			// its own ResourceDescriptor in ResourceManager tree.
			// go thru all unloaded resources, load them, and try to find the
			// object with the given id.
			//
			eObject = findEObjectInUnloadedResources(id);
			if (eObject == null && REPORT_ERROR) {
				handleException(NLS.bind(
						PersistenceResources.objNotFoundError_msg, id));
			}
			return eObject;
		}

		try {
			return getEObject(uri, true);
		} catch (RuntimeException e) {
			return null;
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.emf.ecore.resource.ResourceSet#getEObject(org.eclipse.emf.common.util.URI,
	 *      boolean)
	 */
	public EObject getEObject(URI uri, boolean loadOnDemand) {
		return getEObject(null, uri, loadOnDemand);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.emf.ecore.resource.ResourceSet#createResource(org.eclipse.emf.common.util.URI)
	 */
	public Resource createResource(URI uri) {
		Resource result = doCreateResource(uri);
		result.setTrackingModification(true);
		getResources().add(result);
		return result;
	}
	
	protected Resource doCreateResource(URI uri) {
		return new MultiFileXMIResourceImpl(uri);
	}

	private static boolean backupRequired(Map options) {
		Boolean backup = (Boolean) options
				.get(MultiFileXMISaveImpl.BACK_UP_BEFORE_SAVE);
		return backup != null && backup.booleanValue();
	}

	private void backup(Resource res) {
		try {
			if (res.getContents().isEmpty())
				return;
			Object object = res.getContents().get(0);
			if (!(object instanceof MethodLibrary
					|| object instanceof ResourceManager || object instanceof MethodPlugin))
				return;

			MethodLibrary library = getMethodLibrary();
			String backupDir = new File(library.eResource().getURI()
					.toFileString()).getParent()
					+ File.separator + ".bak"; //$NON-NLS-1$
			SimpleDateFormat dateFormatter = new SimpleDateFormat(
					"yyMMddHHmmss.S"); //$NON-NLS-1$
			if (res == library.eResource()) {
				DateFormat.getInstance().format(
						Calendar.getInstance().getTime());
				File backupFile = new File(backupDir,
						MultiFileSaveUtil.DEFAULT_LIBRARY_MODEL_FILENAME
								+ '_'
								+ dateFormatter.format(Calendar.getInstance()
										.getTime()));
				if (!backupFile.getParentFile().exists()) {
					backupFile.getParentFile().mkdirs();
				}
				FileUtil.moveFile(new File(res.getURI().toFileString()), backupFile);
			} else {
				String relativePath = res.getURI().deresolve(
						library.eResource().getURI()).toFileString();
				File backupFile = new File(backupDir, relativePath
						+ '_'
						+ dateFormatter
								.format(Calendar.getInstance().getTime()));
				if (!backupFile.getParentFile().exists()) {
					backupFile.getParentFile().mkdirs();
				}
				FileUtil.moveFile(new File(res.getURI().toFileString()), backupFile);
			}
		} catch (Exception e) {
			handleException(e);
			handleException(NLS.bind(PersistenceResources.backupError_msg, res));
		}
	}

	void removeURIMappings(EObject eObj, Set modifiedResources) {
		ResourceManager resMgr = MultiFileSaveUtil.getResourceManager(eObj
				.eResource());
		if (resMgr != null && resMgr.eContainer() != null) {
			EcoreUtil.remove(resMgr);
		}
		ResourceDescriptor desc = MultiFileSaveUtil.findResourceDescriptor(
				getRootResourceManager(), MultiFileSaveUtil.getGuid(eObj),
				null, true);
		if (desc != null) {
			if (modifiedResources != null && desc.eResource() != null) {
				modifiedResources.add(desc.eResource());
			}
			EcoreUtil.remove(desc);
		}
	}

	protected void cleanUp(Resource removedResource) throws IOException {
		cleanUp(removedResource, null);
	}

	// protected void cleanUp(EObject obj, Set modifiedResources) {
	// try {
	// // remove all the URI mappings
	// //
	// removeURIMappings(obj, modifiedResources);
	// }
	// finally {
	// Resource resource = obj.eResource();
	// if(resource != null && resource.getContents().contains(obj)) {
	// resource.unload();
	// getResources().remove(resource);
	// }
	// }
	// }

	void cleanUp(Resource removedResource, Set modifiedResources)
			throws IOException {
		try {
			//
			if (removedResource.getContents().isEmpty()) {
				removedResource.unload();
				try {
					removedResource.load(null);
				} catch (FileNotFoundException e) {
					// the file might already be deleted, moved or never
					// creared.
					//
					return;
				}
			}

			EObject obj = MultiFileSaveUtil.getMethodElement(removedResource);

			// if(obj == null) return;
			// boolean cleanFolder;
			// if(obj instanceof MethodPlugin) {
			// cleanFolder = true;
			// }
			// else {
			// cleanFolder = false;
			// }

			// MultiFileURIConverter umaUriConverter =
			// ((MultiFileURIConverter)getURIConverter());
			// umaUriConverter.getResourceManager().removeResourceDescriptor(removedResource.getURI(),
			// cleanFolder);

			// remove all the URI mappings
			//
			removeURIMappings(obj, modifiedResources);

			// if(obj instanceof ProcessPackage) {
			// cleanUpResourceDescriptors((ProcessPackage)obj);
			// }

			removedResource.unload();
		} finally {
			getResources().remove(removedResource);
			URI oldURI = ((MultiFileXMIResourceImpl) removedResource)
					.getOldURI();
			if (oldURI != null) {
				getURIToTempURIMap().remove(oldURI);
			}
		}
	}

	private void cleanUp(ProcessPackage procPkg) {
		// remove ContentDescription of all ProcessElements in this
		// ProcessPackage
		//
		removeContentDescriptions(procPkg);

		EcoreUtil.remove(procPkg);
	}

	/**
	 * @param procPkg
	 */
	private void removeContentDescriptions(ProcessPackage procPkg) {
		for (Iterator iter = procPkg.getProcessElements().iterator(); iter
				.hasNext();) {
			ProcessElement element = (ProcessElement) iter.next();
			if (ContentDescriptionFactory.hasPresentation(element)
					&& element.getPresentation().eResource() != null) {
				removePresentation(element);
			}
		}

		for (Iterator iter = procPkg.getChildPackages().iterator(); iter
				.hasNext();) {
			Object pkg = iter.next();
			if (pkg instanceof ProcessPackage) {
				removeContentDescriptions((ProcessPackage) pkg);
			}
		}
	}

	/**
	 * @param element
	 */
	private void removePresentation(ProcessElement element) {
		Resource resource = element.getPresentation().eResource();
		resource.getContents().remove(element.getPresentation());
	}

	boolean save(Collection resources, Map options, boolean prepareSave)
			throws Exception {
		if (options == null) {
			options = getDefaultSaveOptions();
		}

		Set modifiedResources = new HashSet();
		options.put(MultiFileXMISaveImpl.MODIFIED_RESOURCE_SET,
				modifiedResources);
		try {
			for (Iterator iter = resources.iterator(); iter.hasNext();) {
				Resource res = (Resource) iter.next();
				save(res, options, prepareSave);
			}
			saveModifiedResources(options);
			return true;
		} finally {
			options.remove(MultiFileXMISaveImpl.MODIFIED_RESOURCE_SET);
		}
	}

	boolean save(Resource res, Map options, boolean prepareSave)
			throws Exception {
		Set modifiedResources = (Set) options
				.get(MultiFileXMISaveImpl.MODIFIED_RESOURCE_SET);
		TxRecord txRecord = (TxRecord) options
				.get(MultiFileXMISaveImpl.TX_RECORD);
		List resourcesToCommit = txRecord != null ? txRecord
				.getResourcesToCommit() : null;

		try {
			MethodElement eObj = MultiFileSaveUtil.getMethodElement(res);
			if (eObj != null) {
				if (prepareSave) {
					if (eObj instanceof ProcessComponent) {
						ProcessComponent procComp = (ProcessComponent) eObj;
						Resource content = procComp.getProcess() != null
								&& ContentDescriptionFactory
										.hasPresentation(procComp.getProcess()) ? procComp
								.getProcess().getPresentation().eResource()
								: null;
						boolean alreadyModified = content != null
								&& content.isModified();
						prepareSave(procComp);
						if (content != null && content.isModified()
								&& !alreadyModified) {
							// content is modified as result of prepareSave(),
							// it needs to be saved after model is saved
							//
							modifiedResources.add(content);
						}
					}
				}
				if (res.isModified()) {
					// System.out.println("----> Start saving " + res);
					boolean backupRequired = backupRequired(options);
					if (backupRequired) {
						// back up resource before saving
						//
						backup(res);
					}

					if (resourcesToCommit != null) {
						((MultiFileXMIResourceImpl) res).setTempURI(txRecord
								.getTxID());
						resourcesToCommit.add(res);
					}

					MultiFileSaveUtil.save(res, options);

					// System.out.println("<---- Saved resource: " + res);

					return true;
				}
			}
		} catch (Exception exception) {
			handleException(exception);
			throw exception;
		}

		return false;
	}

	public boolean save(Resource res, Map options) throws Exception {
//		// clear all error markers for this resource
//		//
//		markerMananger.clearMarkers(res);

		if (res == null || res.getContents().isEmpty())
			return false;

		if (options == null) {
			options = getDefaultSaveOptions();
		}

		Set modifiedResources = new HashSet();
		options.put(MultiFileXMISaveImpl.MODIFIED_RESOURCE_SET,
				modifiedResources);
		try {
			boolean ret = save(res, options, true);
			saveModifiedResources(options);
			return ret;
		} finally {
			options.remove(MultiFileXMISaveImpl.MODIFIED_RESOURCE_SET);
		}
	}

	/**
	 * Saves only the modified resources in this resource set.
	 * 
	 * @param options
	 * @throws Exception
	 */
	public void save(Map options) throws Exception {
		save(options, false, true);
	}

	void saveModifiedResources(Map options) {
		Set modifiedResources = (Set) options
				.get(MultiFileXMISaveImpl.MODIFIED_RESOURCE_SET);
		TxRecord txRecord = (TxRecord) options
				.get(MultiFileXMISaveImpl.TX_RECORD);
		List resourcesToCommit = txRecord != null ? txRecord
				.getResourcesToCommit() : null;
		if (modifiedResources == null)
			return;
		boolean backupRequired = backupRequired(options);
		boolean checkModify = MultiFileXMISaveImpl.checkModifyRequired(options);
		while (!modifiedResources.isEmpty()) {
			Iterator iter = new ArrayList(modifiedResources).iterator();
			modifiedResources.clear();
			while (iter.hasNext()) {
				Resource resource = (Resource) iter.next();
				if (checkModify) {
					MultiFileSaveUtil.checkModify(resource);
				}
				// if(resource.isModified()) {
				if (backupRequired) {
					// back up resource before saving
					//
					backup(resource);
				}

				if (resourcesToCommit != null) {
					((MultiFileXMIResourceImpl) resource).setTempURI(txRecord
							.getTxID());
					resourcesToCommit.add(resource);
				}

				MultiFileSaveUtil.save(resource, options);
				// }
			}
		}
	}

	/**
	 * 
	 * @param options
	 * @param saveAll
	 *            if true, force saving all the resources even if they are not
	 *            modified.
	 * @throws Exception
	 */
	public void save(Map options, boolean saveAll) throws Exception {
		save(options, saveAll, false);
	}

	public void save(Map options, boolean saveAll, boolean failSafe)
			throws Exception {
		if (options == null) {
			options = new HashMap(getDefaultSaveOptions());
		}

		MethodLibrary library = getMethodLibrary();
		MultiFileSaveUtil.setGuid(library);

		prepareSave(library);

		// do clean-up this resource set before saving
		//
		cleanUp();

		MultiFileURIConverter umaUriConverter = ((MultiFileURIConverter) getURIConverter());
		umaUriConverter.setLibraryURIs(library);

		Set modifiedResources = new HashSet();
		options.put(MultiFileXMISaveImpl.MODIFIED_RESOURCE_SET,
				modifiedResources);
		MethodLibraryPersister.FailSafePersister persister = null;
		if (failSafe) {
			persister = MethodLibraryPersister.INSTANCE
					.getFailSafePersister(options);
			options = persister.getSaveOptions();
		}
		try {
			List list = getResources();
			int size = list.size();
			// System.out.println("Start saving " + size + " loaded resources: "
			// + list);
			// System.out.println();
			boolean backupRequired = backupRequired(options);
			List saveResources = new ArrayList();
			List moveResources = new ArrayList();
			for (int i = 0; i < size; i++) {
				Resource res = (Resource) list.get(i);

				// don't adjust location of resources automatically
				//
				// if (res.isModified()
				// && MultiFileSaveUtil
				// .adjustLocationRequired((MultiFileXMIResourceImpl) res)) {
				// moveResources.add(res);
				// }

				MethodElement eObj = MultiFileSaveUtil.getMethodElement(res);
				if (eObj != null && eObj instanceof MethodElement) {
					if (saveAll || res.isModified()) {
						// System.out.println("----> Start saving " + res);
						if (failSafe) {
							saveResources.add(res);
						} else {
							if (backupRequired) {
								// back up resource before saving
								//
								backup(res);
							}

							MultiFileSaveUtil.save(res, options);
							
							if(res instanceof MultiFileXMIResourceImpl) {
								((MultiFileXMIResourceImpl)res).updateTimeStamps();
							}
						}
						// System.out.println("<---- Saved resource: " + res);
					}
				}
			}

			if (failSafe) {
				try {
					MultiFileSaveUtil.checkModify(saveResources);
					save(saveResources, options, false);
					persister.commit();
					persister.adjustLocation(moveResources);
				} catch (Exception e) {
					persister.rollback();
					throw e;
				}
			} else {
				for (Iterator iter = moveResources.iterator(); iter.hasNext();) {
					Resource resource = (Resource) iter.next();
					MultiFileSaveUtil.adjustLocation(resource,
							modifiedResources);
				}
				saveModifiedResources(options);
			}
		} catch (Exception exception) {
			handleException(exception);
			throw exception;
		} finally {
			options.remove(MultiFileXMISaveImpl.MODIFIED_RESOURCE_SET);
		}
	}

	/**
	 * Saves the open library under a new location
	 * 
	 * @param newLocation
	 *            directory to save the open library to
	 * @param if
	 *            true, regenerateGUID regenerate GUID for all MethodElements in
	 *            the open library
	 */
	public void saveAs(String newLocation, boolean regenerateGUID,
			IProgressMonitor monitor) throws Exception {
		MethodLibrary lib = getMethodLibrary();
		if (lib == null)
			return;

		// load all the resources in memory
		//
		if (monitor != null)
			monitor.setTaskName(PersistenceResources.loadResourcesTask_name);
		for (Iterator iter = lib.eAllContents(); iter.hasNext();) {
			EObject element = (EObject) iter.next();
			if (regenerateGUID && element instanceof MethodElement) {
				((MethodElement) element).setGuid(UmaUtil.generateGUID());
			}
			for (Iterator iterator = element.eCrossReferences().iterator(); iterator
					.hasNext();) {
				iterator.next();
			}
		}

		// change URI of all loaded resources for new location
		//
		URI oldDir = URI.createFileURI(new File(lib.eResource().getURI()
				.toFileString()).getParent()
				+ File.separator);
		URI newDir = URI.createFileURI(newLocation + File.separator);
		for (Iterator iter = getResources().iterator(); iter.hasNext();) {
			Resource resource = (Resource) iter.next();
			resource.setURI(resource.getURI().replacePrefix(oldDir, newDir));
		}

		// clear the ResourceManager
		//
		ResourceManager resMgr = ((MultiFileURIConverter) getURIConverter())
				.getResourceManager();
		if (resMgr != null) {
			resMgr.getResourceDescriptors().clear();
		}

		if (monitor != null)
			monitor.setTaskName(PersistenceResources.saveLibraryTask_name);
		save(null, true);
	}
	
	public void unloadWithoutRemove(MultiFileXMIResourceImpl resource) {
		MethodElement e = MultiFileSaveUtil.getMethodElement(resource);
		resource.unloadWithoutRemove();
		if(e instanceof MethodConfiguration) {
			MethodLibrary lib = getMethodLibrary();
			if(lib != null) {
				lib.getPredefinedConfigurations().remove(e);
			}
		}
	}

	/**
	 * @throws IOException
	 * 
	 */
	private void cleanUp() throws IOException {
		MethodLibrary library = getMethodLibrary();
		List removedResources = new ArrayList();

		List list = getResources();
		int size = list.size();
		for (int i = 0; i < size; i++) {
			Resource res = (Resource) list.get(i);
			boolean remove = false;
			if (res.getContents().isEmpty()) {
				System.out
						.println("MultiFileResourceSetImpl.cleanUp(): empty resource: " + res); //$NON-NLS-1$
				remove = true;
			} else {
				MethodElement eObj = MultiFileSaveUtil.getMethodElement(res);
				if (eObj == null
						|| (eObj != library && eObj.eContainer() == null)) {
					if (eObj == null) {
						System.out
								.println("MultiFileResourceSetImpl.cleanUp(): resource without any MethodElement: " + res); //$NON-NLS-1$
						remove = true;
					}
					// some resources might be loaded directly, not thru its
					// container. Therefore, we cannot assume
					// that a object without a container in a loaded resource is
					// a removed object.
					//
					else if (eObj != library && eObj.eContainer() == null) {
						System.out
								.println("MultiFileResourceSetImpl.cleanUp(): resource without a container: " + res); //$NON-NLS-1$
					}
				}
			}
			if (remove) {
				// This is a removed object. Add its path to the list for
				// removing its files later.
				//
				removedResources.add(res);
			}
		}

		// do clean-up
		//
		System.out
				.println("MultiFileResourceSetImpl.cleanUp(): removed resources: " + removedResources); //$NON-NLS-1$

		for (Iterator iter = removedResources.iterator(); iter.hasNext();) {
			Resource res = (Resource) iter.next();

			try {
				// remove ResourceDescriptor of the removed resource
				//
				cleanUp(res);
			} catch (Exception e) {
				handleException(e);
			}
		}

		// getResources().removeAll(removedResources);
	}

	/**
	 * Prepares the library for saving
	 */
	private void prepareSave(MethodLibrary library) {
		// check if the library already havs a ResourceManager
		// if not, create ResourceManager for it
		//
		MultiFileXMIResourceImpl resource = (MultiFileXMIResourceImpl) library
				.eResource();
		ResourceManager libResMgr = MultiFileSaveUtil
				.getResourceManager(resource);
		if (libResMgr == null) {
			libResMgr = ResourcemanagerFactory.eINSTANCE
					.createResourceManager();
			resource.getContents().add(0, libResMgr);
			MultiFileSaveUtil.registerWithResourceManager(libResMgr, library,
					resource.getFinalURI());
		}

		List models = library.getMethodPlugins();
		int size = models.size();
		for (int i = 0; i < size; i++) {
			prepareSave((MethodPlugin) models.get(i));
		}

		for (int i = 0; i < size; i++) {
			cleanUp((MethodPlugin) models.get(i));
		}
	}

	private static void cleanUp(MethodPlugin plugin) {
		for (int i = 0; i < PROCESS_PACKAGE_PATHS.length; i++) {
			ProcessPackage pkg = (ProcessPackage) UmaUtil.findMethodPackage(
					plugin, PROCESS_PACKAGE_PATHS[i]);
			if (pkg != null) {
				cleanUpProcesses(pkg);
			}
		}
	}

	private void prepareSave(MethodPlugin model) {
		for (int i = 0; i < PROCESS_PACKAGE_PATHS.length; i++) {
			ProcessPackage pkg = (ProcessPackage) UmaUtil.findMethodPackage(
					model, PROCESS_PACKAGE_PATHS[i]);
			if (pkg != null) {
				prepareSave(pkg);
			}
		}
	}

	/**
	 * Removes process packages of unused activities.
	 * 
	 * @param pkg
	 */
	private static void cleanUpProcesses(ProcessPackage pkg) {
		List list = pkg.getChildPackages();
		int size = list.size();
		for (int i = 0; i < size; i++) {
			Object obj = list.get(i);
			if (obj instanceof ProcessComponent) {
				ProcessComponent procComp = (ProcessComponent) obj;
				List packages = new ArrayList();
				for (Iterator iter = procComp.getChildPackages().iterator(); iter
						.hasNext();) {
					Object element = (Object) iter.next();
					if (element instanceof ProcessPackage) {
						getRemovedPackages((ProcessPackage) element, packages);
					}
				}
				for (Iterator iterator = packages.iterator(); iterator
						.hasNext();) {
					EcoreUtil.remove((EObject) iterator.next());
				}
			}
		}
	}

	private static void getRemovedPackages(ProcessPackage pkg,
			List removedPackages) {
		// find activity
		//
		Activity act = getActivity(pkg);

		if (act == null || getParent(act) == null) {
			// Invalid process package. It does not contain any activity.
			// Add it to the list of packages to be removed.
			//
			removedPackages.add(pkg);
		} else {
			for (Iterator iter = pkg.getChildPackages().iterator(); iter
					.hasNext();) {
				Object element = (Object) iter.next();
				if (element instanceof ProcessPackage) {
					getRemovedPackages((ProcessPackage) element,
							removedPackages);
				}
			}
		}
	}

	private static Activity getActivity(ProcessPackage pkg) {
		// find activity
		//
		Activity act = null;
		for (Iterator iter = pkg.getProcessElements().iterator(); iter
				.hasNext();) {
			Object element = iter.next();
			if (element instanceof Activity) {
				act = (Activity) element;
				break;
			}
		}
		return act;
	}

	private static Object getParent(BreakdownElement e) {
		// ItemProviderAdapter adapter = (ItemProviderAdapter)
		// TngUtil.getAdapter(e, IBSItemProvider.class);
		// return adapter == null ? null : adapter.getParent(e);

		return UmaUtil.getParentActivity(e);
	}

	/**
	 * Creates process package for those activities that still don't belong to
	 * any process package.
	 */
	private void prepareSave(ProcessPackage pkg) {
		List list = pkg.getChildPackages();
		int size = list.size();
		for (int i = 0; i < size; i++) {
			Object obj = list.get(i);
			if (obj instanceof ProcessComponent) {
				prepareSave((ProcessComponent) obj);
			}
		}
	}

	private void prepareSave(ProcessComponent procComp) {
		Process proc = procComp.getProcess();
		if (proc != null) {
			// associateAllAdapters(proc);
			prepareSave(proc);

			cleanUpDanglingDescriptors(procComp);

			List danglingPkgs = new ArrayList();
			for (Iterator iter = new ArrayList(procComp.getChildPackages())
					.iterator(); iter.hasNext();) {
				Object element = (Object) iter.next();
				if (element instanceof ProcessPackage) {
					ProcessPackage pkg = (ProcessPackage) element;
					reorganize(pkg, danglingPkgs);
				}
			}

			// clean up dangling ProcessPackages
			//
			for (Iterator iter = danglingPkgs.iterator(); iter.hasNext();) {
				cleanUp((ProcessPackage) iter.next());
			}

		}
	}

	// /**
	// * @param activity
	// */
	// private static void associateAllAdapters(Activity activity) {
	// AdapterFactory adapterFactory =
	// TngAdapterFactory.INSTANCE.getWBS_ComposedAdapterFactory();
	// ItemProviderAdapter adapter = (ItemProviderAdapter)
	// adapterFactory.adapt(activity, ITreeItemContentProvider.class);
	// for (Iterator iter = adapter.getChildren(activity).iterator();
	// iter.hasNext();) {
	// Object element = iter.next();
	// if(element instanceof Activity) {
	// associateAllAdapters((Activity) element);
	// }
	// }
	// }

	/**
	 * @param activity
	 */
	private static void prepareSave(Activity activity) {
		ProcessPackage parentPkg = (ProcessPackage) activity.eContainer();
		List elements = activity.getBreakdownElements();

		// add new descriptors and other non-activity breakdown elements of the
		// activity to its process package
		//
		for (Iterator iter = elements.iterator(); iter.hasNext();) {
			EObject element = (EObject) iter.next();
			if (!(element instanceof Activity) && element.eContainer() == null) {
				parentPkg.getProcessElements().add(element);
			}
		}

		// add new WorkOrder objects or remove unused WorkOrder objects of this
		// activity
		//
		for (Iterator iter = activity.getLinkToPredecessor().iterator(); iter
				.hasNext();) {
			WorkOrder workOrder = (WorkOrder) iter.next();
			if (workOrder.eContainer() == null) {
				if (workOrder.getPred() != null
						&& getParent(workOrder.getPred()) != null) {
					parentPkg.getProcessElements().add(workOrder);
				}
			} else {
				if (workOrder.getPred() == null
						|| getParent(workOrder.getPred()) == null) {
					if (MultiFileSaveUtil.DEBUG) {
						System.err.println("Invalid WorkOrder: " + workOrder); //$NON-NLS-1$
					}
					parentPkg.getProcessElements().remove(workOrder);
				}
			}
		}

		// removed unused diagrams
		//
		for (Iterator iter = parentPkg.getDiagrams().iterator(); iter.hasNext();) {
			Diagram diagram = (Diagram) iter.next();
			SemanticModelBridge bridge = diagram.getSemanticModel();
			if (bridge instanceof UMASemanticModelBridge) {
				MethodElement element = ((UMASemanticModelBridge) bridge)
						.getElement();
				if (element instanceof Activity
						&& (!(element instanceof Process) && getParent((Activity) element) == null)) {
					iter.remove();
				}
			}
		}

		int size = elements.size();
		for (int i = 0; i < size; i++) {
			Object e = elements.get(i);
			if (e instanceof Activity) {
				Activity act = (Activity) e;
				if (act.eContainer() == null) {
					ProcessPackage pkg = UmaFactory.eINSTANCE
							.createProcessPackage();
					pkg.setName(act.getName());
					pkg.getProcessElements().add(act);
					List descriptors = new ArrayList();
					for (int j = 0; j < act.getBreakdownElements().size(); j++) {
						Object obj = act.getBreakdownElements().get(j);
						if (obj instanceof Descriptor) {
							descriptors.add(obj);
						}
					}
					pkg.getProcessElements().addAll(descriptors);

					parentPkg.getChildPackages().add(pkg);
					// System.out.println("Process package created for activity:
					// '" + act.getName() + "'");
				}
				prepareSave(act);
			}
		}
	}

	private static void cleanUpDanglingDescriptors(ProcessPackage pkg) {
		for (Iterator iter = pkg.getProcessElements().iterator(); iter
				.hasNext();) {
			Object element = iter.next();
			if (element instanceof BreakdownElement
					&& ((BreakdownElement) element).getSuperActivities() == null) {
				boolean remove = false;
				if (element instanceof RoleDescriptor) {
					if (AssociationHelper.getTeamProfiles(
							(RoleDescriptor) element).isEmpty()) {
						remove = true;
					}
				} else if (element instanceof WorkProductDescriptor) {
					if (AssociationHelper.getDeliverableDescriptors(
							(WorkProductDescriptor) element).isEmpty()) {
						remove = true;
					}
				} else if (element instanceof TeamProfile) {
					if (((TeamProfile) element).getSuperTeam() == null) {
						remove = true;
					}
				} else {
					remove = true;
				}

				if (remove) {
					iter.remove();
				}
			}
		}
	}

	/**
	 * Moves the process packages to the right location in the tree if needed.
	 * 
	 * @param pkg
	 * @param danglingPkgs
	 */
	private void reorganize(ProcessPackage pkg, List danglingPkgs) {
		Activity act = getActivity(pkg);
		if (act == null)
			return;

		// make sure the package has the same name as the activity's name
		//
		if (!pkg.getName().equals(act.getName())) {
			pkg.setName(act.getName());
		}

		// check if the container of pkg is actually holding the parent activity
		// of act
		//
		EObject parentAct = (EObject) getParent(act);

		if (parentAct == null) {
			// cleanUpResourceDescriptors(pkg);
			// cleanUp(pkg);
			// return;
			danglingPkgs.add(pkg);
		} else if (parentAct != getActivity((ProcessPackage) pkg.eContainer())) {
			// move the package to the right location, under the process package
			// of parentAct
			//
			ProcessPackage parentPkg = (ProcessPackage) parentAct.eContainer();
			parentPkg.getChildPackages().add(pkg);
		}

		if (parentAct != null) {
			cleanUpDanglingDescriptors(pkg);
		}

		for (Iterator iter = pkg.getChildPackages().iterator(); iter.hasNext();) {
			Object element = (Object) iter.next();
			if (element instanceof ProcessPackage) {
				reorganize((ProcessPackage) element, danglingPkgs);
			}
		}
	}

	public Map getGuidToMethodElementMap() {
		if (guidToMethodElementMap == null) {
			guidToMethodElementMap = new HashMap();
		}
		return guidToMethodElementMap;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.emf.common.notify.impl.BasicNotifierImpl#eNotify(org.eclipse.emf.common.notify.Notification)
	 */
	public void eNotify(Notification notification) {
		try {
			super.eNotify(notification);
		} catch (RuntimeException e) {
			CommonPlugin.INSTANCE
					.log("ERROR notifying changes: resourceSet=" + this + ", notification=" + notification); //$NON-NLS-1$ //$NON-NLS-2$
			CommonPlugin.INSTANCE.log(e);
			e.printStackTrace();
		}
	}

	public void addMarker(Exception e) {
		try {
			// insert problem marker
			//
			markerMananger.notifyException(e);
		} catch (RuntimeException ex) {
			CommonPlugin.INSTANCE.log(ex);
			if (MultiFileSaveUtil.DEBUG) {
				ex.printStackTrace();
			}
		}
	}

	public void setLoading(boolean b) {
		loading = b;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.epf.uma.ecore.IExceptionCollector#collect(java.lang.Exception)
	 */
	public void notifyException(Exception e) {
		if (loadingResourceManagerTree) {
			if (e instanceof ResolveException) {
				if (((ResolveException) e).exception() instanceof MultiFileIOException) {
					return;
				}
			}
		}
		try {
			addMarker(e);
		} catch (Exception ex) {
			//
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.epf.uma.ecore.IProxyResolutionListener#proxyResolved(java.lang.Object,
	 *      java.lang.Object)
	 */
	public void proxyResolved(Object proxy, Object resolved) {
		if(resolved instanceof MethodElement) {
			String guid = MultiFileSaveUtil.getGuid(resolved);
			if (guid != null) {
				getGuidToMethodElementMap().put(guid, resolved);
			}
		}
		markerMananger.proxyResolved(proxy, resolved);
	}

	private EObject getEObjectByGUID(String guid) {
		return (EObject) getGuidToMethodElementMap().get(guid);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.epf.uma.ecore.IUmaResourceSet#getEObject(org.eclipse.emf.ecore.EObject,
	 *      org.eclipse.emf.common.util.URI, boolean)
	 */
	public EObject getEObject(EObject resolver, URI uri, boolean loadOnDemand) {
		// long start;
		// if(MultiFileSaveUtil.PROFILE) {
		// start = System.currentTimeMillis();
		// }

		URI normalized = ((MultiFileURIConverter) getURIConverter()).normalize(
				uri, resolver);
		if (normalized == null) {
			// work-around to look up for object by its GUID in case it had been
			// moved to other resource
			//
			String guid = uri.fragment();
			EObject eObject = getEObjectByGUID(guid);
			if (eObject != null) {
				return eObject;
			}

			String msg = NLS.bind(PersistenceResources.normalizeURIError_msg,
					uri);
			throw new UnnormalizedURIException(msg);
		}
		Resource resource = getResource(normalized.trimFragment(), loadOnDemand);
		if (resource != null) {
			String fragment = normalized.fragment();
			if (fragment == null || fragment.length() == 0) {
				return MultiFileSaveUtil.getMethodElement(resource);
			}
			EObject eObject = resource.getEObject(fragment);
			if (eObject == null && !fragment.equals(uri.authority())) {
				// work-around to look up for object by its GUID in case it had
				// been moved
				//
				return getEObjectByGUID(fragment);
			}
			return eObject;
		} else {
			return null;
		}

		// if(MultiFileSaveUtil.PROFILE) {
		//			
		// }
	}

	// /**
	// * @return Returns the exceptions.
	// */
	// public List getExceptions() {
	// return exceptions;
	// }

	public void loadOppositeFeatures(List oppositeFeatures) {
		int max = oppositeFeatures.size() - 1;
		if (max < 0) {
			return;
		}
		ArrayList elements = new ArrayList(getGuidToMethodElementMap().values());
		HashSet loadedElements = new HashSet();
		while (!elements.isEmpty()) {
			for (Iterator iter = elements.iterator(); iter.hasNext();) {
				Object obj = iter.next();
				if (obj instanceof MethodElement) {
					MethodElement element = (MethodElement) obj;
					for (int i = max; i > -1; i--) {
						EStructuralFeature eFeature = ((OppositeFeature) oppositeFeatures
								.get(i)).getTargetFeature();
						if (eFeature.getContainerClass().isInstance(element)) {
							if (eFeature.isMany()) {
								InternalEList list = (InternalEList) element
										.eGet(eFeature);
								if (!list.isEmpty()) {
									for (Iterator iterator = list.iterator(); iterator
											.hasNext();) {
										iterator.next();
									}
								}
							} else {
								element.eGet(eFeature);
							}
						}
					}
				}
			}

			// gets the newly loaded elements to load their opposite features
			//
			loadedElements.addAll(elements);
			elements = new ArrayList(getGuidToMethodElementMap().values());
			elements.removeAll(loadedElements);
		}
	}

	public void loadOppositeFeatures(List oppositeFeatures, Set deletedGUIDs) {
		int max = oppositeFeatures.size() - 1;
		if (max < 0) {
			return;
		}
		ArrayList elements = new ArrayList(getGuidToMethodElementMap().values());
		HashSet loadedElements = new HashSet();
		while (!elements.isEmpty()) {
			for (Iterator iter = elements.iterator(); iter.hasNext();) {
				Object obj = iter.next();
				if (obj instanceof MethodElement) {
					MethodElement element = (MethodElement) obj;
					for (int i = max; i > -1; i--) {
						EStructuralFeature eFeature = ((OppositeFeature) oppositeFeatures
								.get(i)).getTargetFeature();
						if (eFeature.getContainerClass().isInstance(element)) {
							if (eFeature.isMany()) {
								InternalEList list = (InternalEList) element
										.eGet(eFeature);
								if (!list.isEmpty()) {
									boolean resolve = false;
									check_resolve: for (Iterator iterator = list
											.basicIterator(); iterator
											.hasNext();) {
										InternalEObject e = (InternalEObject) iterator
												.next();
										if (e.eIsProxy()) {
											String guid = e.eProxyURI()
													.fragment();
											if (deletedGUIDs.contains(guid)) {
												resolve = true;
												break check_resolve;
											}
										}
									}
									if (resolve) {
										for (Iterator iterator = list
												.iterator(); iterator.hasNext();) {
											iterator.next();
										}
									}
								}
							} else {
								Object value = element.eGet(eFeature, false);
								if (value instanceof InternalEObject) {
									InternalEObject e = (InternalEObject) value;
									if (e.eIsProxy()) {
										String guid = e.eProxyURI().fragment();
										if (deletedGUIDs.contains(guid)) {
											element.eGet(eFeature);
										}
									}
								}
							}
						}
					}
				}
			}
			// gets the newly loaded elements to load their opposite features
			//
			loadedElements.addAll(elements);
			elements = new ArrayList(getGuidToMethodElementMap().values());
			elements.removeAll(loadedElements);
		}
	}

	public boolean hasUnresolvedProxy() {
		return markerMananger.hasUnresolvedProxy();
	}

	protected Map getURIToTempURIMap() {
		if (URIToTempURIMap == null) {
			URIToTempURIMap = new HashMap();
		}

		return URIToTempURIMap;
	}

	public UnresolvedProxyMarkerManager getMarkerMananger() {
		return markerMananger;
	}

}
