diff options
author | Vincent Lorenzo | 2021-03-17 14:31:22 +0000 |
---|---|---|
committer | Vincent Lorenzo | 2021-03-26 12:40:31 +0000 |
commit | 85070e1988d945c5a33089b3c74d4120d9a1ab44 (patch) | |
tree | 8ed35a98b2c125533d3bcd6b322e553f28dd38b7 /plugins/infra/emf | |
parent | 61273267dbcc345740b17f1a00bc3f1c5557ca29 (diff) | |
download | org.eclipse.papyrus-85070e1988d945c5a33089b3c74d4120d9a1ab44.tar.gz org.eclipse.papyrus-85070e1988d945c5a33089b3c74d4120d9a1ab44.tar.xz org.eclipse.papyrus-85070e1988d945c5a33089b3c74d4120d9a1ab44.zip |
Bug 569357: [Toolsmiths] ElementTypes: Model and Plug-in Validation
- fix exception on attempt to clear unmodifiable list when resetting Architecture Context preferences
- let the workspace copy of an Architecture Context model supersede the deployed platform copy
- ensure that relative cross-document references in the Architecture Context model resolve from workspace to platform where applicable
- ensure loading unique copy of any given Architecture Context model even if redundantly added via preferences
- ensure that ResourcesUtil works in the absence of PDE
- factor out common project builder behaviours, including:
- mapping the project for EMF resources to check
- separate reporting of diagnostics from creation of markers to avoid creating redundant markers
- common framework for EMF model validation checkers on EMF resources
- common framework for build.properties checkers
- common framework for bundle dependencies checkers
- common framework for plugin.xml extensions checkers
- implement the common checkers frameworks for element-types
- absorb the function of the custom ElementTypesConfigurationsValidator into the builder
- add validation of stereotype application matcher and related configurations
- add validation of stereotype reference edge advice configurations
- add inference of profile resources in the same project that need to be packaged by the build.properties file
- handle extraction of bundle dependency name from bundleresource:// URIs
- support aggregation of the same diagnostic from different sources into a single marker
- add a generic checker for custom model validation rules
- implement custom Element Types validation rules for UML Stereotypes
- proper progress monitor delegation in the delegating builder
- normalize URIs with inclusion of mappings from the workspace also
Change-Id: Iefce58ba3a73b985f55b2ef82c45fb114cad4c44
Signed-off-by: Christian W. Damus <give.a.damus@gmail.com>
Also-by: Vincent Lorenzo <vincent.lorenzo@cea.fr>
Diffstat (limited to 'plugins/infra/emf')
3 files changed, 278 insertions, 25 deletions
diff --git a/plugins/infra/emf/org.eclipse.papyrus.infra.emf/META-INF/MANIFEST.MF b/plugins/infra/emf/org.eclipse.papyrus.infra.emf/META-INF/MANIFEST.MF index 184e930d44a..0047aa688a9 100644 --- a/plugins/infra/emf/org.eclipse.papyrus.infra.emf/META-INF/MANIFEST.MF +++ b/plugins/infra/emf/org.eclipse.papyrus.infra.emf/META-INF/MANIFEST.MF @@ -20,7 +20,7 @@ Require-Bundle: org.eclipse.core.expressions;bundle-version="[3.6.0,4.0.0)", org.eclipse.papyrus.infra.core.sashwindows.di;bundle-version="[2.0.0,3.0.0)", org.eclipse.papyrus.infra.tools;bundle-version="[4.0.0,5.0.0)", org.eclipse.uml2.types;bundle-version="[2.5.0,3.0.0)", - org.eclipse.pde.core;bundle-version="[3.14.100,4.0.0)" + org.eclipse.pde.core;bundle-version="[3.14.100,4.0.0)";resolution:=optional Bundle-Vendor: Eclipse Modeling Project Bundle-ActivationPolicy: lazy Bundle-Version: 4.1.0.qualifier diff --git a/plugins/infra/emf/org.eclipse.papyrus.infra.emf/src/org/eclipse/papyrus/infra/emf/utils/PlatformHelper.java b/plugins/infra/emf/org.eclipse.papyrus.infra.emf/src/org/eclipse/papyrus/infra/emf/utils/PlatformHelper.java new file mode 100644 index 00000000000..a02c050cd1f --- /dev/null +++ b/plugins/infra/emf/org.eclipse.papyrus.infra.emf/src/org/eclipse/papyrus/infra/emf/utils/PlatformHelper.java @@ -0,0 +1,210 @@ +/***************************************************************************** + * Copyright (c) 2020 Christian W. Damus, CEA LIST, and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Christian W. Damus - Initial API and implementation + * + *****************************************************************************/ + +package org.eclipse.papyrus.infra.emf.utils; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Objects; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParserFactory; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.Platform; +import org.eclipse.osgi.container.ModuleContainer; +import org.eclipse.papyrus.infra.emf.Activator; +import org.eclipse.pde.core.plugin.IPluginElement; +import org.eclipse.pde.core.plugin.IPluginExtension; +import org.eclipse.pde.core.plugin.IPluginModelBase; +import org.eclipse.pde.core.plugin.PluginRegistry; +import org.osgi.framework.Bundle; +import org.osgi.framework.Constants; +import org.osgi.framework.namespace.IdentityNamespace; +import org.osgi.framework.wiring.BundleCapability; +import org.osgi.framework.wiring.FrameworkWiring; +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.ext.DefaultHandler2; + +/** + * Optional support for a PDE Target platform, to discover the plug-ins against + * which workspace projects (if any) are overlaid. + */ +abstract class PlatformHelper { + + private static final String ECORE_URI_MAPPING_EXTENSION_POINT = "org.eclipse.emf.ecore.uri_mapping"; //$NON-NLS-1$ + private static final String E_EXTENSION = "extension"; //$NON-NLS-1$ + private static final String A_POINT = "point"; //$NON-NLS-1$ + private static final String E_MAPPING = "mapping"; //$NON-NLS-1$ + private static final String A_SOURCE = "source"; //$NON-NLS-1$ + private static final String A_TARGET = "target"; //$NON-NLS-1$ + + static final PlatformHelper INSTANCE; + + static { + PlatformHelper instance; + + try { + instance = new PDEHelper(); + } catch (Exception e) { + // PDE is not available + instance = new InstallHelper(); + } + + INSTANCE = instance; + } + + + /** + * Get the IDs of all bundles available in the target platform, whether that be the + * PDE Target Platform (in case PDE is installed) or else the host installation. + * + * @return the platform bundle IDs + */ + abstract Collection<String> getPlatformBundleIDs(); + + /** + * Return the {@code org.eclipse.emf.ecore.uri_mapping} extension declarations in the given {@code project}. + * + * @param project + * a project in the workspace + * @return its declared URI mappings + */ + abstract Map<String, String> getLocalUriMappings(IProject project); + + // + // Nested types + // + + /** + * The install helper instance gets all resolved (ready/available) bundles in the current installation. + */ + private static final class InstallHelper extends PlatformHelper { + + private static final int AVAILABLE = Bundle.ACTIVE | Bundle.RESOLVED | Bundle.STARTING; + + @Override + Collection<String> getPlatformBundleIDs() { + Collection<String> result = new HashSet<>(); + + FrameworkWiring wiring = Platform.getBundle(Constants.SYSTEM_BUNDLE_SYMBOLICNAME).adapt(FrameworkWiring.class); + Collection<BundleCapability> bundleIdentities = wiring.findProviders(ModuleContainer + .createRequirement(IdentityNamespace.IDENTITY_NAMESPACE, Collections.emptyMap(), Collections.emptyMap())); + for (BundleCapability next : bundleIdentities) { + Bundle bundle = next.getRevision().getBundle(); + if ((bundle.getState() & AVAILABLE) != 0) { + result.add(bundle.getSymbolicName()); + } + } + + return result; + } + + @Override + Map<String, String> getLocalUriMappings(IProject project) { + IFile pluginXML = project.getFile("plugin.xml"); //$NON-NLS-1$ + if (!pluginXML.isAccessible()) { + return Map.of(); + } + + Map<String, String> result = new HashMap<>(); + + try (InputStream input = pluginXML.getContents()) { + SAXParserFactory.newInstance().newSAXParser().parse(input, new DefaultHandler2() { + private boolean inMappings; + + @Override + public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { + // plugin.xml does not use namespaces, so the parser provides empty local names and the local names in qName + if (E_EXTENSION.equals(qName) && ECORE_URI_MAPPING_EXTENSION_POINT.equals(attributes.getValue(A_POINT))) { + inMappings = true; + } else if (inMappings && E_MAPPING.equals(qName)) { + String source = attributes.getValue(A_SOURCE); + String target = attributes.getValue(A_TARGET); + if (source != null && !source.isBlank() && target != null && !target.isBlank()) { + result.put(source, target); + } + } + } + + @Override + public void endElement(String uri, String localName, String qName) throws SAXException { + if (E_EXTENSION.equals(qName)) { + inMappings = false; + } + } + }); + } catch (SAXException | IOException | ParserConfigurationException | CoreException e) { + Activator.log.error("Failed to parse plugin.xml in project" + project.getName(), e); //$NON-NLS-1$ + } + + return result; + } + + } + + /** + * The PDE helper instance gets all active bundles in the current PDE target platform. + */ + private static final class PDEHelper extends PlatformHelper { + + @Override + Collection<String> getPlatformBundleIDs() { + IPluginModelBase[] pluginModels = PluginRegistry.getActiveModels(); + Collection<String> result = new HashSet<>(); + + for (IPluginModelBase next : pluginModels) { + if (next.getBundleDescription() != null) { + result.add(next.getBundleDescription().getSymbolicName()); + } + } + + return result; + } + + @Override + Map<String, String> getLocalUriMappings(IProject project) { + HashMap<String, String> localMappings = new HashMap<>(); + final IPluginModelBase model = PluginRegistry.findModel(project.getName()); + if (model == null) { + // No mappings if no plugin model + return localMappings; + } + + for (IPluginExtension extension : model.getExtensions().getExtensions()) { + if (!Objects.equals(extension.getPoint(), ECORE_URI_MAPPING_EXTENSION_POINT)) { + continue; + } + Arrays.stream(extension.getChildren()) + .filter(IPluginElement.class::isInstance).map(IPluginElement.class::cast) + .filter(element -> Objects.equals(E_MAPPING, element.getName())) + .forEach(element -> localMappings.put(element.getAttribute(A_SOURCE).getValue(), element.getAttribute(A_TARGET).getValue())); + } + + return localMappings; + } + + } + +} diff --git a/plugins/infra/emf/org.eclipse.papyrus.infra.emf/src/org/eclipse/papyrus/infra/emf/utils/ResourceUtils.java b/plugins/infra/emf/org.eclipse.papyrus.infra.emf/src/org/eclipse/papyrus/infra/emf/utils/ResourceUtils.java index 038c00c26f3..c86e011eb37 100644 --- a/plugins/infra/emf/org.eclipse.papyrus.infra.emf/src/org/eclipse/papyrus/infra/emf/utils/ResourceUtils.java +++ b/plugins/infra/emf/org.eclipse.papyrus.infra.emf/src/org/eclipse/papyrus/infra/emf/utils/ResourceUtils.java @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2012 CEA LIST. + * Copyright (c) 2012, 2020 CEA LIST, Christian W. Damus, and others. * * * All rights reserved. This program and the accompanying materials @@ -11,6 +11,7 @@ * * Contributors: * Vincent Lorenzo (CEA LIST) Vincent.Lorenzo@cea.fr - Initial API and implementation + * Christian W. Damus - bug 569357 * *****************************************************************************/ package org.eclipse.papyrus.infra.emf.utils; @@ -23,7 +24,6 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; -import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; @@ -35,25 +35,21 @@ import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.plugin.EcorePlugin; import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.emf.ecore.resource.URIConverter; +import org.eclipse.emf.ecore.resource.impl.ExtensibleURIConverterImpl; import org.eclipse.emf.ecore.resource.impl.URIMappingRegistryImpl; import org.eclipse.emf.ecore.xmi.XMIResource; import org.eclipse.emf.ecore.xmi.XMLResource; -import org.eclipse.pde.core.plugin.IPluginExtension; -import org.eclipse.pde.core.plugin.IPluginModelBase; -import org.eclipse.pde.core.plugin.IPluginObject; -import org.eclipse.pde.core.plugin.PluginRegistry; -import org.eclipse.pde.internal.core.text.plugin.PluginElementNode; /** * * This class provides methods for EMF Resource * */ -@SuppressWarnings("restriction") public class ResourceUtils { - private static final String ECORE_URI_MAPPING_EXTENSION_POINT = "org.eclipse.emf.ecore.uri_mapping"; //$NON-NLS-1$ private static final String PATH_SEPARATOR = "/"; @@ -155,23 +151,43 @@ public class ResourceUtils { return getStringURI(relativePath); } - /** Return the `org.eclipse.emf.ecore.uri_mapping` extension declarations in the given project. */ - public static Map<String, String> getLocalUriMappings(IProject project) { - HashMap<String, String> localMappings = new HashMap<>(); - final IPluginModelBase model = PluginRegistry.findModel(project.getName()); - for (IPluginExtension extension : model.getExtensions().getExtensions()) { - if (!Objects.equals(extension.getPoint(), ECORE_URI_MAPPING_EXTENSION_POINT)) { - continue; - } - List<IPluginObject> pluginObjects = Arrays.stream(extension.getChildren()).filter(child -> Objects.equals("mapping", child.getName())).collect(Collectors.toList()); //$NON-NLS-1$ - pluginObjects.forEach(pluginObject -> { - if ((pluginObject instanceof PluginElementNode)) { - PluginElementNode node = (PluginElementNode) pluginObject; - localMappings.put(node.getAttribute("source").getValue(), node.getAttribute("target").getValue()); //$NON-NLS-1$ //$NON-NLS-2$ + /** + * Create a URI converter that supports not only the registered URI mappings in the target platform, but + * also {@linkplain #getLocalUriMappings(IProject) mappings in <tt>plugin.xml</tt> files in workspace projects}. + * Mappings from the workspace supersede the same prefixes in mappings from the target platform. + * And <tt>platform:/plugin</tt> URIs map into the workspace (and vice-versa) where applicable, per + * the {@linkplain #computePlatformResourceMap() platform resource map}. + * + * @return the workspace-inclusive URI converter + * + * @see ExtensibleURIConverterImpl#getURIMap() + * @see #getLocalUriMappings(IProject) + * @see #computePlatformResourceMap() + */ + public static URIConverter createWorkspaceAwareURIConverter() { + URIConverter result = new ExtensibleURIConverterImpl(); + Map<URI, URI> uriMap = result.getURIMap(); + + uriMap.putAll(computePlatformResourceMap()); + + for (IProject next : ResourcesPlugin.getWorkspace().getRoot().getProjects()) { + if (next.isAccessible()) { + Map<String, String> mappings = getLocalUriMappings(next); + for (Map.Entry<String, String> mapping : mappings.entrySet()) { + URI prefix = URI.createURI(mapping.getKey()); + URI target = URI.createURI(mapping.getValue()); + + uriMap.put(prefix, target); } - }); + } } - return localMappings; + + return result; + } + + /** Return the {@code org.eclipse.emf.ecore.uri_mapping} extension declarations in the given {@code project}. */ + public static Map<String, String> getLocalUriMappings(IProject project) { + return PlatformHelper.INSTANCE.getLocalUriMappings(project); } /** Returns an encoded string representation of the path. */ @@ -181,4 +197,31 @@ public class ResourceUtils { .collect(Collectors.joining(PATH_SEPARATOR)); } + /** + * Compute a mapping of <tt>platform:</tt> scheme URIs for maximal portability of cross-document + * references in resources that are saved (as almost always should be) using the + * {@linkplain org.eclipse.emf.ecore.xmi.impl.URIHandlerImpl.PlatformSchemeAware platform-scheme-aware URI handler}. + * That is the case for all resources saved using the {@linkplain #getSaveOptions() common Papyrus save options}. + * The resulting mapping forwards <tt>platform:/plugin/</tt> URIs to </tt>platform:/resource/</tt> for + * plug-in projects that are imported and open in the workspace and <tt>platform:/resource/</tt> to <tt>platform:/plugin/</tt> + * for all other plug-ins in the PDE Target. + * + * @return the platform URI mappings + * + * @see getSaveOptions() + * @see EcorePlugin#computePlatformPluginToPlatformResourceMap() + * @see EcorePlugin#computePlatformResourceToPlatformPluginMap(Collection) + */ + public static Map<URI, URI> computePlatformResourceMap() { + Map<URI, URI> result = new HashMap<>(); + result.putAll(EcorePlugin.computePlatformPluginToPlatformResourceMap()); + + List<URI> platform = PlatformHelper.INSTANCE.getPlatformBundleIDs().stream() + .map(name -> URI.createPlatformPluginURI(name, true)) + .collect(Collectors.toList()); + result.putAll(EcorePlugin.computePlatformResourceToPlatformPluginMap(platform)); + + return result; + } + } |