diff --git a/io.cucumber.eclipse.java/META-INF/MANIFEST.MF b/io.cucumber.eclipse.java/META-INF/MANIFEST.MF
index e98d6b50..275ce965 100644
--- a/io.cucumber.eclipse.java/META-INF/MANIFEST.MF
+++ b/io.cucumber.eclipse.java/META-INF/MANIFEST.MF
@@ -43,7 +43,8 @@ Require-Bundle: org.eclipse.ui,
Bundle-RequiredExecutionEnvironment: JavaSE-21
Automatic-Module-Name: io.cucumber.eclipse.java
Bundle-ActivationPolicy: lazy
-Import-Package: org.eclipse.unittest.ui,
+Import-Package: io.cucumber.cucumberexpressions;version="[18.0.0,19.0.0)",
+ org.eclipse.unittest.ui,
org.osgi.service.component.annotations;version="1.3.0",
org.osgi.util.tracker
Service-Component: OSGI-INF/io.cucumber.eclipse.java.launching.CucumberRuntimeLauncher.xml,
diff --git a/io.cucumber.eclipse.java/src/io/cucumber/eclipse/java/validation/JavaGlueJob.java b/io.cucumber.eclipse.java/src/io/cucumber/eclipse/java/validation/JavaGlueJob.java
index 25cd0162..e1f515ba 100644
--- a/io.cucumber.eclipse.java/src/io/cucumber/eclipse/java/validation/JavaGlueJob.java
+++ b/io.cucumber.eclipse.java/src/io/cucumber/eclipse/java/validation/JavaGlueJob.java
@@ -20,10 +20,13 @@
import org.eclipse.osgi.service.debug.DebugTrace;
import io.cucumber.core.eventbus.IncrementingUuidGenerator;
+import io.cucumber.core.exception.CompositeCucumberException;
+import io.cucumber.core.exception.CucumberException;
import io.cucumber.core.feature.GluePath;
import io.cucumber.core.gherkin.Feature;
import io.cucumber.core.gherkin.FeatureParserException;
import io.cucumber.core.options.RuntimeOptionsBuilder;
+import io.cucumber.cucumberexpressions.CucumberExpressionException;
import io.cucumber.eclipse.editor.EditorLogging;
import io.cucumber.eclipse.editor.Tracing;
import io.cucumber.eclipse.editor.document.GherkinEditorDocument;
@@ -41,8 +44,8 @@
/**
* Utility class for validating Cucumber glue code in Java projects.
*
- * This class provides the core validation logic that runs Cucumber in dry-run mode
- * to match Gherkin steps with their corresponding Java step definitions.
+ * This class provides the core validation logic that runs Cucumber in dry-run
+ * mode to match Gherkin steps with their corresponding Java step definitions.
*
*/
public final class JavaGlueJob {
@@ -52,45 +55,47 @@ private JavaGlueJob() {
}
/**
- * Validates glue code for multiple documents within the context of a Java project.
- * All documents are processed in a single Cucumber runtime for efficiency.
+ * Validates glue code for multiple documents within the context of a Java
+ * project. All documents are processed in a single Cucumber runtime for
+ * efficiency.
*
- * @param editorDocuments the documents to validate
- * @param javaProject the Java project containing the glue code
+ * @param editorDocuments the documents to validate
+ * @param javaProject the Java project containing the glue code
* @param projectPreferences the project-specific Cucumber preferences
- * @param validationPlugins set of validation plugin class names to use for all documents
- * @param monitor the progress monitor for cancellation
+ * @param validationPlugins set of validation plugin class names to use for all
+ * documents
+ * @param monitor the progress monitor for cancellation
* @return Map of documents to GlueSteps containing matched and available steps
*/
- public static Map validateGlue(Collection editorDocuments,
- IJavaProject javaProject, CucumberJavaPreferences projectPreferences,
- Set validationPlugins, IProgressMonitor monitor) {
-
+ public static Map validateGlue(Collection editorDocuments,
+ IJavaProject javaProject, CucumberJavaPreferences projectPreferences, Set validationPlugins,
+ IProgressMonitor monitor) {
+
Map resultsByDocument = new HashMap<>();
-
+
if (editorDocuments.isEmpty()) {
return resultsByDocument;
}
-
+
long start = System.currentTimeMillis();
DebugTrace debug = Tracing.get();
-
+
try (CucumberRuntime rt = CucumberRuntime.create(javaProject)) {
rt.setGenerator(new IncrementingUuidGenerator());
RuntimeOptionsBuilder runtimeOptions = rt.getRuntimeOptions();
runtimeOptions.setDryRun();
-
+
// Add all features to the runtime and map them by URI
Map documentsByUri = new HashMap<>();
for (GherkinEditorDocument editorDocument : editorDocuments) {
if (monitor.isCanceled()) {
break;
}
-
+
IResource resource = editorDocument.getResource();
monitor.subTask(resource.getName());
debug.traceEntry(PERFORMANCE_STEPS, resource);
-
+
try {
Optional feature = rt.addFeature(editorDocument);
if (feature.isPresent()) {
@@ -102,20 +107,20 @@ public static Map validateGlue(Collection validationPluginInstances = new HashMap<>();
for (String pluginClass : validationPlugins) {
@@ -124,64 +129,103 @@ public static Map validateGlue(Collection steps = stepParserPlugin.getStepList();
-
+
for (Map.Entry entry : documentsByUri.entrySet()) {
if (monitor.isCanceled()) {
break;
}
-
+
URI uri = entry.getKey();
GherkinEditorDocument document = entry.getValue();
IResource resource = document.getResource();
-
+
// Get feature-specific results
Collection> matchedSteps = matchedStepsPlugin.getMatchedStepsForFeature(uri);
Map> snippets = missingStepsPlugin.getSnippetsForFeature(uri);
-
+
// Collect validation errors from plugins
Map validationErrors = new HashMap<>();
for (Plugin plugin : validationPluginInstances.values()) {
addErrors(plugin, validationErrors);
}
-
+
// Create markers for this document
MarkerFactory.validationErrorOnStepDefinition(resource, validationErrors, false);
MarkerFactory.missingSteps(resource, snippets, Activator.PLUGIN_ID, false);
-
- debug.traceExit(PERFORMANCE_STEPS,
- matchedSteps.size() + " step(s) / " + steps.size() + " step(s) matched, "
- + snippets.size() + " snippet(s) where suggested");
-
+
+ debug.traceExit(PERFORMANCE_STEPS, matchedSteps.size() + " step(s) / " + steps.size()
+ + " step(s) matched, " + snippets.size() + " snippet(s) where suggested");
+
resultsByDocument.put(document, new GlueSteps(List.copyOf(steps), List.copyOf(matchedSteps)));
}
-
- debug.trace(PERFORMANCE_STEPS,
- "Total validation time for " + documentsByUri.size() + " document(s): "
+
+ debug.trace(PERFORMANCE_STEPS, "Total validation time for " + documentsByUri.size() + " document(s): "
+ (System.currentTimeMillis() - start) + "ms");
-
} catch (Throwable e) {
- EditorLogging.error("Validate Glue-Code failed", e);
- // Create error markers for all documents
- for (GherkinEditorDocument document : documentsByUri.values()) {
- MarkerFactory.glueValidationError(document.getResource(),
- "Failed to validate step definitions. Check that your project is properly configured and dependencies are available. See error log for details.",
- "glue_validation_error");
- }
+ handleGlueValidationError(e, documentsByUri.values());
}
} catch (CoreException e) {
EditorLogging.error("Failed to create Cucumber runtime", e);
}
-
+
return resultsByDocument;
}
- static Set extractValidationPlugins(GherkinEditorDocument editorDocument,
+ private static void handleGlueValidationError(Throwable e, Collection documents) {
+ if (e instanceof CucumberExpressionException ee) {
+ handleExpressionException(documents, ee);
+ } else if (e instanceof CucumberException cucumber) {
+ handleCucumberException(documents, cucumber);
+ } else {
+ handleGenericError(documents, e);
+ }
+ }
+
+ private static void handleExpressionException(Collection documents,
+ CucumberExpressionException exception) {
+ // TODO handle more specific values see
+ // https://github.com/cucumber/cucumber-expressions/issues/389
+ // we should then be able to add error markers at the offending glue locations
+ String message = exception.getMessage();
+ for (GherkinEditorDocument document : documents) {
+ MarkerFactory.glueValidationError(document.getResource(), message, "glue_validation_error");
+ }
+ }
+
+ private static void handleCucumberException(Collection documents,
+ CucumberException cucumber) {
+ if (cucumber instanceof CompositeCucumberException composite) {
+ Throwable[] suppressed = composite.getSuppressed();
+ for (Throwable throwable : suppressed) {
+ handleGlueValidationError(throwable, documents);
+ }
+ return;
+ }
+ // TODO handle more specific values see
+ // https://github.com/cucumber/cucumber-jvm/issues/3142
+ // we should then be able to add error markers at the offending glue locations
+ String message = cucumber.getMessage();
+ for (GherkinEditorDocument document : documents) {
+ MarkerFactory.glueValidationError(document.getResource(), message, "glue_validation_error");
+ }
+ }
+
+ private static void handleGenericError(Collection documents, Throwable e) {
+ EditorLogging.error("Validate Glue-Code failed", e);
+ for (GherkinEditorDocument document : documents) {
+ MarkerFactory.glueValidationError(document.getResource(),
+ "Failed to validate step definitions. Check that your project is properly configured and dependencies are available. See error log for details.",
+ "glue_validation_error");
+ }
+ }
+
+ static Set extractValidationPlugins(GherkinEditorDocument editorDocument,
CucumberJavaPreferences projectPreferences) {
Set plugins = new LinkedHashSet<>();
IDocument doc = editorDocument.getDocument();