package migration.tools;

import java.io.*;
import java.lang.reflect.Array;
import java.util.*;
import java.util.Map.Entry;

import migration.DerivedReference;
//import migration.Hide;
//import migration.IncomingReference;
import migration.Instance;
import migration.Migration;
//import migration.NewReference;






import migration.ReferencePath;

import org.eclipse.emf.common.util.*;
import org.eclipse.emf.ecore.*;
import org.eclipse.emf.ecore.resource.*;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl;

/**
 * Utilitary class for EMF models.
 * This class gathers usefull functions to load, save and modify easily
 * EMF models and metamodels.
 * @author M. Kerboeuf
 */
public class UtilEMF {

	// PRIVATE **********************************************************************************

	/**
	 * Private class dedicated to the utilitary function changeMetamodel.
	 * This class maps two sets of instances. The first one corresponds to the instances
	 * of a source model. The second one correponds to the instances of a copy of
	 * the source model conforming to another metamodel.
	 * @author M. Kerboeuf
	 */
	private static class EObjectMap {
		private Map<EObject, EObject> map;
		private Map<EObject, EObject> temporalmap;
		private Map<EObject, EObject> initialmap;
		Map<String, Map<String, Map<String, String>>> renameMap;
		private EObject  modelRootObject;
		private EObject  auxmodelRootObject;
		private EPackage oldMetamodelRootPackage;
		private EPackage newMetamodelRootPackage;

		/**
		 * Provides the package matching with a package path corresponding to a given package list.
		 * @param path The package path.
		 * @param ePackage The parent package from which the search starts.
		 * @return The package at the end of the path if it exists or null.
		 */
		private static EPackage getEndOfEPackagePath(List<EPackage> path, EPackage ePackage) {
			for (int i=1; i<path.size(); i++) {
				ePackage = getSubEPackage(ePackage, path.get(i).getName());
				if (ePackage==null) return null;
			}
			return ePackage;
		}

		/**
		 * Creates the list of the nested packages containing a given package.
		 * If this list contains a given first package, then this
		 * package is the first of the resulting list.
		 * @param p A package to scan.
		 * @param startPackage The start package from which the list is supposed to begin.
		 * @return The list of nested packages including p.
		 */
		private static List<EPackage> getEPackagePath(EPackage p, EPackage startPackage) {
			List<EPackage> path = new ArrayList<EPackage>();
			while (p!=null) {
				path.add(0, p);
				if (p.equals(startPackage)) break;
				p = p.getESuperPackage();
			}
			return path;
		}

		/**
		 * Provides a subpackage identified by a given name.
		 * @param p The parent package.
		 * @param ePackageName The name of the subpackage.
		 * @return The subpackage of p with the given name if it exists or null.
		 */
		private static EPackage getSubEPackage(EPackage p, String ePackageName) {
			for(EPackage subPackage : p.getESubpackages())
				if (subPackage.getName().equals(ePackageName)) return subPackage;
			return null;
		}

		/**
		 * Creates a map between the provided source instances and copies of
		 * this instances in the new metamodel.
		 * @param modelRootObject root instance of the source model.
		 * @param newMetamodelRootPackage root package of the new metamodel.
		 */
		public EObjectMap(EObject modelRootObject, EPackage newMetamodelRootPackage) {
			this.modelRootObject = modelRootObject;
			this.oldMetamodelRootPackage = modelRootObject.eClass().getEPackage();
			this.newMetamodelRootPackage = newMetamodelRootPackage;
			this.map = new HashMap<EObject, EObject>();
			for (EObject eo : getAllInstances(modelRootObject)){
				this.map.put(eo, this.createObjectCopy(eo));
			}
		}

		public EObjectMap(EObject modelRootObject, EPackage newMetamodelRootPackage, EObject modelRootObjectCopy) {
			this.modelRootObject = modelRootObject;
			this.oldMetamodelRootPackage = modelRootObject.eClass().getEPackage();
			this.newMetamodelRootPackage = newMetamodelRootPackage;
			this.map = new HashMap<EObject, EObject>();
			this.initialmap = new HashMap<EObject, EObject>();
			for (EObject eo : getAllInstances(modelRootObject)){
				this.map.put(eo, this.createObjectCopy(eo));
			}
			for (EObject eo : getAllInstances(modelRootObjectCopy)){
				this.initialmap.put(eo, this.createObjectCopy(eo));
			}
		}
		
		public EObjectMap(EObject modelRootObject, EPackage newMetamodelRootPackage, EObject modelRootObjectCopy, Map<String, Map<String, Map<String, String>>> renameMap) {
			this.modelRootObject = modelRootObject;
			this.oldMetamodelRootPackage = modelRootObject.eClass().getEPackage();
			this.newMetamodelRootPackage = newMetamodelRootPackage;
			this.map = new HashMap<EObject, EObject>();
			this.initialmap = new HashMap<EObject, EObject>();
			//System.out.println("  renameMap  "+renameMap);
			
			for (EObject eo : getAllInstances(modelRootObject)){
				this.map.put(eo, this.createObjectCopy(eo, renameMap));
			}
			for (EObject eo : getAllInstances(modelRootObjectCopy)){
				this.initialmap.put(eo, this.createObjectCopy(eo, renameMap));
			}
		}


		/**
		 * Creates a map between the provided source instances and copies of
		 * this instances in the new metamodel.
		 * @param modelRootObject root instance of the source model.
		 * @param newMetamodelRootPackage root package of the new metamodel.
		 */
		public EObjectMap(EObject auxmodelRootObject, EObject modelRootObject, EPackage newMetamodelRootPackage) {
			this.auxmodelRootObject = auxmodelRootObject;
			this.modelRootObject = modelRootObject;
			this.oldMetamodelRootPackage = modelRootObject.eClass().getEPackage();
			this.newMetamodelRootPackage = newMetamodelRootPackage;
			this.map = new HashMap<EObject, EObject>();
			for (EObject eo : getAllInstances(modelRootObject)) {
				this.map.put(eo, this.createObjectCopy(eo));
			}		
		}

		/// Migration
		public EObjectMap(EObject auxmodelRootObject, EObject modelRootObject, EPackage newMetamodelRootPackage, Map<String, Map<String,Map<String,String>>> renamingMap) {			
			this.auxmodelRootObject = auxmodelRootObject;
			this.modelRootObject = modelRootObject;
			this.oldMetamodelRootPackage = modelRootObject.eClass().getEPackage();
			this.newMetamodelRootPackage = newMetamodelRootPackage;
			this.map = new HashMap<EObject, EObject>();
			for (EObject eo : getAllInstances(modelRootObject)) {
				this.map.put(eo, this.createObjectCopy(eo, renamingMap));
			}
		}


		/**
		 * Creates in the copy the feature values of the source model.
		 */
		@SuppressWarnings("unchecked")
		public void copyFeatures() {
			for (EObject sourceInstance : this.map.keySet()) {
				EObject targetInstance = this.map.get(sourceInstance);
				// Recreation of changeable features (excluding derived features):
				for (EStructuralFeature sourceFeature : sourceInstance.eClass().getEAllStructuralFeatures()){
					if (sourceInstance.eIsSet(sourceFeature)) {
						EStructuralFeature targetFeature = targetInstance.eClass().getEStructuralFeature(sourceFeature.getName());
						if (targetFeature==null) throw new IllegalArgumentException("The source feature "+sourceInstance.eClass().getName()+"."+sourceFeature.getName()+" is not found in the target metamodel.");
						if (targetFeature.isChangeable() // there is something to set in the target...
								&& (!(targetFeature instanceof EReference) || !((EReference)targetFeature).isContainer())) // ...which is not a container
							if (sourceFeature.isMany()) // update of many valued feature
								for (Object sourceFeatureValue : (EList<?>)sourceInstance.eGet(sourceFeature)){
									((EList<Object>) targetInstance.eGet(targetFeature)).add(this.createFeatureValueCopy(sourceFeature, sourceFeatureValue));
								}
							else { // update of single valued reference
								Object sourceFeatureValue = sourceInstance.eGet(sourceFeature);
								if (sourceFeatureValue!=null) targetInstance.eSet(targetFeature, this.createFeatureValueCopy(sourceFeature, sourceFeatureValue));
							}
					}else{
					}
				}
			}
		}


		public EObject getObjectWithUUID(String uuid){
			EObject object = null;
			for (EObject sourceInstance : this.map.keySet()) {
				if((sourceInstance.eGet(sourceInstance.eClass().getEStructuralFeature("UUID")).toString()).equals(uuid)){
					object = sourceInstance;
					break;
				}
			}
			return object;
		}


		public String getReferenceName(Map<String, Map<String, ArrayList<Object>>> mapHidden, String uuid){
			//System.out.println(" uuid "+uuid);
			String result = null;
			for (Entry<String, Map<String, ArrayList<Object>>> e: mapHidden.entrySet()) {
				String key = e.getKey();
				if(key.equals(uuid)){
					Map<String, ArrayList<Object>> val2 = e.getValue();
					//System.out.println(" key "+key);
					//System.out.println(" val2 "+val2);
					for(Entry<String, ArrayList<Object>> e2: val2.entrySet()){
						result = e2.getKey();
						break;
					}
				}
			}
			return result;		
		}

		@SuppressWarnings("unchecked")
		public void copyFeatures2(Migration migration, Map<String, Map<String, ArrayList<Object>>> mapDerived, 
				Map<String, Map<String, ArrayList<Object>>> mapHidden, EObject modelRootObject2, 
				Map<String,String> newReferenceNameMap, Map<String, Map<String, Map<String, String>>> renameMap) {
			this.renameMap = renameMap;
			//System.out.println("renameMap "+this.renameMap);
			for (EObject sourceInstance : this.map.keySet()) {
				for(Instance instance: migration.getInstances()){
					if(instance.getUUID().equals(sourceInstance.eGet(sourceInstance.eClass().getEStructuralFeature("UUID")).toString())){
						//System.out.println("$$$  sourceInstance "+ instance.getUUID());
						if(!instance.getDerived().isEmpty()){
							EList<DerivedReference> deriveds = instance.getDerived();
							for(DerivedReference derived : deriveds){
								EList<ReferencePath> paths = derived.getPath();
								for(ReferencePath path:paths){
									EObject targetInstance = this.map.get(sourceInstance);
									// Recreation of changeable features (excluding derived features):
									for (EStructuralFeature sourceFeature : sourceInstance.eClass().getEAllStructuralFeatures()){
										//System.out.println("    ## sourceFeature "+ sourceFeature.getName());
										if(sourceFeature.getName().equals(path.getReferenceName())){
											buildMap(migration);
											Map<String, ArrayList<Object>> mapList = mapDerived.get(instance.getUUID());
											//System.out.println("mapList  "+mapList);
											if (sourceInstance.eIsSet(sourceFeature)) {
												//System.out.println("SEEEEEEEEEEEEt");
												EStructuralFeature targetFeature = targetInstance.eClass().getEStructuralFeature(sourceFeature.getName());
												if(targetFeature == null){
													for (Entry<EObject, EObject> e: this.map.entrySet()) {
														EObject key = e.getKey();
														String keyid = key.eGet(key.eClass().getEStructuralFeature("UUID")).toString();
														if(keyid.equals(sourceInstance.eGet(sourceInstance.eClass().getEStructuralFeature("UUID")).toString())){
															//System.out.println("keyid   "+keyid);
															for (Entry<String, ArrayList<Object>> e2: mapList.entrySet()) {
																String refName =  e2.getKey();
																ArrayList<Object> searched =  e2.getValue();
																targetFeature = targetInstance.eClass().getEStructuralFeature(refName);
																//System.out.println("targetFeature   "+targetFeature.getName());
																if (targetFeature.isChangeable()
																		&& (!(targetFeature instanceof EReference) || !((EReference)targetFeature).isContainer())){
																	if (sourceFeature.isMany()){
																		for (EObject sourceInstance2 : UtilEMF.getAllInstances(modelRootObject2)){
																			for (Entry<String, Map<String, ArrayList<Object>>> e3: mapHidden.entrySet()) {
																				String key3 = e3.getKey();
																				if(key3.equals(keyid)){
																					Map<String, ArrayList<Object>> val3 = e3.getValue();
																					for(Entry<String, ArrayList<Object>> e4: val3.entrySet()){
																						ArrayList<Object> refList = e4.getValue();
																						for(Object ref : refList){
																							if(ref.equals(sourceInstance2.eGet(sourceInstance2.eClass().getEStructuralFeature("UUID")).toString())){
																								for (EStructuralFeature sourceFeature2 : sourceInstance2.eClass().getEAllStructuralFeatures()){
																									String oldRefName = getReferenceName(mapHidden, ref.toString());
																									if(sourceFeature2.getName().equals(oldRefName)){
																										Object sourceFeatureValue = sourceInstance2.eGet(sourceFeature2);
																										if (sourceFeatureValue!=null){
																											for(Object search : searched){ 
																												((EList<Object>) targetInstance.eGet(targetFeature)).add(this.createFeatureValueCopy2(sourceFeature2, sourceFeatureValue, search.toString()));
																											}
																										}
																									}
																								}
																							}
																						}
																					}
																				}
																			}
																		}
																	}
																}
															}
														}
													}
												}
											}else{
												EStructuralFeature targetFeature = targetInstance.eClass().getEStructuralFeature(sourceFeature.getName());
												if(targetFeature == null){
													for (Entry<EObject, EObject> e: this.map.entrySet()) {
														EObject key = e.getKey();
														String keyid = key.eGet(key.eClass().getEStructuralFeature("UUID")).toString();
														if(keyid.equals(sourceInstance.eGet(sourceInstance.eClass().getEStructuralFeature("UUID")).toString())){
															//System.out.println("keyid   "+keyid);
															for (Entry<String, ArrayList<Object>> e2: mapList.entrySet()) {
																String refName =  e2.getKey();
																ArrayList<Object> searched =  e2.getValue();
																targetFeature = targetInstance.eClass().getEStructuralFeature(refName);
																//System.out.println("targetFeature   "+targetFeature.getName());
																if (targetFeature.isChangeable()
																		&& (!(targetFeature instanceof EReference) || !((EReference)targetFeature).isContainer())){
																	if (sourceFeature.isMany()){
																		for (EObject sourceInstance2 : UtilEMF.getAllInstances(modelRootObject2)){
																			for (Entry<String, Map<String, ArrayList<Object>>> e3: mapHidden.entrySet()) {
																				String key3 = e3.getKey();
																				if(key3.equals(keyid)){
																					Map<String, ArrayList<Object>> val3 = e3.getValue();
																					for(Entry<String, ArrayList<Object>> e4: val3.entrySet()){
																						ArrayList<Object> refList = e4.getValue();
																						for(Object ref : refList){
																							if(ref.equals(sourceInstance2.eGet(sourceInstance2.eClass().getEStructuralFeature("UUID")).toString())){
																								for (EStructuralFeature sourceFeature2 : sourceInstance2.eClass().getEAllStructuralFeatures()){
																									String oldRefName = getReferenceName(mapHidden, ref.toString());
																									if(sourceFeature2.getName().equals(oldRefName)){
																										Object sourceFeatureValue = sourceInstance2.eGet(sourceFeature2);
																										if (sourceFeatureValue!=null){
																											for(Object search : searched){ 
																												((EList<Object>) targetInstance.eGet(targetFeature)).add(this.createFeatureValueCopy2(sourceFeature2, sourceFeatureValue, search.toString()));
																											}
																										}
																									}
																								}
																							}
																						}
																					}
																				}
																			}
																		}
																	}
																}
															}
														}
													}
												}
											}
										}else{
											if (sourceInstance.eIsSet(sourceFeature)) {
												EStructuralFeature targetFeature = targetInstance.eClass().getEStructuralFeature(sourceFeature.getName());
												EStructuralFeature targetFeature2 = null;
												if (targetFeature==null){
													targetFeature2 = targetInstance.eClass().getEStructuralFeature(newReferenceNameMap.get(sourceFeature.getName())); // ****
													if(targetFeature2 == null){
														throw new IllegalArgumentException("The source feature "+sourceInstance.eClass().getName()+"."+sourceFeature.getName()+" is not found in the target metamodel.");
													}
												}
												if(targetInstance.eClass().getEStructuralFeature(newReferenceNameMap.get(sourceFeature.getName())) != null){
													if (targetFeature2.isChangeable() // there is something to set in the target...
															&& (!(targetFeature2 instanceof EReference) || !((EReference)targetFeature2).isContainer())){ // ...which is not a container
														if (sourceFeature.isMany()){ // update of many valued feature
															for (Object sourceFeatureValue : (EList<?>)sourceInstance.eGet(sourceFeature)){
																((EList<Object>) targetInstance.eGet(targetFeature2)).add(this.createFeatureValueCopy2(sourceFeature, sourceFeatureValue, "5"));
															}
														}else { // update of single valued reference
															Object sourceFeatureValue = sourceInstance.eGet(sourceFeature);
															if (sourceFeatureValue!=null) targetInstance.eSet(targetFeature2, this.createFeatureValueCopy2(sourceFeature, sourceFeatureValue,"5"));
														}
													}
												}else 
													if (targetFeature.isChangeable() // there is something to set in the target...
															&& (!(targetFeature instanceof EReference) || !((EReference)targetFeature).isContainer())){ // ...which is not a container
														if (sourceFeature.isMany()){ // update of many valued feature
															for (Object sourceFeatureValue : (EList<?>)sourceInstance.eGet(sourceFeature)){
																((EList<Object>) targetInstance.eGet(targetFeature)).add(this.createFeatureValueCopy(sourceFeature, sourceFeatureValue));
															}
														}else { // update of single valued reference
															Object sourceFeatureValue = sourceInstance.eGet(sourceFeature);
															if (sourceFeatureValue!=null) targetInstance.eSet(targetFeature, this.createFeatureValueCopy(sourceFeature, sourceFeatureValue));
														}
													}
											}
										}
									}
								}
							}
							// without derived
						}else{
							for (EStructuralFeature sourceFeature : sourceInstance.eClass().getEAllStructuralFeatures()){
								if (sourceInstance.eIsSet(sourceFeature)) {
									EObject targetInstance = this.map.get(sourceInstance);
									EStructuralFeature targetFeature = targetInstance.eClass().getEStructuralFeature(sourceFeature.getName());
									if (targetFeature==null) throw new IllegalArgumentException("The source feature "+sourceInstance.eClass().getName()+"."+sourceFeature.getName()+" is not found in the target metamodel.");
									if (targetFeature.isChangeable() // there is something to set in the target...
											&& (!(targetFeature instanceof EReference) || !((EReference)targetFeature).isContainer())) // ...which is not a container
										if (sourceFeature.isMany()) // update of many valued feature
											for (Object sourceFeatureValue : (EList<?>)sourceInstance.eGet(sourceFeature)){
												((EList<Object>) targetInstance.eGet(targetFeature)).add(this.createFeatureValueCopy(sourceFeature, sourceFeatureValue));
											}
										else { // update of single valued reference
											Object sourceFeatureValue = sourceInstance.eGet(sourceFeature);
											if (sourceFeatureValue!=null) targetInstance.eSet(targetFeature, this.createFeatureValueCopy(sourceFeature, sourceFeatureValue));
										}
								}
							}
						}
					}
				}
			}
		}	

		public static EObject changeMetamodel(EObject modelRootObject, EPackage newMetamodelRootPackage, Migration migrationHide){
			EObjectMap eObjectMap = new EObjectMap(modelRootObject, newMetamodelRootPackage);
			//eObjectMap.copyFeatures(migrationHide);
			return eObjectMap.getRootCopy();
		}

		public void buildMap(Migration migration){
			for (Entry<EObject, EObject> e: this.map.entrySet()) {
				EObject key = e.getKey();
				String keyid = key.eGet(key.eClass().getEStructuralFeature("UUID")).toString();
				//System.out.println("keyid "+keyid);
				EObject value = e.getValue();
				//System.out.println("val "+value);
				//String valueid = value.eGet(value.eClass().getEStructuralFeature("UUID")).toString();
				//System.out.println("valueid "+valueid);
			}
			for (EObject sourceInstance : this.map.keySet()) {
				//System.out.println(" || sourceInstance  "+sourceInstance.eGet(sourceInstance.eClass().getEStructuralFeature("UUID")));
				for (EStructuralFeature sourceFeature : sourceInstance.eClass().getEAllStructuralFeatures()){
					if (sourceInstance.eIsSet(sourceFeature)) {
						if (sourceFeature.isChangeable() // there is something to set in the target...
								&& (!(sourceFeature instanceof EReference) || !((EReference)sourceFeature).isContainer())){
							//System.out.println(" || sourceFeature  "+sourceFeature.getName());
							if (sourceFeature.isMany()){
								for (Object sourceFeatureValue : (EList<?>)sourceInstance.eGet(sourceFeature)){
									//System.out.println(" |-| sourceFeatureValue  "+ sourceFeatureValue);
									//System.out.println("     |--|   "+ getId(sourceFeatureValue));
								}
							}else{
								Object r = sourceInstance.eGet(sourceFeature);
								Object sourceFeatureValue = sourceInstance.eGet(sourceFeature);
								//System.out.println("   || sourceFeatureValue  "+ sourceFeatureValue);
							}
						}
					}
				}
			}
		}

		@SuppressWarnings("unchecked")
		public void copyFeatures(Migration migrationHide) {
			for (EObject sourceInstance : this.map.keySet()) {
				EObject targetInstance = this.map.get(sourceInstance);
				// Recreation of changeable features (excluding derived features):
				for (EStructuralFeature sourceFeature : sourceInstance.eClass().getEAllStructuralFeatures()){
					if (sourceInstance.eIsSet(sourceFeature)) {
						EStructuralFeature targetFeature = targetInstance.eClass().getEStructuralFeature(sourceFeature.getName());
						if (targetFeature==null) throw new IllegalArgumentException("The source feature "+sourceInstance.eClass().getName()+"."+sourceFeature.getName()+" is not found in the target metamodel.");
						if (targetFeature.isChangeable() // there is something to set in the target...
								&& (!(targetFeature instanceof EReference) || !((EReference)targetFeature).isContainer())) // ...which is not a container
							if (sourceFeature.isMany()) // update of many valued feature
								for (Object sourceFeatureValue : (EList<?>)sourceInstance.eGet(sourceFeature)){
									((EList<Object>) targetInstance.eGet(targetFeature)).add(this.createFeatureValueCopy(sourceFeature, sourceFeatureValue));
								}
							else { // update of single valued reference
								Object sourceFeatureValue = sourceInstance.eGet(sourceFeature);
								if (sourceFeatureValue!=null) targetInstance.eSet(targetFeature, this.createFeatureValueCopy(sourceFeature, sourceFeatureValue));
							}
					}else{
						EStructuralFeature targetFeature = targetInstance.eClass().getEStructuralFeature(sourceFeature.getName());
						for (EObject sourceInstance2 : this.map.keySet()) {
							String sourceInstanceId2 = sourceInstance2.eGet(sourceInstance2.eClass().getEStructuralFeature("UUID")).toString();
							for(EObject c:sourceInstance2.eContents()){
							}

							/*EObject targetInstance2 = this.map.get(sourceInstance2);
								System.out.println("   -- targetInstance2 "+targetInstance2);
								for (EStructuralFeature sourceFeature2 : sourceInstance2.eClass().getEAllStructuralFeatures()){
									System.out.println("sourceFeature2 "+ sourceFeature2.getName());
									if(sourceFeature2.getName().equals("neighborhoods")){
										EStructuralFeature targetFeature2 = sourceInstance2.eClass().getEStructuralFeature(sourceFeature2.getName());
									System.out.println("targetFeature2 "+ targetFeature2.getName());
									if (sourceFeature2.isMany()){ // update of many valued feature
										System.out.println("isMany   ");
										for (Object sourceFeatureValue : (EList<?>)sourceInstance2.eGet(sourceFeature2)){
											System.out.println("value   "+sourceFeatureValue);

											//((EList<Object>) targetInstance.eGet(targetFeature)).add(this.createFeatureValueCopy(sourceFeature, sourceFeatureValue));
										}
									}

									}
								}*/
						}

					}

				}
			}
		}

		/**
		 * Creates a copy in the new metamodel of a feature value related to a class
		 * of the source metamodel.
		 * @param sourceFeature Source feature.
		 * @param sourceFeatureValue Source feature value to copy.
		 * @return copy of the source feature value.
		 */
		/*private Object createFeatureValueCopy(EStructuralFeature sourceFeature, Object sourceFeatureValue) {
			EClassifier sourceAttributeType = sourceFeature.getEType();
			System.out.println("sourceAttributeType       "+sourceAttributeType);
			List<EPackage> sourceAttributeTypePackagePath = getEPackagePath(sourceAttributeType.getEPackage(), this.oldMetamodelRootPackage);
			System.out.println("sourceAttributeTypePackagePath "+sourceAttributeTypePackagePath);
			if (!sourceAttributeTypePackagePath.get(0).equals(this.oldMetamodelRootPackage)){
				System.out.println("  # sourceFeatureValue  "+sourceFeatureValue);
				// The type of the feature is not defined within the new package, so we try to keep the current value:
				return sourceFeatureValue;
			}
			EPackage targetPackage = getEndOfEPackagePath(sourceAttributeTypePackagePath, this.newMetamodelRootPackage);
			System.out.println("  targetPackage  "+targetPackage);
			if (targetPackage==null){
				System.out.println("  TP null  ");
				throw new IllegalArgumentException("The target EPackage matching with the source EPackage "+sourceAttributeType.getEPackage()+" is not found.");
			}
			if (sourceAttributeType instanceof EDataType) { // the feature is an attribute: data type (including enum)
				System.out.println("  EDataType  ");
				EClassifier targetAttributeType = targetPackage.getEClassifier(sourceAttributeType.getName());
				if (targetAttributeType==null) throw new IllegalArgumentException("The target EPackage "+targetPackage.getName()+" does not contain the required data type "+sourceAttributeType.getName());
				if (!(targetAttributeType instanceof EDataType)) throw new IllegalArgumentException("The target EPackage "+targetPackage.getName()+" contains a classifier "+sourceAttributeType.getName()+" which is not a data type (and which sould be)");
				return targetPackage.getEFactoryInstance().createFromString(((EDataType)targetAttributeType), sourceFeatureValue.toString());
			}
			System.out.println("  OK  " + this.map.get(sourceFeatureValue));
			// the feature is a local reference:
			return this.map.get(sourceFeatureValue);
		}*/

		private Object createFeatureValueCopy(EStructuralFeature sourceFeature, Object sourceFeatureValue) {
			EClassifier sourceAttributeType = sourceFeature.getEType();
			List<EPackage> sourceAttributeTypePackagePath = getEPackagePath(sourceAttributeType.getEPackage(), this.oldMetamodelRootPackage);
			if (!sourceAttributeTypePackagePath.get(0).equals(this.oldMetamodelRootPackage)){
				//System.out.println("  # sourceFeatureValue  "+sourceFeatureValue);
				// The type of the feature is not defined within the new package, so we try to keep the current value:
				return sourceFeatureValue;
			}
			EPackage targetPackage = getEndOfEPackagePath(sourceAttributeTypePackagePath, this.newMetamodelRootPackage);
			if (targetPackage==null){
				throw new IllegalArgumentException("The target EPackage matching with the source EPackage "+sourceAttributeType.getEPackage()+" is not found.");
			}
			if (sourceAttributeType instanceof EDataType) { // the feature is an attribute: data type (including enum)
				EClassifier targetAttributeType = targetPackage.getEClassifier(sourceAttributeType.getName());
				if (targetAttributeType==null) throw new IllegalArgumentException("The target EPackage "+targetPackage.getName()+" does not contain the required data type "+sourceAttributeType.getName());
				if (!(targetAttributeType instanceof EDataType)) throw new IllegalArgumentException("The target EPackage "+targetPackage.getName()+" contains a classifier "+sourceAttributeType.getName()+" which is not a data type (and which sould be)");
				return targetPackage.getEFactoryInstance().createFromString(((EDataType)targetAttributeType), sourceFeatureValue.toString());
			}
			// the feature is a local reference:
			return this.map.get(sourceFeatureValue);
		}

		private Object createFeatureValueCopy2(EStructuralFeature sourceFeature, Object sourceFeatureValue, String search) {
			EClassifier sourceAttributeType = sourceFeature.getEType();
			//System.out.println("sourceAttributeType       "+sourceAttributeType);
			List<EPackage> sourceAttributeTypePackagePath = getEPackagePath(sourceAttributeType.getEPackage(), this.oldMetamodelRootPackage);
			if (!sourceAttributeTypePackagePath.get(0).equals(this.oldMetamodelRootPackage)){
				//System.out.println("  # sourceFeatureValue  "+sourceFeatureValue);
				// The type of the feature is not defined within the new package, so we try to keep the current value:
				return sourceFeatureValue;
			}
			EPackage targetPackage = getEndOfEPackagePath(sourceAttributeTypePackagePath, this.newMetamodelRootPackage);
			//System.out.println("  targetPackage  "+targetPackage);
			if (targetPackage==null){
				throw new IllegalArgumentException("The target EPackage matching with the source EPackage "+sourceAttributeType.getEPackage()+" is not found.");
			}
			if (sourceAttributeType instanceof EDataType) { // the feature is an attribute: data type (including enum)
				EClassifier targetAttributeType = targetPackage.getEClassifier(sourceAttributeType.getName());
				if (targetAttributeType==null) throw new IllegalArgumentException("The target EPackage "+targetPackage.getName()+" does not contain the required data type "+sourceAttributeType.getName());
				if (!(targetAttributeType instanceof EDataType)) throw new IllegalArgumentException("The target EPackage "+targetPackage.getName()+" contains a classifier "+sourceAttributeType.getName()+" which is not a data type (and which sould be)");
				return targetPackage.getEFactoryInstance().createFromString(((EDataType)targetAttributeType), sourceFeatureValue.toString());
			}
			if(this.map.get(sourceFeatureValue) == null){
				return (Object)getValue(search);
			}else{			
				// the feature is a local reference:
				return this.map.get(sourceFeatureValue);				
			}
		}

		private Object createFeatureValueCopy3(EStructuralFeature sourceFeature, Object sourceFeatureValue, String search) {
			EClassifier sourceAttributeType = sourceFeature.getEType();
			//System.out.println("sourceAttributeType       "+sourceAttributeType);
			List<EPackage> sourceAttributeTypePackagePath = getEPackagePath(sourceAttributeType.getEPackage(), this.oldMetamodelRootPackage);
			if (!sourceAttributeTypePackagePath.get(0).equals(this.oldMetamodelRootPackage)){
				//System.out.println("  # sourceFeatureValue  "+sourceFeatureValue);
				// The type of the feature is not defined within the new package, so we try to keep the current value:
				return sourceFeatureValue;
			}
			EPackage targetPackage = getEndOfEPackagePath(sourceAttributeTypePackagePath, this.newMetamodelRootPackage);
			if (targetPackage==null){
				throw new IllegalArgumentException("The target EPackage matching with the source EPackage "+sourceAttributeType.getEPackage()+" is not found.");
			}
			if (sourceAttributeType instanceof EDataType) { // the feature is an attribute: data type (including enum)
				EClassifier targetAttributeType = targetPackage.getEClassifier(sourceAttributeType.getName());
				if (targetAttributeType==null) throw new IllegalArgumentException("The target EPackage "+targetPackage.getName()+" does not contain the required data type "+sourceAttributeType.getName());
				if (!(targetAttributeType instanceof EDataType)) throw new IllegalArgumentException("The target EPackage "+targetPackage.getName()+" contains a classifier "+sourceAttributeType.getName()+" which is not a data type (and which sould be)");
				return targetPackage.getEFactoryInstance().createFromString(((EDataType)targetAttributeType), sourceFeatureValue.toString());
			}
			return (Object)getValue(search);
		}


		public EObject getValue(String search){
			EObject result = null;
			for (Entry<EObject, EObject> e: this.map.entrySet()) {
				EObject key = e.getKey();
				String keyid = key.eGet(key.eClass().getEStructuralFeature("UUID")).toString();
				if(keyid.equals(search)){
					result = e.getValue();
					break;
				}
			}
			return result;
		}

		public String getId(Object sourceFeatureValue){
			String result = null;
			for (Entry<EObject, EObject> e: this.map.entrySet()) {
				EObject key = e.getKey();
				String keyid = key.eGet(key.eClass().getEStructuralFeature("UUID")).toString();
				EObject value = e.getValue();
				if(value.toString().equals(sourceFeatureValue.toString())){
					result = keyid;
					break;
				}
			}
			return result;
		}

		/**
		 * Creates a copy in the new metamodel of an instance conforming to the class
		 * of the source metamodel.
		 * @param source Object to copy.
		 * @return copy of the source object.
		 */
		private EObject createObjectCopy(EObject source) {
			EClass sourceClassType = source.eClass();
			List<EPackage> sourceClassTypePackagePath = getEPackagePath(sourceClassType.getEPackage(), this.oldMetamodelRootPackage);
			if (!sourceClassTypePackagePath.get(0).equals(this.oldMetamodelRootPackage))
				// The type of the object is not defined within the new package, so we try to create a new instance from this object's class:
				return EcoreUtil.create(sourceClassType);
			EPackage targetPackage = getEndOfEPackagePath(sourceClassTypePackagePath, this.newMetamodelRootPackage);

			if (targetPackage==null) throw new IllegalArgumentException("The target EPackage matching with the source EPackage "+sourceClassType.getEPackage()+" is not found.");
			EClassifier copyClass = targetPackage.getEClassifier(sourceClassType.getName());
			if (copyClass==null){	
				throw new IllegalArgumentException("The target EPackage "+targetPackage.getName()+" does not contain the required class "+sourceClassType.getName());
			}
			if (!(copyClass instanceof EClass)) throw new IllegalArgumentException("The target EPackage "+targetPackage.getName()+" contains a classifier "+sourceClassType.getName()+" which is not an EClass (and which sould be)");
			return EcoreUtil.create((EClass)copyClass);
		}

		public EPackage getContainer(EPackage targetPackage, EClass sourceClassType){
			EPackage subpack = null;
			for( EPackage subp : targetPackage.getESubpackages()){
				EClassifier copyClass2 = subp.getEClassifier(sourceClassType.getName());
				if(copyClass2!=null)
					subpack = subp;
				break;
			}

			return subpack;
		}

		/**
		 *  Creates a copy in the new metamodel of an instance conforming to the class
		 * of the source metamodel. 
		 * 
		 * @param source
		 * @param renameMap
		 * @return
		 */


		// Migration
		private EObject createObjectCopy(EObject source, Map<String, Map<String,Map<String,String>>> renameMap) {
			EClass sourceClassType = source.eClass();
			//System.out.println("renameMap " +renameMap);
			//System.out.println("sourceClassType " +sourceClassType.getName());
			Map<String, Map<String,String>> value = renameMap.get(sourceClassType.getName());
			String key = null ;
			if(value != null){
				for (Entry<String, Map<String,String>> e: value.entrySet()) {
					key = e.getKey();
				}
			}
			List<EPackage> sourceClassTypePackagePath = getEPackagePath(sourceClassType.getEPackage(), this.oldMetamodelRootPackage);
			if (!sourceClassTypePackagePath.get(0).equals(this.oldMetamodelRootPackage))
				// The type of the object is not defined within the new package, so we try to create a new instance from this object's class:
				return EcoreUtil.create(sourceClassType);
			EPackage targetPackage = getEndOfEPackagePath(sourceClassTypePackagePath, this.newMetamodelRootPackage);
			if (targetPackage==null) throw new IllegalArgumentException("The target EPackage matching with the source EPackage "+sourceClassType.getEPackage()+" is not found.");
			EClassifier searched = null;
			if(key != null){
				for(EClassifier tp : targetPackage.getEClassifiers()){
					if(tp.getName().equals(key)){
						searched = tp;
						break;
					}	
				}
			}
			EClassifier copyClass = searched;
			if (copyClass==null) throw new IllegalArgumentException("The target EPackage "+targetPackage.getName()+" does not contain the required class "+sourceClassType.getName());
			if (!(copyClass instanceof EClass)) throw new IllegalArgumentException("The target EPackage "+targetPackage.getName()+" contains a classifier "+sourceClassType.getName()+" which is not an EClass (and which sould be)");
			return EcoreUtil.create((EClass)copyClass);
		}


		/**
		 * Provides the source model copy.
		 * @return the copy of the root instance of the source model.
		 */
		public EObject getRootCopy() {
			return this.map.get(this.modelRootObject);
		}

	}

	// PUBLIC ***********************************************************************************

	/**
	 * Addition of UUID attributes.
	 * @param metamodelFilePath File path of the metamodel to be extended with UUID attibutes.
	 * @return Loaded and updated root package.
	 */
	public static EPackage addUUIDAttribute(String metamodelFilePath) {
		// Metamodel loading:		
		EPackage metamodelRootPackage = loadMetamodel(metamodelFilePath);
		// Update:
		return addUUIDAttribute(metamodelRootPackage);
	}

	/**
	 * Addition of UUID attributes.
	 * @param metamodelURI URI of the metamodel to be extended with UUID attibutes.
	 * @return Loaded and updated root package.
	 */
	public static EPackage addUUIDAttribute(URI metamodelURI) {
		// Metamodel loading:		
		EPackage metamodelRootPackage = loadMetamodel(metamodelURI);
		// Update:
		return addUUIDAttribute(metamodelRootPackage);
	}

	/**
	 * Addition of UUID attributes.
	 * @param metamodelRootPackage Root package of the metamodel to be extended with UUID attibutes.
	 * @return Updated root package.
	 */
	public static EPackage addUUIDAttribute(EPackage metamodelRootPackage) {
		for (TreeIterator<EObject> it = metamodelRootPackage.eAllContents(); it.hasNext();) {
			EObject next = it.next();
			if (next instanceof EClass) { // update of the classes...
				EClass c = (EClass) next;
				boolean toUpdate = true; //...that are not already updated by inheritance
				for (EClass supClass : c.getESuperTypes()) // scan of inherited classes
					if (metamodelRootPackage.equals(supClass.getEPackage())) {
						// an inherited class is already in the same package
						toUpdate = false;
						break;
					}
				if (toUpdate) {
					EAttribute uuid = EcoreFactory.eINSTANCE.createEAttribute();
					uuid.setName("UUID");
					uuid.setEType(EcorePackage.eINSTANCE.getEString());
					uuid.setLowerBound(0);
					uuid.setUpperBound(1);
					uuid.setUnique(true);
					c.getEStructuralFeatures().add(uuid);
				}
			}
		}
		return metamodelRootPackage;
	}

	/*public static EPackage removeUUID(EPackage metamodelRootPackage){
		for(TreeIterator<EObject> it = metamodelRootPackage.eAllContents(); it.hasNext();){
			EObject next = it.next();
			if(next instanceof EClass){
				EClass c = (EClass) next;
				boolean toUpdate = true;
				for(EClass supClass : c.getESuperTypes())
					if(metamodelRootPackage.equals(supClass.getEPackage())){
						toUpdate = false;
						break;
					}
				if(toUpdate){
					for(EAttribute ea: c.getEAllAttributes()){
						if(ea.getName().equals("UUID")){
							c.getEAllAttributes();
						}
					}
				}
			}
		}
		return metamodelRootPackage;
	}*/


	/**
	 * Deletion of UUID attributes
	 * @param metamodelFilePath File path of the metamodel from wich UUID attributes have to me removed.
	 * @return Updated root package
	 */
	public static EPackage removeUUIDAttribute(String metamodelFilePath){
		// Metamodel loading
		EPackage metamodelRootPackage = loadMetamodel(metamodelFilePath);
		// Update
		return removeUUIDAttribute(metamodelRootPackage);
	}

	/*public static EPackage removeUUIDAttribute(URI metamodelURI){
		// Metamodel loading:
		EPackage metamodel
	}*/
	/**
	 * Creation of UUID values.
	 * @param modelRoot Root object of the model to be modified.
	 * @return Updated root object.
	 */
	public static EObject addUUIDValues(EObject modelRoot) {
		// UUID feature of root object:
		EStructuralFeature uuid = null;
		EList<EAttribute> a = modelRoot.eClass().getEAllAttributes();
		for(int i=0; i<a.size() && uuid==null; i++) if (a.get(i).getName().equals("UUID")) uuid = a.get(i);
		// Update of root object :
		if (uuid!=null) modelRoot.eSet(uuid, EcoreUtil.generateUUID());
		// Update of children object :
		for(EObject modelRootChild : modelRoot.eContents()) addUUIDValues(modelRootChild);
		return modelRoot;
	}

	/**
	 * Creates a copy of a given model conforming to a new metamodel.
	 * The new metamodel must be able to produce all the instances of the given model
	 * with all its features. This function is typically used to replace the metamodel
	 * of a model by a direct copy of its current metamodel. 
	 * @param modelRootObject The source model to be recreated with a new metamodel.
	 * @param newMetamodelRootPackage The new metamodel.
	 * @return The copy of the source model with the given new metamodel.
	 */
	public static EObject changeMetamodel(EObject modelRootObject, EPackage newMetamodelRootPackage) {
		EObjectMap eObjectMap = new EObjectMap(modelRootObject, newMetamodelRootPackage);
		eObjectMap.copyFeatures();
		return eObjectMap.getRootCopy();
	}

	public static EObject changeMetamodel(EObject modelRootObject, EObject modelRootObject2, EPackage newMetamodelRootPackage, 
			Migration migrationHide, Map<String, Map<String, ArrayList<Object>>> mapDerivedReferences, Map<String, Map<String, 
			ArrayList<Object>>> mapHiddenReferences, Map<String,String> newRefNameMap, Map<String, Map<String, Map<String, String>>> renameMap) {
		EObjectMap eObjectMap = new EObjectMap(modelRootObject, newMetamodelRootPackage, modelRootObject2, renameMap);
		eObjectMap.copyFeatures2(migrationHide, mapDerivedReferences, mapHiddenReferences, modelRootObject2, newRefNameMap, renameMap);
		return eObjectMap.getRootCopy();
	}


	/**
	 * Creates a copy of a given model conforming to a new metamodel.
	 * The new metamodel must be able to produce all the instances of the given model
	 * with all its features. This function is typically used to replace the metamodel
	 * of a model by a direct copy of its current metamodel. 
	 * @param modelRootObject The source model to be recreated with a new metamodel.
	 * @param newMetamodelRootPackage The new metamodel.
	 * @return The copy of the source model with the given new metamodel.
	 */
	public static EObject changeMetamodel(EObject auxmodelRootObject, EObject modelRootObject, EPackage newMetamodelRootPackage) {
		EObjectMap eObjectMap = new EObjectMap(auxmodelRootObject, modelRootObject, newMetamodelRootPackage);
		eObjectMap.copyFeatures();
		return eObjectMap.getRootCopy();
	}

	public static EObject changeMetamodel(EObject modelRootObject, EPackage newMetamodelRootPackage, Migration migrationHide){
		EObjectMap eObjectMap = new EObjectMap(modelRootObject, newMetamodelRootPackage);
		eObjectMap.copyFeatures(migrationHide);
		return eObjectMap.getRootCopy();
	}

	public static EObject changeMetamodel(EObject auxmodelRootObject, EObject modelRootObject, 
			EPackage newMetamodelRootPackage, Map<String, Map<String, Map<String, String>>> renameMap, Migration migration, boolean reversed) {
		EObjectMap eObjectMap = new EObjectMap(auxmodelRootObject, modelRootObject, newMetamodelRootPackage, renameMap);
		if(!reversed){
			//eObjectMap.copyFeatures(renameMap, migration);
		}
		else{
			//eObjectMap.copyFeaturesReversed(renameMap, migration);
		}
		return eObjectMap.getRootCopy();
	}

	/**
	 * Modification of metamodel.
	 * @param modelRootObject Root object whose metaclass belongs
	 * to the EPackage which is intended to be modified.
	 * @param newMetamodelFilePath File path of the new metamodel. 
	 * @return Updated root object.
	 */
	public static EObject changeMetamodel(EObject modelRootObject, String newMetamodelFilePath) {
		// Metamodel loading:
		EPackage newMetamodelRootPackage = loadMetamodel(newMetamodelFilePath);
		// Model update
		return changeMetamodel(modelRootObject, newMetamodelRootPackage);
	}

	/**
	 * Modification of metamodel.
	 * @param modelRootObject Root object whose metaclass belongs
	 * to the EPackage which is intended to be modified.
	 * @param newMetamodelURI URI of the new metamodel. 
	 * @return Updated root object.
	 */
	public static EObject changeMetamodel(EObject modelRootObject, URI newMetamodelURI) {
		// Metamodel loading:
		EPackage newMetamodelRootPackage = loadMetamodel(newMetamodelURI);
		// Model update
		return changeMetamodel(modelRootObject, newMetamodelRootPackage);
	}

	/**
	 * Dynamic instanciation of a metaclass.
	 * @param className Name of the metaclass to instanciate.
	 * @param metamodelRootPackage Root package of the metamodel defining the metaclass.
	 * @return A new instance of the provided metaclass if it exists or null.
	 */
	public static EObject createInstance(String className, EPackage metamodelRootPackage) {
		EClassifier classToInstanciate = metamodelRootPackage.getEClassifier(className);
		if (classToInstanciate==null || !(classToInstanciate instanceof EClass)) return null;
		return EcoreUtil.create((EClass)classToInstanciate);
	}

	/**
	 * Dynamic instanciation of a metaclass.
	 * @param className Name of the metaclass to instanciate.
	 * @param metamodelFilePath File path of the metamodel defining the metaclass.
	 * @return A new instance of the provided metaclass.
	 */
	public static EObject createInstance(String className, String metamodelFilePath) {
		return createInstance(className, loadMetamodel(metamodelFilePath));
	}

	/**
	 * Dynamic instanciation of a metaclass.
	 * @param className Name of the metaclass to instanciate.
	 * @param metamodelURI URI of the metamodel defining the metaclass.
	 * @return A new instance of the provided metaclass.
	 */
	public static EObject createInstance(String className, URI metamodelURI) {
		return createInstance(className, loadMetamodel(metamodelURI));
	}

	/**
	 * Creation of a default resource set associated to an EPackage NS_URI
	 * and an instance of this EPackage.
	 * @param nsuri NS_URI of the EPackage.
	 * @param metamodelRootPackage Instance of the EPackage.
	 * @return New resource set which package registry maps nsuri to metamodelRootPackage.
	 */
	public static ResourceSet createResourceSet (String nsuri, Object metamodelRootPackage) {
		ResourceSet resourceSet = new ResourceSetImpl();
		// Default XMI extension:
		resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap()
		.put(Resource.Factory.Registry.DEFAULT_EXTENSION, new XMIResourceFactoryImpl());
		// Register:
		resourceSet.getPackageRegistry().put(nsuri, metamodelRootPackage);
		return resourceSet;
	}

	/**
	 * Initialisation of a map relating UUIDs to the corresponding EObjects.
	 * @param root The root instance of the model to scan.
	 * @return The map relating UUIDs to instances of root (root included).
	 */
	public static Map<String, EObject> createUUIDMap(EObject root) {
		Map<String, EObject> map = new HashMap<String, EObject>();
		List<EObject> fifo = new ArrayList<EObject>();
		for(fifo.add(root); !fifo.isEmpty(); ) {
			EObject next = fifo.remove(0);
			String nextUUID = UtilEMF.getUUID(next);
			if (nextUUID!=null) map.put(nextUUID, next);
			fifo.addAll(next.eContents());
		}
		return map;
	}

	/**
	 * Provides the set of external packages definig feature types.
	 * @param root The root package to scan.
	 * @return The set of packages that are not defined within or equal to root, and that
	 * define classifiers used as type of features.
	 */
	public static Set<EPackage> getAllExternalPackages(EPackage root) {
		Set<EPackage> allExternal = new HashSet<EPackage>(); // result: new empty set recursively extended by the subpackages:
		for (EPackage subPackage : root.getESubpackages()) allExternal.addAll(getAllExternalPackages(subPackage));
		// We scan now the direct content of the package:
		for (EClassifier ec : root.getEClassifiers())
			if (ec instanceof EClass)
				for (EStructuralFeature er : ((EClass)ec).getEAllStructuralFeatures()) {
					EPackage refPackage = er.getEType().getEPackage(); // package to check
					boolean inside = false;
					while  (!inside && refPackage!=null) {
						if (refPackage.equals(root)) inside=true;
						refPackage = refPackage.getESuperPackage();
					}
					if (!inside) allExternal.add(er.getEType().getEPackage());
				}		
		return allExternal;
	}

	/**
	 * Creation of a set of instances contained into a root instance, including this root instance.
	 * @param root Root instance.
	 * @return A new set containing all the content of root, including root.
	 */
	public static Set<EObject> getAllInstances(EObject root) {
		Set<EObject> allContent = new HashSet<EObject>();
		allContent.add(root);
		for(TreeIterator<EObject> it = root.eAllContents(); it.hasNext();) allContent.add(it.next());
		return allContent;
	}

	/**
	 * Get the String value of UUID if it exists.
	 * @param o Object to scan.
	 * @return The UUID of o if it exists or null.
	 */
	public static String getUUID (Object o) {
		EObject instance = ( (o==null||!(o instanceof EObject)) ? null : (EObject)o );
		EStructuralFeature esfUUID = (instance==null ? null : instance.eClass().getEStructuralFeature("UUID"));
		Object value = (esfUUID==null ? null : instance.eGet(esfUUID));
		return ( (value==null||!(value instanceof String)) ? null : (String)value );
	}

	/**
	 * Metamodel (Ecore model) loading.
	 * @param metamodelFilePath File path of the metamodel to load.
	 * @return Root package of the loaded metamodel.
	 */
	public static EPackage loadMetamodel (String metamodelFilePath) {
		// Metamodel loading:				
		return loadMetamodel(URI.createFileURI(new File(metamodelFilePath).getAbsolutePath()));
	}

	/**
	 * Metamodel (Ecore model) loading.
	 * @param metamodelURI URI of the metamodel to load.
	 * @return Root package of the loaded metamodel.
	 */
	public static EPackage loadMetamodel (URI metamodelURI) {
		// Model loading:		
		return (EPackage)loadModel(metamodelURI, EcorePackage.eINSTANCE);
	}

	/**
	 * Model loading.
	 * @param inputFilePath File name of the model to load.
	 * @param metamodelRootPackage EPackage expected to be
	 * the container of the metaclass of the model root object.
	 * @return The EObject corresponding to the loaded model root object.
	 */
	public static EObject loadModel(String inputFilePath, EPackage metamodelRootPackage) {
		return loadModel(URI.createFileURI(new File(inputFilePath).getAbsolutePath()), metamodelRootPackage);
	}

	/**
	 * Model loading.
	 * @param modelInputFilePath File name of the model to load.
	 * @param metamodelInputFilePath File name of the corresponding metamodel.
	 * @return The EObject corresponding to the loaded model root object.
	 */
	public static EObject loadModel(String modelInputFilePath, String metamodelInputFilePath) {
		return loadModel(URI.createFileURI(new File(modelInputFilePath).getAbsolutePath()), metamodelInputFilePath);
	}

	/**
	 * Model loading.
	 * @param inputFilePath File name of the model to load.
	 * @param metamodelURI URI of the corresponding metamodel.
	 * @return The EObject corresponding to the loaded model root object.
	 */
	public static EObject loadModel(String inputFilePath, URI metamodelURI) {
		return loadModel(URI.createFileURI(new File(inputFilePath).getAbsolutePath()), metamodelURI);
	}

	/**
	 * Model loading.
	 * @param modeluri URI of the model to load.
	 * @param metamodelRootPackage EPackage expected to be
	 * the container of the metaclass of the model root object.
	 * @return The EObject corresponding to the loaded model root object.
	 */
	public static EObject loadModel (URI modeluri, EPackage metamodelRootPackage) {
		// Creation of a ResourceSet mapping the package ns uri to the package:
		ResourceSet resourceSet = createResourceSet(metamodelRootPackage.getNsURI(), metamodelRootPackage);
		// Creation of a resource linked to the model uri:
		Resource resource = resourceSet.getResource(modeluri, true);
		// First element of the model (root object):
		return resource.getContents().get(0);
	}

	/**
	 * Model loading.
	 * @param modeluri URI of the model to load.
	 * @param metamodelFilePath File name of the corresponding metamodel.
	 * @return The EObject corresponding to the loaded model root object.
	 */
	public static EObject loadModel (URI modeluri, String metamodelFilePath) {
		// Model loading:		
		return loadModel(modeluri, URI.createFileURI(new File(metamodelFilePath).getAbsolutePath()));
	}

	/**
	 * Model loading.
	 * @param modeluri URI of the model to load.
	 * @param metamodelURI URI of the corresponding metamodel.
	 * @return The EObject corresponding to the loaded model root object.
	 */
	public static EObject loadModel (URI modeluri, URI metamodelURI) {
		// Metamodel loading:
		EPackage metamodelRootPackage = loadMetamodel(metamodelURI);
		// Model loading:				
		return loadModel(modeluri, metamodelRootPackage);
	}

	/**
	 * Metamodel processor: removes annotations. 
	 * @param metaModelRootObject Root object of the metamodel to be processed.
	 * @return Root object of the updated metamodel (without annotations).
	 */
	public static EPackage removeAnnotations(EPackage metaModelRootObject) {
		List<EAnnotation> annotToRemove = new ArrayList<EAnnotation>(); // will contain all annotations
		// Scan of metamodel content:
		for (TreeIterator<EObject> it = metaModelRootObject.eAllContents(); it.hasNext(); ) {
			EObject next = it.next();
			String nextEClassName = next.eClass().getName();
			if (nextEClassName.equals("EAnnotation")) annotToRemove.add((EAnnotation)next);
		}
		// Removing references of deleted operation witin annotations:
		for (EAnnotation ea : annotToRemove) EcoreUtil.remove(ea);
		return metaModelRootObject;
	}

	/**
	 * Metamodel processor: removes operations. 
	 * @param metaModelRootObject Root object of the metamodel to be processed.
	 * @return Root object of the updated metamodel (without operations).
	 */
	public static EPackage removeOperations(EPackage metaModelRootObject) {
		List<EOperation>  opToRemove     = new ArrayList<EOperation>();  // will contain removed operations
		List<EAnnotation> annotToProcess = new ArrayList<EAnnotation>(); // will contain all annotations
		// Scan of metamodel content:
		for (TreeIterator<EObject> it = metaModelRootObject.eAllContents(); it.hasNext(); ) {
			EObject next = it.next();
			String nextEClassName = next.eClass().getName();
			if      (nextEClassName.equals("EClass"))      opToRemove.addAll(((EClass)next).getEOperations());
			else if (nextEClassName.equals("EAnnotation")) annotToProcess.add((EAnnotation)next);
		}
		// Removing references of deleted operation witin annotations:
		for (EAnnotation ea : annotToProcess) ea.getReferences().removeAll(opToRemove);
		// Removing operations:
		for (EOperation eo : opToRemove) eo.getEContainingClass().getEOperations().remove(eo);
		return metaModelRootObject;
	}

	/**
	 * Metamodel processor: removes opposite feature from all references. 
	 * @param metaModelRootObject Root object of the metamodel to be processed.
	 * @return Root object of the updated metamodel (without opposite references).
	 */
	public static EPackage removeOppositeFeature(EPackage metaModelRootObject) {
		List<EReference> refToUpdate = new ArrayList<EReference>();  // will contain updated references
		// Scan of metamodel content:
		for (TreeIterator<EObject> it = metaModelRootObject.eAllContents(); it.hasNext(); ) {
			EObject next = it.next();
			String nextEClassName = next.eClass().getName();
			if (nextEClassName.equals("EClass")) refToUpdate.addAll(((EClass)next).getEReferences());
		}
		// Updating operations:
		for (EReference er : refToUpdate) er.setEOpposite(null);
		return metaModelRootObject;
	}

	/**
	 * Deletion of UUID values.
	 * @param modelRoot Root object of the model to be modified.
	 * @return Updated root object.
	 */
	public static EObject removeUUIDValues(EObject modelRoot) {
		// UUID feature of root object:
		EStructuralFeature uuid = null;
		EList<EAttribute> a = modelRoot.eClass().getEAllAttributes();
		for(int i=0; i<a.size() && uuid==null; i++) if (a.get(i).getName().equals("UUID")) uuid = a.get(i);
		// Update of root object :
		if (uuid!=null) modelRoot.eUnset(uuid);
		// Update of children object :
		for(EObject modelRootChild : modelRoot.eContents()) removeUUIDValues(modelRootChild);
		return modelRoot;
	}

	/**
	 * Metamodel saving.
	 * The ressource and NsURI of the package are updated.
	 * The packages corresponding to external dependencies 
	 * are serialised locally together with the metamodel itself.
	 * @param metaModelRootObject Root object of the metamodel to be serialized.
	 * @param outputFilePath Name of the file to be created or updated.
	 * @throws IOException Thrown if the output file cannot be created.
	 * @return The updated root package.
	 */
	public static EPackage saveMetamodel(EPackage metaModelRootObject, String outputFilePath) throws IOException {
		return saveMetamodel(metaModelRootObject, outputFilePath, true);
	}

	/**
	 * Metamodel saving.
	 * The ressource and NsURI of the package are updated.
	 * The packages corresponding to external dependencies 
	 * can be serialised locally together with the metamodel itself.
	 * @param metaModelRootObject Root object of the metamodel to be serialized.
	 * @param outputFilePath Name of the file to be created or updated.
	 * @param withExternalPackageDependencies States if the external dependencies must but serialized or not.
	 * @throws IOException Thrown if the output file cannot be created.
	 * @return The updated root package.
	 */
	public static EPackage saveMetamodel(EPackage metaModelRootObject, String outputFilePath, boolean withExternalPackageDependencies) throws IOException {
		File metamodelFile = new File(outputFilePath);
		String metamodelFilename  = metamodelFile.getAbsolutePath();
		// New NsURI matching with the new file path:
		metaModelRootObject.setNsURI(URI.createFileURI(metamodelFilename).toString());
		// Request for the serialization of external dependencies:
		if (withExternalPackageDependencies) {
			Map<EPackage, Resource> resourcesBackup = new HashMap<EPackage, Resource>();
			for (EPackage ep : getAllExternalPackages(metaModelRootObject)) {
				String pname = ep.getName()+".ecore";
				pname =  Character.toUpperCase(pname.charAt(0)) + pname.substring(1);
				// backup of current resource before saving (and so update):
				Resource currentResource = ep.eResource();
				if (currentResource!=null) resourcesBackup.put(ep, currentResource);
				saveModel(ep, metamodelFile.getParentFile().getAbsolutePath()+"/"+pname);
			}
			// Metamodel serialization:
			saveModel(metaModelRootObject, metamodelFilename);
			// Restoring the initial resources:
			for (EPackage ep : resourcesBackup.keySet()) resourcesBackup.get(ep).getContents().add(ep);
		}
		else saveModel(metaModelRootObject, metamodelFilename);
		return metaModelRootObject;
	}

	/**
	 * Model saving.
	 * @param modelRootObject Root object to be serialized.
	 * @param outputFilePath Name of the file to be created or updated.
	 * @throws IOException Thrown if the output file cannot be created.
	 * @return The updated root object.
	 */
	public static EObject saveModel(EObject modelRootObject, String outputFilePath) throws IOException {
		// Creating the resource set from the metadata of modelRootObject:
		ResourceSet resourceSet = createResourceSet(modelRootObject.eClass().getEPackage().getNsURI(), modelRootObject.eClass().getEPackage());
		// Creating a new resource in this resource set:
		Resource resource = resourceSet.createResource(URI.createFileURI(new File(outputFilePath).getAbsolutePath()));
		// Adding the object in the new resource and saving:
		resource.getContents().add(modelRootObject);
		resource.save(Collections.EMPTY_MAP);
		return modelRootObject;
	}


	/**
	 * 
	 * @param metamodelRootPackage
	 * @return 
	 */
	public static boolean hasUUIDAttribute(EPackage metamodelRootPackage) {
		boolean hasKeys = false;
		for (TreeIterator<EObject> it = metamodelRootPackage.eAllContents(); it.hasNext();) {
			EObject next = it.next();
			if (next instanceof EClass) { // update of the classes...
				EClass c = (EClass) next;
				for(EStructuralFeature feature :c.getEStructuralFeatures() ){
					if(feature.getName().equals("UUID")){
						hasKeys = true;
						break;
					}
				}
				for (EClass supClass : c.getESuperTypes()) {// scan of inherited classes
					for(EStructuralFeature feature :supClass.getEStructuralFeatures() ){
						if(feature.getName().equals("UUID")){
							hasKeys = true;
							break;
						}
					}
				}
			}
		}
		return hasKeys;
	}


	/**
	 * 
	 * @param metamodelRootPackage
	 * @return metamodel without duplicate UUID
	 */
	public static EPackage removeDuplicateUUIDAttribute(EPackage metamodelRootPackage) {
		List<EStructuralFeature> uuidToRemove = new ArrayList<EStructuralFeature>();
		for (TreeIterator<EObject> it = metamodelRootPackage.eAllContents(); it.hasNext();) {
			EObject next = it.next();

			EStructuralFeature uuid_c = null;
			EStructuralFeature uuid_sc = null;

			if (next instanceof EClass) { // update of the classes...
				EClass c = (EClass) next;
				for(EStructuralFeature feature :c.getEStructuralFeatures() ){
					if(feature.getName().equals("UUID")){
						uuid_c = feature;
					}
				}
				for (EClass supClass : c.getESuperTypes()) {// scan of inherited classes
					for(EStructuralFeature feature :supClass.getEStructuralFeatures() ){
						if(feature.getName().equals("UUID")){
							uuid_sc = feature;
							if(uuid_c != null && uuid_sc != null){
								uuidToRemove.add(uuid_c);
							}
						}
					}
				}
			}
		}
		for (EStructuralFeature ef : uuidToRemove) EcoreUtil.remove(ef);
		return metamodelRootPackage;
	}

	/**
	 * 
	 * @param metamodelRootPackage
	 * @return metamodel without UUID
	 */
	public static EPackage removeUUIDAttribute(EPackage metamodelRootPackage){
		List<EStructuralFeature> uuidToRemove = new ArrayList<EStructuralFeature>();
		for(TreeIterator<EObject> it = metamodelRootPackage.eAllContents(); it.hasNext();){
			EObject next = it.next();
			EStructuralFeature uuid = null;
			if(next instanceof EClass){
				EClass c = (EClass) next;
				for(EStructuralFeature feature : c.getEStructuralFeatures()){
					if(feature.getName().equals("UUID")){
						uuid = feature;
						uuidToRemove.add(uuid);
					}
				}
			}
		}
		for(EStructuralFeature ef : uuidToRemove) EcoreUtil.remove(ef);
		return metamodelRootPackage;
	}


	/**
	 * Creation of UUID values.
	 * @param modelRoot Root object of the model to be modified.
	 * @return Updated root object.
	 */

	public static int uuidValue = 0;
	public static String uuidVal = ""+uuidValue;
	public static EObject addUUIDValues(EObject modelRoot, EPackage rootPackage) {
		// UUID feature of root object:
		EStructuralFeature uuid = null;
		EList<EAttribute> a = modelRoot.eClass().getEAllAttributes();
		for(int i=0; i<a.size() && uuid==null; i++) 
			if (a.get(i).getName().equals("UUID")) 
				uuid = a.get(i);
		// Update of root object :
		if (uuid!=null){ 

			if(rootPackage.getName().equals("cityis")){
				if(modelRoot.eClass().getName().equals("Citizen")){
					modelRoot.eSet(uuid,modelRoot.eGet(modelRoot.eClass().getEStructuralFeature("name")));
				}else if(modelRoot.eClass().getName().equals("Address")){
					modelRoot.eSet(uuid,modelRoot.eGet(modelRoot.eClass().getEStructuralFeature("number")).toString());
				}else{
					modelRoot.eSet(uuid,modelRoot.eGet(modelRoot.eClass().getEStructuralFeature("name")));
				}
			}else{
				modelRoot.eSet(uuid, uuidVal);
				uuidValue= uuidValue+1;
				uuidVal= ""+uuidValue;	
			}

			//modelRoot.eSet(uuid, EcoreUtil.generateUUID());
		}
		// Update of children object :
		for(EObject modelRootChild : modelRoot.eContents()) addUUIDValues(modelRootChild, rootPackage);
		return modelRoot;
	}

	public static Map<String, Map<String, ArrayList<Object>>> buildMapDerived() {
		// TODO Auto-generated method stub
		return null;
	}


	/*public static Hide isSourceOfHide(Migration migration, String uuid){
		Hide Hreturn = null;
		for(Instance instance : migration.getInstances()){
			//System.out.println("instance " +instance);
			Hide hide = instance.getHide();
			//System.out.println("hide " +hide);
			if(hide != null){
				for(IncomingReference incoming : hide.getIncoming()){
					for(Instance source : incoming.getSource()){
						if(source.getUUID().equals(uuid)){
							Hreturn = hide;
							break;
						}
					}
				}
			}
		}
		return Hreturn;
	}*/

	/*public static Hide isHide(Migration migration, String uuid){
		Hide Hreturn = null;
		for(Instance instance : migration.getInstances()){
			Hide hide = instance.getHide();
			if(hide != null){
				if(instance.getUUID().equals(uuid)){
					Hreturn = hide;
					break;
				}
			}
		}
		return Hreturn;
	}*/

}