Commit 89bd5d19 authored by Simon Barner's avatar Simon Barner
Browse files

Merge branch '3865' into 'master'

3865: tolerant model loader

See merge request af3/kernel!84
parents 8c9c966c 1584c544
AddMissingAnnotationsMigrationProvider.java a3f2b3cbcd39f85e15bc998650f899f55d9f563e GREEN
RemoveDuplicatedAnnotationsMigrationProvider.java f1bdb4733d5b9c6003a2b7fee59b89240a0a3b61 GREEN
RemoveOutdatedAnnotationInstanceMigrationProvider.java 29c29f2bb7515cad1de45a30ffc185001b47a016 GREEN
AddMissingAnnotationsMigrationProvider.java ebc5b9348b61ffb23493942949ecccf1c1fa2ae1 GREEN
RemoveDuplicatedAnnotationsMigrationProvider.java 6920909f8f211b9c5b5990644b5abcd8c4abaa3a GREEN
RemoveOutdatedAnnotationInstanceMigrationProvider.java 245530d6026f9ff29ffc577983d9de03ae5e75e5 GREEN
......@@ -15,6 +15,7 @@
+--------------------------------------------------------------------------*/
package org.fortiss.tooling.base.migration;
import static java.util.Collections.emptyMap;
import static org.fortiss.tooling.base.utils.AnnotationUtils.instantiateAnnotationsRecursive;
import java.util.ArrayList;
......@@ -49,9 +50,11 @@ public class AddMissingAnnotationsMigrationProvider implements IMigrationProvide
/** {@inheritDoc} */
@Override
public void migrate(ITopLevelElement modelElement, Map<EObject, AnyType> unknownFeatures) {
public Map<EObject, AnyType> migrate(ITopLevelElement modelElement,
Map<EObject, AnyType> unknownFeatures) {
EObject rootElement = modelElement.getRootModelElement();
instantiateAnnotationsRecursive(rootElement);
migratedProjects.add(modelElement);
return emptyMap();
}
}
......@@ -15,6 +15,7 @@
+--------------------------------------------------------------------------*/
package org.fortiss.tooling.base.migration;
import static java.util.Collections.emptyMap;
import static org.fortiss.tooling.kernel.utils.EcoreUtils.getChildrenWithType;
import static org.fortiss.tooling.kernel.utils.EcoreUtils.pickInstanceOf;
import static org.fortiss.tooling.kernel.utils.LoggingUtils.error;
......@@ -87,7 +88,8 @@ public class RemoveDuplicatedAnnotationsMigrationProvider implements IMigrationP
/** {@inheritDoc} */
@Override
public void migrate(ITopLevelElement topLevelElement, Map<EObject, AnyType> unknownFeatures) {
public Map<EObject, AnyType> migrate(ITopLevelElement topLevelElement,
Map<EObject, AnyType> unknownFeatures) {
doInternal(topLevelElement, true);
......@@ -95,5 +97,6 @@ public class RemoveDuplicatedAnnotationsMigrationProvider implements IMigrationP
error(ToolingBaseActivator.getDefault(),
"Duplicate annotations have been removed from \"" + uri.lastSegment() +
"\". Please report this incident since it indicates an internal problem.");
return emptyMap();
}
}
......@@ -15,6 +15,7 @@
+--------------------------------------------------------------------------*/
package org.fortiss.tooling.base.migration;
import static java.util.Collections.emptyMap;
import static org.eclipse.emf.ecore.util.EcoreUtil.delete;
import static org.fortiss.tooling.common.util.LambdaUtils.isAssignableFromAny;
import static org.fortiss.tooling.kernel.utils.EcoreUtils.getChildrenWithType;
......@@ -67,12 +68,14 @@ public abstract class RemoveOutdatedAnnotationInstanceMigrationProvider<T extend
/** {@inheritDoc} */
@Override
public void migrate(ITopLevelElement modelElement, Map<EObject, AnyType> unknownFeatures) {
public Map<EObject, AnyType> migrate(ITopLevelElement modelElement,
Map<EObject, AnyType> unknownFeatures) {
EObject rootModelElement = modelElement.getRootModelElement();
for(T annotation : getChildrenWithType(rootModelElement, annotationType)) {
if(!isAssignableFromAny(modelElementTypes, annotation.getSpecificationOf())) {
delete(annotation);
}
}
return emptyMap();
}
}
......@@ -6,7 +6,7 @@ IEclipseResourceStorageLocationProvider.java 0ab7f304d52a9d86f01f66e308e9a7ca420
IElementCompositor.java 5b0ab1732f71b3f8467e0276c844f0dd549e191f GREEN
ILibraryElementHandler.java 00ef5b25c63b8570006e6f6748aed0da1f33a5f1 GREEN
ILogMessageHandler.java 9ab53e836a095ef00fd84ecc0375167edf593b46 GREEN
IMigrationProvider.java fdb1078dfca10a82a18f79b862d7b8644e80e14e GREEN
IMigrationProvider.java 241bfd8594dfb86ce0f89dc95b43662f52d9e450 GREEN
IPrototypeProvider.java d5e3dbae19b5654caf28b81da6b1609d3c12be12 GREEN
IStorageProvider.java d9b14cdd254d0c956dc5715c1c4d4d955a705dd5 GREEN
ITransformationProvider.java a4ee2ea08720bb2fce29806062eb01499bb5071e GREEN
......
......@@ -45,12 +45,12 @@ public interface IMigrationProvider extends IObjectAware<ITopLevelElement> {
/**
* Applies the provider to the given element.
*
* The parameter "unknownFeatures" returns a map indicating the features that are not recognized
* The parameter "unknownFeatures" indicates 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 a 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!
* @return all unknown features that have successfully been migrated.
*/
void migrate(ITopLevelElement modelElement, Map<EObject, AnyType> unknownFeatures);
Map<EObject, AnyType> migrate(ITopLevelElement modelElement,
Map<EObject, AnyType> unknownFeatures);
}
/*-------------------------------------------------------------------------+
| Copyright 2018 fortiss GmbH |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
+--------------------------------------------------------------------------*/
package org.fortiss.tooling.kernel.extension.base;
import static org.conqat.lib.commons.reflect.ReflectionUtils.isInstanceOfAny;
import static org.eclipse.emf.ecore.util.EcoreUtil.delete;
import static org.fortiss.tooling.common.util.LambdaUtils.asStream;
import java.util.Collection;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Stream;
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.model.INamedCommentedElement;
/**
* Remove artificats related to deprecated use case model.
*
* @author barner
*/
public abstract class RemoveDeprecatedArtifactsMigrationProviderBase implements IMigrationProvider { // NO_UCD
// Migrators are retired after a release, but this base class should be available when needed.
// See https://af3-developer.fortiss.org/projects/autofocus3/wiki/Model_Migration
/** Returns the {@link Collection} of deprecated artifact types to be removed from the model. */
protected abstract Collection<Class<? extends EObject>> getDeprecatedArtifacts();
/**
* Returns the {@link Collection} of deprecated root artifact types (possibly empty subset of
* {@link #getDeprecatedArtifacts()}) for which a comment is added to the migrated model that
* deprecated artifacts have been removed.
*/
protected abstract Collection<Class<? extends EObject>> getDeprecatedRootArtifacts();
/** Returns the {@link Stream} of deprecated model elements to be removed. */
private Stream<EObject> getDeprecatedModelElements(ITopLevelElement modelElement) {
Stream<EObject> root = asStream(modelElement.getRootModelElement().eAllContents());
Collection<Class<? extends EObject>> deprArtifacts = getDeprecatedArtifacts();
return root.filter(
e -> isInstanceOfAny(e, deprArtifacts.toArray(new Class[deprArtifacts.size()])));
}
/** {@inheritDoc} */
@Override
public boolean needMigration(ITopLevelElement modelElement,
Map<EObject, AnyType> unknownFeatures) {
return getDeprecatedModelElements(modelElement).findAny().isPresent();
}
/** {@inheritDoc} */
@Override
public void migrate(ITopLevelElement modelElement, Map<EObject, AnyType> unknownFeatures) {
EObject object;
do {
object = null;
Stream<EObject> deprecatedModelElements = getDeprecatedModelElements(modelElement);
Optional<EObject> anyObject = deprecatedModelElements.findAny();
if(anyObject.isPresent()) {
object = anyObject.get();
if(object.eContainer() instanceof INamedCommentedElement) {
INamedCommentedElement parent = (INamedCommentedElement)object.eContainer();
if(parent.getComment() == null || parent.getComment().isEmpty()) {
for(Class<? extends EObject> rootType : getDeprecatedRootArtifacts()) {
if(rootType.isAssignableFrom(object.getClass())) {
parent.setComment(rootType.getSimpleName() +
" models are no longer supported. This sub-model has been removed automatically!");
break;
}
}
}
}
delete(object);
}
} while(object != null);
}
}
......@@ -7,7 +7,7 @@ ElementCompositorService.java 98c5d27e09881e60aa4f87c1ac0c7787cdec9f7c GREEN
LibraryPrototypeProvider.java b77eddbdca78f561ffb1233e98817be361c690ae GREEN
LibraryService.java d22671ba820466062852c15873698adf28960d94 GREEN
LoggingService.java da784259f7b456b54bf75c41ec268f64919ce78d GREEN
MigrationService.java 632c13563a3d69681e2a608023fcdadbe5340c4b GREEN
MigrationService.java 2f800eac9793aa736089a802bbfc2c4c1c09770d GREEN
PersistencyService.java 103eef642c038ef63fa49b743d803aaa3fea2724 GREEN
PrototypeService.java 18c3db05ab11f189a9711bf241c3c7f35c954a9e GREEN
ToolingKernelInternal.java d624a5f6b237ce993e150e2b8d1b4390e3fc8f7a GREEN
......
......@@ -16,18 +16,21 @@
package org.fortiss.tooling.kernel.internal;
import static java.util.Collections.emptyList;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toSet;
import static org.fortiss.tooling.kernel.utils.EcoreUtils.getFirstChildWithType;
import static org.fortiss.tooling.kernel.utils.LoggingUtils.error;
import static org.fortiss.tooling.kernel.utils.LoggingUtils.warning;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Stream;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.util.FeatureMap;
import org.eclipse.emf.ecore.xml.type.AnyType;
import org.fortiss.tooling.kernel.ToolingKernelActivator;
import org.fortiss.tooling.kernel.extension.IMigrationProvider;
......@@ -97,19 +100,6 @@ public class MigrationService extends ObjectAwareServiceBase<IMigrationProvider>
return false;
}
/** Extracts the list of feature names from a map of features. */
private 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;
}
/** {@inheritDoc} */
@Override
public void migrate(final ITopLevelElement input, final Map<EObject, AnyType> unknownFeatures) {
......@@ -117,17 +107,12 @@ public class MigrationService extends ObjectAwareServiceBase<IMigrationProvider>
@Override
public void run() {
Map<EObject, AnyType> migratedFeatures = new HashMap<EObject, AnyType>();
for(IMigrationProvider provider : getProviders(input)) {
if(provider.needMigration(input, unknownFeatures)) {
provider.migrate(input, unknownFeatures);
migratedFeatures.putAll(provider.migrate(input, unknownFeatures));
}
}
if(!unknownFeatures.isEmpty()) {
error(ToolingKernelActivator.getDefault(),
input.getSaveableName() + " contains one or more unknown feature(s): " +
featuresToStrings(unknownFeatures));
}
for(IMigrationProvider provider : getProviders(input)) {
if(provider.needMigration(input, unknownFeatures)) {
error(ToolingKernelActivator.getDefault(),
......@@ -136,6 +121,19 @@ public class MigrationService extends ObjectAwareServiceBase<IMigrationProvider>
"already performed. Please fix the migrator (or model).");
}
}
unknownFeatures.entrySet().removeAll(migratedFeatures.entrySet());
if(!unknownFeatures.isEmpty()) {
Stream<AnyType> anyTypes = unknownFeatures.values().stream()
.map(v -> v.getMixed()).flatMap(f -> f.stream()).map(e -> e.getValue())
.filter(AnyType.class::isInstance).map(AnyType.class::cast);
String removedTypes = anyTypes.map(a -> a.eClass().getName()).collect(toSet())
.stream().collect(joining(", "));
warning(ToolingKernelActivator.getDefault(), input.getSaveableName() +
" contains one or more feature(s) of the following unkown types that will be deleted: " +
removedTypes + ".");
unknownFeatures.clear();
}
}
});
......
AutoUndoCommandStack.java fc326adf66c6cea2354884cdc240da5f2f82689a GREEN
EMFTransactionalCommand.java ba4b5bead9768b6ce6c955b9238cd96cb722533c GREEN
EclipseResourceStorageService.java e29e32272286921c5e43963253902b3ba54490c7 GREEN
ModelContext.java 55de5f19c5d625f935fb8136ff72d80b3a54ff19 GREEN
ModelContext.java db1735834c85e7b508266f56463d011f2b72af0e GREEN
NonDirtyingEMFTransactionalCommand.java d288ebe35d22442c603496b0c917fb99a8febeea GREEN
......@@ -330,7 +330,9 @@ class ModelContext implements ITopLevelElement, CommandStackListener {
// Step 3..n: Save resources not managed by kernel
for(Resource currentResource : rset.getResources()) {
if(currentResource != resource && !editingDomain.isReadOnly(currentResource)) {
if(currentResource != resource && !editingDomain.isReadOnly(currentResource) &&
!currentResource.getContents().isEmpty()) {
// do not save resources which are not contained (by a FileProject)
try {
currentResource.save(saveOptions);
} catch(IOException e) {
......
......@@ -10,6 +10,6 @@ JavaUtils.java 65cdadfb9137a240ad59992eacf53a15b7f20804 GREEN
KernelModelElementUtils.java fded09befe7e543fc04ea5184ffc1c8a309d7a66 GREEN
LoggingUtils.java 0e0aa5d466d80ea29cfc7e91178b23a5cdd4ddf7 GREEN
PrototypesUtils.java ec75bed75cfc5103f1f38e3a29df86f729428775 GREEN
ResourceUtils.java 698c7db34acb4f1a258a1953e6afcca9823763a8 GREEN
ResourceUtils.java e31eda3fdbedd2e44c85d471f717b14f92a3c663 GREEN
TransformationUtils.java 552d3a9d56d34450be781af828efe0b8aa5d359e GREEN
UniqueIDUtils.java 665955b1790c1bd1c2087e23114da920bfec2265 GREEN
......@@ -51,6 +51,11 @@ import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceFactoryImpl;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.xmi.PackageNotFoundException;
import org.eclipse.emf.ecore.xmi.XMLHelper;
import org.eclipse.emf.ecore.xmi.XMLLoad;
import org.eclipse.emf.ecore.xmi.XMLResource;
import org.eclipse.emf.ecore.xmi.impl.SAXXMIHandler;
import org.eclipse.emf.ecore.xmi.impl.XMILoadImpl;
import org.eclipse.emf.ecore.xmi.impl.XMIResourceImpl;
import org.eclipse.emf.ecore.xmi.impl.XMLResourceFactoryImpl;
import org.eclipse.emf.ecore.xml.type.AnyType;
......@@ -58,6 +63,7 @@ import org.fortiss.tooling.kernel.ToolingKernelActivator;
import org.fortiss.tooling.kernel.extension.data.ITopLevelElement;
import org.fortiss.tooling.kernel.model.IIdLabeled;
import org.fortiss.tooling.kernel.service.IPersistencyService;
import org.xml.sax.helpers.DefaultHandler;
/**
* Utility class for dealing with EMF {@link Resource}s.
......@@ -77,12 +83,79 @@ public final class ResourceUtils {
*/
public static class KernelResourceFactory extends ResourceFactoryImpl {
/**
* XMIHandler that cleans up dangling references when loading models with unknown types
* instead of throwing an exception.
*/
private final class DanglingReferencesCleanupXMIHandler extends SAXXMIHandler {
/** Constructor. */
private DanglingReferencesCleanupXMIHandler(XMLResource xmiResource, XMLHelper helper,
Map<?, ?> options) {
super(xmiResource, helper, options);
}
/** {@inheritDoc} */
@Override
protected void handleForwardReferences(boolean isEndDocument) {
List<SingleReference> toRemoveFwdSingleRefs = new ArrayList<>();
for(SingleReference ref : forwardSingleReferences) {
EObject obj;
try {
obj = xmlResource.getEObject((String)ref.getValue());
} catch(RuntimeException exception) {
obj = null;
}
if(obj == null || obj instanceof AnyType) {
warnRefWithUnknownType((AnyType)obj, ref.getLineNumber(),
ref.getColumnNumber());
toRemoveFwdSingleRefs.add(ref);
}
}
forwardSingleReferences.removeAll(toRemoveFwdSingleRefs);
List<ManyReference> toRemoveFwdManyRefs = new ArrayList<>();
for(ManyReference ref : forwardManyReferences) {
Object[] values = ref.getValues();
for(String id : (String[])values) {
EObject obj;
try {
obj = xmlResource.getEObject(id);
} catch(RuntimeException exception) {
obj = null;
}
if(obj == null || obj instanceof AnyType) {
warnRefWithUnknownType((AnyType)obj, ref.getLineNumber(),
ref.getColumnNumber());
toRemoveFwdManyRefs.add(ref);
break;
}
}
}
forwardManyReferences.removeAll(toRemoveFwdManyRefs);
super.handleForwardReferences(isEndDocument);
}
/** Issues a warning for a given {@link AnyType}d object. */
private void warnRefWithUnknownType(AnyType obj, int line, int col) {
String message = "Removing reference to element with unknown type" +
(obj != null ? " \"" + obj.eClass().getName() + "\"" : "") + " in line " +
line + ", column " + col + ".";
// Static inline not possible: name clash with XMLHandler::warning()
LoggingUtils.warning(ToolingKernelActivator.getDefault(), message);
}
}
/**
* {@link XMIResourceImpl} used to persist models managed by the tooling kernel.
*
* @author barner
*/
private final class KernelXMIResource extends XMIResourceImpl {
/** Constructs a new {@link KernelXMIResource}. */
private KernelXMIResource(URI uri) {
super(uri);
......@@ -99,7 +172,7 @@ public final class ResourceUtils {
public String getID(EObject eObject) {
String id = super.getID(eObject);
if(id == null && eObject instanceof IIdLabeled) {
id = new Integer(((IIdLabeled)eObject).getId()).toString();
id = String.valueOf(((IIdLabeled)eObject).getId());
setID(eObject, id);
}
return id;
......@@ -140,6 +213,18 @@ public final class ResourceUtils {
}
return eObjectToExtensionMap;
}
/** {@inheritDoc} */
@Override
protected XMLLoad createXMLLoad() {
return new XMILoadImpl(createXMLHelper()) {
/** {@inheritDoc} */
@Override
protected DefaultHandler makeDefaultHandler() {
return new DanglingReferencesCleanupXMIHandler(resource, helper, options);
}
};
}
}
/** {@inheritDoc} */
......@@ -196,13 +281,13 @@ public final class ResourceUtils {
/**
* <p>
* Determines the {@link ITopLevelElement} which shares the same {@link ResourceSet} as the a
* {@link Resource} with the specified {@link URI}. If no such {@link Resource} has been loaded,
* {@code null} is returned.
* Determines the {@link ITopLevelElement} which shares the same {@link ResourceSet} as the
* a {@link Resource} with the specified {@link URI}. If no such {@link Resource} has been
* loaded, {@code null} is returned.
* </p>
* <p>
* This is useful to determine to which model managed by the Kernel a given (external) model is
* linked.
* This is useful to determine to which model managed by the Kernel a given (external) model
* is linked.
* </p>
*
* @param uri
......@@ -226,8 +311,8 @@ public final class ResourceUtils {
}
/**
* Obtains a model of a given type from a {@code resourceSet}, or {@code null} if it could not
* be found.
* Obtains a model of a given type from a {@code resourceSet}, or {@code null} if it could
* not be found.
*
* @param resourceSet
* {@link ResourceSet} from which model of given type should be determined
......@@ -251,25 +336,30 @@ public final class ResourceUtils {
}
/**
* Obtains a model of a given type from a {@code resourceSet}, and creates the model if it does
* not exist yet (and adds it to the given {@code resourceSet}). Returns {@code null} if the
* model could not be created.
* Obtains a model of a given type from a {@code resourceSet}, and creates the model if it
* does not exist yet (and adds it to the given {@code resourceSet}). Returns {@code null} if
* the model could not be created.
*
* @param resourceSet
* {@link ResourceSet} from which model of given type should be determined
* @param clazz
* Type of model to be returned.
* @param factory
* {@link EFactory} to create root model element in case the model does not exist in
* {@link EFactory} to create root model element in case the model does not exist
* in
* the {@code resourceSet} yet.
* @param fileExtension
* File extension to be used in case a new resource has to be created and added to
* the given given {@code resourceSet}. In case this parameter is {@code null}, this
* File extension to be used in case a new resource has to be created and added
* to
* the given given {@code resourceSet}. In case this parameter is {@code null},
* this
* method effectively behaves like {@link #getModel(ResourceSet, Class)}.
* @param referenceClazz
* Type of root model element whose {@link Resource} {@link URI} should be used to
* Type of root model element whose {@link Resource} {@link URI} should be used
* to
* derive the base name in case a new {@link Resource} has to be created. In case
* this parameter is {@code null}, the {@link URI} of the first {@link Resource} in
* this parameter is {@code null}, the {@link URI} of the first {@link Resource}
* in
* the given {@link ResourceSet} is used.
*
* @return Model of a given type, or {@code null} if it could not be created.
......@@ -368,12 +458,14 @@ public final class ResourceUtils {
* {@link Resource} for which {@link URI}s of directly or indirectly referencing
* resources should be determined.
* @param includeSelf
* Flag if the {@link URI} of the given {@code resource} should be contained in the
* Flag if the {@link URI} of the given {@code resource} should be contained in
* the
* result.
* @param progressMonitor
* {@link IProgressMonitor} (may be {@code null}). Loading all resources in a
* directory may be a long-running operation.
* @return {@link URI}s of directly or indirectly referencing resources, possibly including the
* @return {@link URI}s of directly or indirectly referencing resources, possibly including
* the
* {@link URI} of the given {@code resource} itself (see {@code includeSelf}).
*/
public static Collection<URI> getReferencingResourceURIs(Resource resource, boolean includeSelf,
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment