/**
 * File:    ConfModelResourceManagerImpl.java
 * Created: 10.07.2008
 *
 *
/*******************************************************************************
 * Copyright (c) 2008-2010 Open Wide SA <www.openwide.fr>
 *
 * 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:
 *    Marc Dutoo, Open Wide SA 
 *    	- initial API and implementation
 *******************************************************************************/

package org.eclipse.jwt.we.conf.model.resource.internal;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.logging.Level;

import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.common.util.WrappedException;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.URIConverter;
import org.eclipse.emf.ecore.resource.impl.ExtensibleURIConverterImpl;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.xmi.ClassNotFoundException;
import org.eclipse.emf.ecore.xmi.PackageNotFoundException;
import org.eclipse.emf.ecore.xmi.XMLResource;
import org.eclipse.jwt.we.conf.model.ConfFactory;
import org.eclipse.jwt.we.conf.model.ConfModel;
import org.eclipse.jwt.we.conf.model.Profile;
import org.eclipse.jwt.we.conf.model.aspects.event.AspectEventManager;
import org.eclipse.jwt.we.conf.model.plugin.ConfPlugin;
import org.eclipse.jwt.we.conf.model.plugin.internal.EMFUtils;
import org.eclipse.jwt.we.conf.model.plugin.internal.Logger;
import org.eclipse.jwt.we.conf.model.resource.ConfModelResourceManager;
import org.eclipse.jwt.we.conf.model.resource.ConfModelStatus;
import org.eclipse.jwt.we.conf.model.resource.ConfResourceException;


/**
 * Manages Aspect resources (confModel)
 * 
 * @version $Id: ConfModelResourceManagerImpl.java,v 1.3 2010-05-27 15:29:51 mdutoo Exp $
 * @author Marc Dutoo
 *
 */
@SuppressWarnings("serial")
public class ConfModelResourceManagerImpl implements ConfModelResourceManager {
	
	protected static Logger logger = Logger.getLogger(ConfModelResourceManagerImpl.class);
	
	protected final static HashMap<String,String> CONFMODEL_SAVE_OPTIONS = new HashMap<String,String>() {{
		put(XMLResource.OPTION_ENCODING, "UTF-8"); //$NON-NLS-1$
		}};

	protected final static URIConverter uriConverter = new ExtensibleURIConverterImpl();
	
	/** Used to manage ConfModel unavailability */
	protected final ConfModelAvailabilityManager confModelAvailabilityManager = new ConfModelAvailabilityManager();


	protected ConfModelResourceManagerImpl() {
		
	}

	/**
	 * Singleton accessor
	 * 
	 * @return The only instance of this class.
	 */
	public static ConfModelResourceManager createConfModelResourceManager() {
		return new ConfModelResourceManagerImpl();
	}
	
	
	/**
	 * Could be used by aspect dev tools to explitly manage the availability flags and causes
	 * @return
	 */
	public ConfModelAvailabilityManager getConfModelAvailabilityManager() {
		return confModelAvailabilityManager;
	}


	/* (non-Javadoc)
	 * @see org.eclipse.jwt.we.conf.resource.ConfModelResourceManager#exportProfiles(java.util.Collection, java.io.File)
	 */
	public ConfModel exportProfilesToFile(Collection<Profile> profiles, File f) throws Exception {
		return exportProfilesToFile(profiles, f);
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jwt.we.conf.resource.ConfModelResourceManager#exportProfilesToFile(java.util.Collection, java.io.File)
	 */
	public ConfModel exportProfiles(Collection<Profile> profiles, File f) throws ConfResourceException, IOException {
		// building conf model
		ConfModel exportConfModel = ConfFactory.eINSTANCE.createConfModel();
		Collection<Profile> exportedProfiles = EcoreUtil.copyAll(profiles);
		exportConfModel.getProfiles().addAll(exportedProfiles);
		
		// creating resourceset and resource
		ResourceSetImpl confResourceSet = new ResourceSetImpl();
		URI confModelUri = URI.createFileURI(f.getAbsolutePath());
		Resource confModelResource = confResourceSet.createResource(confModelUri);
		confModelResource.getContents().add(exportConfModel);

		// saving it
		try {
			confModelResource.save(CONFMODEL_SAVE_OPTIONS); // TODO put(XMLResource.OPTION_ENCODING, "UTF-8") ? dep on ecore.xmi
		} catch (RuntimeException ex) {
			String msg = "Error saving confModel " + confModelUri //$NON-NLS-1$
			+ " after updating it to target the right external resource " + confModelUri; //$NON-NLS-1$
			ConfPlugin.logger.warning(msg, ex);
			throw new ConfResourceException(msg, ex);
		}
		
		return exportConfModel;
	}
	

	/* (non-Javadoc)
	 * @see org.eclipse.jwt.we.conf.resource.ConfModelResourceManager#loadConfModelOfModel(org.eclipse.emf.ecore.EObject, boolean)
	 */
	public ConfModel loadConfModelOfModel(EObject modelRoot, boolean createIfDoesntExist)
			throws ConfResourceException {
		ConfModelStatus cmStatus = new ConfModelStatus();
		cmStatus.setCreateIfDoesntExist(createIfDoesntExist);
		ConfModel confModel = loadConfModelOfModel(modelRoot, cmStatus);
		if (cmStatus.getSeverity() == IStatus.ERROR) {
			for (IStatus childStatus : cmStatus.getChildren()) {
				if (childStatus.getSeverity() == IStatus.ERROR) {
					// throwing the first error found TODO better : all of them
					throw new ConfResourceException(childStatus.getMessage(),
							childStatus.getException());
				}
			}
			throw new ConfResourceException("Unknown error loading conf model"); //$NON-NLS-1$
		}
		return confModel;
	}
	
	/* (non-Javadoc)
	 * @see org.eclipse.jwt.we.conf.resource.ConfModelResourceManager#loadConfModelOfModel(org.eclipse.emf.ecore.EObject, org.eclipse.jwt.we.conf.resource.ConfModelStatus)
	 */
	public ConfModel loadConfModelOfModel(EObject modelRoot, ConfModelStatus cmStatus) {
		if (modelRoot == null) {
			// guard
			return null;
		}
		
		// get model resource URI
		Resource modelResource = modelRoot.eResource();
		if (modelResource == null) {
			// happens ex. when modelRoot is actually a modelElement that has just been deleted 
			return null;
		}
		URI modelUri = modelResource.getURI();
		if (modelUri == null) {
			// happens when error loading model (ex. embeddedconf feature not found)
			// NB. no other way to detect it (isLoaded() or getErrors())
			return null;
		}
		ResourceSet modelResourceSet = modelResource.getResourceSet();
		
		// build expected confModel URI
		URI confModelUri = buildConfModelUriForModel(modelUri);
		
		// find and load conf model ; may throw ConfResourceException
		if (cmStatus.isCreateIfDoesntExist()) {
			// first unflag availability, since createIfDoesntExist means creation
			confModelAvailabilityManager.unflagUnavailability(modelRoot, confModelUri);
		}
		// copy status so the configuration (bypass, patch...) is kept
		ConfModelStatus tryGettingExistingOneFirstStatus = new ConfModelStatus(cmStatus);
		tryGettingExistingOneFirstStatus.setTryGettingExistingOneFirst(true);
		ConfModel confModel = loadConfModel(confModelUri, modelResourceSet, modelRoot,
				tryGettingExistingOneFirstStatus);

		if (cmStatus.isCreateIfDoesntExist()) {
			// don't put the status in error or try availability, still try to create
			
		} else if (tryGettingExistingOneFirstStatus.getSeverity() == IStatus.ERROR) {
			cmStatus.addAll(tryGettingExistingOneFirstStatus); 
			return null;
			
		} else if (confModel == null && !cmStatus.isBypassFlaggedUnavailable()) {
			// TODO could be done better, with error code ?!
			// if existing one found by loadConfModel(), should have been unflagged
			String unavailableMsg = confModelAvailabilityManager.flaggedUnavailable(modelRoot, confModelUri);
			if (unavailableMsg != null) {
				cmStatus.add(new Status(IStatus.ERROR, ConfPlugin.CONF_PLUGIN_ID,
						ConfModelStatus.RESOURCE_NOT_ALREADY_LOADED,
						"ConfModel resource unavailable: " + unavailableMsg, null)); //$NON-NLS-1$
				return null;
			}
		} // else available or bypassed
		
		if (confModel == null) {
			if (cmStatus.isCreateIfDoesntExist()) {
				// let's create a new conf model for this model ; may throw ConfResourceException
				try {
					confModel = createConfModel(confModelUri, modelResourceSet, modelRoot);
				} catch (ConfResourceException crex) {
					cmStatus.add(new Status(IStatus.ERROR, ConfPlugin.CONF_PLUGIN_ID,
							ConfModelStatus.CONF_MODEL_CREATION_ERROR,
							crex.getMessage(), crex));
					return null;
				}
				
				// unflag avail., since creation happened without error (was flagged just before)
				confModelAvailabilityManager.unflagUnavailability(modelRoot, confModelUri);
				
			} // else let it be null
			
		} else {
			// finally ensure that it refers to this model
			EObject confModelEnrichedModel = confModel.getEnrichedModel();
			
			if (confModelEnrichedModel == null) {
				// no referenced model ; TODO does this case happen ?
				String msg = "Found ConfModel at " + confModelUri //$NON-NLS-1$
						+ " but has no enriched model set" //$NON-NLS-1$
						+ " while looking for expected ConfModel of model " //$NON-NLS-1$
						+ modelUri;
				logger.debug(msg);
				if (cmStatus.isDontTryPatchingNoEnrichedModelReference()) {
					cmStatus.add(new Status(IStatus.ERROR, ConfPlugin.CONF_PLUGIN_ID,
							ConfModelStatus.CONF_MODEL_PATCHABLE_NO_ENRICHED_MODEL_REFERENCE,
							msg, null));
					return null;
				}
				cmStatus.add(new Status(IStatus.INFO, ConfPlugin.CONF_PLUGIN_ID,
						ConfModelStatus.CONF_MODEL_PATCHABLE_NO_ENRICHED_MODEL_REFERENCE,
						msg, null));
				// trying to patch it
				confModel.setEnrichedModel(modelRoot);
				
			} else {
				// getting existing or non-existing, proxy enrichedModel URI
				URI confModelEnrichedModelResourceURI = EMFUtils.getResourceOrProxyURI(confModelEnrichedModel);
				
				// there is already a referenced enriched model !
				if (confModelEnrichedModelResourceURI != null && !confModelEnrichedModelResourceURI.equals(modelUri)) {
					// not the same : happens ex. when opening a just copied & pasted workflow / conf
					String msg = "Found ConfModel at " + confModelUri //$NON-NLS-1$
							+ " but already refers to another enriched model at " //$NON-NLS-1$
							+ confModelEnrichedModelResourceURI
							+ " while looking for expected ConfModel of model " //$NON-NLS-1$
							+ modelUri;
					if (cmStatus.isDontTryPatchingReferencesAnotherEnrichedModel()) {
						logger.severe(msg);
						cmStatus.add(new Status(IStatus.ERROR, ConfPlugin.CONF_PLUGIN_ID,
								ConfModelStatus.CONF_MODEL_PATCHABLE_REFERENCES_ANOTHER_ENRICHED_MODEL, msg, null));
					}
					logger.warning(msg);
					cmStatus.add(new Status(IStatus.WARNING, ConfPlugin.CONF_PLUGIN_ID,
							ConfModelStatus.CONF_MODEL_PATCHABLE_REFERENCES_ANOTHER_ENRICHED_MODEL, msg, null));
					
					// TODO could copy the ConfModel or ask for confirmation before replacing the enriched model...
					// let's try to patch it ; may throw ConfResourceException
					try {
						confModel = migrateConfModelToModel(confModel, confModelEnrichedModelResourceURI, modelUri, modelRoot);
						// NB. this has set also the confModel's enrichedModel, saved it, 
						// and removed the duplicated resources in the resourceset
					} catch (ConfResourceException e) {
						cmStatus.add(new Status(IStatus.ERROR, ConfPlugin.CONF_PLUGIN_ID,
								ConfModelStatus.CONF_MODEL_ANOTHER_ENRICHED_MODEL_MIGRATION_ERROR,
								e.getMessage(), e));
						return null;
					}
					
				} // else ok, it's the same enriched model	
			}
		}
		
		return confModel;
	}

	protected URI buildConfModelUriForModel(URI modelUri) {
		String confModelFragment = modelUri.fragment();
		String[] confModelSegments = modelUri.segments();
		if (confModelFragment != null && confModelFragment.length() != 0) {
			confModelFragment += "_conf"; //$NON-NLS-1$
			
		} else {
			confModelSegments[confModelSegments.length - 1] = modelUri.lastSegment() + "_conf"; //$NON-NLS-1$
		}
		return URI.createHierarchicalURI(modelUri.scheme(), modelUri.authority(), 
				modelUri.device(), confModelSegments, modelUri.query(), confModelFragment);
	}
	

	/* (non-Javadoc)
	 * @see org.eclipse.jwt.we.conf.resource.ConfModelResourceManager#createConfModel(org.eclipse.emf.common.util.URI, org.eclipse.emf.ecore.resource.ResourceSet, org.eclipse.emf.ecore.EObject)
	 */
	public ConfModel createConfModel(URI confModelUri, ResourceSet modelResourceSet,
			EObject modelRoot) throws ConfResourceException {
		if (modelResourceSet == null) {
			if (modelRoot != null) {
				modelResourceSet = modelRoot.eResource().getResourceSet();
			} else {
				modelResourceSet = new ResourceSetImpl();
			}
		}
		
		ConfModel confModel = ConfFactory.eINSTANCE.createConfModel();
		Resource confModelResource = null;
		try {
			confModelResource = modelResourceSet.createResource(confModelUri);
		} catch (RuntimeException ex) {
			String msg = "Error creating ConfModel in new file " + confModelUri //$NON-NLS-1$
			+ ((modelRoot != null) ? " while looking for expected ConfModel of model " //$NON-NLS-1$
					+ modelRoot.eResource().getURI() : ""); //$NON-NLS-1$
			logger.severe(msg, ex);
			EMFResourceUtils.cleanBadlyLoadedResource(modelResourceSet, confModelUri);
			throw new ConfResourceException(msg, ex);
		}

		confModelResource.getContents().add(confModel);
		if (modelRoot != null) {
			// setting the new enriched model
			confModel.setEnrichedModel(modelRoot);
			
			// let's listen for the model's saves, to be able to save the confModel then :
			Resource modelResource = modelRoot.eResource();
			modelResource.eAdapters().add(new ConfModelSavingModelResourceAdapter(
					confModelResource, modelResource.getURI()));
		}
		
		// saving the whole lot
		try {
			confModelResource.save(CONFMODEL_SAVE_OPTIONS);
		} catch (Exception ex) {
			String msg = "Error saving ConfModel " + confModelUri //$NON-NLS-1$
			+ " after patching it with the right enrichedModel" //$NON-NLS-1$
			+ ((modelRoot != null) ? " while looking for expected ConfModel of model " //$NON-NLS-1$
					+ modelRoot.eResource().getURI() : ""); //$NON-NLS-1$
			logger.warning(msg, ex);
			throw new ConfResourceException(msg, ex);
		}
		return confModel;
	}
	
	
	/* (non-Javadoc)
	 * @see org.eclipse.jwt.we.conf.resource.ConfModelResourceManager#loadConfModel(org.eclipse.emf.common.util.URI, org.eclipse.emf.ecore.resource.ResourceSet, org.eclipse.emf.ecore.EObject, boolean)
	 */
	public ConfModel loadConfModel(URI confModelUri, ResourceSet modelResourceSet,
			EObject modelRoot, boolean tryGettingExistingOneFirst) throws ConfResourceException {
		ConfModelStatus cmStatus = new ConfModelStatus();
		cmStatus.setTryGettingExistingOneFirst(tryGettingExistingOneFirst);
		ConfModel confModel = loadConfModel(confModelUri, modelResourceSet, modelRoot, cmStatus);
		if (cmStatus.getSeverity() == IStatus.ERROR) {
			for (IStatus childStatus : cmStatus.getChildren()) {
				if (childStatus.getSeverity() == IStatus.ERROR) {
					// throwing the first error found TODO better : all of them
					throw new ConfResourceException(childStatus.getMessage(),
							childStatus.getException());
				}
			}
			throw new ConfResourceException("Unknown error loading conf model at " //$NON-NLS-1$
					+ confModelUri);
		}
		return confModel;
	}
	
	/* (non-Javadoc)
	 * @see org.eclipse.jwt.we.conf.resource.ConfModelResourceManager#loadConfModelStatus(org.eclipse.emf.common.util.URI, org.eclipse.emf.ecore.resource.ResourceSet, org.eclipse.emf.ecore.EObject, org.eclipse.jwt.we.conf.resource.ConfModelStatus)
	 */
	public ConfModel loadConfModel(URI confModelUri, ResourceSet modelResourceSet,
			EObject modelRoot, ConfModelStatus cmStatus) {
		if (modelResourceSet == null) {
			if (modelRoot != null) {
				modelResourceSet = modelRoot.eResource().getResourceSet();
			} else {
				modelResourceSet = new ResourceSetImpl();
			}
		}

		Resource confModelResource = null;
		if (cmStatus.isTryGettingExistingOneFirst()) {
			// let's try to get the already loaded resource :
			try {
				// NB. with !loadOnDemand it is very efficient ; if it does not exist, it simply returns null
				confModelResource = modelResourceSet.getResource(confModelUri, false);
				// NB. if gets here, confModelResource is either null or a (rightly ?) existing one
				if (confModelResource != null) {
					return getConfModelFromConfModelResource(confModelResource);
				}
			} catch (RuntimeException ex) {
				// NB. this error probably never happens since we're here in !loadOnDemand mode,
				// but if it happens often the "resource recorded in error" test should be done earlier
				String msg = "Error trying to get already loaded ConfModel from file " + confModelUri //$NON-NLS-1$
						+ ((modelRoot != null) ? " while looking for expected ConfModel of model " //$NON-NLS-1$
								+ modelRoot.eResource().getURI() : ""); //$NON-NLS-1$
				//+ " while looking for expected ConfModel of model " + modelUri, ex); // TODO rm
				logger.log(Level.INFO, msg, ex); // TODO rather in debug level if actually expected ?
				cmStatus.add(new Status(IStatus.INFO, ConfPlugin.CONF_PLUGIN_ID,
						ConfModelStatus.RESOURCE_NOT_ALREADY_LOADED, msg, ex));
				
				// cleaning error
				EMFResourceUtils.cleanBadlyLoadedResource(modelResourceSet, confModelUri);
				confModelResource = null;
				// NB. we don't record the error since we did not try loadOnDemand
			}
		}
		
		// Now not loaded yet, try to load it
		try {
			// checking whether we remember a previous loading failure
			if (confModelAvailabilityManager.flaggedUnavailable(modelRoot, confModelUri) != null) {
				// NB. to allow dynamic reload of bad conf files once corrected, remove this
				// NB. flag has to be removed explicitly using TODO reload
				// NB. not louder logging because when it happens, it happens a lot TODO uncomment
				//if (logger.isDebugEnabled()) {
				//	logger.debug("Remembering a previous load");
				//}
				if (cmStatus.isBypassFlaggedUnavailable()) {
					// unflagg availability to allow another try
					confModelAvailabilityManager.unflagUnavailability(modelRoot, confModelUri);
				} else {
					// there is a previous load failure, but don't return the corresponding error,
					// rather let the caller use the flagged unavailability message for this
					return null;
				}
			}
			
			// checking wether it exists and flagging it
			boolean resourceExists = uriConverter.exists(confModelUri, CONFMODEL_SAVE_OPTIONS);
			if (!resourceExists) {
				String msg = "ConfModel resource doesn't exist"; //$NON-NLS-1$
				// NB. in the normal case that no conf model exists,
				// the status is set to the appropriate error, but
				// no exception will be thrown up by AspectManagerImpl when using it,
				// and the Profile Management UI will not say anything either
				handleResourceLoadingError(modelRoot, null, confModelUri,
						cmStatus, ConfModelStatus.RESOURCE_DOES_NOT_EXIST, msg, null);
				return null;
			}
			
			confModelResource = modelResourceSet.getResource(confModelUri, true);
		
			if (confModelResource == null) {
				// shouldn't happen
				String msg = "ConfModel resource can't be loaded"; //$NON-NLS-1$
				handleResourceLoadingError(modelRoot, null, confModelUri,
						cmStatus, ConfModelStatus.RESOURCE_CANT_BE_LOADED, msg, null);
				return null;
			}
			
		} catch (RuntimeException rex) {
			Throwable actualException = (rex instanceof WrappedException) ? rex.getCause() : rex;
			if (actualException instanceof PackageNotFoundException)
			{
				// incomplete_jwt_extension_conf
			}
			else if (actualException instanceof ClassNotFoundException)
			{
				// incorrect_metamodel_extension_definition
				
			} else {
				String msg = "Error loading ConfModel"; //$NON-NLS-1$
				handleResourceLoadingError(modelRoot, modelResourceSet, confModelUri,
						cmStatus, ConfModelStatus.RESOURCE_LOADING_ERROR, msg, actualException);
			}

			if (!cmStatus.isTryPatchingMissingEmf()) {
				String msg = "Patchable (but won't try to patch) error " //$NON-NLS-1$
					+ "loading conf model resource"; //$NON-NLS-1$
				handleResourceLoadingError(modelRoot, modelResourceSet, confModelUri,
						cmStatus, ConfModelStatus.RESOURCE_PATCHABLE_MISSING_EMF,
						msg, actualException);
				return null;
				
			} else {
				// trying to patch conf model (also cleans resourceSet and refreshes) :
				ConfModelPatchingContext confModelPatcher = new ConfModelPatchingContext(confModelUri, modelResourceSet);
				try {
					confModelPatcher.auditConfModel();
					confModelPatcher.patchConfModel();
					
					// trying to reload the patched confModel :
					confModelResource = modelResourceSet.getResource(confModelUri, true);

					String msg = "Patched ConfModel file " + confModelUri //$NON-NLS-1$
							+ " by removing unknown profiles " + confModelPatcher.getNokProfileNameSet() //$NON-NLS-1$
							+ " and associated aspect instances. A backup has been made." //$NON-NLS-1$
							+ " Have a look at your plugin configuration." //$NON-NLS-1$
							+ ((modelRoot != null) ? " While looking for expected ConfModel of model " //$NON-NLS-1$
							+ modelRoot.eResource().getURI() : ""); //$NON-NLS-1$
					logger.severe(msg); // so it goes to the console
					cmStatus.add(new Status(IStatus.INFO, ConfPlugin.CONF_PLUGIN_ID,
							ConfModelStatus.RESOURCE_SUCCESSFULLY_PATCHED, msg, null));
					
				} catch (ConfResourceException crex) {
					String msg = "Error patching and reloading ConfModel"; //$NON-NLS-1$
					handleResourceLoadingError(modelRoot, modelResourceSet, confModelUri,
							cmStatus, ConfModelStatus.RESOURCE_PATCHING_ERROR, msg, crex);
					return null;
				}
			}
		}

		// let's look in the loaded Resource for the confModel
		ConfModel confModel = getConfModelFromConfModelResource(confModelResource);
		
		if (confModel != null && modelRoot != null) {
			// let's listen for the model's saves, to be able to save the confModel then :
			// cleaning up possible obsolete ConfModelSavingModelResourceAdapters
			Resource modelResource = modelRoot.eResource();
			for (Adapter resourceAdapter : new ArrayList<Adapter>(modelResource.eAdapters())) {
				if (resourceAdapter.isAdapterForType(ConfModelResourceManager.class)) {
					// TODO resourceAdapter
					modelResource.eAdapters().remove(resourceAdapter);
				}
			}
			// listening using a new adapter
			modelResource.eAdapters().add(new ConfModelSavingModelResourceAdapter(
					confModelResource, modelResource.getURI())); // TODO in SaveCommand ??
			modelRoot.eAdapters().add(new AspectEventManager()); // TODO not used anymore for onCreated, remove ?
		}
		
		return confModel;
	}

	/**
	 * 
	 * @param modelRoot
	 * @param modelResourceSet if null, badly loaded resources won't be cleaned
	 * @param confModelUri
	 * @param cmStatus
	 * @param code
	 * @param msg
	 * @param ex may be null
	 */
	private void handleResourceLoadingError(
			EObject modelRoot, ResourceSet modelResourceSet, URI confModelUri,
			ConfModelStatus cmStatus, int code, String msg, Throwable ex) {
		String errMsg = msg + " for ConfModel file " + confModelUri //$NON-NLS-1$
			+ ((modelRoot != null) ? " while looking for expected ConfModel of model " //$NON-NLS-1$
					+ modelRoot.eResource().getURI() : ""); //$NON-NLS-1$
		
		// flagging unavailability (error or no conf model)
		confModelAvailabilityManager.flagUnavailable(modelRoot, confModelUri, msg);
		
		if (modelResourceSet != null) {
			EMFResourceUtils.cleanBadlyLoadedResource(modelResourceSet, confModelUri);
		}

		// logging
		logger.log(Level.SEVERE, errMsg, ex);
		cmStatus.add(new Status(IStatus.ERROR, ConfPlugin.CONF_PLUGIN_ID, code, errMsg, ex));
		// NB. cause may be PackageNotFoundException
		// NB. before, would have thrown a ConfResourceException here
	}
	
	
	private ConfModel getConfModelFromConfModelResource(Resource confModelResource) {
		if (confModelResource == null) {
			return null;
		}
		EList<EObject> confModelResourceContents = confModelResource.getContents();
		if (confModelResourceContents == null || confModelResourceContents.isEmpty()) {
			return null;
		}
		
		EObject confModelObject = confModelResourceContents.get(0);
		if (confModelObject instanceof ConfModel) {
			// found it !	
			return (ConfModel) confModelObject;
		} // else TODO log
		
		return  null;
	}
	

	/* (non-Javadoc)
	 * @see org.eclipse.jwt.we.conf.resource.ConfModelResourceManager#migrateConfModelToModel(org.eclipse.jwt.we.conf.ConfModel, org.eclipse.emf.common.util.URI, org.eclipse.emf.common.util.URI, org.eclipse.emf.ecore.EObject)
	 */
	public ConfModel migrateConfModelToModel(ConfModel confModel, URI oldModelURI, URI newModelURI,
			EObject modelRoot) throws ConfResourceException {
		// update
		EMFResourceUtils.updateModelExternalResourceUri(confModel, oldModelURI, newModelURI);

		// saving it
		URI confModelUri = confModel.eResource().getURI();
		try {
			confModel.eResource().save(CONFMODEL_SAVE_OPTIONS);
		} catch (Exception ex) {
			String msg = "Error saving confModel " + confModelUri //$NON-NLS-1$
				+ " after updating it to target the right external resource " + newModelURI; //$NON-NLS-1$
			ConfPlugin.logger.warning(msg, ex);
			throw new ConfResourceException(msg, ex);
		}

		// let's remove the duplicated resource in the resource set by unload and reload confModel :
		cleanModelResourceSetFromConfModel(confModelUri, oldModelURI, newModelURI, modelRoot);

		// loading back a single ownURI resource in the model resource set :
		return loadConfModel(confModelUri, null, modelRoot, true);
	}
	
	/**
	 * Removes all confModel, oldModel and newModel resources from the resource set
	 * save the one newModel Resource containing modelRoot
	 * @param confModelUri
	 * @param oldModelURI
	 * @param newModelURI
	 * @param modelRoot
	 */
	protected void cleanModelResourceSetFromConfModel(URI confModelUri,
			URI oldModelURI, URI newModelURI, EObject modelRoot) {
		// unloading all resources at ownURI (including the first, i.e. confModelRoot's) :
		Resource modelResource = modelRoot.eResource();
		ResourceSet modelResourceSet = modelResource.getResourceSet();
		
		EMFResourceUtils.removeResourceFromResourceSet(confModelUri, modelResourceSet); // confModelUri must be unloaded to clean the newModelURI resources
		EMFResourceUtils.removeResourceFromResourceSet(oldModelURI, modelResourceSet); // oldModelURI resources are obsolete
		
		// unloading all resources pointing at newURI (that may have been doubly loaded) save the original one :
		for (Resource resource : new ArrayList<Resource>(modelResourceSet.getResources())) {
			if (newModelURI.equals(resource.getURI()) && modelResource != resource) {
				// NB. modelResource is necessarily in the modelResourceSet
				if (resource.isLoaded()) {
					resource.unload();
				}
				modelResourceSet.getResources().remove(resource);
			}
		}
		
		/*if (!modelResource.isLoaded()) {
			// if modelResource has nonetheless been unloaded (because listens to unloads), reload it
			modelResource.getResourceSet().getResource(modelResource.getURI(), true);
		}*/
	}

	
	/* (non-Javadoc)
	 * @see org.eclipse.jwt.we.conf.resource.ConfModelResourceManager#deleteConfModel(org.eclipse.jwt.we.conf.ConfModel)
	 */
	public boolean deleteConfModel(ConfModel confModel) {
		return EMFResourceUtils.deleteEMFResource(confModel);
	}

}
