Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian W. Damus2020-12-11 20:50:56 +0000
committerChristian W. Damus2020-12-11 20:50:56 +0000
commit15b20e90540e9d3c7fc1022a0aae492a61d579ee (patch)
tree39842b17be869e7eec531643e36c87ab460d91d2
parent21127e8b971970df64664cf8b5317c2ece7b55bd (diff)
downloadorg.eclipse.papyrus-15b20e90540e9d3c7fc1022a0aae492a61d579ee.tar.gz
org.eclipse.papyrus-15b20e90540e9d3c7fc1022a0aae492a61d579ee.tar.xz
org.eclipse.papyrus-15b20e90540e9d3c7fc1022a0aae492a61d579ee.zip
Bug 569357: [Toolsmiths] ElementTypes: Model and Plug-in Validation
- handle extraction of bundle dependency name from bundleresource:// URIs - aupport aggregation of the same diagnostic from different sources into a single marker https://bugs.eclipse.org/bugs/show_bug.cgi?id=569357 Signed-off-by: Christian W. Damus <give.a.damus@gmail.com> Change-Id: Id284fafe7d33c06628becfe13fec6144ac81dfc0
-rw-r--r--plugins/toolsmiths/builder/org.eclipse.papyrus.toolsmiths.plugin.builder/src/org/eclipse/papyrus/toolsmiths/plugin/builder/PluginCheckerBuilder.java11
-rw-r--r--plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.common/src/org/eclipse/papyrus/toolsmiths/validation/common/checkers/AbstractPluginChecker.java10
-rw-r--r--plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.common/src/org/eclipse/papyrus/toolsmiths/validation/common/checkers/IPluginChecker2.java196
-rw-r--r--plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.common/src/org/eclipse/papyrus/toolsmiths/validation/common/checkers/ModelDependenciesChecker.java56
-rw-r--r--plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.common/src/org/eclipse/papyrus/toolsmiths/validation/common/utils/CheckerDiagnosticChain.java113
-rw-r--r--plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.common/src/org/eclipse/papyrus/toolsmiths/validation/common/utils/MarkersService.java2
6 files changed, 371 insertions, 17 deletions
diff --git a/plugins/toolsmiths/builder/org.eclipse.papyrus.toolsmiths.plugin.builder/src/org/eclipse/papyrus/toolsmiths/plugin/builder/PluginCheckerBuilder.java b/plugins/toolsmiths/builder/org.eclipse.papyrus.toolsmiths.plugin.builder/src/org/eclipse/papyrus/toolsmiths/plugin/builder/PluginCheckerBuilder.java
index d698ad0c8e5..64e49f148f9 100644
--- a/plugins/toolsmiths/builder/org.eclipse.papyrus.toolsmiths.plugin.builder/src/org/eclipse/papyrus/toolsmiths/plugin/builder/PluginCheckerBuilder.java
+++ b/plugins/toolsmiths/builder/org.eclipse.papyrus.toolsmiths.plugin.builder/src/org/eclipse/papyrus/toolsmiths/plugin/builder/PluginCheckerBuilder.java
@@ -21,6 +21,7 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
+import java.util.stream.Stream;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
@@ -29,13 +30,13 @@ import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.SubMonitor;
-import org.eclipse.emf.common.util.BasicDiagnostic;
import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.common.util.DiagnosticChain;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.papyrus.toolsmiths.validation.common.checkers.DiagnosticEquivalence;
import org.eclipse.papyrus.toolsmiths.validation.common.checkers.IPluginChecker2;
+import org.eclipse.papyrus.toolsmiths.validation.common.utils.CheckerDiagnosticChain;
import org.eclipse.papyrus.toolsmiths.validation.common.utils.MarkersService;
import com.google.common.collect.ListMultimap;
@@ -117,7 +118,7 @@ public class PluginCheckerBuilder extends AbstractPapyrusBuilder {
// And one more for actually creating problem markers.
SubMonitor subMonitor = SubMonitor.convert(monitor, (1 + sets.keySet().size()) * checkerFactories.size() + 1);
- BasicDiagnostic diagnostics = new BasicDiagnostic();
+ CheckerDiagnosticChain diagnostics = new CheckerDiagnosticChain();
// First, see about checking the project as a whole
check(builtProject, null, null, diagnostics, subMonitor);
@@ -130,7 +131,7 @@ public class PluginCheckerBuilder extends AbstractPapyrusBuilder {
// Create markers if the validation is not OK
if (diagnostics.getSeverity() > Diagnostic.OK) {
- wrap(diagnostics).getChildren().stream().distinct().forEach(this::createMarker);
+ wrap(diagnostics.stream()).distinct().forEach(this::createMarker);
}
subMonitor.worked(1);
@@ -189,4 +190,8 @@ public class PluginCheckerBuilder extends AbstractPapyrusBuilder {
: diagnosticEquivalence.wrap(diagnostic);
}
+ private Stream<Diagnostic> wrap(Stream<Diagnostic> diagnostics) {
+ return diagnostics.map(this::wrap);
+ }
+
}
diff --git a/plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.common/src/org/eclipse/papyrus/toolsmiths/validation/common/checkers/AbstractPluginChecker.java b/plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.common/src/org/eclipse/papyrus/toolsmiths/validation/common/checkers/AbstractPluginChecker.java
index 4699ce1c29f..c044fef3a59 100644
--- a/plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.common/src/org/eclipse/papyrus/toolsmiths/validation/common/checkers/AbstractPluginChecker.java
+++ b/plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.common/src/org/eclipse/papyrus/toolsmiths/validation/common/checkers/AbstractPluginChecker.java
@@ -17,6 +17,7 @@ package org.eclipse.papyrus.toolsmiths.validation.common.checkers;
import java.util.List;
import java.util.Optional;
+import java.util.stream.Stream;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
@@ -25,6 +26,7 @@ import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.emf.common.util.BasicDiagnostic;
import org.eclipse.emf.common.util.Diagnostic;
+import org.eclipse.papyrus.toolsmiths.validation.common.utils.CheckerDiagnosticChain;
import org.eclipse.papyrus.toolsmiths.validation.common.utils.MarkersService;
import com.google.common.collect.Lists;
@@ -78,13 +80,13 @@ public abstract class AbstractPluginChecker implements IPluginChecker, IPluginCh
@Override
public void check(IProgressMonitor monitor) {
- BasicDiagnostic diagnostics = new BasicDiagnostic();
+ CheckerDiagnosticChain diagnostics = new CheckerDiagnosticChain(diagnosticEquivalence);
check(diagnostics, monitor);
// Create markers if the validation is not OK
if (diagnostics.getSeverity() > Diagnostic.OK) {
- wrap(diagnostics).getChildren().stream().distinct().forEach(this::createMarker);
+ wrap(diagnostics.stream()).distinct().forEach(this::createMarker);
}
}
@@ -129,4 +131,8 @@ public abstract class AbstractPluginChecker implements IPluginChecker, IPluginCh
: diagnosticEquivalence.wrap(diagnostic);
}
+ private Stream<Diagnostic> wrap(Stream<Diagnostic> diagnostics) {
+ return diagnostics.map(this::wrap);
+ }
+
}
diff --git a/plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.common/src/org/eclipse/papyrus/toolsmiths/validation/common/checkers/IPluginChecker2.java b/plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.common/src/org/eclipse/papyrus/toolsmiths/validation/common/checkers/IPluginChecker2.java
index 3c2b062d1f6..0341f72a5e0 100644
--- a/plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.common/src/org/eclipse/papyrus/toolsmiths/validation/common/checkers/IPluginChecker2.java
+++ b/plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.common/src/org/eclipse/papyrus/toolsmiths/validation/common/checkers/IPluginChecker2.java
@@ -15,10 +15,14 @@
package org.eclipse.papyrus.toolsmiths.validation.common.checkers;
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
@@ -30,6 +34,7 @@ import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.common.util.DiagnosticChain;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.osgi.util.NLS;
import org.eclipse.papyrus.infra.emf.utils.ResourceUtils;
import org.eclipse.papyrus.toolsmiths.validation.common.Activator;
@@ -167,6 +172,69 @@ public interface IPluginChecker2 {
return new MarkerAttribute(IMarker.LOCATION, location);
}
+ /**
+ * Create a dynamic message argument to be plugged into the message when creating a marker from the diagnostic.
+ * The power of dynamic messages comes from how they are accumulated: when appending a diagnostic that has dynamic
+ * message arguments to a {@code DiagnosticChain}, the message arguments will be merged with an existing dynamic-message
+ * diagnostic that represents the same problem (is otherwise equivalent). Of course, this only works with the
+ * diagnostic chain that is passed by the <em>Plugin Builder</em> and to its checkers.
+ *
+ * @param index
+ * the message parameter index to fill
+ * @param value
+ * the value to fill it with
+ *
+ * @return the dynamic message argument
+ */
+ static DynamicMessageArgument dynamicMessageArgument(int index, Object value) {
+ return new DynamicMessageArgument(index, value);
+ }
+
+ /**
+ * Query whether a {@code diagnostic} has a "dynamic message" that is composed from its
+ * arguments at the time of creating the marker.
+ *
+ * @param diagnostic
+ * a diagnostic
+ * @return whether it has any {@linkplain #dynamicMessageArgument(int, Object) dynamic message arguments}
+ *
+ * @see #dynamicMessageArgument(int, Object)
+ */
+ static boolean hasDynamicMessage(Diagnostic diagnostic) {
+ List<?> data = diagnostic.getData();
+ return data != null && data.stream().anyMatch(DynamicMessageArgument.class::isInstance);
+ }
+
+ /**
+ * Compose the "dynamic message" from a {@code diagnostic}.
+ *
+ * @param diagnostic
+ * a diagnostic that {@linkplain #hasDynamicMessage(Diagnostic) has a dynamic message}
+ * @return the dynamic message
+ *
+ * @see #hasDynamicMessage(Diagnostic)
+ * @see #dynamicMessageArgument(int, Object)
+ */
+ static String getDynamicMessage(Diagnostic diagnostic) {
+ Object[] args = DynamicMessageArgument.stream(diagnostic)
+ .map(DynamicMessageArgument::getValueAsString)
+ .toArray();
+ return NLS.bind(diagnostic.getMessage(), args);
+ }
+
+ /**
+ * Get the message of a {@code diagnostic}, whether it is a {@linkplain Diagnostic#getMessage() static message}
+ * or a {@linkplain #getDynamicMessage(Diagnostic) dynamic message}. Whichever it is, it will be the message
+ * that should be stored in the marker.
+ *
+ * @param diagnostic
+ * a diagnostic
+ * @return its message
+ */
+ static String getMessage(Diagnostic diagnostic) {
+ return hasDynamicMessage(diagnostic) ? getDynamicMessage(diagnostic) : diagnostic.getMessage();
+ }
+
//
// Nested types
//
@@ -392,4 +460,132 @@ public interface IPluginChecker2 {
}
+ /**
+ * <p>
+ * A token to put in the {@linkplain Diagnostic#getData() data list} of a {@link Diagnostic} to specify a positional
+ * argument in a dynamic message that is composed when creating the marker from the message pattern.
+ * </p>
+ * <p>
+ * The values of dynamic message arguments are ignored in the comparison of {@link Diagnostic}s because all values
+ * in the same position from diagnostics of the same message pattern and severity (and other attributes) are combined
+ * when generating the message for the marker.
+ * </p>
+ */
+ static final class DynamicMessageArgument implements Comparable<DynamicMessageArgument> {
+ private final int index;
+ private Object value;
+
+ /**
+ * Create a new positional dynamic message argument.
+ *
+ * @param index
+ * the index of the message parameter to substitute
+ * @param value
+ * the value to substitute for that parameter
+ */
+ public DynamicMessageArgument(int index, Object value) {
+ this.index = index;
+ this.value = value;
+ }
+
+ public int getIndex() {
+ return index;
+ }
+
+ public Object getValue() {
+ return value;
+ }
+
+ public String getValueAsString() {
+ if (value == null) {
+ return ""; //$NON-NLS-1$
+ }
+ if (value instanceof Collection) {
+ return ((Collection<?>) value).stream()
+ .filter(Objects::nonNull)
+ .map(Object::toString)
+ .collect(Collectors.joining(", ")); //$NON-NLS-1$
+ }
+ return String.valueOf(value);
+ }
+
+ public void merge(DynamicMessageArgument argument) {
+ if (argument.getIndex() != getIndex()) {
+ throw new IllegalArgumentException("attempt to merge arguments at different positions"); //$NON-NLS-1$
+ }
+
+ if (this.value == null) {
+ this.value = safeCopy(argument.getValue());
+ } else if (this.value instanceof Collection<?>) {
+ @SuppressWarnings("unchecked")
+ Collection<Object> collection = (Collection<Object>) this.value;
+ merge(collection, safeCopy(argument.getValue()));
+ } else {
+ Collection<Object> collection = new ArrayList<>();
+ collection.add(this.value);
+ merge(collection, safeCopy(argument.getValue()));
+ this.value = collection;
+ }
+ }
+
+ private Object safeCopy(Object value) {
+ if (value instanceof Collection) {
+ return List.copyOf((Collection<?>) value);
+ }
+
+ return value;
+ }
+
+ private void merge(Collection<Object> collection, Object value) {
+ if (value == null) {
+ // Pass
+ } else if (value instanceof Collection) {
+ collection.addAll((Collection<?>) value);
+ } else {
+ collection.add(value);
+ }
+ }
+
+ @Override
+ public int compareTo(DynamicMessageArgument o) {
+ return Integer.compare(getIndex(), o.getIndex());
+ }
+
+ @Override
+ public int hashCode() {
+ return index;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof DynamicMessageArgument)) {
+ return false;
+ }
+ DynamicMessageArgument other = (DynamicMessageArgument) obj;
+ return other.getIndex() == getIndex();
+ }
+
+ @Override
+ public String toString() {
+ return String.format("{%d}=\"%s\"", index, value);
+ }
+
+ /**
+ * Obtain a stream over the dynamic message arguments of a {@code diagnostic}, in position order.
+ *
+ * @param diagnostic
+ * a diagnostic
+ * @return its dynamic message arguments, in order
+ */
+ public static Stream<DynamicMessageArgument> stream(Diagnostic diagnostic) {
+ return diagnostic.getData().stream()
+ .filter(DynamicMessageArgument.class::isInstance).map(DynamicMessageArgument.class::cast)
+ .sorted();
+ }
+
+ }
+
}
diff --git a/plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.common/src/org/eclipse/papyrus/toolsmiths/validation/common/checkers/ModelDependenciesChecker.java b/plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.common/src/org/eclipse/papyrus/toolsmiths/validation/common/checkers/ModelDependenciesChecker.java
index d2e2b8d9426..51e2cb5986a 100644
--- a/plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.common/src/org/eclipse/papyrus/toolsmiths/validation/common/checkers/ModelDependenciesChecker.java
+++ b/plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.common/src/org/eclipse/papyrus/toolsmiths/validation/common/checkers/ModelDependenciesChecker.java
@@ -17,12 +17,15 @@
package org.eclipse.papyrus.toolsmiths.validation.common.checkers;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
import java.util.function.ToIntFunction;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import org.eclipse.core.filebuffers.FileBuffers;
import org.eclipse.core.filebuffers.ITextFileBuffer;
@@ -47,6 +50,7 @@ import org.eclipse.papyrus.toolsmiths.validation.common.utils.ProjectManagementS
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.Bundle;
import org.osgi.framework.Constants;
/**
@@ -56,6 +60,9 @@ import org.osgi.framework.Constants;
@SuppressWarnings("restriction")
public class ModelDependenciesChecker extends AbstractPluginChecker {
+ /** The URI scheme for Equinox's internal <tt>bundleresource://</tt> URIs. */
+ private static final String BUNDLE_RESOURCE_SCHEME = "bundleresource"; //$NON-NLS-1$
+
/**
* The EMF model resource.
*/
@@ -67,6 +74,9 @@ public class ModelDependenciesChecker extends AbstractPluginChecker {
private final List<Function<? super Resource, ? extends Collection<String>>> additionalDependencyFunctions = new ArrayList<>();
+ /** Regex to parse the bundle ID out of a URI of <tt>bundleresource:</tt> scheme. */
+ private final Pattern bundleResourceAuthorityPattern = Pattern.compile("^\\d+"); //$NON-NLS-1$
+
/**
* Initializes me to report all missing bundle dependencies as errors.
*
@@ -189,7 +199,7 @@ public class ModelDependenciesChecker extends AbstractPluginChecker {
if (!requiredPlugins.isEmpty()) {
requiredPlugins.stream().forEach(requiredPlugin -> {
int severity = severityFunction.applyAsInt(requiredPlugin);
- errors.add(new ManifestError(getMarkerType(), "The plug-in '" + requiredPlugin + "' must be declared as required plug-in (for '" + resourceName + "').", severity, Constants.REQUIRE_BUNDLE));
+ errors.add(new ManifestError(getMarkerType(), "The plug-in ''" + requiredPlugin + "'' must be declared as required plug-in (for ''{0}'').", severity, Constants.REQUIRE_BUNDLE, resourceName));
});
reportErrors(diagnostics, errors);
}
@@ -209,7 +219,7 @@ public class ModelDependenciesChecker extends AbstractPluginChecker {
final IFile manifestFile = ProjectManagementService.getManifestFile(getProject());
BundleModel textBundleModel = prepareTextBundleModel(manifestFile);
errors.stream().forEach(error -> {
- reportBundleError(diagnostics, manifestFile, textBundleModel, error.type, error.message, error.severity, error.header);
+ reportBundleError(diagnostics, manifestFile, textBundleModel, error.type, error.message, error.severity, error.header, error.dependentName);
});
}
}
@@ -229,14 +239,22 @@ public class ModelDependenciesChecker extends AbstractPluginChecker {
* the severity of the marker to create
* @param header
* the header entry of the manifest file on which marker is created.
+ * @param sourceName
+ * the name of the source of the problem, e.g. the file or whatever that implies the problem
*/
- private void reportBundleError(DiagnosticChain diagnostics, IFile manifestFile, BundleModel textBundleModel, String type, String message, int severity, String header) {
+ private void reportBundleError(DiagnosticChain diagnostics, IFile manifestFile, BundleModel textBundleModel, String type, String message, int severity, String header, String sourceName) {
Diagnostic diagnostic;
if (textBundleModel != null) {
- diagnostic = createDiagnostic(manifestFile, severity, 0, message,
- IPluginChecker2.markerType(type),
- IPluginChecker2.lineNumber(getLineNumber(textBundleModel.getBundle().getManifestHeader(header))));
+ List<Object> data = new ArrayList<>(Arrays.asList(IPluginChecker2.markerType(type),
+ IPluginChecker2.lineNumber(getLineNumber(textBundleModel.getBundle().getManifestHeader(header)))));
+
+ if (sourceName != null) {
+ // All source names for the same dependency are collected and dynamically injected into the diagnostic message
+ data.add(IPluginChecker2.dynamicMessageArgument(0, sourceName));
+ }
+
+ diagnostic = createDiagnostic(manifestFile, severity, 0, message, data.toArray());
} else {
diagnostic = createDiagnostic(manifestFile, severity, 0, message);
@@ -381,10 +399,24 @@ public class ModelDependenciesChecker extends AbstractPluginChecker {
private String getPluginNameFromURI(final URI uri) {
String pluginName = null;
- // Take we correct segment (without authority)
- final int takenSegment = uri.hasAuthority() ? 0 : 1;
- if (uri.segmentCount() > takenSegment) {
- pluginName = uri.segment(takenSegment);
+ if ((uri.isPlatformPlugin() || uri.isPlatformResource()) && uri.segmentCount() > 1) {
+ pluginName = uri.segment(1);
+ } else if (BUNDLE_RESOURCE_SCHEME.equals(uri.scheme()) && uri.hasAuthority()) {
+ Bundle bundle = null;
+ Matcher m = bundleResourceAuthorityPattern.matcher(uri.authority());
+ if (m.find()) {
+ long bundleID = Long.parseLong(m.group());
+ bundle = Activator.getDefault().getBundle().getBundleContext().getBundle(bundleID);
+ }
+ if (bundle != null) {
+ pluginName = bundle.getSymbolicName();
+ }
+ } else {
+ // Best guess. Take the correct segment (without authority)
+ final int takenSegment = uri.hasAuthority() ? 0 : 1;
+ if (uri.segmentCount() > takenSegment) {
+ pluginName = uri.segment(takenSegment);
+ }
}
return pluginName;
@@ -435,12 +467,14 @@ public class ModelDependenciesChecker extends AbstractPluginChecker {
private final String message;
private final int severity;
private final String header;
+ private final String dependentName;
- public ManifestError(String type, String message, int severity, String header) {
+ ManifestError(String type, String message, int severity, String header, String dependentName) {
this.type = type;
this.message = message;
this.severity = severity;
this.header = header;
+ this.dependentName = dependentName;
}
}
}
diff --git a/plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.common/src/org/eclipse/papyrus/toolsmiths/validation/common/utils/CheckerDiagnosticChain.java b/plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.common/src/org/eclipse/papyrus/toolsmiths/validation/common/utils/CheckerDiagnosticChain.java
new file mode 100644
index 00000000000..39cc4b4e13a
--- /dev/null
+++ b/plugins/toolsmiths/validation/org.eclipse.papyrus.toolsmiths.validation.common/src/org/eclipse/papyrus/toolsmiths/validation/common/utils/CheckerDiagnosticChain.java
@@ -0,0 +1,113 @@
+/*****************************************************************************
+ * Copyright (c) 2020 CEA LIST and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * CEA LIST - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.toolsmiths.validation.common.utils;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+import org.eclipse.emf.common.util.Diagnostic;
+import org.eclipse.emf.common.util.DiagnosticChain;
+import org.eclipse.papyrus.toolsmiths.validation.common.checkers.DiagnosticEquivalence;
+import org.eclipse.papyrus.toolsmiths.validation.common.checkers.IPluginChecker2;
+import org.eclipse.papyrus.toolsmiths.validation.common.checkers.IPluginChecker2.DynamicMessageArgument;
+
+/**
+ * A diagnostic chain implementation that supports the merging of {@link Diagnostic}s for
+ * {@linkplain IPluginChecker2#dynamicMessageArgument(int, Object) dynamic message arguments}.
+ *
+ * @see IPluginChecker2#dynamicMessageArgument(int, Object)
+ * @see IPluginChecker2#getDynamicMessage(Diagnostic)
+ */
+public final class CheckerDiagnosticChain implements DiagnosticChain, Iterable<Diagnostic> {
+
+ private final DiagnosticEquivalence diagnosticEquivalence;
+ private final List<Diagnostic> diagnostics = new ArrayList<>();
+ private int severity;
+
+ /**
+ * Initializes me with the specified diagnostic equivalence semantics.
+ *
+ * @param diagnosticEquivalence
+ * my diagnostic equivalence semantics
+ */
+ public CheckerDiagnosticChain(DiagnosticEquivalence diagnosticEquivalence) {
+ super();
+
+ this.diagnosticEquivalence = diagnosticEquivalence;
+ }
+
+ /**
+ * Initializes me with the default diagnostic equivalence.
+ *
+ * @see DiagnosticEquivalence#DEFAULT
+ */
+ public CheckerDiagnosticChain() {
+ this(DiagnosticEquivalence.DEFAULT);
+ }
+
+ @Override
+ public void add(Diagnostic diagnostic) {
+ // Find an existing equivalent diagnostic and merge the message arguments
+ Optional<Diagnostic> existing = !IPluginChecker2.hasDynamicMessage(diagnostic)
+ ? Optional.empty()
+ : stream().filter(diag -> diagnosticEquivalence.equals(diag, diagnostic)).findFirst();
+
+ existing.ifPresentOrElse(target -> {
+ // The diagnostics could not have been equivalent had they not the same number of arguments
+ List<DynamicMessageArgument> targetArgs = DynamicMessageArgument.stream(target).collect(Collectors.toList());
+ List<DynamicMessageArgument> sourceArgs = DynamicMessageArgument.stream(diagnostic).collect(Collectors.toList());
+ IntStream.range(0, targetArgs.size()).forEach(i -> targetArgs.get(i).merge(sourceArgs.get(i)));
+ }, () -> {
+ // This is the first diagnostic on this dynamic message pattern
+ diagnostics.add(diagnostic);
+ severity = Math.max(severity, diagnostic.getSeverity());
+ });
+ }
+
+ @Override
+ public void addAll(Diagnostic diagnostic) {
+ diagnostic.getChildren().forEach(this::add);
+ }
+
+ @Override
+ public void merge(Diagnostic diagnostic) {
+ if (diagnostic.getChildren().isEmpty()) {
+ add(diagnostic);
+ } else {
+ addAll(diagnostic);
+ }
+ }
+
+ @Override
+ public Iterator<Diagnostic> iterator() {
+ return Collections.unmodifiableList(diagnostics).iterator();
+ }
+
+ public Stream<Diagnostic> stream() {
+ return diagnostics.stream();
+ }
+
+ public int getSeverity() {
+ return severity;
+ }
+
+}
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 894d8c06575..9ce43d6bb4d 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
@@ -84,7 +84,7 @@ public class MarkersService {
? IMarker.SEVERITY_WARNING
: IMarker.SEVERITY_ERROR;
- IMarker result = createMarker(resource, type, diagnostic.getMessage(), severity);
+ IMarker result = createMarker(resource, type, IPluginChecker2.getMessage(diagnostic), severity);
String target = null;
StringBuilder related = null;

Back to the top