diff options
author | Camille Letavernier | 2020-12-02 10:23:09 +0000 |
---|---|---|
committer | Camille Letavernier | 2020-12-16 10:07:50 +0000 |
commit | c2e8e12dadfed2b3243898ac7164b6c1fac5fed1 (patch) | |
tree | 99094982fa50a9661914287677dc30f2e67b1bde | |
parent | 658d4ba2ef2e72ab7866fefcdc129a8ae00fdb8e (diff) | |
download | org.eclipse.papyrus-bugs/569354-typesGeneration.tar.gz org.eclipse.papyrus-bugs/569354-typesGeneration.tar.xz org.eclipse.papyrus-bugs/569354-typesGeneration.zip |
Bug 569354: [Toolsmiths] ElementTypes generation from a Profile shouldbugs/569354-typesGeneration
rely on the new concepts
https://bugs.eclipse.org/bugs/show_bug.cgi?id=569354
- Move the plug-in metadata generation from the Profile-to-Types Wizard
to a headless Generator
- The generator can now also be used for existing element type models
Change-Id: I42c1c8670c52da0f4b1b25caea96b2b4c6e302b1
Signed-off-by: Camille Letavernier <cletavernier@eclipsesource.com>
4 files changed, 285 insertions, 126 deletions
diff --git a/plugins/toolsmiths/assistants/org.eclipse.papyrus.uml.profile.types.generator.ui/META-INF/MANIFEST.MF b/plugins/toolsmiths/assistants/org.eclipse.papyrus.uml.profile.types.generator.ui/META-INF/MANIFEST.MF index 5b803616b8e..60e4c55283a 100644 --- a/plugins/toolsmiths/assistants/org.eclipse.papyrus.uml.profile.types.generator.ui/META-INF/MANIFEST.MF +++ b/plugins/toolsmiths/assistants/org.eclipse.papyrus.uml.profile.types.generator.ui/META-INF/MANIFEST.MF @@ -12,7 +12,8 @@ Require-Bundle: org.eclipse.emf.databinding;bundle-version="[1.5.0,2.0.0)", org.eclipse.papyrus.uml.profile.types.generator;bundle-version="[3.0.0,4.0.0)", org.eclipse.uml2.uml.editor;bundle-version="[5.5.0,6.0.0)", org.eclipse.xtend.lib;bundle-version="[2.22.0,3.0.0)", - org.eclipse.papyrus.eclipse.project.editors;bundle-version="[3.0.0,4.0.0)" + org.eclipse.papyrus.eclipse.project.editors;bundle-version="[3.0.0,4.0.0)", + org.eclipse.papyrus.toolsmiths;bundle-version="[2.0.0,3.0.0)" Export-Package: org.eclipse.papyrus.uml.profile.types.generator.ui.internal, org.eclipse.papyrus.uml.profile.types.generator.ui.internal.handlers, org.eclipse.papyrus.uml.profile.types.generator.ui.internal.wizards;x-friends:="org.eclipse.papyrus.uml.profile.assistants.generator.ui" diff --git a/plugins/toolsmiths/assistants/org.eclipse.papyrus.uml.profile.types.generator.ui/src/org/eclipse/papyrus/uml/profile/types/generator/ui/internal/wizards/GeneratorWizard.java b/plugins/toolsmiths/assistants/org.eclipse.papyrus.uml.profile.types.generator.ui/src/org/eclipse/papyrus/uml/profile/types/generator/ui/internal/wizards/GeneratorWizard.java index 60faa12a354..8a4fe8a0edb 100644 --- a/plugins/toolsmiths/assistants/org.eclipse.papyrus.uml.profile.types.generator.ui/src/org/eclipse/papyrus/uml/profile/types/generator/ui/internal/wizards/GeneratorWizard.java +++ b/plugins/toolsmiths/assistants/org.eclipse.papyrus.uml.profile.types.generator.ui/src/org/eclipse/papyrus/uml/profile/types/generator/ui/internal/wizards/GeneratorWizard.java @@ -18,13 +18,10 @@ package org.eclipse.papyrus.uml.profile.types.generator.ui.internal.wizards; import java.lang.reflect.InvocationTargetException; import java.util.Arrays; +import java.util.Collections; import java.util.List; -import java.util.Set; import java.util.concurrent.Callable; -import org.eclipse.core.resources.IProject; -import org.eclipse.core.resources.ResourcesPlugin; -import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; @@ -36,8 +33,8 @@ import org.eclipse.jface.dialogs.DialogSettings; import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.jface.wizard.Wizard; import org.eclipse.osgi.util.NLS; -import org.eclipse.papyrus.eclipse.project.editors.project.PluginEditor; import org.eclipse.papyrus.infra.ui.util.UIUtil; +import org.eclipse.papyrus.toolsmiths.types.generator.TypesPluginGenerator; import org.eclipse.papyrus.uml.profile.types.generator.AbstractGenerator; import org.eclipse.papyrus.uml.profile.types.generator.ElementTypesGenerator; import org.eclipse.papyrus.uml.profile.types.generator.Identifiers; @@ -47,11 +44,9 @@ import org.eclipse.ui.PartInitException; import org.eclipse.ui.ide.IDE; import org.eclipse.ui.statushandlers.StatusManager; import org.eclipse.uml2.uml.Profile; -import org.w3c.dom.Element; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; -import com.google.common.collect.Sets; import com.google.common.util.concurrent.Futures; /** @@ -65,10 +60,6 @@ public class GeneratorWizard extends Wizard { // FIXME : should be provided by an element type plugin private static final String ELEMENTTYPESCONFIGURATIONS = "elementtypesconfigurations"; //$NON-NLS-1$ - public static final String TYPES_CORE_PLUGIN = "org.eclipse.papyrus.infra.types.core"; - public static final String TYPES_CORE_PLUGIN_MIN = "5.0.0"; - public static final String TYPES_CORE_PLUGIN_MAX = "6.0.0"; - private final IWorkbenchPage page; private final GeneratorWizardModel model; @@ -216,118 +207,8 @@ public class GeneratorWizard extends Wizard { return new Status(IStatus.WARNING, getClass(), "The target model is not located in a workspace project; impossible to configure the plug-in."); } - String projectName = outputModelURI.segment(1); - IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName); - if (!project.exists()) { - return new Status(IStatus.WARNING, getClass(), "The target model is not located in a workspace project; impossible to configure the plug-in."); - } - - if (!project.isOpen()) { - try { - project.open(null); - } catch (CoreException e) { - Activator.log.error(e); - return new Status(IStatus.WARNING, getClass(), "The target model is located in a workspace project that couldn't be opened; impossible to configure the plug-in."); - } - } - - try { - PluginEditor editor = new PluginEditor(project); - editor.init(); - - if (!editor.exists()) { - return new Status(IStatus.WARNING, getClass(), "The target model is not located in an Eclipse Plug-in Project; impossible to configure the plug-in."); - } - - Set<String> files = Sets.newHashSet(PluginEditor.BUILD_PROPERTIES_FILE, PluginEditor.PLUGIN_XML_FILE); - addDependencies(editor); - addExtensions(editor, outputModelURI, identifiers); - addBuildProperties(editor, outputModelURI); - - editor.save(); - } catch (Exception ex) { - return new Status(IStatus.WARNING, getClass(), "The target model is not located in an Eclipse Plug-in Project; impossible to configure the plug-in.", ex); - } - - return Status.OK_STATUS; - } - - /** - * Add required plug-in dependencies to the target plug-in - * - * @param editor - * The plug-in to configure - */ - protected void addDependencies(PluginEditor editor) { - if (!editor.hasDependency(TYPES_CORE_PLUGIN)) { - editor.addDependency(TYPES_CORE_PLUGIN, String.format("[%s, %s)", TYPES_CORE_PLUGIN_MIN, TYPES_CORE_PLUGIN_MAX)); - } - // TODO Find and Add dependencies to plug-ins contributing the referenced models (e.g. UML, extended profiles) - } - - /** - * Add required contributions to extension points in the target plug-in, - * for the given element types model. - * - * @param editor - * The plug-in to configure - * @param outputModelURI - * The generated model being contributed - */ - protected void addExtensions(PluginEditor editor, URI outputModelURI, Identifiers identifiers) { - if (!editor.pluginManifestExists()) { - editor.getPluginEditor().create(); - } - editor.addToBuild("plugin.xml"); - - Element ext = editor.addExtension("org.eclipse.papyrus.infra.types.core.elementTypeSetConfiguration"); - Element elementTypeSet = editor.addChild(ext, "elementTypeSet"); - elementTypeSet.setAttribute("path", toLocalProjectPath(outputModelURI).toString()); - elementTypeSet.setAttribute("clientContextID", identifiers.getContextId()); - } - - /** - * Add required build.properties entries in the target plug-in, for the - * model being generated. - * - * @param editor - * The plug-in to configure - * @param outputModelURI - * The generated model - */ - protected void addBuildProperties(PluginEditor editor, URI outputModelURI) { - IPath path = toLocalProjectPath(outputModelURI); - - // Trim one segment to exclude the file name (we always export the parent folder), - // and convert to a workspace-relative path - path = path.removeLastSegments(1); - - // Add trailing / because we export a folder - editor.addToBuild(path.toString() + "/"); - } - - /** - * <p> - * Converts a platform:/resource/ {@link URI} to a path relative - * to the current project. - * </p> - * <p> - * The <code>platform:/resource/projectName/folderName/fileName.fileExtension</code> {@link URI} - * becomes a <code>folderName/fileName.fileExtension</code> {@link IPath}. - * </p> - * - * @param outputModelURI - * The URI to convert to a local project path - * @return - * The {@link IPath} relative to the Project Root, corresponding to this URI - */ - protected IPath toLocalProjectPath(URI outputModelURI) { - IPath path = new Path(outputModelURI.toPlatformString(true)); - - // Remove the project name, as we export paths relative to the project folder - path = path.removeFirstSegments(1); - - return path; + IPath modelPath = new Path(outputModelURI.toPlatformString(true)); + return new TypesPluginGenerator().generate(Collections.singleton(modelPath), identifiers.getContextId()); } protected void addGenerators(List<? super AbstractGenerator<Profile, ?>> generators, Identifiers identifiers, GeneratorWizardModel wizardModel) { diff --git a/plugins/toolsmiths/org.eclipse.papyrus.toolsmiths/META-INF/MANIFEST.MF b/plugins/toolsmiths/org.eclipse.papyrus.toolsmiths/META-INF/MANIFEST.MF index fcbe3480ade..3e3d422943d 100644 --- a/plugins/toolsmiths/org.eclipse.papyrus.toolsmiths/META-INF/MANIFEST.MF +++ b/plugins/toolsmiths/org.eclipse.papyrus.toolsmiths/META-INF/MANIFEST.MF @@ -13,13 +13,15 @@ Require-Bundle: org.eclipse.draw2d;bundle-version="[3.10.0,4.0.0)", org.eclipse.pde.ui;bundle-version="[3.11.0,4.0.0)", org.eclipse.ui.ide;bundle-version="[3.17.0,4.0.0)", org.eclipse.ui.views.properties.tabbed;bundle-version="[3.8.0,4.0.0)", - org.eclipse.uml2.types;bundle-version="[2.5.0,3.0.0)" + org.eclipse.uml2.types;bundle-version="[2.5.0,3.0.0)", + com.google.guava;bundle-version="[21.0.0,28.0.0)" Export-Package: org.eclipse.papyrus.toolsmiths.factory, org.eclipse.papyrus.toolsmiths.generator, org.eclipse.papyrus.toolsmiths.model.customizationplugin, org.eclipse.papyrus.toolsmiths.model.customizationplugin.impl, org.eclipse.papyrus.toolsmiths.model.customizationplugin.util, - org.eclipse.papyrus.toolsmiths.plugin + org.eclipse.papyrus.toolsmiths.plugin, + org.eclipse.papyrus.toolsmiths.types.generator Bundle-Vendor: %providerName Bundle-ActivationPolicy: lazy Bundle-ClassPath: . diff --git a/plugins/toolsmiths/org.eclipse.papyrus.toolsmiths/src/org/eclipse/papyrus/toolsmiths/types/generator/TypesPluginGenerator.java b/plugins/toolsmiths/org.eclipse.papyrus.toolsmiths/src/org/eclipse/papyrus/toolsmiths/types/generator/TypesPluginGenerator.java new file mode 100644 index 00000000000..e7831118939 --- /dev/null +++ b/plugins/toolsmiths/org.eclipse.papyrus.toolsmiths/src/org/eclipse/papyrus/toolsmiths/types/generator/TypesPluginGenerator.java @@ -0,0 +1,275 @@ +/***************************************************************************** + * Copyright (c) 2020 EclipseSource 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: + * Camille Letavernier - Bug 569354 + * + *****************************************************************************/ +package org.eclipse.papyrus.toolsmiths.types.generator; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.papyrus.eclipse.project.editors.project.PluginEditor; +import org.eclipse.papyrus.toolsmiths.Activator; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +/** + * <p> + * Generates the plug-in metadata from an *.elementtypesconfiguration model. + * </p> + * + * <p> + * Supported artifacts: + * <ul> + * <li>build.properties</li> + * <li>plugin.xml</li> + * <li>MANIFEST.MF</li> + * </ul> + * </p> + */ +public class TypesPluginGenerator { + + public static final String TYPES_CORE_PLUGIN = "org.eclipse.papyrus.infra.types.core"; + public static final String TYPES_CORE_PLUGIN_MIN = "5.0.0"; + public static final String TYPES_CORE_PLUGIN_MAX = "6.0.0"; + + // Element Type Extension Point + private static final String EXTENSION_POINT = "org.eclipse.papyrus.infra.types.core.elementTypeSetConfiguration"; + private static final String ELEMENT_TYPE_SET = "elementTypeSet"; + private static final String CLIENT_CONTEXT_ID = "clientContextID"; + private static final String PATH = "path"; + + /** + * <p> + * Configure the plug-ins owning the target models (e.g. Add necessary dependencies + * to Papyrus ElementTypes framework, as well a required ElementType plug-in dependencies, + * ensure build.properties contains all the necessary resources...). + * </p> + * + * <p> + * For target projects that are not an Eclipse plug-in project, this method does nothing and returns a warning. + * </p> + * + * @param modelPaths + * The collection of paths. Each path represents an elementtypeconfiguration model. + * @param contextId + * The clientContextID for which the type models should be registered. For Papyrus, this is usually + * <code>org.eclipse.papyrus.infra.services.edit.TypeContext</code>, but this may vary in some + * specific applications / customization scenarios. + * @return + * The status representing the result of this configuration operation. + */ + public IStatus generate(Collection<IPath> elementTypesConfigurationModels, String contextId) { + Map<IProject, Collection<IPath>> paths = groupByProject(elementTypesConfigurationModels); + for (var entry : paths.entrySet()) { + IProject project = entry.getKey(); + IStatus configurePlugin = configurePlugin(project, entry.getValue(), contextId); + } + return Status.OK_STATUS; + } + + /** + * <p> + * Configure the plug-in owning the target model (e.g. Add necessary dependencies + * to Papyrus ElementTypes framework, as well a required ElementType plug-in dependencies, + * ensure build.properties contains all the necessary resources...). + * </p> + * + * <p> + * If the target project is not an Eclipse plug-in project, this method does nothing and returns a warning. + * </p> + * + * @param project + * The project containing the models + * @param modelPaths + * The collection of paths. Each path represents an elementtypeconfiguration model + * @param contextId + * The clientContextID for which the type models should be registered. For Papyrus, this is usually + * <code>org.eclipse.papyrus.infra.services.edit.TypeContext</code>, but this may vary in some + * specific applications / customization scenarios. + * @return + * The status representing the result of this configuration operation. + */ + protected IStatus configurePlugin(IProject project, Collection<IPath> modelPaths, String contextId) { + // Should always be a workspace project; but let's make sure + // if (!modelPath.isPlatformResource()) { + // return new Status(IStatus.WARNING, getClass(), "The target model is not located in a workspace project; impossible to configure the plug-in."); + // } + + if (!project.exists()) { + return new Status(IStatus.WARNING, getClass(), "The target model is not located in a workspace project; impossible to configure the plug-in."); + } + + if (!project.isOpen()) { + try { + project.open(null); + } catch (CoreException e) { + Activator.log.error(e); + return new Status(IStatus.WARNING, getClass(), "The target model is located in a workspace project that couldn't be opened; impossible to configure the plug-in."); + } + } + + try { + PluginEditor editor = new PluginEditor(project); + editor.init(); + + if (!editor.exists()) { + return new Status(IStatus.WARNING, getClass(), "The target model is not located in an Eclipse Plug-in Project; impossible to configure the plug-in."); + } + + addDependencies(editor); + addExtensions(editor, modelPaths, contextId); + for (IPath path : modelPaths) { + addBuildProperties(editor, path); + } + + editor.save(); + } catch (Exception ex) { + return new Status(IStatus.WARNING, getClass(), "The target model is not located in an Eclipse Plug-in Project; impossible to configure the plug-in.", ex); + } + + return Status.OK_STATUS; + } + + /** + * Add required plug-in dependencies to the target plug-in + * + * @param editor + * The plug-in to configure + */ + protected void addDependencies(PluginEditor editor) { + if (!editor.hasDependency(TYPES_CORE_PLUGIN)) { + editor.addDependency(TYPES_CORE_PLUGIN, String.format("[%s, %s)", TYPES_CORE_PLUGIN_MIN, TYPES_CORE_PLUGIN_MAX)); + } + // TODO Find and Add dependencies to plug-ins contributing the referenced models (e.g. UML, extended profiles) + } + + /** + * Add required contributions to extension points in the target plug-in, + * for the given element types model. + * + * @param editor + * The plug-in to configure. + * @param modelPaths + * The models to add to the extensions. + * @param contextId + * The clientContextID for which the type model should be registered. + */ + protected void addExtensions(PluginEditor editor, Collection<IPath> modelPaths, String contextId) { + if (!editor.pluginManifestExists()) { + editor.getPluginEditor().create(); + } + editor.addToBuild("plugin.xml"); + + // Identify the missing paths. Convert the IPath to the String-format used in the extension point + Set<String> missingPaths = modelPaths.stream() // + .map(this::toLocalProjectPath) // + .map(IPath::toString) // + .collect(Collectors.toSet()); + + // Check if the extension(s) already exist(s). + List<Node> extensions = editor.getExtensions(EXTENSION_POINT); + for (Node extension : extensions) { + NodeList childNodes = extension.getChildNodes(); + for (int i = 0; i < childNodes.getLength(); i++) { + Node childNode = childNodes.item(i); + if (childNode.getNodeType() == Node.ELEMENT_NODE && ELEMENT_TYPE_SET.equals(childNode.getNodeName())) { + Element childElement = (Element) childNode; + if (contextId.equals(childElement.getAttribute(CLIENT_CONTEXT_ID)) && childElement.hasAttribute(PATH)) { + missingPaths.remove(childElement.getAttribute(PATH)); + } + } + } + } + + for (String modelPath : missingPaths) { + Element ext = editor.addExtension(EXTENSION_POINT); + Element elementTypeSet = editor.addChild(ext, ELEMENT_TYPE_SET); + elementTypeSet.setAttribute(PATH, modelPath); + elementTypeSet.setAttribute(CLIENT_CONTEXT_ID, contextId); + } + } + + /** + * Add required build.properties entries in the target plug-in, for the + * model being generated. + * + * @param editor + * The plug-in to configure + * @param modelPath + * The element types model path + */ + protected void addBuildProperties(PluginEditor editor, IPath modelPath) { + IPath path = toLocalProjectPath(modelPath); + + // Trim one segment to exclude the file name (we always export the parent folder), + // and convert to a workspace-relative path + path = path.removeLastSegments(1); + + // Add trailing / because we export a folder + editor.addToBuild(path.toString() + "/"); + } + + + /** + * <p> + * Converts a workspace {@link IPath} to a path relative + * to the current project. + * </p> + * <p> + * The <code>/projectName/folderName/fileName.fileExtension</code> {@link IPath} + * becomes <code>folderName/fileName.fileExtension</code>. + * </p> + * + * @param modelPath + * The IPath to convert to a local project path + * @return + * The {@link IPath} relative to the Project Root, corresponding to this URI + */ + protected IPath toLocalProjectPath(IPath modelPath) { + // Remove the project name, as we export paths relative to the project folder + IPath path = modelPath.removeFirstSegments(1); + + return path; + } + + private Map<IProject, Collection<IPath>> groupByProject(Collection<IPath> elementTypesConfigurationModels) { + Map<IProject, Collection<IPath>> result = new HashMap<>(); + + for (IPath configPath : elementTypesConfigurationModels) { + result.computeIfAbsent(getProject(configPath), project -> new ArrayList<>()).add(configPath); + } + + return result; + } + + private IProject getProject(IPath path) { + if (path.segmentCount() == 0) { + return null; + } + IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); + return root.getProject(path.segment(0)); + } +} |