diff --git a/org.fortiss.tooling.kernel/src/org/fortiss/tooling/kernel/utils/.ratings b/org.fortiss.tooling.kernel/src/org/fortiss/tooling/kernel/utils/.ratings index d85861e2edafbeb32da7cbcff68cc64ad45f8ba9..7cc1b6750d18a16c2ec2dcfeec8dd2261d3f216d 100644 --- a/org.fortiss.tooling.kernel/src/org/fortiss/tooling/kernel/utils/.ratings +++ b/org.fortiss.tooling.kernel/src/org/fortiss/tooling/kernel/utils/.ratings @@ -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 diff --git a/org.fortiss.tooling.kernel/src/org/fortiss/tooling/kernel/utils/ResourceUtils.java b/org.fortiss.tooling.kernel/src/org/fortiss/tooling/kernel/utils/ResourceUtils.java index 698c7db34acb4f1a258a1953e6afcca9823763a8..e397569bf8d128b3391f1bdc09b941502a8da321 100644 --- a/org.fortiss.tooling.kernel/src/org/fortiss/tooling/kernel/utils/ResourceUtils.java +++ b/org.fortiss.tooling.kernel/src/org/fortiss/tooling/kernel/utils/ResourceUtils.java @@ -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,