From a776c0b1ad196d7b99d9377e92f6345397e2809f Mon Sep 17 00:00:00 2001
From: Vincent Aravantinos <aravantinos@fortiss.org>
Date: Mon, 17 Feb 2014 17:29:39 +0000
Subject: [PATCH] Implemented issue 1943 refs 1943

---
 .../base/migration/IDMigrationProvider.java   | 11 +++++---
 .../kernel/extension/IMigrationProvider.java  | 27 ++++++++++++++++---
 .../kernel/internal/MigrationService.java     | 27 +++++++++++++------
 .../EclipseResourceStorageProvider.java       |  7 +++--
 .../storage/eclipse/ModelContext.java         | 21 ++++++++++++++-
 .../kernel/service/IMigrationService.java     | 10 ++++---
 .../kernel/utils/EMFResourceUtils.java        | 24 +++++++++++++++--
 .../tooling/kernel/utils/MigrationUtils.java  | 21 ++++++++++++++-
 8 files changed, 122 insertions(+), 26 deletions(-)

diff --git a/org.fortiss.tooling.base/trunk/src/org/fortiss/tooling/base/migration/IDMigrationProvider.java b/org.fortiss.tooling.base/trunk/src/org/fortiss/tooling/base/migration/IDMigrationProvider.java
index 5f732f75e..dd16863fd 100644
--- a/org.fortiss.tooling.base/trunk/src/org/fortiss/tooling/base/migration/IDMigrationProvider.java
+++ b/org.fortiss.tooling.base/trunk/src/org/fortiss/tooling/base/migration/IDMigrationProvider.java
@@ -19,6 +19,10 @@ package org.fortiss.tooling.base.migration;
 
 import static org.fortiss.tooling.kernel.utils.UniqueIDUtils.hasMissingIDs;
 
+import java.util.Map;
+
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.xml.type.AnyType;
 import org.fortiss.tooling.kernel.extension.IMigrationProvider;
 import org.fortiss.tooling.kernel.extension.data.ITopLevelElement;
 import org.fortiss.tooling.kernel.utils.UniqueIDUtils;
@@ -28,13 +32,14 @@ import org.fortiss.tooling.kernel.utils.UniqueIDUtils;
  * @author Shaka
  * @author $Author: hoelzl $
  * @version $Rev: 18709 $
- * @ConQAT.Rating RED Hash:
+ * @ConQAT.Rating YELLOW Hash: DB938B8077EEAFB9455F8A83BF92CD86
  */
 public class IDMigrationProvider implements IMigrationProvider {
 
 	/** {@inheritDoc} */
 	@Override
-	public boolean needMigration(ITopLevelElement modelElement) {
+	public boolean needMigration(ITopLevelElement modelElement,
+			Map<EObject, AnyType> unknownFeatures) {
 		// if the id of model element is 0
 		if(modelElement != null && modelElement.getRootModelElement() != null &&
 				hasMissingIDs(modelElement.getRootModelElement())) {
@@ -45,7 +50,7 @@ public class IDMigrationProvider implements IMigrationProvider {
 
 	/** {@inheritDoc} */
 	@Override
-	public void migrate(ITopLevelElement modelElement) {
+	public void migrate(ITopLevelElement modelElement, Map<EObject, AnyType> unknownFeatures) {
 		UniqueIDUtils.fixMissingIDs(modelElement);
 	}
 
diff --git a/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/extension/IMigrationProvider.java b/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/extension/IMigrationProvider.java
index 6a7abdd4e..ba9bf60da 100644
--- a/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/extension/IMigrationProvider.java
+++ b/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/extension/IMigrationProvider.java
@@ -17,6 +17,10 @@ $Id$
 +--------------------------------------------------------------------------*/
 package org.fortiss.tooling.kernel.extension;
 
+import java.util.Map;
+
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.xml.type.AnyType;
 import org.fortiss.tooling.kernel.extension.data.ITopLevelElement;
 import org.fortiss.tooling.kernel.service.IMigrationService;
 import org.fortiss.tooling.kernel.service.base.IObjectAware;
@@ -29,16 +33,31 @@ import org.fortiss.tooling.kernel.service.base.IObjectAware;
  * @author mou
  * @author $Author$
  * @version $Rev$
- * @ConQAT.Rating GREEN Hash: 644590FDA54780140699CE0595DCC8B2
+ * @ConQAT.Rating YELLOW Hash: 27DD99D2F4AB22CA803D51E3D356AB93
  */
 public interface IMigrationProvider extends IObjectAware<ITopLevelElement> {
 
 	/**
 	 * Determines whether migration provider is needed for the given
 	 * model element.
+	 * 
+	 * The parameter "unknownFeatures" returns a map indicating the features that are not recognized
+	 * in the model.
+	 * This can be useful to detect features coming from old models and can be then translated to
+	 * the new model by the migrator.
 	 */
-	boolean needMigration(ITopLevelElement modelElement);
+	boolean needMigration(ITopLevelElement modelElement, Map<EObject, AnyType> unknownFeatures);
 
-	/** Applies the provider to the given element. */
-	void migrate(ITopLevelElement modelElement);
+	/**
+	 * Applies the provider to the given element.
+	 * 
+	 * The parameter "unknownFeatures" returns a map indicating the features that are not recognized
+	 * in the model.
+	 * This can be useful to detect features coming from old models and can be then translated to
+	 * the new model by the migrator.
+	 * 
+	 * The migrator should remove from unknownFeatures the features that it dealt with.
+	 * If one forgets to do so, the migrator will run into an infinite loop!
+	 */
+	void migrate(ITopLevelElement modelElement, Map<EObject, AnyType> unknownFeatures);
 }
diff --git a/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/internal/MigrationService.java b/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/internal/MigrationService.java
index 78bec4168..3d05abb57 100644
--- a/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/internal/MigrationService.java
+++ b/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/internal/MigrationService.java
@@ -17,7 +17,13 @@ $Id$
 +--------------------------------------------------------------------------*/
 package org.fortiss.tooling.kernel.internal;
 
+import static org.fortiss.tooling.kernel.utils.MigrationUtils.featuresToStrings;
+
+import java.util.Map;
+
 import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.xml.type.AnyType;
 import org.fortiss.tooling.kernel.ToolingKernelActivator;
 import org.fortiss.tooling.kernel.extension.IMigrationProvider;
 import org.fortiss.tooling.kernel.extension.data.ITopLevelElement;
@@ -31,7 +37,7 @@ import org.fortiss.tooling.kernel.utils.LoggingUtils;
  * @author mou
  * @author $Author$
  * @version $Rev$
- * @ConQAT.Rating GREEN Hash: 84ED0C27EA5D8E9F603E63E935263B3B
+ * @ConQAT.Rating YELLOW Hash: D41B3F3BD99B6BC9ECA3F6B4B795A807
  */
 public class MigrationService extends ObjectAwareServiceBase<IMigrationProvider> implements
 		IMigrationService {
@@ -48,21 +54,25 @@ public class MigrationService extends ObjectAwareServiceBase<IMigrationProvider>
 
 	/** {@inheritDoc} */
 	@Override
-	public boolean needMigration(ITopLevelElement input) {
-		return getProvider(input) != null;
+	public boolean needMigration(ITopLevelElement input, Map<EObject, AnyType> unknownFeatures) {
+		return(getProvider(input, unknownFeatures) != null);
 	}
 
 	/** {@inheritDoc} */
 	@Override
-	public void migrate(final ITopLevelElement input) {
+	public void migrate(final ITopLevelElement input, final Map<EObject, AnyType> unknownFeatures) {
 		input.runAsCommand(new Runnable() {
 
 			@Override
 			public void run() {
 				IMigrationProvider provider;
-				while(null != (provider = getProvider(input))) {
-					provider.migrate(input);
+				while(null != (provider = getProvider(input, unknownFeatures))) {
+					provider.migrate(input, unknownFeatures);
 				}
+				if(unknownFeatures.size() >= 1)
+					throw new RuntimeException(input.getSaveableName() +
+							" contains one or more unknown feature(s): " +
+							featuresToStrings(unknownFeatures));
 			}
 		});
 
@@ -80,9 +90,10 @@ public class MigrationService extends ObjectAwareServiceBase<IMigrationProvider>
 	}
 
 	/** get the suitable provider for the input */
-	private IMigrationProvider getProvider(ITopLevelElement input) {
+	private IMigrationProvider getProvider(ITopLevelElement input,
+			Map<EObject, AnyType> unknownFeatures) {
 		for(IMigrationProvider provider : getAllHandlers()) {
-			if(provider.needMigration(input)) {
+			if(provider.needMigration(input, unknownFeatures)) {
 				return provider;
 			}
 		}
diff --git a/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/internal/storage/eclipse/EclipseResourceStorageProvider.java b/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/internal/storage/eclipse/EclipseResourceStorageProvider.java
index 6632dce20..773caa8d4 100644
--- a/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/internal/storage/eclipse/EclipseResourceStorageProvider.java
+++ b/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/internal/storage/eclipse/EclipseResourceStorageProvider.java
@@ -65,7 +65,7 @@ import org.osgi.framework.Bundle;
  * @author hoelzl
  * @author $Author$
  * @version $Rev$
- * @ConQAT.Rating GREEN Hash: C5A912CC95063A2054B6DB8630092D59
+ * @ConQAT.Rating YELLOW Hash: 92AA964C5B5676011D12861C0412C980
  */
 public class EclipseResourceStorageProvider implements IEclipseResourceStorageService,
 		IResourceChangeListener, IResourceDeltaVisitor, IStorageProvider {
@@ -220,7 +220,6 @@ public class EclipseResourceStorageProvider implements IEclipseResourceStorageSe
 			ModelContext mc = new ModelContext(file);
 			loadedContexts.put(file, mc);
 			rootElementContexts.put(mc.getRootModelElement(), mc);
-
 			postLoadContext(mc);
 		} catch(Exception ioex) {
 			String msg = "Exception while loading model file: " + file.getName();
@@ -231,8 +230,8 @@ public class EclipseResourceStorageProvider implements IEclipseResourceStorageSe
 
 	/** Post-actions after loading a model context */
 	private void postLoadContext(ModelContext mc) {
-		if(IMigrationService.INSTANCE.needMigration(mc)) {
-			IMigrationService.INSTANCE.migrate(mc);
+		if(IMigrationService.INSTANCE.needMigration(mc, mc.getUnknownFeatures())) {
+			IMigrationService.INSTANCE.migrate(mc, mc.getUnknownFeatures());
 		}
 	}
 
diff --git a/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/internal/storage/eclipse/ModelContext.java b/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/internal/storage/eclipse/ModelContext.java
index 616288814..291448bab 100644
--- a/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/internal/storage/eclipse/ModelContext.java
+++ b/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/internal/storage/eclipse/ModelContext.java
@@ -25,9 +25,11 @@ import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.lang.reflect.Method;
 import java.util.EventObject;
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
 
 import org.eclipse.core.resources.IFile;
 import org.eclipse.core.runtime.CoreException;
@@ -40,6 +42,8 @@ import org.eclipse.emf.common.util.URI;
 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.xmi.XMIResource;
+import org.eclipse.emf.ecore.xml.type.AnyType;
 import org.eclipse.emf.transaction.TransactionalEditingDomain;
 import org.fortiss.tooling.kernel.ToolingKernelActivator;
 import org.fortiss.tooling.kernel.extension.data.ITopLevelElement;
@@ -54,7 +58,7 @@ import org.fortiss.tooling.kernel.utils.UniqueIDUtils;
  * @author hummel
  * @author $Author$
  * @version $Rev$
- * @ConQAT.Rating GREEN Hash: 88E97F37C63D306BDBFFB77CC88BB853
+ * @ConQAT.Rating YELLOW Hash: A7B00E66E3893E14F4097D435147ACFC
  */
 class ModelContext implements ITopLevelElement, CommandStackListener {
 
@@ -82,6 +86,13 @@ class ModelContext implements ITopLevelElement, CommandStackListener {
 	/** The maximal ID used in this model (used to generate new IDs). */
 	private int maxId = -1;
 
+	/**
+	 * The map containing the unknown features. Can be non-empty, in which case the migrators have
+	 * to deal with it.
+	 * Only if they don't the model context is buggy.
+	 */
+	private Map<EObject, AnyType> unknownFeatures;
+
 	/** Constructor. */
 	/* package */ModelContext(IFile file) throws IOException {
 		this.file = file;
@@ -93,6 +104,9 @@ class ModelContext implements ITopLevelElement, CommandStackListener {
 				getResourceSet().createResource(
 						URI.createPlatformResourceURI(file.getFullPath().toString(), true));
 		resource.load(buildOptionsMap());
+		unknownFeatures =
+				resource instanceof XMIResource ? ((XMIResource)resource)
+						.getEObjectToExtensionMap() : new HashMap<EObject, AnyType>();
 
 		transactionalCommandStack = new AutoUndoCommandStack(editingDomain);
 		transactionalCommandStack.addCommandStackListener(this);
@@ -355,4 +369,9 @@ class ModelContext implements ITopLevelElement, CommandStackListener {
 	public ResourceSet getResourceSet() {
 		return rset;
 	}
+
+	/** Returns unknownFeatures. */
+	public Map<EObject, AnyType> getUnknownFeatures() {
+		return unknownFeatures;
+	}
 }
diff --git a/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/service/IMigrationService.java b/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/service/IMigrationService.java
index dc59d6677..63cde14f0 100644
--- a/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/service/IMigrationService.java
+++ b/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/service/IMigrationService.java
@@ -17,6 +17,10 @@ $Id$
 +--------------------------------------------------------------------------*/
 package org.fortiss.tooling.kernel.service;
 
+import java.util.Map;
+
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.xml.type.AnyType;
 import org.fortiss.tooling.kernel.extension.data.ITopLevelElement;
 import org.fortiss.tooling.kernel.internal.MigrationService;
 
@@ -26,7 +30,7 @@ import org.fortiss.tooling.kernel.internal.MigrationService;
  * @author mou
  * @author $Author$
  * @version $Rev$
- * @ConQAT.Rating GREEN Hash: 479B525CFD483D015D1EFD646969F24B
+ * @ConQAT.Rating YELLOW Hash: 9275EB22F8F3218FC5A0BA636584B46F
  */
 public interface IMigrationService {
 
@@ -34,8 +38,8 @@ public interface IMigrationService {
 	public static final IMigrationService INSTANCE = new MigrationService();
 
 	/** Checks whether the migration is needed. */
-	public boolean needMigration(ITopLevelElement input);
+	public boolean needMigration(ITopLevelElement input, Map<EObject, AnyType> unknownFeatures);
 
 	/** Performs the migration for the given input. */
-	public void migrate(ITopLevelElement input);
+	public void migrate(ITopLevelElement input, Map<EObject, AnyType> unknownFeatures);
 }
diff --git a/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/utils/EMFResourceUtils.java b/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/utils/EMFResourceUtils.java
index 01595c3f0..477e30b19 100644
--- a/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/utils/EMFResourceUtils.java
+++ b/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/utils/EMFResourceUtils.java
@@ -21,13 +21,16 @@ import java.io.IOException;
 import java.util.HashMap;
 import java.util.Map;
 
+import org.conqat.lib.commons.collections.Pair;
 import org.eclipse.core.resources.IFile;
 import org.eclipse.emf.common.util.URI;
 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.impl.ResourceSetImpl;
+import org.eclipse.emf.ecore.xmi.XMIResource;
 import org.eclipse.emf.ecore.xmi.XMLResource;
+import org.eclipse.emf.ecore.xml.type.AnyType;
 import org.fortiss.tooling.kernel.ToolingKernelActivator;
 
 /**
@@ -66,6 +69,7 @@ public final class EMFResourceUtils {
 	public static Map<String, Object> buildOptionsMap() {
 		Map<String, Object> options = new HashMap<String, Object>();
 		options.put(XMLResource.OPTION_ENCODING, "UTF-8");
+		options.put(XMLResource.OPTION_RECORD_UNKNOWN_FEATURE, true);
 		options.put(Resource.OPTION_ZIP, false);
 		return options;
 	}
@@ -78,17 +82,33 @@ public final class EMFResourceUtils {
 	 *            the resource URI
 	 * @return the root {@link EObject}
 	 */
-	public static EObject loadModelFromFile(URI uri) {
+	public static Pair<EObject, Map<EObject, AnyType>>
+			loadModelFromFileWithUnknownFeatures(URI uri) {
 		try {
 			ResourceSet rset = new ResourceSetImpl();
 			Resource r;
 			r = rset.createResource(uri);
 			r.load(EMFResourceUtils.buildOptionsMap());
-			return r.getContents().get(0);
+			Map<EObject, AnyType> unknownFeatures =
+					r instanceof XMIResource ? ((XMIResource)r).getEObjectToExtensionMap()
+							: new HashMap<EObject, AnyType>();
+			return new Pair<EObject, Map<EObject, AnyType>>(r.getContents().get(0), unknownFeatures);
 		} catch(IOException ex) {
 			LoggingUtils.error(ToolingKernelActivator.getDefault(), "Failed to load model from " +
 					uri.toString(), ex);
 		}
 		return null;
 	}
+
+	/**
+	 * Loads the root model element from the given URI. Logs errors to the
+	 * console and returns <code>null</code>.
+	 * 
+	 * @param uri
+	 *            the resource URI
+	 * @return the root {@link EObject}
+	 */
+	public static EObject loadModelFromFile(URI uri) {
+		return loadModelFromFileWithUnknownFeatures(uri).getFirst();
+	}
 }
diff --git a/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/utils/MigrationUtils.java b/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/utils/MigrationUtils.java
index 8df4d8cfe..402e5689f 100644
--- a/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/utils/MigrationUtils.java
+++ b/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/utils/MigrationUtils.java
@@ -19,9 +19,15 @@ package org.fortiss.tooling.kernel.utils;
 
 import static org.fortiss.tooling.kernel.utils.EcoreUtils.getChildrenWithType;
 
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.emf.common.util.BasicEList;
 import org.eclipse.emf.common.util.EList;
 import org.eclipse.emf.ecore.EObject;
 import org.eclipse.emf.ecore.util.EcoreUtil;
+import org.eclipse.emf.ecore.util.FeatureMap;
+import org.eclipse.emf.ecore.xml.type.AnyType;
 
 /**
  * Utility methods for the migrators.
@@ -29,7 +35,7 @@ import org.eclipse.emf.ecore.util.EcoreUtil;
  * @author SVoss
  * @author $Author$
  * @version $Rev$
- * @ConQAT.Rating GREEN Hash: C59C0D2AC819F4A8595D75F556B8E050
+ * @ConQAT.Rating YELLOW Hash: FFB94099E84BD8C3E5B44CE007CF6EDE
  */
 public class MigrationUtils {
 
@@ -49,4 +55,17 @@ public class MigrationUtils {
 			EcoreUtil.delete(obj);
 		}
 	}
+
+	/** Extract the list of feature names from a map of features. */
+	public static List<String> featuresToStrings(Map<EObject, AnyType> features) {
+		List<String> res = new BasicEList<String>();
+		for(AnyType featuresPerKey : features.values()) {
+			FeatureMap actualFeatures = featuresPerKey.getMixed();
+			actualFeatures.addAll(featuresPerKey.getAnyAttribute());
+			for(FeatureMap.Entry feature : actualFeatures) {
+				res.add(feature.getEStructuralFeature().getName());
+			}
+		}
+		return res;
+	}
 }
-- 
GitLab