From 4cc60a9edaae3e2876d1cfe907bb5dd96b2e4365 Mon Sep 17 00:00:00 2001 From: Laurent Goubet Date: Fri, 5 Jun 2015 11:38:02 +0200 Subject: [PATCH] Proper loading of the profiles and packages from the SRS We need to load the profiles and EPackages completely, even from the SynchronizedResourceSet. This commit disables the custom parser pool used from this resource set in such cases to prevent their associated EFactory from failing. Bug: 468922 Change-Id: Icaf90fbdc3b68365b5f1e9a48e6eea201b27fb0b --- .../AbstractMonitoredProxyCreationListener.java | 2 +- .../logical/resolver/ModelsResolution.java | 21 +- .../RemoteMonitoredProxyCreationListener.java | 2 +- .../resolver/ResourceDependencyLocalResolver.java | 9 +- .../logical/resolver/RevisionedURIConverter.java | 4 +- .../logical/resolver/SynchronizedResourceSet.java | 178 ++++++-- .../utils/ForwardingXMLDefaultHandler.java | 276 ++++++++++++ .../ide/internal/utils/ForwardingXMLHandler.java | 4 +- .../ide/internal/utils/ForwardingXMLHelper.java | 312 ++++++++++++++ .../utils/INamespaceDeclarationListener.java | 30 ++ .../ide/internal/utils/IProxyCreationListener.java | 41 ++ .../internal/utils/NoNotificationParserPool.java | 4 +- .../ide/internal/utils/NotLoadingResourceSet.java | 15 +- .../ide/internal/utils/NotifyingParserPool.java | 265 ++++++++++++ .../internal/utils/ProxyNotifierParserPool.java | 474 --------------------- .../emf/compare/ide/internal/utils/URIStorage.java | 29 +- .../emf/compare/ide/utils/ResourceUtil.java | 11 +- .../emf/compare/ide/utils/StorageURIConverter.java | 8 +- .../META-INF/MANIFEST.MF | 4 +- .../build.properties | 3 +- .../org.eclipse.emf.compare.uml2.ide/plugin.xml | 11 + .../uml2/ide/ResourceSetProfileUnloader.java | 99 +++++ .../META-INF/MANIFEST.MF | 2 +- .../rcp/internal/policy/UMLLoadOnDemandPolicy.java | 20 +- 24 files changed, 1258 insertions(+), 566 deletions(-) create mode 100644 plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/internal/utils/ForwardingXMLDefaultHandler.java create mode 100644 plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/internal/utils/ForwardingXMLHelper.java create mode 100644 plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/internal/utils/INamespaceDeclarationListener.java create mode 100644 plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/internal/utils/IProxyCreationListener.java create mode 100644 plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/internal/utils/NotifyingParserPool.java delete mode 100644 plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/internal/utils/ProxyNotifierParserPool.java create mode 100644 plugins/org.eclipse.emf.compare.uml2.ide/plugin.xml create mode 100644 plugins/org.eclipse.emf.compare.uml2.ide/src/org/eclipse/emf/compare/uml2/ide/ResourceSetProfileUnloader.java diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/resolver/AbstractMonitoredProxyCreationListener.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/resolver/AbstractMonitoredProxyCreationListener.java index edb247e21..8cea7a238 100644 --- a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/resolver/AbstractMonitoredProxyCreationListener.java +++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/resolver/AbstractMonitoredProxyCreationListener.java @@ -12,7 +12,7 @@ package org.eclipse.emf.compare.ide.ui.internal.logical.resolver; import static com.google.common.base.Preconditions.checkNotNull; -import org.eclipse.emf.compare.ide.internal.utils.ProxyNotifierParserPool.IProxyCreationListener; +import org.eclipse.emf.compare.ide.internal.utils.IProxyCreationListener; import org.eclipse.emf.compare.ide.ui.internal.util.ThreadSafeProgressMonitor; /** diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/resolver/ModelsResolution.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/resolver/ModelsResolution.java index 499007a23..f2ad7668c 100644 --- a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/resolver/ModelsResolution.java +++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/resolver/ModelsResolution.java @@ -18,13 +18,12 @@ import static org.eclipse.emf.compare.ide.ui.internal.util.PlatformElementUtil.a import static org.eclipse.emf.compare.ide.utils.ResourceUtil.asURI; import com.google.common.base.Function; +import com.google.common.base.Predicate; import com.google.common.collect.Iterables; import com.google.common.collect.Sets; -import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashSet; -import java.util.List; import java.util.Set; import java.util.concurrent.Callable; @@ -40,7 +39,6 @@ import org.eclipse.emf.compare.ide.ui.logical.SynchronizationModel; import org.eclipse.emf.compare.ide.utils.ResourceUtil; import org.eclipse.emf.compare.ide.utils.StorageTraversal; import org.eclipse.emf.compare.ide.utils.StorageURIConverter; -import org.eclipse.emf.ecore.resource.Resource; /** * Computation that resolves 2 or 3 storages (left, right and potentially origin). @@ -422,13 +420,14 @@ public class ModelsResolution extends AbstractResolution { scheduler.setComputedElements(transform(converter.getLoadedRevisions(), asURI())); Iterable urisToResolve = transform(additionalStorages, asURI()); + urisToResolve = Iterables.filter(urisToResolve, new Predicate() { + public boolean apply(URI input) { + return input != null && input.isPlatformResource(); + } + }); scheduler.computeAll(transform(urisToResolve, resolveRemoteURI(tspm, resourceSet))); - // Unload all remaining resources from resource set - List resources = new ArrayList(resourceSet.getResources()); - for (Resource r : resources) { - resourceSet.unload(r, tspm); - } + resourceSet.dispose(); if (tspm.isCanceled()) { throw new OperationCanceledException(); @@ -531,11 +530,7 @@ public class ModelsResolution extends AbstractResolution { scheduler.clearComputedElements(); - // Unload all remaining resources from resource set - List resources = new ArrayList(resourceSet.getResources()); - for (Resource r : resources) { - resourceSet.unload(r, tspm); - } + resourceSet.dispose(); if (logger.isDebugEnabled()) { logger.debug("resolveRemotetraversal() - END for " + start); //$NON-NLS-1$ diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/resolver/RemoteMonitoredProxyCreationListener.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/resolver/RemoteMonitoredProxyCreationListener.java index e96c69247..4cd054fef 100644 --- a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/resolver/RemoteMonitoredProxyCreationListener.java +++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/resolver/RemoteMonitoredProxyCreationListener.java @@ -12,7 +12,7 @@ package org.eclipse.emf.compare.ide.ui.internal.logical.resolver; import org.eclipse.emf.common.util.URI; -import org.eclipse.emf.compare.ide.internal.utils.ProxyNotifierParserPool.IProxyCreationListener; +import org.eclipse.emf.compare.ide.internal.utils.IProxyCreationListener; import org.eclipse.emf.compare.ide.ui.internal.util.ThreadSafeProgressMonitor; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EStructuralFeature; diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/resolver/ResourceDependencyLocalResolver.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/resolver/ResourceDependencyLocalResolver.java index 4e4e9f122..437625b16 100644 --- a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/resolver/ResourceDependencyLocalResolver.java +++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/resolver/ResourceDependencyLocalResolver.java @@ -19,11 +19,9 @@ import com.google.common.collect.Multimap; import com.google.common.collect.Sets; import com.google.common.eventbus.EventBus; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashSet; -import java.util.List; import java.util.Set; import org.apache.log4j.Logger; @@ -37,7 +35,6 @@ import org.eclipse.emf.common.util.URI; import org.eclipse.emf.compare.ide.ui.internal.util.ThreadSafeProgressMonitor; import org.eclipse.emf.compare.ide.utils.ResourceUtil; import org.eclipse.emf.compare.internal.utils.Graph; -import org.eclipse.emf.ecore.resource.Resource; /** * The default implementation of the {@link IResourceDependencyProvider}. @@ -268,11 +265,7 @@ public class ResourceDependencyLocalResolver implements IResourceDependencyLocal })); updateChangedResources(resourceSet, diagnostic, tspm); - // Unload all remaining resources from resource set - List resources = new ArrayList(resourceSet.getResources()); - for (Resource r : resources) { - resourceSet.unload(r, tspm); - } + resourceSet.dispose(); } /** diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/resolver/RevisionedURIConverter.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/resolver/RevisionedURIConverter.java index d159f8c46..a25809800 100644 --- a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/resolver/RevisionedURIConverter.java +++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/resolver/RevisionedURIConverter.java @@ -118,7 +118,9 @@ public final class RevisionedURIConverter extends StorageURIConverter { final URI normalizedUri = normalize(uri); // If this uri points to the plugins directory, load it directly if (normalizedUri.isPlatformPlugin() || normalizedUri.toString().matches("(\\.\\./)+?plugins/.*")) { //$NON-NLS-1$ - stream = super.createInputStream(normalizedUri, options); + // This uri will be used later on to determine whether the storage has already been loaded or not. + // Use the unmodified URI instead of the normalized one here. + stream = super.createInputStream(uri, options); } else { // Otherwise, load it from the repository (resource might not yet (or no longer) exist locally) final IResource targetFile = getResourceFromURI(normalizedUri); diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/resolver/SynchronizedResourceSet.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/resolver/SynchronizedResourceSet.java index 967a1e847..7f9640556 100644 --- a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/resolver/SynchronizedResourceSet.java +++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/resolver/SynchronizedResourceSet.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2011, 2014 Obeo and others. + * Copyright (c) 2011, 2015 Obeo and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -11,13 +11,19 @@ *******************************************************************************/ package org.eclipse.emf.compare.ide.ui.internal.logical.resolver; +import com.google.common.collect.Sets; + import java.io.IOException; +import java.io.InputStream; import java.util.Collection; +import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.ReentrantLock; import org.apache.log4j.Logger; import org.eclipse.core.runtime.IProgressMonitor; @@ -26,9 +32,12 @@ import org.eclipse.emf.common.notify.NotificationChain; import org.eclipse.emf.common.util.AbstractEList; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.compare.ide.internal.utils.DisposableResourceSet; +import org.eclipse.emf.compare.ide.internal.utils.INamespaceDeclarationListener; +import org.eclipse.emf.compare.ide.internal.utils.IProxyCreationListener; import org.eclipse.emf.compare.ide.internal.utils.NoNotificationParserPool; -import org.eclipse.emf.compare.ide.internal.utils.ProxyNotifierParserPool.IProxyCreationListener; import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.resource.ContentHandler; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.URIConverter; import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; @@ -41,7 +50,7 @@ import org.eclipse.emf.ecore.xmi.XMLResource; * @author Laurent Goubet */ // Visible for testing -public class SynchronizedResourceSet extends ResourceSetImpl { +public class SynchronizedResourceSet extends ResourceSetImpl implements DisposableResourceSet { /** The logger. */ private static final Logger LOGGER = Logger.getLogger(SynchronizedResourceSet.class); @@ -49,6 +58,20 @@ public class SynchronizedResourceSet extends ResourceSetImpl { private final ConcurrentHashMap uriCache; /** + * The list of URIs corresponding to namespaces as declared in the xml files ("xmlns:"). These must not go + * through our usual xml parser as they have to be properly (and completely) loaded. They also need to be + * kept in this resource set and made available for other resources to find as they might have associated + * factories to create objects. + */ + private final Set namespaceURIs; + + /** Keeps track of the packages manually loaded for some of this resource set's resources. */ + private final Set loadedPackages; + + /** Never try and load the same package twice. We'll use this lock to prevent that from happening. */ + private final ReentrantLock packageLoadingLock; + + /** * Constructor. * * @param proxyListener @@ -57,6 +80,9 @@ public class SynchronizedResourceSet extends ResourceSetImpl { public SynchronizedResourceSet(IProxyCreationListener proxyListener) { this.uriCache = new ConcurrentHashMap(); this.resources = new SynchronizedResourcesEList(); + this.namespaceURIs = Sets.newSetFromMap(new ConcurrentHashMap()); + this.loadedPackages = Sets.newSetFromMap(new ConcurrentHashMap()); + this.packageLoadingLock = new ReentrantLock(true); this.loadOptions = super.getLoadOptions(); /* * This resource set is specifically designed to resolve cross resources links, it thus spends a lot @@ -65,6 +91,11 @@ public class SynchronizedResourceSet extends ResourceSetImpl { */ final NoNotificationParserPool parserPool = new NoNotificationParserPool(true); parserPool.addProxyListener(proxyListener); + parserPool.addNamespaceDeclarationListener(new INamespaceDeclarationListener() { + public void schemaLocationDeclared(String key, URI uri) { + namespaceURIs.add(uri.trimFragment()); + } + }); loadOptions.put(XMLResource.OPTION_USE_PARSER_POOL, parserPool); loadOptions.put(XMLResource.OPTION_USE_DEPRECATED_METHODS, Boolean.FALSE); @@ -75,6 +106,11 @@ public class SynchronizedResourceSet extends ResourceSetImpl { * needs to put "null" values in there. see https://bugs.eclipse.org/bugs/show_bug.cgi?id=403425 for * more details. */ + /* + * Most of the existing options we are not using from here, since none of them seems to have a single + * effect on loading performance, whether we're looking at time or memory. See also + * https://www.eclipse.org/forums/index.php/t/929918/ + */ } /** @@ -115,6 +151,10 @@ public class SynchronizedResourceSet extends ResourceSetImpl { if (former != null) { result = former; } + } else if (namespaceURIs.contains(uri)) { + // This uri points to an EPackage (or profile) that needs to be loaded completely and + // normally for its factory to be useable. + result = demandPackageLoad(uri); } } @@ -145,6 +185,40 @@ public class SynchronizedResourceSet extends ResourceSetImpl { } /** + * Loads the given URI as an EMF EPackage. We will disable our custom parser pool for this one as it need + * to be loaded properly for its factory to be functional. + * + * @param uri + * The uri to load. + * @return The loaded resource. + */ + private Resource loadPackage(URI uri) { + final URI trimmed = uri.trimFragment(); + final Resource resource = createResource(trimmed, ContentHandler.UNSPECIFIED_CONTENT_TYPE); + if (resource == null) { + return null; + } + + InputStream stream = null; + try { + stream = getURIConverter().createInputStream(trimmed, null); + resource.load(stream, Collections.emptyMap()); + } catch (IOException e) { + handleDemandLoadException(resource, e); + } finally { + if (stream != null) { + try { + stream.close(); + } catch (IOException e) { + // handled by the outer try + } + } + } + loadedPackages.add(resource); + return resource; + } + + /** * {@inheritDoc} * * @see org.eclipse.emf.ecore.resource.impl.ResourceSetImpl#handleDemandLoadException(org.eclipse.emf.ecore.resource.Resource, @@ -191,20 +265,28 @@ public class SynchronizedResourceSet extends ResourceSetImpl { if (LOGGER.isDebugEnabled()) { LOGGER.debug("SRS@" + Integer.toHexString(hashCode()) + ".getResource for " + uri); //$NON-NLS-1$ //$NON-NLS-2$ } - Resource demanded = uriCache.get(uri); + final URI normalized = getURIConverter().normalize(uri); + Resource demanded = uriCache.get(normalized); if (demanded == null) { final EPackage ePackage = getPackageRegistry().getEPackage(uri.toString()); if (ePackage != null) { - demanded = ePackage.eResource(); if (LOGGER.isDebugEnabled()) { - LOGGER.debug("SRS@" + Integer.toHexString(hashCode()) + ".getResource - caching " + uri); //$NON-NLS-1$ //$NON-NLS-2$ + LOGGER.debug("SRS@" + Integer.toHexString(hashCode()) + ".getResource - found in package registry : " + uri); //$NON-NLS-1$ //$NON-NLS-2$ } - Resource former = uriCache.putIfAbsent(uri, demanded); - if (former != null) { - demanded = former; + demanded = ePackage.eResource(); + if (demanded != null) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("SRS@" + Integer.toHexString(hashCode()) + ".getResource - caching " + uri); //$NON-NLS-1$ //$NON-NLS-2$ + } + Resource former = uriCache.putIfAbsent(normalized, demanded); + if (former != null) { + demanded = former; + } } - } else { - // simply return null + } else if (namespaceURIs.contains(uri)) { + // This uri points to an EPackage (or profile) that needs to be loaded completely and + // normally. + demanded = demandPackageLoad(uri); } } else if (LOGGER.isDebugEnabled()) { LOGGER.debug("SRS@" + Integer.toHexString(hashCode()) + ".getResource - FOUND in cache " + uri); //$NON-NLS-1$ //$NON-NLS-2$ @@ -213,6 +295,40 @@ public class SynchronizedResourceSet extends ResourceSetImpl { } /** + * This will be used to load the uris matching those from {@link #namespaceURIs} normally. + * + * @param uri + * The uri of the package to load. + * @return The loaded package's resource. + */ + private Resource demandPackageLoad(URI uri) { + final URI normalized = getURIConverter().normalize(uri); + + packageLoadingLock.lock(); + try { + Resource demanded = uriCache.get(normalized); + if (demanded == null) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("SRS@" + Integer.toHexString(hashCode()) + ".getResource - loaded package normally : " + uri); //$NON-NLS-1$ //$NON-NLS-2$ + } + demanded = loadPackage(uri); + if (demanded != null) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("SRS@" + Integer.toHexString(hashCode()) + ".getResource - caching package " + uri); //$NON-NLS-1$ //$NON-NLS-2$ + } + Resource former = uriCache.putIfAbsent(normalized, demanded); + if (former != null) { + demanded = former; + } + } + } + return demanded; + } finally { + packageLoadingLock.unlock(); + } + } + + /** * {@inheritDoc} * * @see org.eclipse.emf.ecore.resource.impl.ResourceSetImpl#createResource(org.eclipse.emf.common.util.URI) @@ -230,29 +346,7 @@ public class SynchronizedResourceSet extends ResourceSetImpl { */ @Override public synchronized Resource createResource(URI uri, String contentType) { - // In some cases like the load of Profile in UML via pathmaps, - // The XMLHandler#getPackageForURI will call createResource after having got null by getResource() - final URI normalizedURI = getURIConverter().normalize(uri); - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("SRS@" + Integer.toHexString(hashCode()) + ".createResource " + uri); //$NON-NLS-1$ //$NON-NLS-2$ - } - if (uriCache.containsKey(normalizedURI)) { - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("SRS@" + Integer.toHexString(hashCode()) + ".createResource FOUND IN CACHE " + uri); //$NON-NLS-1$ //$NON-NLS-2$ - } - return uriCache.get(normalizedURI); - } - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("SRS@" + Integer.toHexString(hashCode()) + ".createResource CREATING " + uri); //$NON-NLS-1$ //$NON-NLS-2$ - } - Resource created = super.createResource(uri, contentType); - // putIfAbsent will return atomically the one that's already registered - // if another instance has been registered in between - Resource former = uriCache.putIfAbsent(normalizedURI, created); - if (former != null) { - created = former; - } - return created; + return super.createResource(uri, contentType); } /** @@ -276,6 +370,22 @@ public class SynchronizedResourceSet extends ResourceSetImpl { } /** + * {@inheritDoc} + */ + public void dispose() { + // unload these completely instead of using #unload(Resource) + for (Resource resource : loadedPackages) { + final URI uri = resource.getURI(); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("SRS@" + Integer.toHexString(hashCode()) + ".unload " + uri); //$NON-NLS-1$ //$NON-NLS-2$ + } + uriCache.remove(uri); + resource.unload(); + getResources().remove(resource); + } + } + + /** * A synchronized implementation of {@link ResourcesEList}. *

* Note that this cannot be extracted out of the {@link SynchronizedResourceSet} since the diff --git a/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/internal/utils/ForwardingXMLDefaultHandler.java b/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/internal/utils/ForwardingXMLDefaultHandler.java new file mode 100644 index 000000000..ed3d38d4d --- /dev/null +++ b/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/internal/utils/ForwardingXMLDefaultHandler.java @@ -0,0 +1,276 @@ +/******************************************************************************* + * Copyright (c) 2015 Obeo. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.emf.compare.ide.internal.utils; + +import java.io.IOException; +import java.util.Map; + +import org.eclipse.emf.ecore.xmi.XMLDefaultHandler; +import org.eclipse.emf.ecore.xmi.XMLHelper; +import org.eclipse.emf.ecore.xmi.XMLResource; +import org.xml.sax.Attributes; +import org.xml.sax.InputSource; +import org.xml.sax.Locator; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; +import org.xml.sax.helpers.DefaultHandler; + +/** + * This implementation of an {@link XMLDefaultHandler} will forward all calls to its delegate. + * + * @author Laurent Goubet + */ +public class ForwardingXMLDefaultHandler extends DefaultHandler implements XMLDefaultHandler { + /** The delegate to which we'll forward all calls. */ + protected final XMLDefaultHandler delegate; + + /** + * Creates our forwarding handler given its delegate. + * + * @param delegate + * The delegate instance to forward method calls to. + */ + public ForwardingXMLDefaultHandler(XMLDefaultHandler delegate) { + this.delegate = delegate; + } + + /** + * Returns the delegate instance that methods are forwarded to. + * + * @return The delegate instance that methods are forwarded to. + */ + protected XMLDefaultHandler delegate() { + return this.delegate; + } + + /** + * {@inheritDoc} + */ + @Override + public void characters(char[] arg0, int arg1, int arg2) throws SAXException { + delegate().characters(arg0, arg1, arg2); + } + + /** + * {@inheritDoc} + */ + @Override + public void endDocument() throws SAXException { + delegate().endDocument(); + } + + /** + * {@inheritDoc} + */ + @Override + public void endElement(String arg0, String arg1, String arg2) throws SAXException { + delegate().endElement(arg0, arg1, arg2); + } + + /** + * {@inheritDoc} + */ + @Override + public void endPrefixMapping(String arg0) throws SAXException { + delegate().endPrefixMapping(arg0); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean equals(Object arg0) { + return delegate().equals(arg0); + } + + /** + * {@inheritDoc} + */ + @Override + public void error(SAXParseException arg0) throws SAXException { + delegate().error(arg0); + } + + /** + * {@inheritDoc} + */ + @Override + public void fatalError(SAXParseException arg0) throws SAXException { + delegate().fatalError(arg0); + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + return delegate().hashCode(); + } + + /** + * {@inheritDoc} + */ + @Override + public void ignorableWhitespace(char[] arg0, int arg1, int arg2) throws SAXException { + delegate().ignorableWhitespace(arg0, arg1, arg2); + } + + /** + * {@inheritDoc} + */ + @Override + public void notationDecl(String arg0, String arg1, String arg2) throws SAXException { + delegate().notationDecl(arg0, arg1, arg2); + } + + /** + * {@inheritDoc} + */ + @Override + public void processingInstruction(String arg0, String arg1) throws SAXException { + delegate().processingInstruction(arg0, arg1); + } + + /** + * {@inheritDoc} + */ + @Override + public InputSource resolveEntity(String arg0, String arg1) throws IOException, SAXException { + return delegate().resolveEntity(arg0, arg1); + } + + /** + * {@inheritDoc} + */ + @Override + public void setDocumentLocator(Locator arg0) { + delegate().setDocumentLocator(arg0); + } + + /** + * {@inheritDoc} + */ + @Override + public void skippedEntity(String arg0) throws SAXException { + delegate().skippedEntity(arg0); + } + + /** + * {@inheritDoc} + */ + @Override + public void startDocument() throws SAXException { + delegate().startDocument(); + } + + /** + * {@inheritDoc} + */ + @Override + public void startElement(String arg0, String arg1, String arg2, Attributes arg3) throws SAXException { + delegate().startElement(arg0, arg1, arg2, arg3); + } + + /** + * {@inheritDoc} + */ + @Override + public void startPrefixMapping(String arg0, String arg1) throws SAXException { + delegate().startPrefixMapping(arg0, arg1); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return delegate().toString(); + } + + /** + * {@inheritDoc} + */ + @Override + public void unparsedEntityDecl(String arg0, String arg1, String arg2, String arg3) throws SAXException { + delegate().unparsedEntityDecl(arg0, arg1, arg2, arg3); + } + + /** + * {@inheritDoc} + */ + @Override + public void warning(SAXParseException arg0) throws SAXException { + delegate().warning(arg0); + } + + /** + * {@inheritDoc} + */ + public void comment(char[] arg0, int arg1, int arg2) throws SAXException { + delegate().comment(arg0, arg1, arg2); + } + + /** + * {@inheritDoc} + */ + public void endCDATA() throws SAXException { + delegate().endCDATA(); + } + + /** + * {@inheritDoc} + */ + public void endDTD() throws SAXException { + delegate().endDTD(); + } + + /** + * {@inheritDoc} + */ + public void endEntity(String arg0) throws SAXException { + delegate().endEntity(arg0); + } + + /** + * {@inheritDoc} + */ + public void startCDATA() throws SAXException { + delegate().startCDATA(); + } + + /** + * {@inheritDoc} + */ + public void startDTD(String arg0, String arg1, String arg2) throws SAXException { + delegate().startDTD(arg0, arg1, arg2); + } + + /** + * {@inheritDoc} + */ + public void startEntity(String arg0) throws SAXException { + delegate().startEntity(arg0); + } + + /** + * {@inheritDoc} + */ + public void reset() { + delegate().reset(); + } + + /** + * {@inheritDoc} + */ + public void prepare(XMLResource resource, XMLHelper helper, Map options) { + delegate().prepare(resource, helper, options); + } +} diff --git a/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/internal/utils/ForwardingXMLHandler.java b/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/internal/utils/ForwardingXMLHandler.java index c8106be94..ad2eaba1b 100644 --- a/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/internal/utils/ForwardingXMLHandler.java +++ b/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/internal/utils/ForwardingXMLHandler.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2012 Obeo. + * Copyright (c) 2012, 2015 Obeo. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -72,7 +72,7 @@ public class ForwardingXMLHandler extends XMLHandler { }); /** The delegate to which we'll forward all calls. */ - protected XMLHandler delegate; + protected final XMLHandler delegate; /** * Creates this forwarding handler given its delegate. All other parameters are only used to call the diff --git a/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/internal/utils/ForwardingXMLHelper.java b/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/internal/utils/ForwardingXMLHelper.java new file mode 100644 index 000000000..567bea946 --- /dev/null +++ b/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/internal/utils/ForwardingXMLHelper.java @@ -0,0 +1,312 @@ +/******************************************************************************* + * Copyright (c) 2015 Obeo. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.emf.compare.ide.internal.utils; + +import java.util.List; +import java.util.Map; + +import org.eclipse.emf.common.util.EMap; +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EClassifier; +import org.eclipse.emf.ecore.EDataType; +import org.eclipse.emf.ecore.EFactory; +import org.eclipse.emf.ecore.ENamedElement; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.util.ExtendedMetaData; +import org.eclipse.emf.ecore.xmi.DanglingHREFException; +import org.eclipse.emf.ecore.xmi.NameInfo; +import org.eclipse.emf.ecore.xmi.XMIException; +import org.eclipse.emf.ecore.xmi.XMLHelper; +import org.eclipse.emf.ecore.xmi.XMLResource; +import org.eclipse.emf.ecore.xmi.XMLResource.XMLMap; + +/** + * This implementation of an {@link XMLHelper} will forward all calls to its delegate. + * + * @author Laurent Goubet + */ +public class ForwardingXMLHelper implements XMLHelper { + /** The actual helper we'll delegate all calls to. */ + private final XMLHelper delegate; + + /** + * Default constructor. + * + * @param delegate + * The actual helper we'll delegate all calls to. + */ + public ForwardingXMLHelper(XMLHelper delegate) { + this.delegate = delegate; + } + + /** {@inheritDoc} */ + public void setOptions(Map options) { + delegate.setOptions(options); + } + + /** {@inheritDoc} */ + public void setNoNamespacePackage(EPackage pkg) { + delegate.setNoNamespacePackage(pkg); + } + + /** {@inheritDoc} */ + public EPackage getNoNamespacePackage() { + return delegate.getNoNamespacePackage(); + } + + /** {@inheritDoc} */ + public void setAnySimpleType(EClass type) { + delegate.setAnySimpleType(type); + } + + /** {@inheritDoc} */ + public void setXMLMap(XMLMap map) { + delegate.setXMLMap(map); + } + + /** {@inheritDoc} */ + public XMLMap getXMLMap() { + return delegate.getXMLMap(); + } + + /** {@inheritDoc} */ + public void setExtendedMetaData(ExtendedMetaData extendedMetaData) { + delegate.setExtendedMetaData(extendedMetaData); + } + + /** {@inheritDoc} */ + public ExtendedMetaData getExtendedMetaData() { + return delegate.getExtendedMetaData(); + } + + /** {@inheritDoc} */ + public XMLResource getResource() { + return delegate.getResource(); + } + + /** {@inheritDoc} */ + public Object getValue(EObject eObject, EStructuralFeature eStructuralFeature) { + return delegate.getValue(eObject, eStructuralFeature); + } + + /** {@inheritDoc} */ + public String getName(ENamedElement eNamedElement) { + return delegate.getName(eNamedElement); + } + + /** {@inheritDoc} */ + public String getQName(EClass eClass) { + return delegate.getQName(eClass); + } + + /** {@inheritDoc} */ + public void populateNameInfo(NameInfo nameInfo, EClass eClass) { + delegate.populateNameInfo(nameInfo, eClass); + } + + /** {@inheritDoc} */ + public String getQName(EDataType eDataType) { + return delegate.getQName(eDataType); + } + + /** {@inheritDoc} */ + public void populateNameInfo(NameInfo nameInfo, EDataType eDataType) { + delegate.populateNameInfo(nameInfo, eDataType); + } + + /** {@inheritDoc} */ + public String getQName(EStructuralFeature feature) { + return delegate.getQName(feature); + } + + /** {@inheritDoc} */ + public void populateNameInfo(NameInfo nameInfo, EStructuralFeature feature) { + delegate.populateNameInfo(nameInfo, feature); + } + + /** {@inheritDoc} */ + public String getPrefix(String namespaceURI) { + return delegate.getPrefix(namespaceURI); + } + + /** {@inheritDoc} */ + public String getPrefix(EPackage ePackage) { + return delegate.getPrefix(ePackage); + } + + /** {@inheritDoc} */ + public String getNamespaceURI(String prefix) { + return delegate.getNamespaceURI(prefix); + } + + /** {@inheritDoc} */ + public List getPrefixes(EPackage ePackage) { + return delegate.getPrefixes(ePackage); + } + + /** {@inheritDoc} */ + public String getID(EObject eObject) { + return delegate.getID(eObject); + } + + /** {@inheritDoc} */ + public String getIDREF(EObject eObject) { + return delegate.getIDREF(eObject); + } + + /** {@inheritDoc} */ + public String getHREF(EObject eObject) { + return delegate.getHREF(eObject); + } + + /** {@inheritDoc} */ + public URI deresolve(URI uri) { + return delegate.deresolve(uri); + } + + /** {@inheritDoc} */ + public EPackage[] packages() { + return delegate.packages(); + } + + /** + * {@inheritDoc} + * + * @deprecated + */ + @Deprecated + public EObject createObject(EFactory eFactory, String name) { + return delegate.createObject(eFactory, name); + } + + /** {@inheritDoc} */ + public EObject createObject(EFactory eFactory, EClassifier type) { + return delegate.createObject(eFactory, type); + } + + /** {@inheritDoc} */ + public EClassifier getType(EFactory eFactory, String typeName) { + return delegate.getType(eFactory, typeName); + } + + /** {@inheritDoc} */ + public void setValue(EObject eObject, EStructuralFeature eStructuralFeature, Object value, int position) { + delegate.setValue(eObject, eStructuralFeature, value, position); + } + + /** {@inheritDoc} */ + public EStructuralFeature getFeature(EClass eClass, String namespaceURI, String name) { + return delegate.getFeature(eClass, namespaceURI, name); + } + + /** {@inheritDoc} */ + public EStructuralFeature getFeature(EClass eClass, String namespaceURI, String name, boolean isElement) { + return delegate.getFeature(eClass, namespaceURI, name, isElement); + } + + /** {@inheritDoc} */ + public int getFeatureKind(EStructuralFeature feature) { + return delegate.getFeatureKind(feature); + } + + /** {@inheritDoc} */ + public String getXMLEncoding(String javaEncoding) { + return delegate.getXMLEncoding(javaEncoding); + } + + /** {@inheritDoc} */ + public String getJavaEncoding(String xmlEncoding) { + return delegate.getJavaEncoding(xmlEncoding); + } + + /** {@inheritDoc} */ + public List setManyReference(ManyReference reference, String location) { + return delegate.setManyReference(reference, location); + } + + /** {@inheritDoc} */ + public void setCheckForDuplicates(boolean checkForDuplicates) { + delegate.setCheckForDuplicates(checkForDuplicates); + } + + /** {@inheritDoc} */ + public void setProcessDanglingHREF(String value) { + delegate.setProcessDanglingHREF(value); + } + + /** {@inheritDoc} */ + public DanglingHREFException getDanglingHREFException() { + return delegate.getDanglingHREFException(); + } + + /** {@inheritDoc} */ + public URI resolve(URI relative, URI base) { + return delegate.resolve(relative, base); + } + + /** {@inheritDoc} */ + public void addPrefix(String prefix, String uri) { + delegate.addPrefix(prefix, uri); + } + + /** {@inheritDoc} */ + public Map getAnyContentPrefixToURIMapping() { + return delegate.getAnyContentPrefixToURIMapping(); + } + + /** {@inheritDoc} */ + public void recordPrefixToURIMapping() { + delegate.recordPrefixToURIMapping(); + } + + /** {@inheritDoc} */ + public String getURI(String prefix) { + return delegate.getURI(prefix); + } + + /** {@inheritDoc} */ + public void pushContext() { + delegate.pushContext(); + } + + /** {@inheritDoc} */ + public void popContext() { + delegate.popContext(); + } + + /** {@inheritDoc} */ + public void popContext(Map prefixesToFactories) { + delegate.popContext(prefixesToFactories); + } + + /** {@inheritDoc} */ + public String convertToString(EFactory factory, EDataType dataType, Object data) { + return delegate.convertToString(factory, dataType, data); + } + + /** {@inheritDoc} */ + public EMap getPrefixToNamespaceMap() { + return delegate.getPrefixToNamespaceMap(); + } + + /** {@inheritDoc} */ + public void setPrefixToNamespaceMap(EMap prefixToNamespaceMap) { + delegate.setPrefixToNamespaceMap(prefixToNamespaceMap); + } + + /** {@inheritDoc} */ + public void setMustHavePrefix(boolean mustHavePrefix) { + delegate.setMustHavePrefix(mustHavePrefix); + } +} diff --git a/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/internal/utils/INamespaceDeclarationListener.java b/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/internal/utils/INamespaceDeclarationListener.java new file mode 100644 index 000000000..7319cc331 --- /dev/null +++ b/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/internal/utils/INamespaceDeclarationListener.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2015 Obeo. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.emf.compare.ide.internal.utils; + +import org.eclipse.emf.common.util.URI; + +/** + * The listener interface for receiving namespace declaration events from the XML parsers. + * + * @author Laurent Goubet + */ +public interface INamespaceDeclarationListener { + /** + * Notified when a schema location is declared from the XMLHandler. + * + * @param key + * Key for this schema location mapping. + * @param uri + * URI for this schema. + */ + void schemaLocationDeclared(String key, URI uri); +} diff --git a/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/internal/utils/IProxyCreationListener.java b/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/internal/utils/IProxyCreationListener.java new file mode 100644 index 000000000..b52ddaec1 --- /dev/null +++ b/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/internal/utils/IProxyCreationListener.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2015 Obeo. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.emf.compare.ide.internal.utils; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.resource.Resource; + +/** + * Describes the contract for a proxy creation listener as can be notifier from this pool's created parsers. + * + * @author Laurent Goubet + */ +public interface IProxyCreationListener { + /** + * This will be called when a proxy is created from one of the parser pool's parsers. + * + * @param source + * The resource in which a proxy has been created towards another. + * @param eObject + * The EObject on which some feature is going to be set with a proxy value. + * @param eStructuralFeature + * The structural feature which value will contain a proxy. + * @param proxy + * The actual proxy created for this eObject's feature. + * @param position + * Position at which the proxy is going to be inserted. This will be set to -1 + * when the proxy is added at the end of the eStructuralFeature's values list (for + * multi-valued features) or if said feature is single-valued. + */ + void proxyCreated(Resource source, EObject eObject, EStructuralFeature eStructuralFeature, EObject proxy, + int position); +} diff --git a/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/internal/utils/NoNotificationParserPool.java b/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/internal/utils/NoNotificationParserPool.java index bae330868..14940d45e 100644 --- a/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/internal/utils/NoNotificationParserPool.java +++ b/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/internal/utils/NoNotificationParserPool.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2012 Obeo. + * Copyright (c) 2012, 2015 Obeo. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -32,7 +32,7 @@ import org.eclipse.emf.ecore.xmi.impl.XMLHandler; * * @author Laurent Goubet */ -public class NoNotificationParserPool extends ProxyNotifierParserPool { +public class NoNotificationParserPool extends NotifyingParserPool { /** * Default constructor. * diff --git a/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/internal/utils/NotLoadingResourceSet.java b/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/internal/utils/NotLoadingResourceSet.java index 360a82e49..54b4829f3 100644 --- a/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/internal/utils/NotLoadingResourceSet.java +++ b/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/internal/utils/NotLoadingResourceSet.java @@ -52,7 +52,6 @@ import org.eclipse.emf.compare.ide.EMFCompareIDEPlugin; import org.eclipse.emf.compare.ide.hook.IResourceSetHook; import org.eclipse.emf.compare.ide.internal.EMFCompareIDEMessages; import org.eclipse.emf.compare.ide.internal.hook.ResourceSetHookRegistry; -import org.eclipse.emf.compare.ide.internal.utils.ProxyNotifierParserPool.IProxyCreationListener; import org.eclipse.emf.compare.ide.utils.StorageTraversal; import org.eclipse.emf.compare.rcp.EMFCompareRCPPlugin; import org.eclipse.emf.compare.rcp.policy.ILoadOnDemandPolicy; @@ -428,7 +427,7 @@ public final class NotLoadingResourceSet extends ResourceSetImpl implements Disp @Override public Map getLoadOptions() { this.loadOptions = super.getLoadOptions(); - final ProxyNotifierParserPool parserPool = new ProxyNotifierParserPool(false); + final NotifyingParserPool parserPool = new NotifyingParserPool(false); parserPool.addProxyListener(this); loadOptions.put(XMLResource.OPTION_USE_PARSER_POOL, parserPool); loadOptions.put(XMLResource.OPTION_USE_DEPRECATED_METHODS, Boolean.FALSE); @@ -457,12 +456,14 @@ public final class NotLoadingResourceSet extends ResourceSetImpl implements Disp // removing the uml CacheAdapter isn't enough either // we need to get rid of all adapters manually for (Resource resource : currentResources) { - TreeIterator allContents = EcoreUtil.getAllProperContents(resource, false); - while (allContents.hasNext()) { - final EObject next = allContents.next(); - next.eAdapters().clear(); + if (resource.isLoaded()) { + TreeIterator allContents = EcoreUtil.getAllProperContents(resource, false); + while (allContents.hasNext()) { + final EObject next = allContents.next(); + next.eAdapters().clear(); + } + resource.eAdapters().clear(); } - resource.eAdapters().clear(); } getResources().clear(); diff --git a/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/internal/utils/NotifyingParserPool.java b/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/internal/utils/NotifyingParserPool.java new file mode 100644 index 000000000..202b298dc --- /dev/null +++ b/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/internal/utils/NotifyingParserPool.java @@ -0,0 +1,265 @@ +/******************************************************************************* + * Copyright (c) 2014, 2015 Obeo. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.emf.compare.ide.internal.utils; + +import java.util.Map; +import java.util.StringTokenizer; + +import org.eclipse.core.runtime.ListenerList; +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.util.ExtendedMetaData; +import org.eclipse.emf.ecore.xmi.XMLDefaultHandler; +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.XMLParserPoolImpl; +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; + +/** + * This implementation of an XML parser pool will notify a list of {@link INamespaceDeclarationListener + * namespace declaration listeners} of all namespaces declared in the parsed resource (xsi:schemalocation), + * then a list of {@link IProxyCreationListener proxy creation listeners} of each and every proxy it sees + * while loading an XML file as an EMF model. + * + * @author Laurent Goubet + */ +public class NotifyingParserPool extends XMLParserPoolImpl { + /** Only set containment reference values, ignore the rest. */ + protected final boolean containmentOnly; + + /** The list of parties interested by our proxies. */ + private ListenerList proxyListeners; + + /** The list of parties interested in the declaration of namespaces. */ + private ListenerList namespaceDeclarationListeners; + + /** + * Default constructor. + * + * @param containmentOnly + * only set containment reference values. The model will be mostly empty except for its + * containment tree. + */ + public NotifyingParserPool(boolean containmentOnly) { + super(true); + this.proxyListeners = new ListenerList(); + this.namespaceDeclarationListeners = new ListenerList(); + this.containmentOnly = containmentOnly; + } + + /** {@inheritDoc} */ + @Override + public synchronized XMLDefaultHandler getDefaultHandler(XMLResource resource, XMLLoad xmlLoad, + XMLHelper helper, Map options) { + final NotifyingXMLHelper wrapper = new NotifyingXMLHelper(helper, containmentOnly); + for (Object listener : proxyListeners.getListeners()) { + wrapper.addProxyListener((IProxyCreationListener)listener); + } + final XMLDefaultHandler handler = createDefaultHandler(resource, xmlLoad, wrapper, options); + final NamespaceDeclarationNotifyingXMLDefaultHandler handlerWrapper = new NamespaceDeclarationNotifyingXMLDefaultHandler( + handler); + for (Object listener : namespaceDeclarationListeners.getListeners()) { + handlerWrapper.addNamespaceDeclarationListener((INamespaceDeclarationListener)listener); + } + handlerWrapper.prepare(resource, wrapper, options); + return handlerWrapper; + } + + /** + * Create the default (unwrapped) XMLDefaultHandler. This is merely a call to super but can + * be sub-classed. + * + * @param resource + * The resource to load. + * @param xmlLoad + * The XML load to pass on tho the handler. + * @param helper + * The XML helper to pass on tho the handler. + * @param options + * The load options for this resource. + * @return The created XMLDefaultHandler. + * @see #getDefaultHandler(XMLResource, XMLLoad, XMLHelper, Map) + */ + protected XMLDefaultHandler createDefaultHandler(XMLResource resource, XMLLoad xmlLoad, XMLHelper helper, + Map options) { + return super.getDefaultHandler(resource, xmlLoad, helper, options); + } + + /** + * Add a proxy creation listener to this parser pool's list. + * + * @param listener + * The listener to add to this pool's list. + */ + public void addProxyListener(IProxyCreationListener listener) { + proxyListeners.add(listener); + } + + /** + * Remove a proxy creation listener from this parser pool's list. + * + * @param listener + * The listener to remove from this pool's list. + */ + public void removeProxyListener(IProxyCreationListener listener) { + proxyListeners.remove(listener); + } + + /** + * Add a namespace declaration listener to this parser pool's list. + * + * @param listener + * The listener to add to this pool's list. + */ + public void addNamespaceDeclarationListener(INamespaceDeclarationListener listener) { + namespaceDeclarationListeners.add(listener); + } + + /** + * Remove a namespace declaration listener from this parser pool's list. + * + * @param listener + * The listener to remove from this pool's list. + */ + public void removeNamespaceDeclarationListener(INamespaceDeclarationListener listener) { + namespaceDeclarationListeners.remove(listener); + } + + /** + * An XMLDefaultHandler that will notify interested {@link INamespaceDeclarationListener listeners} of its + * namespace declarations. + */ + private static class NamespaceDeclarationNotifyingXMLDefaultHandler extends ForwardingXMLDefaultHandler { + /** The list of parties interested in the declaration of namespaces. */ + private ListenerList namespaceDeclarationListeners; + + /** true only when we're parsing the very first element. */ + private boolean isRoot; + + /** + * Constructs a wrapper given its delegate. + * + * @param delegate + * The delegate handler. + */ + public NamespaceDeclarationNotifyingXMLDefaultHandler(XMLDefaultHandler delegate) { + super(delegate); + this.namespaceDeclarationListeners = new ListenerList(); + } + + @Override + public void startDocument() throws SAXException { + isRoot = true; + super.startDocument(); + } + + @Override + public void startElement(String arg0, String arg1, String arg2, Attributes arg3) throws SAXException { + if (isRoot) { + String xsiSchemaLocation = arg3.getValue(ExtendedMetaData.XSI_URI, + XMLResource.SCHEMA_LOCATION); + if (xsiSchemaLocation != null) { + declareSchemaLocation(xsiSchemaLocation); + } + isRoot = false; + } + super.startElement(arg0, arg1, arg2, arg3); + } + + /** + * Add a namespace declaration listener to this helper's list. + * + * @param listener + * The listener to add to this helper's list. + */ + public void addNamespaceDeclarationListener(INamespaceDeclarationListener listener) { + namespaceDeclarationListeners.add(listener); + } + + /** + * We've read the headers of the resource to load. Notify our {@link #namespaceDeclarationListeners + * listeners} about the schema locations that can be found therein. + * + * @param xsiSchemaLocation + * The String of xsi:schemalocation declarations in the file. + */ + private void declareSchemaLocation(String xsiSchemaLocation) { + StringTokenizer stringTokenizer = new StringTokenizer(xsiSchemaLocation, " "); //$NON-NLS-1$ + while (stringTokenizer.hasMoreTokens()) { + String key = stringTokenizer.nextToken(); + if (stringTokenizer.hasMoreTokens()) { + String value = stringTokenizer.nextToken(); + URI uri = URI.createURI(value); + for (Object listener : namespaceDeclarationListeners.getListeners()) { + ((INamespaceDeclarationListener)listener).schemaLocationDeclared(key, uri); + } + } + } + } + } + + /** + * An XMLHelper wrapper that's capable of notifying {@link IProxyCreationListener listeners}s about proxy + * creations. + */ + private static class NotifyingXMLHelper extends ForwardingXMLHelper { + /** The list of parties interested by our proxy creations. */ + private final ListenerList proxyListeners; + + /** Only set containment reference values, ignore the rest. */ + private final boolean containmentOnly; + + /** + * Constructs a wrapper given its delegate XMLHelper. + * + * @param delegate + * The delegate XMLHelper. + * @param containmentOnly + * Only set containment reference values. + */ + public NotifyingXMLHelper(XMLHelper delegate, boolean containmentOnly) { + super(delegate); + this.proxyListeners = new ListenerList(); + this.containmentOnly = containmentOnly; + } + + /** {@inheritDoc} */ + @Override + public void setValue(EObject eObject, EStructuralFeature eStructuralFeature, Object value, + int position) { + if (!containmentOnly + || (eStructuralFeature instanceof EReference && ((EReference)eStructuralFeature) + .isContainment())) { + super.setValue(eObject, eStructuralFeature, value, position); + } + if (value instanceof EObject && ((EObject)value).eIsProxy()) { + for (Object listener : proxyListeners.getListeners()) { + ((IProxyCreationListener)listener).proxyCreated(getResource(), eObject, + eStructuralFeature, (EObject)value, position); + } + } + } + + /** + * Add a proxy creation listener to this helper's list. + * + * @param listener + * The listener to add to this helper's list. + */ + public void addProxyListener(IProxyCreationListener listener) { + proxyListeners.add(listener); + } + } +} diff --git a/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/internal/utils/ProxyNotifierParserPool.java b/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/internal/utils/ProxyNotifierParserPool.java deleted file mode 100644 index e33af4f0f..000000000 --- a/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/internal/utils/ProxyNotifierParserPool.java +++ /dev/null @@ -1,474 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 Obeo. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Obeo - initial API and implementation - *******************************************************************************/ -package org.eclipse.emf.compare.ide.internal.utils; - -import java.util.List; -import java.util.Map; - -import org.eclipse.core.runtime.ListenerList; -import org.eclipse.emf.common.util.EMap; -import org.eclipse.emf.common.util.URI; -import org.eclipse.emf.ecore.EClass; -import org.eclipse.emf.ecore.EClassifier; -import org.eclipse.emf.ecore.EDataType; -import org.eclipse.emf.ecore.EFactory; -import org.eclipse.emf.ecore.ENamedElement; -import org.eclipse.emf.ecore.EObject; -import org.eclipse.emf.ecore.EPackage; -import org.eclipse.emf.ecore.EReference; -import org.eclipse.emf.ecore.EStructuralFeature; -import org.eclipse.emf.ecore.resource.Resource; -import org.eclipse.emf.ecore.util.ExtendedMetaData; -import org.eclipse.emf.ecore.xmi.DanglingHREFException; -import org.eclipse.emf.ecore.xmi.NameInfo; -import org.eclipse.emf.ecore.xmi.XMIException; -import org.eclipse.emf.ecore.xmi.XMLDefaultHandler; -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.XMLResource.XMLMap; -import org.eclipse.emf.ecore.xmi.impl.XMLParserPoolImpl; - -/** - * This implementation of an XML parser pool will notify a list of {@link IProxyCreationListener proxy - * creation listeners} of each and every proxy it sees while loading an XML file as an EMF model. - * - * @author Laurent Goubet - */ -public class ProxyNotifierParserPool extends XMLParserPoolImpl { - /** Only set containment reference values, ignore the rest. */ - protected final boolean containmentOnly; - - /** The list of parties interested by our proxies. */ - private ListenerList proxyListeners; - - /** - * Default constructor. - * - * @param containmentOnly - * only set containment reference values. The model will be mostly empty except for its - * containment tree. - */ - public ProxyNotifierParserPool(boolean containmentOnly) { - super(true); - this.proxyListeners = new ListenerList(); - this.containmentOnly = containmentOnly; - } - - /** {@inheritDoc} */ - @Override - public synchronized XMLDefaultHandler getDefaultHandler(XMLResource resource, XMLLoad xmlLoad, - XMLHelper helper, Map options) { - final ProxyNotifierXMLHelper wrapper = new ProxyNotifierXMLHelper(helper, containmentOnly); - for (Object listener : proxyListeners.getListeners()) { - wrapper.addProxyListener((IProxyCreationListener)listener); - } - final XMLDefaultHandler handler = createDefaultHandler(resource, xmlLoad, wrapper, options); - handler.prepare(resource, wrapper, options); - return handler; - } - - /** - * Create the default (unwrapped) XMLDefaultHandler. This is merely a call to super but can - * be sub-classed. - * - * @param resource - * The resource to load. - * @param xmlLoad - * The XML load to pass on tho the handler. - * @param helper - * The XML helper to pass on tho the handler. - * @param options - * The load options for this resource. - * @return The created XMLDefaultHandler. - * @see #getDefaultHandler(XMLResource, XMLLoad, XMLHelper, Map) - */ - protected XMLDefaultHandler createDefaultHandler(XMLResource resource, XMLLoad xmlLoad, XMLHelper helper, - Map options) { - return super.getDefaultHandler(resource, xmlLoad, helper, options); - } - - /** - * Add a proxy creation listener to this parser pool's list. - * - * @param listener - * The listener to add to this pool's list. - */ - public void addProxyListener(IProxyCreationListener listener) { - proxyListeners.add(listener); - } - - /** - * Remove a proxy creation listener from this parser pool's list. - * - * @param listener - * The listener to remove from this pool's list. - */ - public void removeProxyListener(IProxyCreationListener listener) { - proxyListeners.remove(listener); - } - - /** - * Describes the contract for a proxy creation listener as can be notifier from this pool's created - * parsers. - */ - public interface IProxyCreationListener { - /** - * This will be called when a proxy is created from one of the parser pool's parsers. - * - * @param source - * The resource in which a proxy has been created towards another. - * @param eObject - * The EObject on which some feature is going to be set with a proxy value. - * @param eStructuralFeature - * The structural feature which value will contain a proxy. - * @param proxy - * The actual proxy created for this eObject's feature. - * @param position - * Position at which the proxy is going to be inserted. This will be set to -1 - * when the proxy is added at the end of the eStructuralFeature's values list - * (for multi-valued features) or if said feature is single-valued. - */ - void proxyCreated(Resource source, EObject eObject, EStructuralFeature eStructuralFeature, - EObject proxy, int position); - } - - /** - * An XMLHelper wrapper that's capable of notifying {@link IProxyCreationListener listeners}s about proxy - * creations. - */ - private static class ProxyNotifierXMLHelper extends ForwardingXMLHelper { - /** The list of parties interested by our proxy creations. */ - private final ListenerList proxyListeners; - - /** Only set containment reference values, ignore the rest. */ - private final boolean containmentOnly; - - /** - * Constructs a wrapper given its delegate XMLHelper. - * - * @param delegate - * The delegate XMLHelper. - * @param containmentOnly - * Only set containment reference values. - */ - public ProxyNotifierXMLHelper(XMLHelper delegate, boolean containmentOnly) { - super(delegate); - this.proxyListeners = new ListenerList(); - this.containmentOnly = containmentOnly; - } - - /** {@inheritDoc} */ - @Override - public void setValue(EObject eObject, EStructuralFeature eStructuralFeature, Object value, - int position) { - if (!containmentOnly - || (eStructuralFeature instanceof EReference && ((EReference)eStructuralFeature) - .isContainment())) { - super.setValue(eObject, eStructuralFeature, value, position); - } - if (value instanceof EObject && ((EObject)value).eIsProxy()) { - for (Object listener : proxyListeners.getListeners()) { - ((IProxyCreationListener)listener).proxyCreated(getResource(), eObject, - eStructuralFeature, (EObject)value, position); - } - } - } - - /** - * Add a proxy creation listener to this parser pool's list. - * - * @param listener - * The listener to add to this pool's list. - */ - public void addProxyListener(IProxyCreationListener listener) { - proxyListeners.add(listener); - } - } - - /** A delegating XMLHelper. */ - private static class ForwardingXMLHelper implements XMLHelper { - /** The actual helper we'll delegate all calls to. */ - private final XMLHelper delegate; - - /** - * Default constructor. - * - * @param delegate - * The actual helper we'll delegate all calls to. - */ - public ForwardingXMLHelper(XMLHelper delegate) { - this.delegate = delegate; - } - - /** {@inheritDoc} */ - public void setOptions(Map options) { - delegate.setOptions(options); - } - - /** {@inheritDoc} */ - public void setNoNamespacePackage(EPackage pkg) { - delegate.setNoNamespacePackage(pkg); - } - - /** {@inheritDoc} */ - public EPackage getNoNamespacePackage() { - return delegate.getNoNamespacePackage(); - } - - /** {@inheritDoc} */ - public void setAnySimpleType(EClass type) { - delegate.setAnySimpleType(type); - } - - /** {@inheritDoc} */ - public void setXMLMap(XMLMap map) { - delegate.setXMLMap(map); - } - - /** {@inheritDoc} */ - public XMLMap getXMLMap() { - return delegate.getXMLMap(); - } - - /** {@inheritDoc} */ - public void setExtendedMetaData(ExtendedMetaData extendedMetaData) { - delegate.setExtendedMetaData(extendedMetaData); - } - - /** {@inheritDoc} */ - public ExtendedMetaData getExtendedMetaData() { - return delegate.getExtendedMetaData(); - } - - /** {@inheritDoc} */ - public XMLResource getResource() { - return delegate.getResource(); - } - - /** {@inheritDoc} */ - public Object getValue(EObject eObject, EStructuralFeature eStructuralFeature) { - return delegate.getValue(eObject, eStructuralFeature); - } - - /** {@inheritDoc} */ - public String getName(ENamedElement eNamedElement) { - return delegate.getName(eNamedElement); - } - - /** {@inheritDoc} */ - public String getQName(EClass eClass) { - return delegate.getQName(eClass); - } - - /** {@inheritDoc} */ - public void populateNameInfo(NameInfo nameInfo, EClass eClass) { - delegate.populateNameInfo(nameInfo, eClass); - } - - /** {@inheritDoc} */ - public String getQName(EDataType eDataType) { - return delegate.getQName(eDataType); - } - - /** {@inheritDoc} */ - public void populateNameInfo(NameInfo nameInfo, EDataType eDataType) { - delegate.populateNameInfo(nameInfo, eDataType); - } - - /** {@inheritDoc} */ - public String getQName(EStructuralFeature feature) { - return delegate.getQName(feature); - } - - /** {@inheritDoc} */ - public void populateNameInfo(NameInfo nameInfo, EStructuralFeature feature) { - delegate.populateNameInfo(nameInfo, feature); - } - - /** {@inheritDoc} */ - public String getPrefix(String namespaceURI) { - return delegate.getPrefix(namespaceURI); - } - - /** {@inheritDoc} */ - public String getPrefix(EPackage ePackage) { - return delegate.getPrefix(ePackage); - } - - /** {@inheritDoc} */ - public String getNamespaceURI(String prefix) { - return delegate.getNamespaceURI(prefix); - } - - /** {@inheritDoc} */ - public List getPrefixes(EPackage ePackage) { - return delegate.getPrefixes(ePackage); - } - - /** {@inheritDoc} */ - public String getID(EObject eObject) { - return delegate.getID(eObject); - } - - /** {@inheritDoc} */ - public String getIDREF(EObject eObject) { - return delegate.getIDREF(eObject); - } - - /** {@inheritDoc} */ - public String getHREF(EObject eObject) { - return delegate.getHREF(eObject); - } - - /** {@inheritDoc} */ - public URI deresolve(URI uri) { - return delegate.deresolve(uri); - } - - /** {@inheritDoc} */ - public EPackage[] packages() { - return delegate.packages(); - } - - /** - * {@inheritDoc} - * - * @deprecated - */ - @Deprecated - public EObject createObject(EFactory eFactory, String name) { - return delegate.createObject(eFactory, name); - } - - /** {@inheritDoc} */ - public EObject createObject(EFactory eFactory, EClassifier type) { - return delegate.createObject(eFactory, type); - } - - /** {@inheritDoc} */ - public EClassifier getType(EFactory eFactory, String typeName) { - return delegate.getType(eFactory, typeName); - } - - /** {@inheritDoc} */ - public void setValue(EObject eObject, EStructuralFeature eStructuralFeature, Object value, - int position) { - delegate.setValue(eObject, eStructuralFeature, value, position); - } - - /** {@inheritDoc} */ - public EStructuralFeature getFeature(EClass eClass, String namespaceURI, String name) { - return delegate.getFeature(eClass, namespaceURI, name); - } - - /** {@inheritDoc} */ - public EStructuralFeature getFeature(EClass eClass, String namespaceURI, String name, - boolean isElement) { - return delegate.getFeature(eClass, namespaceURI, name, isElement); - } - - /** {@inheritDoc} */ - public int getFeatureKind(EStructuralFeature feature) { - return delegate.getFeatureKind(feature); - } - - /** {@inheritDoc} */ - public String getXMLEncoding(String javaEncoding) { - return delegate.getXMLEncoding(javaEncoding); - } - - /** {@inheritDoc} */ - public String getJavaEncoding(String xmlEncoding) { - return delegate.getJavaEncoding(xmlEncoding); - } - - /** {@inheritDoc} */ - public List setManyReference(ManyReference reference, String location) { - return delegate.setManyReference(reference, location); - } - - /** {@inheritDoc} */ - public void setCheckForDuplicates(boolean checkForDuplicates) { - delegate.setCheckForDuplicates(checkForDuplicates); - } - - /** {@inheritDoc} */ - public void setProcessDanglingHREF(String value) { - delegate.setProcessDanglingHREF(value); - } - - /** {@inheritDoc} */ - public DanglingHREFException getDanglingHREFException() { - return delegate.getDanglingHREFException(); - } - - /** {@inheritDoc} */ - public URI resolve(URI relative, URI base) { - return delegate.resolve(relative, base); - } - - /** {@inheritDoc} */ - public void addPrefix(String prefix, String uri) { - delegate.addPrefix(prefix, uri); - } - - /** {@inheritDoc} */ - public Map getAnyContentPrefixToURIMapping() { - return delegate.getAnyContentPrefixToURIMapping(); - } - - /** {@inheritDoc} */ - public void recordPrefixToURIMapping() { - delegate.recordPrefixToURIMapping(); - } - - /** {@inheritDoc} */ - public String getURI(String prefix) { - return delegate.getURI(prefix); - } - - /** {@inheritDoc} */ - public void pushContext() { - delegate.pushContext(); - } - - /** {@inheritDoc} */ - public void popContext() { - delegate.popContext(); - } - - /** {@inheritDoc} */ - public void popContext(Map prefixesToFactories) { - delegate.popContext(prefixesToFactories); - } - - /** {@inheritDoc} */ - public String convertToString(EFactory factory, EDataType dataType, Object data) { - return delegate.convertToString(factory, dataType, data); - } - - /** {@inheritDoc} */ - public EMap getPrefixToNamespaceMap() { - return delegate.getPrefixToNamespaceMap(); - } - - /** {@inheritDoc} */ - public void setPrefixToNamespaceMap(EMap prefixToNamespaceMap) { - delegate.setPrefixToNamespaceMap(prefixToNamespaceMap); - } - - /** {@inheritDoc} */ - public void setMustHavePrefix(boolean mustHavePrefix) { - delegate.setMustHavePrefix(mustHavePrefix); - } - } -} diff --git a/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/internal/utils/URIStorage.java b/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/internal/utils/URIStorage.java index 53ba4cd37..594e19ec8 100644 --- a/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/internal/utils/URIStorage.java +++ b/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/internal/utils/URIStorage.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2014 Obeo. + * Copyright (c) 2014, 2015 Obeo. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -76,7 +76,7 @@ public class URIStorage implements IStorage { public InputStream getContents() throws CoreException { final Map options = Collections.singletonMap(URIConverter.OPTION_URI_CONVERTER, converter); try { - return handler.createInputStream(uri, options); + return handler.createInputStream(converter.normalize(uri), options); } catch (IOException e) { throw new CoreException(new Status(IStatus.ERROR, EMFCompareIDEPlugin.PLUGIN_ID, e.getMessage(), e)); @@ -90,12 +90,13 @@ public class URIStorage implements IStorage { */ public IPath getFullPath() { final String path; - if (uri.isRelative()) { - path = uri.toString(); - } else if (uri.isPlatformResource()) { - path = uri.toPlatformString(true); + final URI normalized = converter.normalize(uri); + if (normalized.isRelative()) { + path = normalized.toString(); + } else if (normalized.isPlatformResource()) { + path = normalized.toPlatformString(true); } else { - path = uri.toString(); + path = normalized.toString(); } return new Path(path); } @@ -106,7 +107,7 @@ public class URIStorage implements IStorage { * @see org.eclipse.core.resources.IStorage#getName() */ public String getName() { - return URI.decode(uri.lastSegment()); + return URI.decode(converter.normalize(uri).lastSegment()); } /** @@ -117,7 +118,7 @@ public class URIStorage implements IStorage { public boolean isReadOnly() { final Map options = Collections.singletonMap(URIConverter.OPTION_REQUESTED_ATTRIBUTES, Collections.singleton(URIConverter.ATTRIBUTE_READ_ONLY)); - final Map attributes = handler.getAttributes(uri, options); + final Map attributes = handler.getAttributes(converter.normalize(uri), options); return Boolean.TRUE.equals(attributes.get(URIConverter.ATTRIBUTE_READ_ONLY)); } @@ -169,5 +170,15 @@ public class URIStorage implements IStorage { } return true; } + // CHECKSTYLE:ON + + /** + * Returns the unmodified URI for this storage (will need normalization). + * + * @return The unmodified URI for this storage. + */ + public URI getURI() { + return uri; + } } diff --git a/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/utils/ResourceUtil.java b/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/utils/ResourceUtil.java index 860110560..9d03794f8 100644 --- a/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/utils/ResourceUtil.java +++ b/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/utils/ResourceUtil.java @@ -38,6 +38,7 @@ import org.eclipse.emf.common.util.EList; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.common.util.WrappedException; import org.eclipse.emf.compare.ide.EMFCompareIDEPlugin; +import org.eclipse.emf.compare.ide.internal.utils.URIStorage; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.ResourceSet; @@ -238,8 +239,14 @@ public final class ResourceUtil { * @return The created URI. */ public static URI createURIFor(IStorage storage) { + URI shortcut = null; if (storage instanceof IFile) { - return createURIFor((IFile)storage); + shortcut = createURIFor((IFile)storage); + } else if (storage instanceof URIStorage) { + shortcut = ((URIStorage)storage).getURI(); + } + if (shortcut != null) { + return shortcut; } String path = getFixedPath(storage).toString(); @@ -256,7 +263,7 @@ public final class ResourceUtil { } else if (hasStoragePathProvider(storage)) { uri = URI.createPlatformResourceURI(path, true); } else { - uri = URI.createFileURI(path); + uri = URI.createURI(path, true); } final IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); diff --git a/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/utils/StorageURIConverter.java b/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/utils/StorageURIConverter.java index 39afd6b8e..dd4879be0 100644 --- a/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/utils/StorageURIConverter.java +++ b/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/utils/StorageURIConverter.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2011, 2012 Obeo. + * Copyright (c) 2011, 2015 Obeo. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -67,7 +67,11 @@ public class StorageURIConverter extends DelegatingURIConverter { public InputStream createInputStream(URI uri, Map options) throws IOException { final URI normalizedURI = normalize(uri); final URIHandler handler = getURIHandler(normalizedURI); - getLoadedRevisions().add(createStorage(normalizedURI, handler, this)); + // Only keep track of the loaded resource if it's a platform:/resource. + // Resources loaded from plugins don't need to be part of our final logical model. + if (uri.isPlatformResource()) { + getLoadedRevisions().add(createStorage(uri, handler, this)); + } final Map actualOptions = Maps.newLinkedHashMap(); actualOptions.put(URIConverter.OPTION_URI_CONVERTER, this); diff --git a/plugins/org.eclipse.emf.compare.uml2.ide/META-INF/MANIFEST.MF b/plugins/org.eclipse.emf.compare.uml2.ide/META-INF/MANIFEST.MF index 35dbfa8db..874d1f0bc 100644 --- a/plugins/org.eclipse.emf.compare.uml2.ide/META-INF/MANIFEST.MF +++ b/plugins/org.eclipse.emf.compare.uml2.ide/META-INF/MANIFEST.MF @@ -7,7 +7,9 @@ Bundle-Activator: org.eclipse.emf.compare.uml2.ide.Activator Require-Bundle: org.eclipse.ui, org.eclipse.core.runtime, org.eclipse.emf.compare.ide, - org.eclipse.uml2.uml + org.eclipse.uml2.uml, + org.eclipse.emf.compare.rcp;bundle-version="2.3.0", + org.eclipse.emf.compare.uml2.rcp;bundle-version="2.3.0" Bundle-RequiredExecutionEnvironment: J2SE-1.5 Bundle-ActivationPolicy: lazy Bundle-Vendor: %providerName diff --git a/plugins/org.eclipse.emf.compare.uml2.ide/build.properties b/plugins/org.eclipse.emf.compare.uml2.ide/build.properties index cdd2db7f1..f70828ba5 100644 --- a/plugins/org.eclipse.emf.compare.uml2.ide/build.properties +++ b/plugins/org.eclipse.emf.compare.uml2.ide/build.properties @@ -3,5 +3,6 @@ output.. = bin/ bin.includes = META-INF/,\ .,\ about.html,\ - plugin.properties + plugin.properties,\ + plugin.xml src.includes = about.html diff --git a/plugins/org.eclipse.emf.compare.uml2.ide/plugin.xml b/plugins/org.eclipse.emf.compare.uml2.ide/plugin.xml new file mode 100644 index 000000000..8d4c649b4 --- /dev/null +++ b/plugins/org.eclipse.emf.compare.uml2.ide/plugin.xml @@ -0,0 +1,11 @@ + + + + + + + + + diff --git a/plugins/org.eclipse.emf.compare.uml2.ide/src/org/eclipse/emf/compare/uml2/ide/ResourceSetProfileUnloader.java b/plugins/org.eclipse.emf.compare.uml2.ide/src/org/eclipse/emf/compare/uml2/ide/ResourceSetProfileUnloader.java new file mode 100644 index 000000000..4df8490b6 --- /dev/null +++ b/plugins/org.eclipse.emf.compare.uml2.ide/src/org/eclipse/emf/compare/uml2/ide/ResourceSetProfileUnloader.java @@ -0,0 +1,99 @@ +/******************************************************************************* + * Copyright (c) 2012, 2015 Obeo and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Obeo - initial API and implementation + * Stefan Dirix - bug 460780 + *******************************************************************************/ +package org.eclipse.emf.compare.uml2.ide; + +import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.Set; + +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.compare.ide.hook.IResourceSetHook; +import org.eclipse.emf.compare.uml2.rcp.internal.policy.UMLLoadOnDemandPolicy; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.emf.ecore.resource.ResourceSet; +import org.eclipse.emf.ecore.resource.URIConverter; +import org.eclipse.emf.ecore.resource.impl.ExtensibleURIConverterImpl; +import org.eclipse.uml2.common.util.CacheAdapter; +import org.eclipse.uml2.uml.Profile; +import org.eclipse.uml2.uml.ProfileApplication; + +/** + * The {@link UMLLoadOnDemandPolicy} will load profiles in the resource set used by EMF Compare. These will be + * referenced in package registries and extended meta-data for their resource set. We want them to be properly + * unloaded and will do so from here. + * + * @author Laurent Goubet + */ +public class ResourceSetProfileUnloader extends UMLLoadOnDemandPolicy implements IResourceSetHook { + public boolean isHookFor(Collection uris) { + for (URI uri : uris) { + if (UML_EXTENSION.equals(uri.fileExtension())) { + return true; + } + } + return false; + } + + public void preLoadingHook(ResourceSet resourceSet, Collection uris) { + // We're not interested in this event + } + + public void postLoadingHook(ResourceSet resourceSet, Collection uris) { + // We're not interested in this event + } + + public void onDispose(Iterable resources) { + URIConverter uriConverter = new ExtensibleURIConverterImpl(); + for (Resource resource : resources) { + if (resource.isLoaded()) { + URI uri = resource.getURI(); + URI normalizedURI = uriConverter.normalize(uri); + if (isConventionalURIForUMLProfile(normalizedURI) || isUMLMetaModel(normalizedURI) + || isRegisteredUMLProfile(normalizedURI, uriConverter)) { + for (EObject child : resource.getContents()) { + // TODO Are profiles always at the root? + if (child instanceof Profile) { + disposeProfileApplications((Profile)child); + break; + } + } + resource.unload(); + } + } + } + } + + /** + * UML will hold onto the profiles in memory even if we unload them completely unless we do the same for + * the resources they're referenced from. + * + * @param profile + * The profile which applications we need to dispose of. + */ + private void disposeProfileApplications(Profile profile) { + final Set unloadMe = new LinkedHashSet(); + Collection settings = CacheAdapter.getInstance().getInverseReferences( + profile, false); + for (EStructuralFeature.Setting setting : settings) { + if (setting.getEObject() instanceof ProfileApplication) { + unloadMe.add(setting.getEObject().eResource()); + } + } + for (Resource unload : unloadMe) { + if (unload.isLoaded()) { + unload.unload(); + } + } + } +} diff --git a/plugins/org.eclipse.emf.compare.uml2.rcp/META-INF/MANIFEST.MF b/plugins/org.eclipse.emf.compare.uml2.rcp/META-INF/MANIFEST.MF index 75f83fdf4..cc8656b49 100644 --- a/plugins/org.eclipse.emf.compare.uml2.rcp/META-INF/MANIFEST.MF +++ b/plugins/org.eclipse.emf.compare.uml2.rcp/META-INF/MANIFEST.MF @@ -13,4 +13,4 @@ Bundle-Vendor: %providerName Bundle-Localization: plugin Import-Package: com.google.common.base;version="[11.0.0,16.0.0)", com.google.common.collect;version="[11.0.0,16.0.0)" -Export-Package: org.eclipse.emf.compare.uml2.rcp.internal.policy;x-internal:=true +Export-Package: org.eclipse.emf.compare.uml2.rcp.internal.policy;x-friends:="org.eclipse.emf.compare.uml2.ide" diff --git a/plugins/org.eclipse.emf.compare.uml2.rcp/src/org/eclipse/emf/compare/uml2/rcp/internal/policy/UMLLoadOnDemandPolicy.java b/plugins/org.eclipse.emf.compare.uml2.rcp/src/org/eclipse/emf/compare/uml2/rcp/internal/policy/UMLLoadOnDemandPolicy.java index 69227be22..8098a4f02 100644 --- a/plugins/org.eclipse.emf.compare.uml2.rcp/src/org/eclipse/emf/compare/uml2/rcp/internal/policy/UMLLoadOnDemandPolicy.java +++ b/plugins/org.eclipse.emf.compare.uml2.rcp/src/org/eclipse/emf/compare/uml2/rcp/internal/policy/UMLLoadOnDemandPolicy.java @@ -41,13 +41,19 @@ import org.eclipse.uml2.uml.UMLPlugin; * @author Mikael Barbero */ public class UMLLoadOnDemandPolicy implements ILoadOnDemandPolicy { - /** Keep track of the normalizations we've already made. */ - private final BiMap profileNsURIToNormalized = HashBiMap.create(); - /** The {@link URI} of the UML metamodel offered by the UML2 Eclipse plugins */ private static final String PLATFORM_UML_METAMODEL_URI = "platform:/plugin/org.eclipse.uml2.uml.resources/metamodels/UML.metamodel.uml"; //$NON-NLS-1$ /** + * The UML file extension. We'll react to resource sets containing at least one resource with this + * extension. + */ + protected static final String UML_EXTENSION = "uml"; //$NON-NLS-1$ + + /** Keep track of the normalizations we've already made. */ + private final BiMap profileNsURIToNormalized = HashBiMap.create(); + + /** * {@inheritDoc} * * @see org.eclipse.emf.compare.ide.policy.ILoadOnDemandPolicy#isAuthorizing(org.eclipse.emf.compare.ide.policy.URI) @@ -69,7 +75,7 @@ public class UMLLoadOnDemandPolicy implements ILoadOnDemandPolicy { * {@link URIConverter} to use for the registered profile URIs. * @return */ - private boolean isRegisteredUMLProfile(URI normalizedURI, final URIConverter uriConverter) { + protected boolean isRegisteredUMLProfile(URI normalizedURI, final URIConverter uriConverter) { for (Map.Entry entry : UMLPlugin.getEPackageNsURIToProfileLocationMap().entrySet()) { if (!profileNsURIToNormalized.containsKey(entry.getKey())) { profileNsURIToNormalized.put(entry.getKey(), uriConverter.normalize(entry.getValue())); @@ -89,10 +95,10 @@ public class UMLLoadOnDemandPolicy implements ILoadOnDemandPolicy { * @return true if the input URI is considered as a profile URI, false * otherwise. */ - private boolean isConventionalURIForUMLProfile(URI normalizedURI) { + protected boolean isConventionalURIForUMLProfile(URI normalizedURI) { URI noFragmentURI = normalizedURI.trimFragment(); String firstFileExtension = noFragmentURI.fileExtension(); - if ("uml".equals(firstFileExtension)) { //$NON-NLS-1$ + if (UML_EXTENSION.equals(firstFileExtension)) { URI withoutFirstFileExtension = noFragmentURI.trimFileExtension(); String secondFileExtension = withoutFirstFileExtension.fileExtension(); if ("profile".equals(secondFileExtension)) { //$NON-NLS-1$ @@ -109,7 +115,7 @@ public class UMLLoadOnDemandPolicy implements ILoadOnDemandPolicy { * input URI to test. * @return {@code true} if the given {@link URI} corresponds to the UML metamodel. */ - private boolean isUMLMetaModel(URI normalizedURI) { + protected boolean isUMLMetaModel(URI normalizedURI) { URI noFragmentURI = normalizedURI.trimFragment(); return PLATFORM_UML_METAMODEL_URI.equals(noFragmentURI.toString()); } -- 2.11.4.GIT