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 5f732f75e0c1f9f7df2c8cad1837ce7454610241..dd16863fd39ca3b4879915eb8ace2da8b5e500d1 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 6a7abdd4e13435d1a482156c1ebb31ad9603f72a..ba9bf60dafdf968fbc903b8904d8d9239c0c25bb 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 78bec416827fd5b946ce4bd5173be01bd1a2475f..3d05abb5712dea60a29723c09d757cd1c545a8cf 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 6632dce20c0ecb5b1c278ac6c2859852363f6673..773caa8d4f83a53b52ffe13382293a94467268a0 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 6162888146e34a1d2b1800f3252e2de68d69a896..291448bab5a7fdf4fc91a459a6423dab1430b5a0 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 dc59d66772360d68beb80c0b3337fc7a868485f5..63cde14f0c6084ade724676c2d388f6e4dbeaa8c 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 01595c3f08b470ef1a63053a0b9685517f91dec8..477e30b19501808fbc82b4bfa683152f9938e601 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 8df4d8cfe410e987be70ac910e38d0b035ecbdee..402e5689f02b67790316567307420cec876ae636 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; + } }