Commit dea3ad31 authored by Simon Barner's avatar Simon Barner
Browse files

Add DanglingReferencesCleanupXMIHandler

*  XMIHandler that cleans up dangling references when loading models
   with unknown types instead of throwing an exception.

Issue-Ref: 3865
Issue-Url: https://af3-developer.fortiss.org/issues/3865

Signed-off-by: Simon Barner's avatarSimon Barner <barner@fortiss.org>
parent 66bcbf52
......@@ -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 e397569bf8d128b3391f1bdc09b941502a8da321 YELLOW
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);
......@@ -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} */
......@@ -147,6 +232,7 @@ public final class ResourceUtils {
public Resource createResource(URI uri) {
return new KernelXMIResource(uri);
}
}
/**
......@@ -196,13 +282,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 +312,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 +337,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 +459,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,
......
Supports Markdown
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