diff options
author | Remi Schnekenburger | 2020-11-25 11:05:47 +0000 |
---|---|---|
committer | Remi Schnekenburger | 2020-12-02 07:48:39 +0000 |
commit | e48f865e06153a2b1c73d3c9705cb710b3df7192 (patch) | |
tree | baa927e679fedce9876ba73d4f4b4cdb33547e8b | |
parent | 44d5f47da1ed0489e05d400fa789c48908aa34b8 (diff) | |
parent | d4efe73f35e33b31a04e2030c767eda78989ea8b (diff) | |
download | org.eclipse.papyrus-bugs/568495-staticprofile-quickfix.tar.gz org.eclipse.papyrus-bugs/568495-staticprofile-quickfix.tar.xz org.eclipse.papyrus-bugs/568495-staticprofile-quickfix.zip |
Merge branch 'bugs/568495-staticprofile-gerrit' intobugs/568495-staticprofile-quickfix
bugs/568494-toolsmiths-dev
- new plugin parser to profile validation plugin to share it
between builders and validate handler
- better check of URI mapping for Papyrus profile extension
- Reuse "MarkerService" instead of duplicating marker creation methods
- Implement the Manifest checker for profiles
- Implement the Build.properties checker for profiles
- Support nice position on text editor for build.properties and manifest
files while reusing internal PDE text models
- Update dependencies to be compliant with 2020-12 version
Change-Id: I1c7b796b784d30ff2edd55dbb6050a83fa361059
Signed-off-by: Remi Schnekenburger <rschnekenburger@eclipsesource.com>
21 files changed, 1536 insertions, 131 deletions
diff --git a/plugins/toolsmiths/builder/org.eclipse.papyrus.toolsmiths.plugin.builder/.classpath b/plugins/toolsmiths/builder/org.eclipse.papyrus.toolsmiths.plugin.builder/.classpath index eca7bdba8f0..e801ebfb468 100755 --- a/plugins/toolsmiths/builder/org.eclipse.papyrus.toolsmiths.plugin.builder/.classpath +++ b/plugins/toolsmiths/builder/org.eclipse.papyrus.toolsmiths.plugin.builder/.classpath @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <classpath> - <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11"/> <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> <classpathentry kind="src" path="src"/> <classpathentry kind="output" path="bin"/> diff --git a/plugins/toolsmiths/builder/org.eclipse.papyrus.toolsmiths.plugin.builder/.settings/org.eclipse.jdt.core.prefs b/plugins/toolsmiths/builder/org.eclipse.papyrus.toolsmiths.plugin.builder/.settings/org.eclipse.jdt.core.prefs index 81430a42326..e63dbb3b5b4 100755 --- a/plugins/toolsmiths/builder/org.eclipse.papyrus.toolsmiths.plugin.builder/.settings/org.eclipse.jdt.core.prefs +++ b/plugins/toolsmiths/builder/org.eclipse.papyrus.toolsmiths.plugin.builder/.settings/org.eclipse.jdt.core.prefs @@ -1,16 +1,18 @@ eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=11 org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.compliance=11 org.eclipse.jdt.core.compiler.debug.lineNumber=generate org.eclipse.jdt.core.compiler.debug.localVariable=generate org.eclipse.jdt.core.compiler.debug.sourceFile=generate org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.release=disabled -org.eclipse.jdt.core.compiler.source=1.8 +org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning +org.eclipse.jdt.core.compiler.release=enabled +org.eclipse.jdt.core.compiler.source=11 org.eclipse.jdt.core.formatter.align_assignment_statements_on_columns=false org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines=2147483647 org.eclipse.jdt.core.formatter.align_type_members_on_columns=false diff --git a/plugins/toolsmiths/builder/org.eclipse.papyrus.toolsmiths.plugin.builder/META-INF/MANIFEST.MF b/plugins/toolsmiths/builder/org.eclipse.papyrus.toolsmiths.plugin.builder/META-INF/MANIFEST.MF index 0e1532fe312..b99d47dd1bf 100755 --- a/plugins/toolsmiths/builder/org.eclipse.papyrus.toolsmiths.plugin.builder/META-INF/MANIFEST.MF +++ b/plugins/toolsmiths/builder/org.eclipse.papyrus.toolsmiths.plugin.builder/META-INF/MANIFEST.MF @@ -5,7 +5,7 @@ Bundle-SymbolicName: org.eclipse.papyrus.toolsmiths.plugin.builder;singleton:=tr Bundle-Version: 1.0.0.qualifier Bundle-Vendor: Eclipse Modeling Project Automatic-Module-Name: org.eclipse.papyrus.infra.nattable.builder -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 +Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy Bundle-Activator: org.eclipse.papyrus.toolsmiths.plugin.builder.Activator Require-Bundle: org.eclipse.core.resources;bundle-version="[3.13.0,4.0.0)", @@ -24,4 +24,10 @@ Require-Bundle: org.eclipse.core.resources;bundle-version="[3.13.0,4.0.0)", org.eclipse.papyrus.infra.ui;bundle-version="[3.0.0,4.0.0)", org.eclipse.papyrus.emf;bundle-version="[2.0.0,3.0.0)", org.eclipse.papyrus.toolsmiths.validation.architecture;bundle-version="[2.0.0,3.0.0)", - org.eclipse.papyrus.toolsmiths.validation.elementtypes;bundle-version="[2.0.0,3.0.0)" + org.eclipse.papyrus.toolsmiths.validation.elementtypes;bundle-version="[2.0.0,3.0.0)", + org.eclipse.papyrus.infra.core;bundle-version="[4.0.0,5.0.0)", + org.eclipse.emf.transaction;bundle-version="[1.9.1,2.0.0)", + org.eclipse.papyrus.uml.tools;bundle-version="[5.0.0,6.0.0)", + org.eclipse.uml2.uml;bundle-version="[5.5.0,6.0.0)", + org.eclipse.papyrus.uml.tools.utils;bundle-version="[4.0.0,5.0.0)", + org.eclipse.papyrus.toolsmiths.validation.profile;bundle-version="[2.0.0,3.0.0)" diff --git a/plugins/toolsmiths/builder/org.eclipse.papyrus.toolsmiths.plugin.builder/src/org/eclipse/papyrus/toolsmiths/plugin/builder/AbstractPapyrusBuilder.java b/plugins/toolsmiths/builder/org.eclipse.papyrus.toolsmiths.plugin.builder/src/org/eclipse/papyrus/toolsmiths/plugin/builder/AbstractPapyrusBuilder.java index 962c38b1657..638daf2a896 100755 --- a/plugins/toolsmiths/builder/org.eclipse.papyrus.toolsmiths.plugin.builder/src/org/eclipse/papyrus/toolsmiths/plugin/builder/AbstractPapyrusBuilder.java +++ b/plugins/toolsmiths/builder/org.eclipse.papyrus.toolsmiths.plugin.builder/src/org/eclipse/papyrus/toolsmiths/plugin/builder/AbstractPapyrusBuilder.java @@ -34,6 +34,7 @@ public abstract class AbstractPapyrusBuilder { private ProjectDependencyHelper DEPENDENCY_HELPER = ProjectDependencyHelper.INSTANCE; /** + * Run the build on the specified project. * * @param builtProject * the current build project @@ -52,6 +53,17 @@ public abstract class AbstractPapyrusBuilder { public abstract IProject[] build(IProject builtProject, final PapyrusPluginBuilder papyrusBuilder, int kind, Map<String, String> args, IProgressMonitor monitor) throws CoreException; /** + * see {@link IncrementalProjectBuilder#clean(IProgressMonitor)} + * + * @param monitor + * @param iProject + * @throws CoreException + */ + public void clean(IProgressMonitor monitor, IProject iProject) throws CoreException { + // default implementation does nothing + } + + /** * * @param res * the resource to mark with an error diff --git a/plugins/toolsmiths/builder/org.eclipse.papyrus.toolsmiths.plugin.builder/src/org/eclipse/papyrus/toolsmiths/plugin/builder/Activator.java b/plugins/toolsmiths/builder/org.eclipse.papyrus.toolsmiths.plugin.builder/src/org/eclipse/papyrus/toolsmiths/plugin/builder/Activator.java index bbeafad025f..b3a2c63d5fd 100755 --- a/plugins/toolsmiths/builder/org.eclipse.papyrus.toolsmiths.plugin.builder/src/org/eclipse/papyrus/toolsmiths/plugin/builder/Activator.java +++ b/plugins/toolsmiths/builder/org.eclipse.papyrus.toolsmiths.plugin.builder/src/org/eclipse/papyrus/toolsmiths/plugin/builder/Activator.java @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2020 CEA LIST and others. + * Copyright (c) 2020 CEA LIST, 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 @@ -10,6 +10,7 @@ * * Contributors: * Vincent Lorenzo (CEA LIST) <vincent.lorenzo@cea.fr> - Initial API and implementation + * Remi Schnekenburger (EclipseSource) - Bug 568495 * *****************************************************************************/ @@ -29,7 +30,7 @@ public class Activator extends AbstractUIPlugin { /** * The path of the papyrus icon in the project */ - public static final String PAPYRUS_ICON_PATH = "/icons/papyrus.png"; + public static final String PAPYRUS_ICON_PATH = "/icons/papyrus.png"; //$NON-NLS-1$ /** * The log @@ -64,6 +65,10 @@ public class Activator extends AbstractUIPlugin { // manifest builder PapyrusPluginBuilder.addManifestBuilder(new ManifestBuilder()); + PapyrusPluginBuilder.addManifestBuilder(new StaticProfileManifestBuilder()); + + // extension builder + PapyrusPluginBuilder.addPluginBuilder(new StaticProfileExtensionsBuilder()); } /** diff --git a/plugins/toolsmiths/builder/org.eclipse.papyrus.toolsmiths.plugin.builder/src/org/eclipse/papyrus/toolsmiths/plugin/builder/PapyrusPluginBuilder.java b/plugins/toolsmiths/builder/org.eclipse.papyrus.toolsmiths.plugin.builder/src/org/eclipse/papyrus/toolsmiths/plugin/builder/PapyrusPluginBuilder.java index 88b87d1064f..856cbf49c85 100755 --- a/plugins/toolsmiths/builder/org.eclipse.papyrus.toolsmiths.plugin.builder/src/org/eclipse/papyrus/toolsmiths/plugin/builder/PapyrusPluginBuilder.java +++ b/plugins/toolsmiths/builder/org.eclipse.papyrus.toolsmiths.plugin.builder/src/org/eclipse/papyrus/toolsmiths/plugin/builder/PapyrusPluginBuilder.java @@ -109,6 +109,27 @@ public class PapyrusPluginBuilder extends IncrementalProjectBuilder { return wantedDeltaProjects.toArray(new IProject[wantedDeltaProjects.size()]); } + @Override + protected void clean(IProgressMonitor monitor) throws CoreException { + super.clean(monitor); + if (isPapyrusModelBuilderActivated()) { + for (final AbstractPapyrusBuilder builder : PapyrusPluginBuilder.modelBuilders) { + builder.clean(monitor, getProject()); + } + } + + if (isPapyrusManifestBuilderActivated()) { + for (final AbstractPapyrusBuilder builder : PapyrusPluginBuilder.manifestBuilders) { + builder.clean(monitor, getProject()); + } + } + + for (final AbstractPapyrusBuilder builder : PapyrusPluginBuilder.pluginBuilders) { + builder.clean(monitor, getProject()); + } + + } + /** * * @return diff --git a/plugins/toolsmiths/builder/org.eclipse.papyrus.toolsmiths.plugin.builder/src/org/eclipse/papyrus/toolsmiths/plugin/builder/StaticProfileExtensionsBuilder.java b/plugins/toolsmiths/builder/org.eclipse.papyrus.toolsmiths.plugin.builder/src/org/eclipse/papyrus/toolsmiths/plugin/builder/StaticProfileExtensionsBuilder.java new file mode 100644 index 00000000000..b4df30bd187 --- /dev/null +++ b/plugins/toolsmiths/builder/org.eclipse.papyrus.toolsmiths.plugin.builder/src/org/eclipse/papyrus/toolsmiths/plugin/builder/StaticProfileExtensionsBuilder.java @@ -0,0 +1,80 @@ +/***************************************************************************** + * Copyright (c) 2020 CEA LIST, 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: + * Remi Schnekenburger (EclipseSource) - Initial API and implementation + * + *****************************************************************************/ +package org.eclipse.papyrus.toolsmiths.plugin.builder; + +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.papyrus.toolsmiths.plugin.builder.helper.StaticProfileHelper; +import org.eclipse.papyrus.toolsmiths.validation.profile.internal.checkers.ProfileExtensionsChecker; +import org.eclipse.pde.core.plugin.IPluginExtension; +import org.eclipse.pde.core.plugin.IPluginModelBase; +import org.eclipse.pde.core.plugin.PluginRegistry; +import org.eclipse.pde.internal.core.builders.PDEMarkerFactory; +import org.eclipse.uml2.uml.Profile; + +/** + * Builder that checks extensions for the plugin.xml file when project contains a static profile. + */ +@SuppressWarnings("restriction") +public class StaticProfileExtensionsBuilder extends AbstractPapyrusBuilder { + + @Override + public IProject[] build(IProject builtProject, PapyrusPluginBuilder papyrusBuilder, int kind, Map<String, String> args, IProgressMonitor monitor) throws CoreException { + + if (papyrusBuilder.isInterrupted() || monitor.isCanceled()) { + return null; + } + // check static profile presence? + Map<IFile, List<Profile>> profiles = StaticProfileHelper.findStaticProfiles(builtProject, true); + + if (profiles.entrySet().isEmpty()) { + return null; + } + // retrieve information from plugin extensions, warning, this iterates several time on the same parser with different file. Check that clean does not happen between various profiles + for (Entry<IFile, List<Profile>> entry : profiles.entrySet()) { + ProfileExtensionsChecker profileExtensionsChecker = new ProfileExtensionsChecker(builtProject, entry.getKey(), entry.getValue()); + profileExtensionsChecker.check(monitor); + } + + return null; + + } + + @Override + public void clean(IProgressMonitor monitor, IProject project) throws CoreException { + super.clean(monitor, project); + + final IPluginModelBase pluginModelBase = PluginRegistry.findModel(project); + if (pluginModelBase == null) { + return; + } + + // clean the kind of markers created by the specific plugin error reporter. + pluginModelBase.getUnderlyingResource().deleteMarkers(PDEMarkerFactory.MARKER_ID, true, IResource.DEPTH_INFINITE); + for (IPluginExtension extenstion : pluginModelBase.getExtensions().getExtensions()) { + extenstion.getModel().getUnderlyingResource().deleteMarkers(PDEMarkerFactory.MARKER_ID, true, IResource.DEPTH_INFINITE); + + // clean only once the plugin.xml file + break; + } + } +} diff --git a/plugins/toolsmiths/builder/org.eclipse.papyrus.toolsmiths.plugin.builder/src/org/eclipse/papyrus/toolsmiths/plugin/builder/StaticProfileManifestBuilder.java b/plugins/toolsmiths/builder/org.eclipse.papyrus.toolsmiths.plugin.builder/src/org/eclipse/papyrus/toolsmiths/plugin/builder/StaticProfileManifestBuilder.java new file mode 100644 index 00000000000..54fe28d4c41 --- /dev/null +++ b/plugins/toolsmiths/builder/org.eclipse.papyrus.toolsmiths.plugin.builder/src/org/eclipse/papyrus/toolsmiths/plugin/builder/StaticProfileManifestBuilder.java @@ -0,0 +1,66 @@ +/***************************************************************************** + * Copyright (c) 2020 CEA LIST, 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: + * Remi Schnekenburger - Initial API and implementation + * + *****************************************************************************/ + +package org.eclipse.papyrus.toolsmiths.plugin.builder; + +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.papyrus.toolsmiths.plugin.builder.helper.StaticProfileHelper; +import org.eclipse.papyrus.toolsmiths.validation.profile.internal.checkers.ProfileBuildChecker; +import org.eclipse.papyrus.toolsmiths.validation.profile.internal.checkers.ProfileDependenciesChecker; +import org.eclipse.uml2.uml.Profile; + +/** + * Builder that checks manifest for static profiles within a Papyrus plugin. + */ +public class StaticProfileManifestBuilder extends AbstractPapyrusBuilder { + + @Override + public IProject[] build(IProject builtProject, PapyrusPluginBuilder papyrusBuilder, int kind, Map<String, String> args, IProgressMonitor monitor) throws CoreException { + if (papyrusBuilder.isInterrupted() || monitor.isCanceled()) { + return null; + } + + // check static profile presence? + Map<IFile, List<Profile>> profiles = StaticProfileHelper.findStaticProfiles(builtProject, false); + + if (profiles.entrySet().isEmpty()) { + return null; + } + + // retrieve information from plugin extensions, warning, this iterates several time on the same parser with different file. Check that clean does not happen between various profiles + for (Entry<IFile, List<Profile>> entry : profiles.entrySet()) { + Resource resource = entry.getValue().size() > 0 ? entry.getValue().get(0).eResource() : null; + if (resource == null) { + return null; + } + ProfileDependenciesChecker profileDependenciesChecker = new ProfileDependenciesChecker(builtProject, entry.getKey(), resource); + profileDependenciesChecker.check(monitor); + + ProfileBuildChecker profileBuildChecker = new ProfileBuildChecker(builtProject, entry.getKey()); + profileBuildChecker.check(monitor); + } + + return new IProject[] { builtProject }; + } + +} diff --git a/plugins/toolsmiths/builder/org.eclipse.papyrus.toolsmiths.plugin.builder/src/org/eclipse/papyrus/toolsmiths/plugin/builder/helper/StaticProfileHelper.java b/plugins/toolsmiths/builder/org.eclipse.papyrus.toolsmiths.plugin.builder/src/org/eclipse/papyrus/toolsmiths/plugin/builder/helper/StaticProfileHelper.java new file mode 100644 index 00000000000..67d5a49ee21 --- /dev/null +++ b/plugins/toolsmiths/builder/org.eclipse.papyrus.toolsmiths.plugin.builder/src/org/eclipse/papyrus/toolsmiths/plugin/builder/helper/StaticProfileHelper.java @@ -0,0 +1,187 @@ +/***************************************************************************** + * Copyright (c) 2020 CEA LIST, 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: + * Remi Schnekenburger - Initial API and implementation + * + *****************************************************************************/ +package org.eclipse.papyrus.toolsmiths.plugin.builder.helper; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IResourceVisitor; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.transaction.TransactionalEditingDomain; +import org.eclipse.papyrus.infra.core.editor.ModelSetServiceFactory; +import org.eclipse.papyrus.infra.core.resource.EditingDomainServiceFactory; +import org.eclipse.papyrus.infra.core.resource.ModelMultiException; +import org.eclipse.papyrus.infra.core.resource.ModelSet; +import org.eclipse.papyrus.infra.core.resource.ModelsReader; +import org.eclipse.papyrus.infra.core.resource.NotFoundException; +import org.eclipse.papyrus.infra.core.resource.sasheditor.DiModel; +import org.eclipse.papyrus.infra.core.services.ExtensionServicesRegistry; +import org.eclipse.papyrus.infra.core.services.ServiceDescriptor; +import org.eclipse.papyrus.infra.core.services.ServiceDescriptor.ServiceTypeKind; +import org.eclipse.papyrus.infra.core.services.ServiceException; +import org.eclipse.papyrus.infra.core.services.ServiceStartKind; +import org.eclipse.papyrus.infra.core.services.ServicesRegistry; +import org.eclipse.papyrus.infra.core.utils.ServiceUtils; +import org.eclipse.papyrus.toolsmiths.plugin.builder.Activator; +import org.eclipse.papyrus.uml.tools.model.UmlUtils; +import org.eclipse.papyrus.uml.tools.utils.PackageUtil; +import org.eclipse.uml2.uml.Profile; + +/** + * Helper for Static profile builders. + */ +public class StaticProfileHelper { + + private StaticProfileHelper() { + // no instance + } + + /** + * Return the list of the static profile files (.di) for a given project. + * + * @param builtProject + * the project in which static profiles are found. + * @param includeSubProfiles + * <code>true</code> if all profiles within the files are to be returned, only the root profile if <code>false</code> + * @return the map of the found profile files, with the list of contained profile model elements as a value + */ + public final static Map<IFile, List<Profile>> findStaticProfiles(IProject builtProject, boolean includeSubProfiles) { + StaticProfileResourceVisitor visitor = new StaticProfileResourceVisitor(includeSubProfiles); + try { + builtProject.accept(visitor, IResource.DEPTH_INFINITE, true); + return visitor.foundProfiles; + } catch (CoreException e) { + Activator.log.error(e); + } + return null; + } + + private static class StaticProfileResourceVisitor implements IResourceVisitor { + + private static final String GENMODEL_EXTENSION = "genmodel"; + private Map<IFile, List<Profile>> foundProfiles = new HashMap<>(); + + private final boolean includeSubProfiles; + + public StaticProfileResourceVisitor(boolean includeSubProfiles) { + this.includeSubProfiles = includeSubProfiles; + } + + @Override + public boolean visit(IResource resource) throws CoreException { + + if (resource.getType() == IResource.FILE && DiModel.DI_FILE_EXTENSION.equals(resource.getFileExtension())) { + // check sibling gen model file + // full path for gen model + String fileName = resource.getFullPath().lastSegment(); + String genModelName = fileName.substring(0, fileName.length() - DiModel.DI_FILE_EXTENSION.length()).concat(GENMODEL_EXTENSION); + IResource genModelResource = resource.getParent().findMember(genModelName); + if (genModelResource == null) { + return true; + } + + // papyrus model found, now trying to load to see if this is a profile + URI diFileUri = URI.createPlatformResourceURI(resource.getFullPath().toString(), true); + ModelSet modelSet = initialiseModelSet(diFileUri); + if (modelSet == null) { + return true; + } + try { + EObject root = UmlUtils.getUmlModel(modelSet).lookupRoot(); + if (root instanceof Profile) { + Profile profile = (Profile) root; + List<Profile> profiles = new ArrayList<>(); + profiles.add(profile); + if (includeSubProfiles) { + profiles.addAll(PackageUtil.getSubProfiles(profile)); + } + foundProfiles.put((IFile) resource, profiles); + } + } catch (NotFoundException e) { + } + } + + return true; + } + + private ModelSet initialiseModelSet(URI diFileUri) { + ServicesRegistry service = null; + + try { + service = new ExtensionServicesRegistry(); + } catch (ServiceException e) { + Activator.log.error(e); + return null; + } + + // Override service factory for Model Set + ServiceDescriptor descriptor = new ServiceDescriptor(ModelSet.class, ModelSetServiceFactory.class.getName(), + ServiceStartKind.STARTUP, 10); + descriptor.setServiceTypeKind(ServiceTypeKind.serviceFactory); + service.add(descriptor); + + // Override factory for editing domain + descriptor = new ServiceDescriptor(TransactionalEditingDomain.class, + EditingDomainServiceFactory.class.getName(), ServiceStartKind.STARTUP, 10, + Collections.singletonList(ModelSet.class.getName())); + descriptor.setServiceTypeKind(ServiceTypeKind.serviceFactory); + service.add(descriptor); + + try { + service.startServicesByClassKeys(ModelSet.class, TransactionalEditingDomain.class); + } catch (ServiceException e) { + Activator.log.error(e); + } + + ModelSet modelSet = null; + try { + modelSet = ServiceUtils.getInstance().getModelSet(service); + } catch (ServiceException e) { + // Ignore service exception + } + + // Instantiate a Model set + if (modelSet == null) { + modelSet = new ModelSet(); + try { + ModelSetServiceFactory.setServiceRegistry(modelSet, service); + } catch (ServiceException e) { + // Ignore service exception + } + } + + // Read all Model from selected file + ModelsReader modelsReader = new ModelsReader(); + modelsReader.readModel(modelSet); + try { + modelSet.loadModels(diFileUri); + } catch (ModelMultiException e) { + Activator.log.error(e); + } + // Initialize an editing domain + modelSet.getTransactionalEditingDomain(); + return modelSet; + } + } + +} diff --git a/plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.common/src/org/eclipse/papyrus/toolsmiths/validation/common/internal/utils/MarkersManagementUtils.java b/plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.common/src/org/eclipse/papyrus/toolsmiths/validation/common/internal/utils/MarkersManagementUtils.java index 49a9f1d75ad..b5fe7657c71 100644 --- a/plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.common/src/org/eclipse/papyrus/toolsmiths/validation/common/internal/utils/MarkersManagementUtils.java +++ b/plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.common/src/org/eclipse/papyrus/toolsmiths/validation/common/internal/utils/MarkersManagementUtils.java @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2019 CEA LIST and others. + * Copyright (c) 2019, 2020 CEA LIST, 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 @@ -10,6 +10,7 @@ * * Contributors: * Nicolas FAUVERGUE (CEA LIST) nicolas.fauvergue@cea.fr - Initial API and implementation + * Remi Schnekenburger (EclipseSource) - Bug 568495 * *****************************************************************************/ @@ -39,11 +40,32 @@ public class MarkersManagementUtils { * @return The created marker or <code>null</code> if there is an error. */ public static IMarker createMarker(final IResource resource, final String type, final String message, final int severity) { + IMarker createdMarker = createMarker(resource, type); + if (createdMarker != null) { + try { + createdMarker.setAttribute(IMarker.MESSAGE, message); + createdMarker.setAttribute(IMarker.SEVERITY, severity); + } catch (CoreException e) { + Activator.log.error("Error while setting marker attributes", e); //$NON-NLS-1$ + } + } + + return createdMarker; + } + + /** + * This allows to create a marker for a resource. + * + * @param resource + * The resource where create the marker. + * @param type + * The type of the marker validation. + * @return The created marker or <code>null</code> if there is an error. + */ + public static IMarker createMarker(final IResource resource, final String type) { IMarker createdMarker = null; try { createdMarker = resource.createMarker(type); - createdMarker.setAttribute(IMarker.MESSAGE, message); - createdMarker.setAttribute(IMarker.SEVERITY, severity); } catch (CoreException e) { Activator.log.error("Error while creating marker", e); //$NON-NLS-1$ } diff --git a/plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.common/src/org/eclipse/papyrus/toolsmiths/validation/common/utils/MarkersService.java b/plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.common/src/org/eclipse/papyrus/toolsmiths/validation/common/utils/MarkersService.java index 7692b97f84c..cc18322ad52 100644 --- a/plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.common/src/org/eclipse/papyrus/toolsmiths/validation/common/utils/MarkersService.java +++ b/plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.common/src/org/eclipse/papyrus/toolsmiths/validation/common/utils/MarkersService.java @@ -43,6 +43,19 @@ public class MarkersService { } /** + * This allows to create a marker for a resource. + * + * @param resource + * The resource where create the marker. + * @param type + * The type of the marker validation. + * @return The created marker or <code>null</code> if there is an error. + */ + public static IMarker createMarker(final IResource resource, final String type) { + return MarkersManagementUtils.createMarker(resource, type); + } + + /** * This allows to delete markers of a resource. * * @param resource diff --git a/plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.profile/META-INF/MANIFEST.MF b/plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.profile/META-INF/MANIFEST.MF index 75c8c03f8a5..165b943b58e 100644 --- a/plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.profile/META-INF/MANIFEST.MF +++ b/plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.profile/META-INF/MANIFEST.MF @@ -6,12 +6,18 @@ Require-Bundle: org.eclipse.core.expressions;bundle-version="[3.6.0,4.0.0)", org.eclipse.papyrus.infra.core.log;bundle-version="[2.0.0,3.0.0)", org.eclipse.papyrus.toolsmiths.validation.common;bundle-version="[2.0.0,3.0.0)", org.eclipse.papyrus.uml.tools;bundle-version="[5.0.0,6.0.0)", - org.eclipse.pde.core;bundle-version="[3.13.0,4.0.0)", + org.eclipse.pde.core;bundle-version="[3.13.0,3.15.0)", org.eclipse.ui.views.properties.tabbed;bundle-version="[3.8.0,4.0.0)", - org.eclipse.uml2.uml;bundle-version="[5.5.0,6.0.0)" + org.eclipse.uml2.uml;bundle-version="[5.5.0,6.0.0)", + org.eclipse.papyrus.infra.internationalization;bundle-version="[2.0.0,3.0.0)", + org.eclipse.jface.text;bundle-version="[3.16.300,4.0.0)", + org.eclipse.core.filebuffers;bundle-version="[3.6.1000,4.0.0)", + org.eclipse.papyrus.uml.tools.utils;bundle-version="[4.0.0,5.0.0)", + org.eclipse.emf.codegen.ecore;bundle-version="[2.22.0,3.0.0)" Export-Package: org.eclipse.papyrus.toolsmiths.validation.profile, org.eclipse.papyrus.toolsmiths.validation.profile.checkers, - org.eclipse.papyrus.toolsmiths.validation.profile.constants + org.eclipse.papyrus.toolsmiths.validation.profile.constants, + org.eclipse.papyrus.toolsmiths.validation.profile.internal.checkers;x-friends:="org.eclipse.papyrus.toolsmiths.plugin.builder" Bundle-Vendor: %providerName Bundle-ActivationPolicy: lazy Bundle-ClassPath: . diff --git a/plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.profile/src/org/eclipse/papyrus/toolsmiths/validation/profile/internal/checkers/ProfileBuildChecker.java b/plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.profile/src/org/eclipse/papyrus/toolsmiths/validation/profile/internal/checkers/ProfileBuildChecker.java index 133935fdf56..bfc56d0b137 100644 --- a/plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.profile/src/org/eclipse/papyrus/toolsmiths/validation/profile/internal/checkers/ProfileBuildChecker.java +++ b/plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.profile/src/org/eclipse/papyrus/toolsmiths/validation/profile/internal/checkers/ProfileBuildChecker.java @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2019 CEA LIST and others. + * Copyright (c) 2019, 2020 CEA LIST, 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 @@ -10,22 +10,47 @@ * * Contributors: * Nicolas FAUVERGUE (CEA LIST) nicolas.fauvergue@cea.fr - Initial API and implementation + * Remi Schnekenburger (EclipseSource) - Bug 568495 * *****************************************************************************/ package org.eclipse.papyrus.toolsmiths.validation.profile.internal.checkers; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import org.eclipse.core.filebuffers.FileBuffers; +import org.eclipse.core.filebuffers.ITextFileBuffer; +import org.eclipse.core.filebuffers.ITextFileBufferManager; +import org.eclipse.core.filebuffers.LocationKind; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.emf.codegen.ecore.genmodel.GenModel; +import org.eclipse.emf.codegen.ecore.genmodel.GenPackage; +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.impl.EPackageImpl; +import org.eclipse.emf.ecore.plugin.EcorePlugin; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.emf.ecore.resource.ResourceSet; +import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; import org.eclipse.papyrus.toolsmiths.validation.common.checkers.IPluginChecker; import org.eclipse.papyrus.toolsmiths.validation.common.utils.MarkersService; import org.eclipse.papyrus.toolsmiths.validation.common.utils.ProjectManagementService; -import org.eclipse.papyrus.toolsmiths.validation.profile.constants.ProfilePluginValidationConstants; +import org.eclipse.papyrus.toolsmiths.validation.profile.Activator; +import org.eclipse.papyrus.uml.tools.model.UmlModel; import org.eclipse.pde.core.build.IBuild; import org.eclipse.pde.core.build.IBuildEntry; import org.eclipse.pde.core.build.IBuildModel; +import org.eclipse.pde.internal.core.builders.PDEMarkerFactory; +import org.eclipse.pde.internal.core.text.build.BuildEntry; +import org.eclipse.pde.internal.core.text.build.BuildModel; /** * This class allows to check the 'build.properties' needed for the profile file. @@ -62,10 +87,14 @@ public class ProfileBuildChecker implements IPluginChecker { * * @see org.eclipse.papyrus.toolsmiths.validation.common.checkers.IPluginChecker#check(org.eclipse.core.runtime.IProgressMonitor) */ + @SuppressWarnings("restriction") @Override public void check(final IProgressMonitor monitor) { if (null != monitor) { + if (monitor.isCanceled()) { + return; + } monitor.subTask("Validate 'build.properties' file for profile '" + profileFile.getName() + "'."); //$NON-NLS-1$ //$NON-NLS-2$ } @@ -74,31 +103,47 @@ public class ProfileBuildChecker implements IPluginChecker { if (null != buildModel) { // Create the conditions: - // - Boolean to determinate if the build contains folder containing the profile + // - Booleans to determinate if the build contains folder containing the required files boolean containsProfile = false; + boolean containsGenModel = false; + boolean containsEcore = false; // Calculate the profile path - final String profilePath = profileFile.getProjectRelativePath().toString(); + final String profileUMLPath = profileFile.getProjectRelativePath().removeFileExtension().addFileExtension(UmlModel.UML_FILE_EXTENSION).toString(); + final String profileGenModelPath = profileFile.getProjectRelativePath().removeFileExtension().addFileExtension("genmodel").toString(); + final Optional<String> ecoreFile = profileEcorePath(profileGenModelPath); + final IBuild build = buildModel.getBuild(); final IBuildEntry buildEntry = build.getEntry(IBuildEntry.BIN_INCLUDES); // Iterate on existing tokens final String[] tokens = buildEntry.getTokens(); - for (int i = 0; i < tokens.length && !containsProfile; i++) { - containsProfile = profilePath.startsWith(tokens[i]); + for (int i = 0; i < tokens.length; i++) { + String token = tokens[i]; + if (profileUMLPath.startsWith(token)) { + containsProfile = true; + } + if (profileGenModelPath.startsWith(token)) { + containsGenModel = true; + } + if (ecoreFile.isPresent() && ecoreFile.get().startsWith(token)) { + containsEcore = true; + } } + List<BuildError> errors = new ArrayList<>(); // Create marker for UMLProfile extension point if needed if (!containsProfile) { - final IFile buildPropertiesFile = ProjectManagementService.getBuildFile(project); - - MarkersService.createMarker( - buildPropertiesFile, - ProfilePluginValidationConstants.PROFILE_PLUGIN_VALIDATION_TYPE, - "The build does not contains entry for file '" + profilePath + "'", //$NON-NLS-1$ //$NON-NLS-2$ - IMarker.SEVERITY_ERROR); + errors.add(new BuildError(PDEMarkerFactory.MARKER_ID, "The build does not contains entry for profile '" + profileUMLPath + "'", IMarker.SEVERITY_ERROR, IBuildEntry.BIN_INCLUDES)); } + if (!containsGenModel) { + errors.add(new BuildError(PDEMarkerFactory.MARKER_ID, "The build does not contains entry for genModel file '" + profileGenModelPath + "'", IMarker.SEVERITY_WARNING, IBuildEntry.BIN_INCLUDES)); + } + if (!containsEcore) { + errors.add(new BuildError(PDEMarkerFactory.MARKER_ID, "The build does not contains entry for ecore file '" + ecoreFile.orElse("[notfound]") + "'", IMarker.SEVERITY_WARNING, IBuildEntry.BIN_INCLUDES)); + } + reportErrors(errors); } if (null != monitor) { @@ -106,4 +151,188 @@ public class ProfileBuildChecker implements IPluginChecker { } } + /** + * @param profileGenModelPath + * @return + */ + private Optional<String> profileEcorePath(String profileGenModelPath) { + Optional<GenModel> genModel = loadGenModel(profileGenModelPath); + return genModel.map(this::ecoreFilePath); + } + + private String ecoreFilePath(GenModel genModel) { + GenPackage genPackage = genModel.getGenPackages().get(0); + EPackageImpl ecoreImpl = ((EPackageImpl) genPackage.getEcorePackage()); + URI ecoreURI = ecoreImpl.eResource().getURI(); + List<String> segments = new ArrayList<>(ecoreURI.segmentsList()); + // return only relative path from plugin root (platform:/plugin/<plugin-id>/ is removed) + return String.join("/", segments.subList(2, segments.size())); + } + + /** + * Load the genmodel located at specified URI and return the root element or <code>null</code> if none could be found. + * + * @param profileGenModelPath + * project relative path to the gen model file to load + * @return the GenModel root element or empty optional in case of loading issues + */ + private Optional<GenModel> loadGenModel(String profileGenModelPath) { + try { + URI uri = URI.createPlatformPluginURI(project.getName() + "/" + profileGenModelPath, true); + ResourceSet set = new ResourceSetImpl(); + set.getURIConverter().getURIMap().putAll(EcorePlugin.computePlatformURIMap(true)); + Resource genModelResource = set.getResource(uri, true); + if (genModelResource != null && genModelResource.getContents().size() > 0) { + EObject root = genModelResource.getContents().get(0); + if (root instanceof GenModel) { + return Optional.of((GenModel) root); + } + } + } catch (Exception e) { + Activator.log.warn("Papyrus Build Checker (genmodel loading): " + e.getMessage()); + } + return Optional.empty(); + } + + + /** + * generate markers for the specified list of errors. + * + * @param errors + * the list of errors for which markers will be created. + */ + @SuppressWarnings("restriction") + private void reportErrors(List<BuildError> errors) { + final IFile buildPropertiesFile = ProjectManagementService.getBuildFile(project); + BuildModel textBuildModel = prepareTextBuildModel(buildPropertiesFile); + + errors.stream().forEach(error -> { + reportBuildError(buildPropertiesFile, textBuildModel, error.type, error.message, error.severity, error.entry); + }); + } + + /** + * Create a Marker on the specified file, with the given parameters. + * + * @param buildFile + * the file on which the marker should be created. + * @param textBuildModel + * the textual model representation of the build.properties file + * @param type + * the type of the marker to create + * @param message + * the message of the marker to create + * @param severity + * the severity of the marker to create + * @param header + * the header entry of the build file on which marker is created. + */ + @SuppressWarnings("restriction") + private void reportBuildError(IFile buildFile, BuildModel textBuildModel, String type, String message, int severity, String entry) { + if (textBuildModel != null) { + IMarker marker = MarkersService.createMarker(buildFile, type, message, severity); + try { + marker.setAttribute(IMarker.LINE_NUMBER, getLineNumber(textBuildModel.getBuild().getEntry(entry))); + } catch (CoreException e) { + Activator.log.error(e); + } + } else { + MarkersService.createMarker(buildFile, type, message, severity); + } + } + + /** + * Read and parse the build.properties file to create the abstract representation. + * + * @param file + * the file to parse + * @return the model of the build file. + */ + @SuppressWarnings("restriction") + private BuildModel prepareTextBuildModel(IFile file) { + try { + IDocument doc = createDocument(file); + if (doc == null) { + return null; + } + BuildModel bm = new BuildModel(doc, true); + bm.load(); + if (!bm.isLoaded()) { + return null; + } + return bm; + } catch (CoreException e) { + Activator.log.error(e); + return null; + } + } + + /** + * Read the manifest file and provide a {@link IDocument}. + * + * @param file + * the file to parse + * @return the document of the textual file. + */ + protected IDocument createDocument(IFile file) { + if (!file.exists()) { + return null; + } + ITextFileBufferManager manager = FileBuffers.getTextFileBufferManager(); + if (manager == null) { + return null; + } + try { + manager.connect(file.getFullPath(), LocationKind.NORMALIZE, null); + ITextFileBuffer textBuf = manager.getTextFileBuffer(file.getFullPath(), LocationKind.NORMALIZE); + IDocument document = textBuf.getDocument(); + manager.disconnect(file.getFullPath(), LocationKind.NORMALIZE, null); + return document; + } catch (CoreException e) { + Activator.log.error(e); + } + return null; + } + + /** + * Retrieve the line number for the specified entry in the build.properties file. + * + * @param imh + * the entry to be retrieved. + * @return the line number or <code>0</code> if none was found. + */ + @SuppressWarnings("restriction") + private int getLineNumber(IBuildEntry ibe) { + if (!(ibe instanceof BuildEntry)) { + return 0; + } + BuildEntry be = (BuildEntry) ibe; + org.eclipse.jface.text.IDocument doc = ((BuildModel) be.getModel()).getDocument(); + try { + int buildEntryLineNumber = doc.getLineOfOffset(be.getOffset()) + 1; + // we are interested in the build entry name + // (getLineOfOffset is 0-indexed, need 1-indexed) + return buildEntryLineNumber; + } catch (BadLocationException e) { + Activator.log.error(e); + } + return 0; + } + + /** + * Representation of an error on the build.properties file. + */ + private static class BuildError { + public final String entry; + public final int severity; + public final String message; + public final String type; + + public BuildError(String type, String message, int severity, String entry) { + this.type = type; + this.message = message; + this.severity = severity; + this.entry = entry; + } + } } diff --git a/plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.profile/src/org/eclipse/papyrus/toolsmiths/validation/profile/internal/checkers/ProfileDependenciesChecker.java b/plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.profile/src/org/eclipse/papyrus/toolsmiths/validation/profile/internal/checkers/ProfileDependenciesChecker.java index 157d03aa903..e3bc4f8f331 100644 --- a/plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.profile/src/org/eclipse/papyrus/toolsmiths/validation/profile/internal/checkers/ProfileDependenciesChecker.java +++ b/plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.profile/src/org/eclipse/papyrus/toolsmiths/validation/profile/internal/checkers/ProfileDependenciesChecker.java @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2019 CEA LIST and others. + * Copyright (c) 2019, 2020 CEA LIST, 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 @@ -10,29 +10,43 @@ * * Contributors: * Nicolas FAUVERGUE (CEA LIST) nicolas.fauvergue@cea.fr - Initial API and implementation + * Remi Schnekenburger (EclipseSource) - Bug 568495 * *****************************************************************************/ - package org.eclipse.papyrus.toolsmiths.validation.profile.internal.checkers; +import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; +import org.eclipse.core.filebuffers.FileBuffers; +import org.eclipse.core.filebuffers.ITextFileBuffer; +import org.eclipse.core.filebuffers.ITextFileBufferManager; +import org.eclipse.core.filebuffers.LocationKind; +import org.eclipse.core.internal.runtime.MetaDataKeeper; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.impl.URIMappingRegistryImpl; import org.eclipse.emf.ecore.util.EcoreUtil; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; import org.eclipse.osgi.service.resolver.BundleSpecification; import org.eclipse.papyrus.toolsmiths.validation.common.checkers.IPluginChecker; import org.eclipse.papyrus.toolsmiths.validation.common.utils.MarkersService; import org.eclipse.papyrus.toolsmiths.validation.common.utils.ProjectManagementService; -import org.eclipse.papyrus.toolsmiths.validation.profile.constants.ProfilePluginValidationConstants; +import org.eclipse.papyrus.toolsmiths.validation.profile.Activator; +import org.eclipse.pde.internal.core.builders.PDEMarkerFactory; +import org.eclipse.pde.internal.core.ibundle.IManifestHeader; +import org.eclipse.pde.internal.core.text.bundle.BundleModel; +import org.eclipse.pde.internal.core.text.bundle.ManifestHeader; +import org.osgi.framework.Constants; /** * This class allows to check that the plug-in has the correct dependencies depending to the external profile references. @@ -41,15 +55,8 @@ public class ProfileDependenciesChecker implements IPluginChecker { /** * The plug-ins to detect as warning instead of errors. - * This can be filled. */ - @SuppressWarnings("serial") - private static Set<String> WARNING_PLUGINS_EXCEPTION = new HashSet<String>() { - { - add("org.eclipse.uml2.uml.resources"); //$NON-NLS-1$ - } - }; - + private static Set<String> WARNING_PLUGINS_EXCEPTION = Set.of("org.eclipse.uml2.uml.resources"); //$NON-NLS-1$ /** * The current project resource. @@ -88,6 +95,7 @@ public class ProfileDependenciesChecker implements IPluginChecker { * * @see org.eclipse.papyrus.toolsmiths.validation.common.checkers.IPluginChecker#check(org.eclipse.core.runtime.IProgressMonitor) */ + @SuppressWarnings("restriction") @Override public void check(final IProgressMonitor monitor) { @@ -110,29 +118,132 @@ public class ProfileDependenciesChecker implements IPluginChecker { requiredPlugins.removeIf(requiredPlugin -> existingRequiredPlugins.contains(requiredPlugin)); } + List<ManifestError> errors = new ArrayList<>(); // If requiredPlugins is not empty, that means, the dependency is not available in the profile plug-in // So, create the warning markers if (!requiredPlugins.isEmpty()) { - final IFile manifestFile = ProjectManagementService.getManifestFile(project); - requiredPlugins.stream().forEach(requiredPlugin -> { int severity = IMarker.SEVERITY_ERROR; if (WARNING_PLUGINS_EXCEPTION.contains(requiredPlugin)) { severity = IMarker.SEVERITY_WARNING; } - MarkersService.createMarker(manifestFile, - ProfilePluginValidationConstants.PROFILE_PLUGIN_VALIDATION_TYPE, - "The plug-in '" + requiredPlugin + "' must be defined as required plug-in (for profile '" + profileFile.getName() + "').", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - severity); + errors.add(new ManifestError(PDEMarkerFactory.MARKER_ID, "The plug-in '" + requiredPlugin + "' must be defined as required plug-in (for profile '" + profileFile.getName() + "').", severity, Constants.REQUIRE_BUNDLE)); }); } + reportErrors(errors); + if (null != monitor) { monitor.worked(1); } } /** + * generate markers for the specified list of errors. + * + * @param errors + * the list of errors for which markers will be created. + */ + @SuppressWarnings("restriction") + private void reportErrors(List<ManifestError> errors) { + if (!errors.isEmpty()) { + final IFile manifestFile = ProjectManagementService.getManifestFile(project); + BundleModel textBundleModel = prepareTextBundleModel(manifestFile); + errors.stream().forEach(error -> { + reportBundleError(manifestFile, textBundleModel, error.type, error.message, error.severity, error.header); + }); + } + } + + /** + * Create a Marker on the specified file, with the given parameters. + * + * @param manifestFile + * the file on which the marker should be created. + * @param textBundleModel + * the textual model representation of the Manifest.MF file + * @param type + * the type of the marker to create + * @param message + * the message of the marker to create + * @param severity + * the severity of the marker to create + * @param header + * the header entry of the manifest file on which marker is created. + */ + @SuppressWarnings("restriction") + private void reportBundleError(IFile manifestFile, BundleModel textBundleModel, String type, String message, int severity, String header) { + if (textBundleModel != null) { + IMarker marker = MarkersService.createMarker(manifestFile, type, message, severity); + try { + marker.setAttribute(IMarker.LINE_NUMBER, getLineNumber(textBundleModel.getBundle().getManifestHeader(header))); + } catch (CoreException e) { + Activator.log.error(e); + } + } else { + MarkersService.createMarker(manifestFile, type, message, severity); + } + } + + /** + * Read and parse the manifest file to create the abstract representation. + * + * @param manifestFile + * the file to parse + * @return the model of the manifest file. + */ + @SuppressWarnings("restriction") + private BundleModel prepareTextBundleModel(IFile manifestFile) { + try { + IDocument doc = createDocument(manifestFile); + if (doc == null) { + return null; + } + BundleModel bm = new BundleModel(doc, true); + bm.load(); + if (!bm.isLoaded()) { + return null; + } + return bm; + } catch (CoreException e) { + Activator.log.error(e); + return null; + } + } + + /** + * Read the manifest file and provide a {@link IDocument}. + * + * @param file + * the file to parse + * @return the document of the textual file. + */ + protected IDocument createDocument(IFile file) { + if (!file.exists()) { + return null; + } + ITextFileBufferManager manager = FileBuffers.getTextFileBufferManager(); + if (manager == null) { + return null; + } + IDocument document = null; + try { + manager.connect(file.getFullPath(), LocationKind.NORMALIZE, null); + ITextFileBuffer textBuf = manager.getTextFileBuffer(file.getFullPath(), LocationKind.NORMALIZE); + document = textBuf.getDocument(); + } catch (CoreException e) { + Activator.log.error(e); + } finally { + try { + manager.disconnect(file.getFullPath(), LocationKind.NORMALIZE, null); + } catch (CoreException e) { + Activator.log.error(e); + } + } + return document; + } + + /** * This allows to get the external references paths. * * @param project @@ -143,8 +254,10 @@ public class ProfileDependenciesChecker implements IPluginChecker { * The resource to get external references paths. * @return The external references paths. */ + @SuppressWarnings("restriction") private Collection<URI> getExternalReferencesPaths(final IProject project, final IFile profileFile, final Resource resource) { final Collection<URI> externalReferencesPaths = new HashSet<>(); + String metadataPath = MetaDataKeeper.getMetaArea().getMetadataLocation().toString(); // First step, resolve all references EcoreUtil.resolveAll(resource); @@ -161,12 +274,14 @@ public class ProfileDependenciesChecker implements IPluginChecker { if (null == correspondingURI) { // If this case, the pathmap cannot be resolved, so create a marker MarkersService.createMarker(profileFile, - ProfilePluginValidationConstants.PROFILE_PLUGIN_VALIDATION_TYPE, + PDEMarkerFactory.MARKER_ID, "The pathmap '" + resourceURI.toString() + "' cannot be resolved.", //$NON-NLS-1$ //$NON-NLS-2$ IMarker.SEVERITY_ERROR); } else { externalReferencesPaths.add(correspondingURI); } + } else if (resourceURI.toFileString().startsWith(metadataPath)) { + // avoid adding dependencies towards local metadata (case of internationalization) } else { externalReferencesPaths.add(resourceURI); } @@ -174,6 +289,7 @@ public class ProfileDependenciesChecker implements IPluginChecker { } return externalReferencesPaths; + } /** @@ -242,4 +358,47 @@ public class ProfileDependenciesChecker implements IPluginChecker { return pluginName; } + + /** + * Retrieve the line number for the specified header in the Manifest file. + * + * @param imh + * the manifest header to be retrieved. + * @return the line number or <code>0</code> if none was found. + */ + @SuppressWarnings("restriction") + private int getLineNumber(IManifestHeader imh) { + if (!(imh instanceof ManifestHeader)) { + return 0; + } + ManifestHeader mh = (ManifestHeader) imh; + org.eclipse.jface.text.IDocument doc = ((BundleModel) mh.getModel()).getDocument(); + try { + int bundleEntryLineNumber = doc.getLineOfOffset(mh.getOffset()) + 1; + // we are interested in the build entry name + // (getLineOfOffset is 0-indexed, need 1-indexed) + return bundleEntryLineNumber; + } catch (BadLocationException e) { + Activator.log.error(e); + } + return 0; + } + + /** + * Representation of an error on the Manifest file. + */ + private static class ManifestError { + + private final String type; + private final String message; + private final int severity; + private final String header; + + public ManifestError(String type, String message, int severity, String header) { + this.type = type; + this.message = message; + this.severity = severity; + this.header = header; + } + } } diff --git a/plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.profile/src/org/eclipse/papyrus/toolsmiths/validation/profile/internal/checkers/ProfileExtensionsChecker.java b/plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.profile/src/org/eclipse/papyrus/toolsmiths/validation/profile/internal/checkers/ProfileExtensionsChecker.java index 842db8d0996..59d862a5b75 100644 --- a/plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.profile/src/org/eclipse/papyrus/toolsmiths/validation/profile/internal/checkers/ProfileExtensionsChecker.java +++ b/plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.profile/src/org/eclipse/papyrus/toolsmiths/validation/profile/internal/checkers/ProfileExtensionsChecker.java @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2019 CEA LIST and others. + * Copyright (c) 2019, 2020 CEA LIST, 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 @@ -10,33 +10,30 @@ * * Contributors: * Nicolas FAUVERGUE (CEA LIST) nicolas.fauvergue@cea.fr - Initial API and implementation + * Remi Schnekenburger (EclipseSource) - Bug 568495 * *****************************************************************************/ - package org.eclipse.papyrus.toolsmiths.validation.profile.internal.checkers; import java.util.Collection; -import java.util.HashSet; -import java.util.Iterator; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.emf.ecore.xmi.XMIResource; +import org.eclipse.osgi.util.NLS; import org.eclipse.papyrus.toolsmiths.validation.common.checkers.IPluginChecker; import org.eclipse.papyrus.toolsmiths.validation.common.utils.MarkersService; import org.eclipse.papyrus.toolsmiths.validation.common.utils.ProjectManagementService; -import org.eclipse.papyrus.toolsmiths.validation.profile.constants.ProfilePluginValidationConstants; -import org.eclipse.pde.core.plugin.IPluginAttribute; -import org.eclipse.pde.core.plugin.IPluginElement; -import org.eclipse.pde.core.plugin.IPluginExtension; -import org.eclipse.pde.core.plugin.IPluginObject; +import org.eclipse.papyrus.toolsmiths.validation.profile.internal.messages.Messages; +import org.eclipse.pde.internal.core.builders.DefaultSAXParser; +import org.eclipse.pde.internal.core.builders.PDEMarkerFactory; import org.eclipse.uml2.uml.Profile; /** * This class allows to check the extensions of the 'plugin.xml' needed for the profiles. */ +@SuppressWarnings("restriction") public class ProfileExtensionsChecker implements IPluginChecker { /** @@ -79,84 +76,22 @@ public class ProfileExtensionsChecker implements IPluginChecker { @Override public void check(final IProgressMonitor monitor) { - if (null != monitor) { - monitor.subTask("Validate 'plugin.xml' file for profile '" + profileFile.getName() + "'."); //$NON-NLS-1$ //$NON-NLS-2$ + if (monitor != null && monitor.isCanceled()) { + return; } - // Create the conditions: - // - Copy of existing profiles (that will be removed if there are found in uml generated package extension points) - // - Boolean to check if the UMLProfile is defined for the profile - final Collection<Profile> profiles = new HashSet<>(existingProfiles); - boolean foundExtensionUMLProfile = false; - - // Get all the extensions of the plug-in to check - final Iterator<IPluginExtension> extensions = ProjectManagementService.getPluginExtensions(project).iterator(); - while (extensions.hasNext()) { - final IPluginExtension extension = extensions.next(); - // Check if the UML profile extension point is managed (warning because this one can be managed outside of this plug-in) - if (!foundExtensionUMLProfile && ProfilePluginValidationConstants.UMLPROFILE_EXTENSION_POINT.equals(extension.getPoint())) { - for (final IPluginObject pluginObject : extension.getChildren()) { - if (pluginObject instanceof IPluginElement && "profile".equals(pluginObject.getName())) { //$NON-NLS-1$ - for (final IPluginAttribute pluginAtttribute : ((IPluginElement) pluginObject).getAttributes()) { - if ("path".equals(pluginAtttribute.getName())) { //$NON-NLS-1$ - final String locationValue = pluginAtttribute.getValue(); - if (locationValue.endsWith(profileFile.getName())) { - foundExtensionUMLProfile = true; - } - } - } - } - } - } - - // Manage the uml profile registration (check only if - if (!profiles.isEmpty() && ProfilePluginValidationConstants.UML_GENERATED_PACKAGE_EXTENSION_POINT.equals(extension.getPoint())) { - for (final IPluginObject pluginObject : extension.getChildren()) { - if (pluginObject instanceof IPluginElement && "profile".equals(pluginObject.getName())) { //$NON-NLS-1$ - for (final IPluginAttribute pluginAtttribute : ((IPluginElement) pluginObject).getAttributes()) { - if ("location".equals(pluginAtttribute.getName())) { //$NON-NLS-1$ - final String locationValue = pluginAtttribute.getValue(); + final IFile pluginXML = ProjectManagementService.getPluginXMLFile(project); - final Iterator<Profile> profilesIt = profiles.iterator(); - while (profilesIt.hasNext()) { - final Profile currentProfile = profilesIt.next(); - final String profileId = ((XMIResource) currentProfile.eResource()).getID(currentProfile); - if (locationValue.endsWith(profileFile.getName() + "#" + profileId)) { //$NON-NLS-1$ - profilesIt.remove(); - break; - } - } - } - } - } - } - } + if (pluginXML == null) { + MarkersService.createMarker(profileFile, PDEMarkerFactory.MARKER_ID, "No extensions are declared for this static profile", IMarker.SEVERITY_ERROR); + return; } - - // If there is a problem, get the plugin.xml file to mark the correct file for problems - if (!foundExtensionUMLProfile || !profiles.isEmpty()) { - final IFile pluginXMLFile = ProjectManagementService.getPluginXMLFile(project); - - // Create marker for UMLProfile extension point if needed - if (!foundExtensionUMLProfile) { - MarkersService.createMarker( - pluginXMLFile, - ProfilePluginValidationConstants.PROFILE_PLUGIN_VALIDATION_TYPE, - "The extension point '" + ProfilePluginValidationConstants.UMLPROFILE_EXTENSION_POINT + "' should be created for profile '" + profileFile.getName() + "'", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - IMarker.SEVERITY_WARNING); - } - // Create markers (one by missing profile) for uml generated package extension point if needed - if (!profiles.isEmpty()) { - for (final Profile profile : profiles) { - MarkersService.createMarker( - pluginXMLFile, - ProfilePluginValidationConstants.PROFILE_PLUGIN_VALIDATION_TYPE, - "There is no extension point '" + ProfilePluginValidationConstants.UML_GENERATED_PACKAGE_EXTENSION_POINT + "' for profile '" + profile.getName() + "'.", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - IMarker.SEVERITY_ERROR); - } - } + monitor.subTask(NLS.bind(Messages.StaticProfileExtensionsBuilder_subTask_checkingFile, profileFile)); + for (Profile profile : existingProfiles) { + StaticProfilePluginErrorReporter reporter = new StaticProfilePluginErrorReporter(pluginXML, profile, profileFile); + DefaultSAXParser.parse(pluginXML, reporter); + reporter.validateContent(monitor); } - if (null != monitor) { monitor.worked(1); } diff --git a/plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.profile/src/org/eclipse/papyrus/toolsmiths/validation/profile/internal/checkers/SelectiveDeleteErrorReporter.java b/plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.profile/src/org/eclipse/papyrus/toolsmiths/validation/profile/internal/checkers/SelectiveDeleteErrorReporter.java new file mode 100644 index 00000000000..78e99bfd327 --- /dev/null +++ b/plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.profile/src/org/eclipse/papyrus/toolsmiths/validation/profile/internal/checkers/SelectiveDeleteErrorReporter.java @@ -0,0 +1,160 @@ +/******************************************************************************* + * Copyright (c) 2018 Julian Honnen, EclipseSource and others. + * + * 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 + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Julian Honnen <julian.honnen@vector.com> - initial API and implementation + * Remi Schnekenburger (EclipseSource) - Bug 568495 + * + *******************************************************************************/ +package org.eclipse.papyrus.toolsmiths.validation.profile.internal.checkers; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; + +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.papyrus.toolsmiths.validation.common.utils.MarkersService; +import org.eclipse.papyrus.toolsmiths.validation.profile.Activator; +import org.eclipse.pde.internal.core.PDECore; +import org.eclipse.pde.internal.core.builders.IncrementalErrorReporter; +import org.eclipse.pde.internal.core.builders.PDEMarkerFactory; + +/** + * Local copy of the {@link IncrementalErrorReporter} from PDE. + * <p> + * Overrides marker deletion to delete only some selected markers by id) + * </p> + */ +@SuppressWarnings("restriction") +public class SelectiveDeleteErrorReporter extends IncrementalErrorReporter { + + public static final String SOURCE_ID = "source_id"; + private final IResource fResource; + private final Collection<VirtualMarker> fReportedMarkers = new ArrayList<>(); + private int fErrorCount; + private final String sourceID; + + public SelectiveDeleteErrorReporter(IResource file, String sourceID) { + super(file); + fResource = file; + this.sourceID = sourceID; + } + + @Override + public VirtualMarker addMarker(String message, int lineNumber, int severity, int problemID, String category) { + + if (lineNumber == -1) { + lineNumber = 1; + } + + if (severity == IMarker.SEVERITY_ERROR) { + fErrorCount++; + } + + VirtualMarker marker = new VirtualMarker(); + marker.setAttribute(PDEMarkerFactory.PROBLEM_ID, problemID); + marker.setAttribute(PDEMarkerFactory.CAT_ID, category); + marker.setAttribute(IMarker.MESSAGE, message); + marker.setAttribute(IMarker.SEVERITY, severity); + marker.setAttribute(IMarker.LINE_NUMBER, lineNumber); + + fReportedMarkers.add(marker); + + return marker; + } + + @Override + public void applyMarkers() { + IMarker[] existingMarkers; + try { + // This seem to be for compatibility with some legacy code, + // PDE builders don't create markers with this type anymore + fResource.deleteMarkers(IMarker.PROBLEM, false, IResource.DEPTH_ZERO); + existingMarkers = fResource.findMarkers(PDEMarkerFactory.MARKER_ID, false, IResource.DEPTH_ZERO); + } catch (CoreException e) { + PDECore.logException(e); + // If we can't read existing, let delete them before we create new + existingMarkers = new IMarker[0]; + try { + fResource.deleteMarkers(PDEMarkerFactory.MARKER_ID, false, IResource.DEPTH_ZERO); + } catch (CoreException e1) { + PDECore.logException(e1); + } + } + + // iterate over existing markers to check which are resolved now + for (IMarker marker : existingMarkers) { + boolean resolved = true; + Map<String, Object> existingAttributes = null; + + // Iterate over new markers to filter out all we already know + for (Iterator<VirtualMarker> it = fReportedMarkers.iterator(); it.hasNext();) { + VirtualMarker reportedMarker = it.next(); + if (existingAttributes == null) { + try { + existingAttributes = marker.getAttributes(); + } catch (Exception e) { + PDECore.logException(e); + // assume the marker is not accessible, can be deleted + break; + } + } + // Same marker is found, no need to create again + if (reportedMarker.getAttributes().equals(existingAttributes)) { + resolved = false; + it.remove(); + break; + } + } + + // The marker was not reported again, the old one can be deleted + // this matchSource was added to allow for multiple passes of the xml parser + if (resolved && matchSource(marker)) { + try { + marker.delete(); + } catch (CoreException e) { + PDECore.logException(e); + } + } + } + + // Create only new markers + for (VirtualMarker reportedMarker : fReportedMarkers) { + try { + IMarker marker = MarkersService.createMarker(fResource, PDEMarkerFactory.MARKER_ID); + if (marker != null) { + marker.setAttributes(reportedMarker.getAttributes()); + } + } catch (CoreException e) { + PDECore.logException(e); + } + } + } + + private boolean matchSource(IMarker marker) { + String markerID = null; + try { + markerID = (String) marker.getAttribute(SOURCE_ID); + } catch (CoreException e) { + Activator.log.error(e); + } + return this.sourceID.equals(markerID); + } + + @Override + public int getErrorCount() { + return fErrorCount; + } + + +} diff --git a/plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.profile/src/org/eclipse/papyrus/toolsmiths/validation/profile/internal/checkers/StaticProfilePluginErrorReporter.java b/plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.profile/src/org/eclipse/papyrus/toolsmiths/validation/profile/internal/checkers/StaticProfilePluginErrorReporter.java new file mode 100644 index 00000000000..391338861ce --- /dev/null +++ b/plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.profile/src/org/eclipse/papyrus/toolsmiths/validation/profile/internal/checkers/StaticProfilePluginErrorReporter.java @@ -0,0 +1,455 @@ +/***************************************************************************** + * Copyright (c) 2020 CEA LIST, 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: + * Remi SChnekenburger (EclipseSource) - Initial API and implementation + * + *****************************************************************************/ +package org.eclipse.papyrus.toolsmiths.validation.profile.internal.checkers; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.resource.impl.URIMappingRegistryImpl; +import org.eclipse.emf.ecore.xmi.XMIResource; +import org.eclipse.osgi.util.NLS; +import org.eclipse.papyrus.toolsmiths.validation.profile.Activator; +import org.eclipse.papyrus.toolsmiths.validation.profile.internal.messages.Messages; +import org.eclipse.papyrus.uml.tools.model.UmlModel; +import org.eclipse.pde.internal.core.builders.CompilerFlags; +import org.eclipse.pde.internal.core.builders.IncrementalErrorReporter.VirtualMarker; +import org.eclipse.pde.internal.core.builders.PluginBaseErrorReporter; +import org.eclipse.pde.internal.core.builders.XMLErrorReporter; +import org.eclipse.uml2.uml.Profile; +import org.eclipse.uml2.uml.Stereotype; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +/** + * Error reporter for specific extensions for static profiles. + */ +@SuppressWarnings("restriction") +public class StaticProfilePluginErrorReporter extends PluginBaseErrorReporter { + + private static final String NAME = "name"; + + private static final String PLATFORM_PLUGIN = "platform:/plugin/"; + + private static final String LOCATION = "location"; + + private static final String ORG_ECLIPSE_UML2_UML_GENERATED_PACKAGE = "org.eclipse.uml2.uml.generated_package";//$NON-NLS-1$ + + private static final String ORG_ECLIPSE_EMF_ECORE_GENERATED_PACKAGE = "org.eclipse.emf.ecore.generated_package";//$NON-NLS-1$ + private static final String NS_URI = "nsURI"; //$NON-NLS-1$ + private static final String ECORE_EPACKAGE_STEREOTYPE = "Ecore::EPackage"; //$NON-NLS-1$ + + private static final String PAPYRUS_UML_PROFILES_EXTENSION_POINT_FULL_ID = "org.eclipse.papyrus.uml.extensionpoints.UMLProfile"; //$NON-NLS-1$ + private static final String STATIC_PROFILE_CATEGORY = "Papyrus-staticProfile";//$NON-NLS-1$ + + public static final String STATIC_PROFILE_MARKER_ATTRIBUTE = "staticProfile"; //$NON-NLS-1$ + + private static final String POINT = "point"; //$NON-NLS-1$ + + private static final int FIX_ID = 0; + + private static final String SEPARATOR = "_"; + + private final List<String> foundPoints = new ArrayList<>(); + + private final Map<String, String> localURIMappings = new HashMap<>(); + + private Profile profile; + private IFile profileFile; + + private String sourceID; + + private List<Element> papyrusProfileExtensions = new ArrayList<>(); + + /** + * Constructor. + * + * @param file + * the plugin.xml file + * @param profile + * the profile model element + * @param profileFile + * the profile containing file. + */ + public StaticProfilePluginErrorReporter(IFile file, Profile profile, IFile profileFile) { + super(file); + this.profile = profile; + this.fFile = file; + this.profileFile = profileFile; + sourceID = sourceID(profileFile, profile); + replaceReporter(this, file); + + } + + /** + * Replace the reporter created by default on abstract class, to implement our specific one. + * + * @see SelectiveDeleteErrorReporter. + */ + private void replaceReporter(StaticProfilePluginErrorReporter reporter, IFile file) { + Field errorReporterField; + try { + errorReporterField = XMLErrorReporter.class.getDeclaredField("fErrorReporter"); //$NON-NLS-1$ + errorReporterField.setAccessible(true); + Field modifiersField = Field.class.getDeclaredField("modifiers");//$NON-NLS-1$ + modifiersField.setAccessible(true); + modifiersField.setInt(modifiersField, modifiersField.getModifiers() & ~Modifier.FINAL); + errorReporterField.set(reporter, new SelectiveDeleteErrorReporter(file, sourceID)); + } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { + Activator.log.error(e); + } + + } + + /** + * Returns a unique id for the specified profile model element in the given profile file. + * + * @param file + * the profile file + * @param profile + * the profile model element + * @return the unique identifier for this profile in the plugin, which will allow to identify markers associated to this profile on the plugin.xml + */ + private static String sourceID(IFile file, Profile profile) { + StringBuilder builder = new StringBuilder(); + builder.append("staticprofile"); + builder.append(SEPARATOR); + builder.append(file.getProjectRelativePath().toString()); + builder.append(SEPARATOR); + String name = profile.getQualifiedName(); + builder.append(name); + return builder.toString(); + } + + @Override + protected String getRootElementName() { + return "plugin"; + } + + @Override + public void validate(IProgressMonitor monitor) { + super.validate(monitor); + + postValidatePapyrusExtensions(); + + // now check the found extensions and throw error for inexisting ones + if (!foundPoints.contains(ORG_ECLIPSE_EMF_ECORE_GENERATED_PACKAGE)) { + reportNoEcoreGeneratedPackage(); + } + if (!foundPoints.contains(ORG_ECLIPSE_UML2_UML_GENERATED_PACKAGE)) { + reportNoUML2GeneratedPackage(); + } + if (!foundPoints.contains(PAPYRUS_UML_PROFILES_EXTENSION_POINT_FULL_ID)) { + if (profile.getNestingPackage() == null) { + reportNoPapyrusProfile(); + } + } + } + + /** + * Validate the specific static profile extensions once the validation has visited all extensions in the plugin.xml file. + */ + private void postValidatePapyrusExtensions() { + if (profile.getNestingPackage() != null) { + return; + } + + String profilePath = profileFile.getProjectRelativePath().removeFileExtension().addFileExtension(UmlModel.UML_FILE_EXTENSION).toString(); + + for (Element papyrusProfileExtension : papyrusProfileExtensions) { + String name = papyrusProfileExtension.getAttribute(NAME); + String path = decodePath(papyrusProfileExtension.getAttribute("path")); + // check path + if (profilePath.equals(path)) { + foundPoints.add(PAPYRUS_UML_PROFILES_EXTENSION_POINT_FULL_ID); + // UI name shall not be null + if (name == null || name.isEmpty()) { + VirtualMarker marker = report(NLS.bind(Messages.StaticProfilePluginErrorReporter_uiLabelIsNull, profile.getLabel()), getLine(papyrusProfileExtension, NAME), CompilerFlags.WARNING, FIX_ID, papyrusProfileExtension, NAME, + STATIC_PROFILE_CATEGORY); + addMarkerAttribute(marker, STATIC_PROFILE_MARKER_ATTRIBUTE, profile.getLabel()); + } + } + } + } + + /** + * Reports that no ECore generated package was found for the current profile. + */ + private void reportNoEcoreGeneratedPackage() { + VirtualMarker marker = reportForProfile(NLS.bind(Messages.StaticProfilePluginErrorReporter_noEcoreGeneratedPackageFound, profile.getLabel()), 1, CompilerFlags.ERROR, STATIC_PROFILE_CATEGORY); + addMarkerAttribute(marker, STATIC_PROFILE_MARKER_ATTRIBUTE, profile.getLabel()); + } + + private VirtualMarker reportForProfile(String message, int line, int severity, String category) { + VirtualMarker marker = report(message, line, severity, category); + addMarkerID(marker); + return marker; + } + + private void addMarkerID(VirtualMarker marker) { + if (marker == null) { + return; + } + addMarkerAttribute(marker, SelectiveDeleteErrorReporter.SOURCE_ID, sourceID); + } + + /** + * Reports that no UML2 generated package was found for the current profile. + */ + private void reportNoUML2GeneratedPackage() { + VirtualMarker marker = reportForProfile(NLS.bind(Messages.StaticProfilePluginErrorReporter_noUML2GeneratedPackage, profile.getLabel()), 1, CompilerFlags.ERROR, STATIC_PROFILE_CATEGORY); + addMarkerAttribute(marker, STATIC_PROFILE_MARKER_ATTRIBUTE, profile.getLabel()); + } + + /** + * Reports that no Papyrus profile extension was found for the current profile. + */ + private void reportNoPapyrusProfile() { + VirtualMarker marker = reportForProfile(NLS.bind(Messages.StaticProfilePluginErrorReporter_NoPapyrusProfileExtensionFound, profile.getLabel()), 1, CompilerFlags.WARNING, STATIC_PROFILE_CATEGORY); + addMarkerAttribute(marker, STATIC_PROFILE_MARKER_ATTRIBUTE, profile.getLabel()); + } + + @Override + protected void validateExtension(Element element) { + // do not let default validation be done, this will be done by standard plugin builder + String pointID = element.getAttribute(POINT); + // find the correct checker + switch (pointID) { + case ORG_ECLIPSE_EMF_ECORE_GENERATED_PACKAGE: + if (foundPoints.contains(ORG_ECLIPSE_EMF_ECORE_GENERATED_PACKAGE)) { + // already found the extension that should be checked, so avoid checking others. + break; + } + // check if this is the correct profile to test + checkEcoreGeneratedPackage(element); + break; + case ORG_ECLIPSE_UML2_UML_GENERATED_PACKAGE: + if (foundPoints.contains(ORG_ECLIPSE_UML2_UML_GENERATED_PACKAGE)) { + break; + } + checkUML2GeneratedPackage(element); + break; + case PAPYRUS_UML_PROFILES_EXTENSION_POINT_FULL_ID: + if (foundPoints.contains(PAPYRUS_UML_PROFILES_EXTENSION_POINT_FULL_ID)) { + break; + } + checkPapyrusProfile(element); + break; + case "org.eclipse.emf.ecore.uri_mapping": + collectMapping(element); + break; + default: + break; + } + + } + + private void collectMapping(Element element) { + for (int i = 0; i < element.getChildNodes().getLength(); i++) { + Node node = element.getChildNodes().item(i); + if (node instanceof Element && "mapping".equals(((Element) node).getNodeName())) {//$NON-NLS-1$ + Element profileElement = (Element) node; + String sourceURI = profileElement.getAttribute("source");//$NON-NLS-1$ + String targetURI = profileElement.getAttribute("target");//$NON-NLS-1$ + localURIMappings.put(sourceURI, targetURI); + } + } + } + + private void checkPapyrusProfile(Element element) { + /* + * <extension point="org.eclipse.papyrus.uml.extensionpoints.UMLProfile"> + * <profile + * description="UML profile for SysML (from OMG SysML V1.4)" + * iconpath="resources/icons/SysMLProfile.gif" + * name="SysML 1.4" + * path="pathmap://SysML14_PROFILES/SysML.profile.uml" + * provider="Eclipse Modeling Project"> + * </profile> + * </extension> + */ + // should check only for root profiles + if (profile.getNestingPackage() != null) { + return; + } + + for (int i = 0; i < element.getChildNodes().getLength(); i++) { + Node node = element.getChildNodes().item(i); + if (node instanceof Element && "profile".equals(((Element) node).getNodeName())) {//$NON-NLS-1$ + papyrusProfileExtensions.add((Element) node); + } + } + + } + + private void checkUML2GeneratedPackage(Element element) { + /* + * <extension point="org.eclipse.uml2.uml.generated_package"> + * <profile uri="http://www.eclipse.org/papyrus/sysml/1.4/SysML" + * location="pathmap://SysML14_PROFILES/SysML.profile.uml#SysML"/> + * <profile + * location="pathmap://SysML14_PROFILES/SysML.profile.uml#SysML.package_packagedElement_Activities" + * uri="http://www.eclipse.org/papyrus/sysml/1.4/SysML/Activities"> + * </profile> + */ + for (int i = 0; i < element.getChildNodes().getLength(); i++) { + Node node = element.getChildNodes().item(i); + if (node instanceof Element && "profile".equals(((Element) node).getNodeName())) { //$NON-NLS-1$ + Element profileElement = (Element) node; + String extensionNsURI = profileElement.getAttribute("uri");//$NON-NLS-1$ + String extensionlocation = profileElement.getAttribute(LOCATION); + + // check this is the good extension point => genmodel should match first + String stereotypeNsURI = profileURI(profile); + if (Objects.equals(stereotypeNsURI, extensionNsURI)) { + foundPoints.add(ORG_ECLIPSE_UML2_UML_GENERATED_PACKAGE); + + final String profileId = ((XMIResource) profile.eResource()).getID(profile); + String uml2ProfileFile = profileFile.getProjectRelativePath().removeFileExtension().addFileExtension(UmlModel.UML_FILE_EXTENSION).lastSegment(); + + if (!extensionlocation.endsWith(uml2ProfileFile + "#" + profileId)) { //$NON-NLS-1$ + VirtualMarker marker = reportForProfile(NLS.bind(Messages.StaticProfilePluginErrorReporter_wrongLocationForProfile, profile.getLabel()), getLine(profileElement, LOCATION), CompilerFlags.ERROR, FIX_ID, profileElement, LOCATION, + STATIC_PROFILE_CATEGORY); + addMarkerAttribute(marker, STATIC_PROFILE_MARKER_ATTRIBUTE, profile.getLabel()); + } + } + } + } + + } + + private VirtualMarker reportForProfile(String message, int line, int severity, int fixId, Element element, String attrName, + String category) { + VirtualMarker marker = report(message, line, severity, fixId, element, attrName, category); + addMarkerID(marker); + return marker; + } + + private void checkEcoreGeneratedPackage(Element element) { + for (int i = 0; i < element.getChildNodes().getLength(); i++) { + Node node = element.getChildNodes().item(i); + if (node instanceof Element && "package".equals(((Element) node).getNodeName())) {//$NON-NLS-1$ + Element packageElement = (Element) node; + String extensionNsURI = packageElement.getAttribute("uri");//$NON-NLS-1$ + String extensionGenModel = packageElement.getAttribute("genModel");//$NON-NLS-1$ + + // check this is the good extension point => genmodel should match first + if (extensionGenModel == null) { + return; + } + + // retrieve profile file path and compare with given path in extension point + IPath projectRelativePath = profileFile.getProjectRelativePath(); + String genModelFile = projectRelativePath.removeFileExtension().addFileExtension("genmodel").toString(); //$NON-NLS-1$ + // compare with genmodel + if (!Objects.equals(genModelFile, extensionGenModel)) { + // this one did not match, return; + return; + } + + // compare URI with profile uri in stereotype ecore::epackage + String stereotypeNsURI = profileURI(profile); + if (Objects.equals(stereotypeNsURI, extensionNsURI)) { + foundPoints.add(ORG_ECLIPSE_EMF_ECORE_GENERATED_PACKAGE); + } + } + } + } + + private static String profileURI(Profile profile) { + Stereotype ePackageSt = profile.getAppliedStereotype(ECORE_EPACKAGE_STEREOTYPE); + if (ePackageSt != null) { + Object value = profile.getValue(ePackageSt, NS_URI); + return (String) value; + + } + return null; + } + + private String decodePath(String path) { + if (path == null) { + return null; + } + + // check pathmap, relative URI or platform based uri + if (path.startsWith("pathmap://")) { + // try to decode using uri mappers extensions + return decodePathmapPath(path); + } else if (path.startsWith(PLATFORM_PLUGIN)) { + // check if path is valid within the plugin + return decodePlatformPath(path); + } + + // relative path? + return path; + } + + private String decodePlatformPath(String path) { + return cutPluginPath(path); + } + + private String decodePathmapPath(String path) { + String decodePath = null; + // check first local mappings + for (Entry<String, String> entry : localURIMappings.entrySet()) { + String sourceURI = entry.getKey().toString(); + if (path.startsWith(sourceURI)) { + String targetURI = entry.getValue(); + decodePath = replaceString(path, sourceURI, targetURI); + return cutPluginPath(decodePath); + } + } + for (Entry<URI, URI> entry : URIMappingRegistryImpl.INSTANCE.entrySet()) { + String sourceURI = entry.getKey().toString(); + if (path.startsWith(sourceURI)) { + String targetURI = entry.getValue().toString(); + decodePath = replaceString(path, sourceURI, targetURI); + return cutPluginPath(decodePath); + } + } + + // cut platform:/plugin/<profile-name> to get a project relative path + return path; + } + + private String cutPluginPath(String decodePath) { + if (decodePath.startsWith(PLATFORM_PLUGIN)) { + String cutPath = decodePath.substring(PLATFORM_PLUGIN.length()); + int index = cutPath.indexOf('/'); + cutPath = cutPath.substring(index + 1); // remove initial '/' + return cutPath; + } + return decodePath; + } + + private String replaceString(String path, String sourceURI, String targetURI) { + String newPath = path.substring(sourceURI.length(), path.length()); + if (!targetURI.endsWith("/")) { + newPath = "/".concat(newPath); + } + newPath = targetURI.concat(newPath); + return newPath; + } + +} diff --git a/plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.profile/src/org/eclipse/papyrus/toolsmiths/validation/profile/internal/messages/Messages.java b/plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.profile/src/org/eclipse/papyrus/toolsmiths/validation/profile/internal/messages/Messages.java new file mode 100644 index 00000000000..8e7f7931a0b --- /dev/null +++ b/plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.profile/src/org/eclipse/papyrus/toolsmiths/validation/profile/internal/messages/Messages.java @@ -0,0 +1,38 @@ +/***************************************************************************** + * Copyright (c) 2020 CEA LIST, 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: + * Remi Schnekenburger (EclipseSource) - Initial API and implementation + * + *****************************************************************************/ +package org.eclipse.papyrus.toolsmiths.validation.profile.internal.messages; + +import org.eclipse.osgi.util.NLS; + +/** + * Messages internationalization + */ +public class Messages extends NLS { + private static final String BUNDLE_NAME = "org.eclipse.papyrus.toolsmiths.validation.profile.internal.messages.messages"; //$NON-NLS-1$ + public static String StaticProfileExtensionsBuilder_nsURI_differs; + public static String StaticProfileExtensionsBuilder_subTask_checkingFile; + public static String StaticProfilePluginErrorReporter_noEcoreGeneratedPackageFound; + public static String StaticProfilePluginErrorReporter_NoPapyrusProfileExtensionFound; + public static String StaticProfilePluginErrorReporter_noUML2GeneratedPackage; + public static String StaticProfilePluginErrorReporter_uiLabelIsNull; + public static String StaticProfilePluginErrorReporter_wrongLocationForProfile; + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + private Messages() { + } +} diff --git a/plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.profile/src/org/eclipse/papyrus/toolsmiths/validation/profile/internal/messages/messages.properties b/plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.profile/src/org/eclipse/papyrus/toolsmiths/validation/profile/internal/messages/messages.properties new file mode 100644 index 00000000000..8083ca69eda --- /dev/null +++ b/plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.profile/src/org/eclipse/papyrus/toolsmiths/validation/profile/internal/messages/messages.properties @@ -0,0 +1,7 @@ +StaticProfileExtensionsBuilder_nsURI_differs=NsURI in profile {0} and in extension point {1} differ +StaticProfileExtensionsBuilder_subTask_checkingFile=checking file: {0} +StaticProfilePluginErrorReporter_noEcoreGeneratedPackageFound=No Ecore generated Package found for profile {0} +StaticProfilePluginErrorReporter_NoPapyrusProfileExtensionFound=No Papyrus extension declaration found for profile {0} +StaticProfilePluginErrorReporter_noUML2GeneratedPackage=No UML2 generated Package found for {0} +StaticProfilePluginErrorReporter_uiLabelIsNull=UI Label is null for the profile {0} +StaticProfilePluginErrorReporter_wrongLocationForProfile=Wrong location for profile {0} diff --git a/releng/Papyrus - All.launch b/releng/Papyrus - All.launch index f5935ec336a..5de5d5ec513 100644 --- a/releng/Papyrus - All.launch +++ b/releng/Papyrus - All.launch @@ -15,5 +15,7 @@ <booleanAttribute key="M2_UPDATE_SNAPSHOTS" value="false"/> <stringAttribute key="M2_USER_SETTINGS" value=""/> <booleanAttribute key="M2_WORKSPACE_RESOLUTION" value="false"/> +<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_CLASSPATH_ONLY_JAR" value="false"/> +<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11"/> <stringAttribute key="org.eclipse.jdt.launching.WORKING_DIRECTORY" value="${project_loc:releng}"/> </launchConfiguration> diff --git a/tests/junit/plugins/toolsmiths/org.eclipse.papyrus.toolsmiths.validation.elementtypes.tests/src/org/eclipse/papyrus/toolsmiths/validation/elementtypes/tests/ElementTypesPluginValidationTest.java b/tests/junit/plugins/toolsmiths/org.eclipse.papyrus.toolsmiths.validation.elementtypes.tests/src/org/eclipse/papyrus/toolsmiths/validation/elementtypes/tests/ElementTypesPluginValidationTest.java index 63e4dca7f24..22d97389f6d 100644 --- a/tests/junit/plugins/toolsmiths/org.eclipse.papyrus.toolsmiths.validation.elementtypes.tests/src/org/eclipse/papyrus/toolsmiths/validation/elementtypes/tests/ElementTypesPluginValidationTest.java +++ b/tests/junit/plugins/toolsmiths/org.eclipse.papyrus.toolsmiths.validation.elementtypes.tests/src/org/eclipse/papyrus/toolsmiths/validation/elementtypes/tests/ElementTypesPluginValidationTest.java @@ -84,12 +84,12 @@ public class ElementTypesPluginValidationTest extends AbstractPapyrusTest { // Now check the markers Assert.assertNotNull("The markers have to be found", markers); //$NON-NLS-1$ - Assert.assertEquals("The number of markers is not correct", 10, markers.size()); //$NON-NLS-1$ + Assert.assertEquals("The number of markers is not correct", 11, markers.size()); //$NON-NLS-1$ // Check the elementtypesconfigurations file markers final List<IMarker> elementtypesFileMarkers = markers.stream().filter(marker -> marker.getResource().getFullPath().toString().endsWith("BookStore.elementtypesconfigurations")).collect(Collectors.toList()); //$NON-NLS-1$ Assert.assertNotNull("Elementtypesconfigurations file markers are not found", elementtypesFileMarkers); //$NON-NLS-1$ - Assert.assertEquals("The number of markers for elementtypesconfigurations file is not correct", 1, elementtypesFileMarkers.size()); //$NON-NLS-1$ + Assert.assertEquals("The number of markers for elementtypesconfigurations file is not correct", 2, elementtypesFileMarkers.size()); //$NON-NLS-1$ Assert.assertTrue("The severity of elementtypesconfigurations marker is not correct", isMarkerSeverity(elementtypesFileMarkers.get(0), IMarker.SEVERITY_ERROR)); //$NON-NLS-1$ // Check the dependencies markers |