diff --git a/.mvn/jvm.config b/.mvn/jvm.config
index 7fecadb38e..e465a14f8e 100644
--- a/.mvn/jvm.config
+++ b/.mvn/jvm.config
@@ -1 +1,11 @@
-Dfile.encoding=UTF-8
+--add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED
+--add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED
+--add-exports jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED
+--add-exports jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED
+--add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED
+--add-exports jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED
+--add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED
+--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
+--add-opens jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED
+--add-opens jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED
diff --git a/cucumber-bom/pom.xml b/cucumber-bom/pom.xml
index c6ac71b25e..90b241742c 100644
--- a/cucumber-bom/pom.xml
+++ b/cucumber-bom/pom.xml
@@ -13,19 +13,19 @@
Cucumber-JVM: Bill of Materials
- 12.0.0
- 18.0.1
- 0.3.0
- 36.1.0
- 22.0.0
- 0.10.0
- 30.1.0
- 2.4.1
- 14.6.0
- 8.0.0
- 0.2.0
- 0.7.0
- 0.1.0
+ 13.0.0-SNAPSHOT
+ 19.0.0-SNAPSHOT
+ 0.4.0-SNAPSHOT
+ 38.0.0-SNAPSHOT
+ 23.0.0-SNAPSHOT
+ 0.12.0-SNAPSHOT
+ 32.0.0-SNAPSHOT
+ 3.0.0-SNAPSHOT
+ 15.0.0-SNAPSHOT
+ 9.0.0-SNAPSHOT
+ 0.3.0-SNAPSHOT
+ 0.8.0-SNAPSHOT
+ 0.2.0-SNAPSHOT
diff --git a/cucumber-cdi2/src/main/java/io/cucumber/cdi2/package-info.java b/cucumber-cdi2/src/main/java/io/cucumber/cdi2/package-info.java
new file mode 100644
index 0000000000..69a0f5bbde
--- /dev/null
+++ b/cucumber-cdi2/src/main/java/io/cucumber/cdi2/package-info.java
@@ -0,0 +1,4 @@
+@NullMarked
+package io.cucumber.cdi2;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/cucumber-core/src/main/java/cucumber/api/cli/Main.java b/cucumber-core/src/main/java/cucumber/api/cli/Main.java
index ef0267ca6a..acedbdd5a4 100644
--- a/cucumber-core/src/main/java/cucumber/api/cli/Main.java
+++ b/cucumber-core/src/main/java/cucumber/api/cli/Main.java
@@ -7,9 +7,13 @@
* @deprecated use {@link io.cucumber.core.cli.Main} instead.
*/
@Deprecated
-public class Main {
+public final class Main {
private static final Logger log = LoggerFactory.getLogger(Main.class);
+
+ private Main(){
+ /* no-op */
+ }
public static void main(String[] argv) {
byte exitStatus = run(argv, Thread.currentThread().getContextClassLoader());
diff --git a/cucumber-core/src/main/java/cucumber/api/cli/package-info.java b/cucumber-core/src/main/java/cucumber/api/cli/package-info.java
new file mode 100644
index 0000000000..fe5f58ed50
--- /dev/null
+++ b/cucumber-core/src/main/java/cucumber/api/cli/package-info.java
@@ -0,0 +1,4 @@
+@NullMarked
+package cucumber.api.cli;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/cucumber-core/src/main/java/io/cucumber/core/api/TypeRegistry.java b/cucumber-core/src/main/java/io/cucumber/core/api/TypeRegistry.java
deleted file mode 100644
index b1122bb787..0000000000
--- a/cucumber-core/src/main/java/io/cucumber/core/api/TypeRegistry.java
+++ /dev/null
@@ -1,68 +0,0 @@
-package io.cucumber.core.api;
-
-import io.cucumber.cucumberexpressions.ParameterByTypeTransformer;
-import io.cucumber.cucumberexpressions.ParameterType;
-import io.cucumber.datatable.DataTableType;
-import io.cucumber.datatable.TableCellByTypeTransformer;
-import io.cucumber.datatable.TableEntryByTypeTransformer;
-import io.cucumber.docstring.DocStringType;
-import org.apiguardian.api.API;
-import org.apiguardian.api.API.Status;
-
-/**
- * The type registry records defines parameter types, data table types and
- * docstring transformers.
- *
- * @deprecated use the dedicated type annotations to register data table and
- * parameter types instead
- */
-@API(status = Status.STABLE)
-@Deprecated
-public interface TypeRegistry {
-
- /**
- * Defines a new parameter type.
- *
- * @param parameterType The new parameter type.
- */
- void defineParameterType(ParameterType> parameterType);
-
- /**
- * Defines a new docstring type.
- *
- * @param docStringType The new docstring type.
- */
- void defineDocStringType(DocStringType docStringType);
-
- /**
- * Defines a new data table type.
- *
- * @param tableType The new table type.
- */
- void defineDataTableType(DataTableType tableType);
-
- /**
- * Set default transformer for parameters which are not defined by
- * {@code defineParameterType(ParameterType>))}
- *
- * @param defaultParameterByTypeTransformer default transformer
- */
- void setDefaultParameterTransformer(ParameterByTypeTransformer defaultParameterByTypeTransformer);
-
- /**
- * Set default transformer for entries which are not defined by
- * {@code defineDataTableType(new DataTableType(Class,TableEntryTransformer))}
- *
- * @param tableEntryByTypeTransformer default transformer
- */
- void setDefaultDataTableEntryTransformer(TableEntryByTypeTransformer tableEntryByTypeTransformer);
-
- /**
- * Set default transformer for cells which are not defined by
- * {@code defineDataTableType(new DataTableType(Class,TableEntryTransformer))}
- *
- * @param tableCellByTypeTransformer default transformer
- */
- void setDefaultDataTableCellTransformer(TableCellByTypeTransformer tableCellByTypeTransformer);
-
-}
diff --git a/cucumber-core/src/main/java/io/cucumber/core/backend/Backend.java b/cucumber-core/src/main/java/io/cucumber/core/backend/Backend.java
index 676fd43cbc..d178594f94 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/backend/Backend.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/backend/Backend.java
@@ -1,6 +1,7 @@
package io.cucumber.core.backend;
import org.apiguardian.api.API;
+import org.jspecify.annotations.Nullable;
import java.net.URI;
import java.util.List;
@@ -15,7 +16,9 @@ public interface Backend {
* @param glue Glue that provides the steps to be executed.
* @param gluePaths The locations for the glue to be loaded.
*/
- void loadGlue(Glue glue, List gluePaths);
+ default void loadGlue(Glue glue, List gluePaths){
+
+ }
/**
* Invoked before a new scenario starts. Implementations should do any
@@ -23,13 +26,20 @@ public interface Backend {
* step definitions can be loaded here. These step definitions should
* implement {@link ScenarioScoped}
*/
- void buildWorld();
+ default void buildWorld() {
+
+ }
/**
* Invoked at the end of a scenario, after hooks
*/
- void disposeWorld();
-
- Snippet getSnippet();
+ default void disposeWorld(){
+
+ }
+
+ @Nullable
+ default Snippet getSnippet(){
+ return null;
+ }
}
diff --git a/cucumber-core/src/main/java/io/cucumber/core/backend/CucumberInvocationTargetException.java b/cucumber-core/src/main/java/io/cucumber/core/backend/CucumberInvocationTargetException.java
index 7cfd04de76..a31c235357 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/backend/CucumberInvocationTargetException.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/backend/CucumberInvocationTargetException.java
@@ -4,6 +4,8 @@
import java.lang.reflect.InvocationTargetException;
+import static java.util.Objects.requireNonNull;
+
/**
* Thrown when an exception was thrown by glue code. Not to be confused with
* {@link CucumberBackendException} which is thrown when the backend failed to
@@ -13,11 +15,12 @@
public final class CucumberInvocationTargetException extends RuntimeException {
private final Located located;
- private final InvocationTargetException invocationTargetException;
+ private final Throwable cause;
public CucumberInvocationTargetException(Located located, InvocationTargetException invocationTargetException) {
+ super(invocationTargetException.getCause());
this.located = located;
- this.invocationTargetException = invocationTargetException;
+ this.cause = requireNonNull(invocationTargetException.getCause());
}
/**
@@ -33,7 +36,7 @@ public Located getLocated() {
}
@Override
- public Throwable getCause() {
- return invocationTargetException.getCause();
+ public synchronized Throwable getCause() {
+ return cause;
}
}
diff --git a/cucumber-core/src/main/java/io/cucumber/core/backend/DefaultObjectFactory.java b/cucumber-core/src/main/java/io/cucumber/core/backend/DefaultObjectFactory.java
index c3e0320c60..4a48a7ba8b 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/backend/DefaultObjectFactory.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/backend/DefaultObjectFactory.java
@@ -27,24 +27,28 @@ public final class DefaultObjectFactory implements ObjectFactory {
private final Map, Object> instances = new HashMap<>();
+ @Override
public void start() {
// No-op
}
+ @Override
public void stop() {
instances.clear();
}
+ @Override
public boolean addClass(Class> clazz) {
return true;
}
+ @Override
public T getInstance(Class type) {
- T instance = type.cast(instances.get(type));
- if (instance == null) {
- instance = cacheNewInstance(type);
+ Object instance = instances.get(type);
+ if (instance != null) {
+ return type.cast(instance);
}
- return instance;
+ return cacheNewInstance(type);
}
private T cacheNewInstance(Class type) {
diff --git a/cucumber-core/src/main/java/io/cucumber/core/backend/HookDefinition.java b/cucumber-core/src/main/java/io/cucumber/core/backend/HookDefinition.java
index f1aed8f08d..999777d717 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/backend/HookDefinition.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/backend/HookDefinition.java
@@ -25,6 +25,6 @@ enum HookType {
BEFORE_STEP,
- AFTER_STEP;
+ AFTER_STEP
}
}
diff --git a/cucumber-core/src/main/java/io/cucumber/core/backend/Located.java b/cucumber-core/src/main/java/io/cucumber/core/backend/Located.java
index 57005ad9c4..80dbc6f076 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/backend/Located.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/backend/Located.java
@@ -8,9 +8,10 @@
public interface Located {
/**
+ * Return true if this matches the location. This is used to filter stack traces.
+ *
* @param stackTraceElement The location of the step.
- * @return Return true if this matches the location. This
- * is used to filter stack traces.
+ * @return Return true if this matches the location.
*/
boolean isDefinedAt(StackTraceElement stackTraceElement);
diff --git a/cucumber-core/src/main/java/io/cucumber/core/backend/Options.java b/cucumber-core/src/main/java/io/cucumber/core/backend/Options.java
index a3554484ec..7ae205b5bd 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/backend/Options.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/backend/Options.java
@@ -1,7 +1,9 @@
package io.cucumber.core.backend;
+import org.jspecify.annotations.Nullable;
+
public interface Options {
- Class extends ObjectFactory> getObjectFactoryClass();
+ @Nullable Class extends ObjectFactory> getObjectFactoryClass();
}
diff --git a/cucumber-core/src/main/java/io/cucumber/core/backend/Snippet.java b/cucumber-core/src/main/java/io/cucumber/core/backend/Snippet.java
index 88392e4245..6b8a260d7c 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/backend/Snippet.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/backend/Snippet.java
@@ -12,30 +12,35 @@ public interface Snippet {
/**
* The language of the generated snippet.
- *
- * @see io.cucumber.messages.types.Snippet#getLanguage()
+ *
* @return the language of the generated snippet.
+ * @see io.cucumber.messages.types.Snippet#getLanguage()
*/
default Optional language() {
return Optional.empty();
}
/**
- * @return a {@link java.text.MessageFormat} template used to generate a
- * snippet. The template can access the following variables:
- *
- *
{0} : Step Keyword
- *
{1} : Value of {@link #escapePattern(String)}
- *
{2} : Function name
- *
{3} : Value of {@link #arguments(Map)}
- *
{4} : Regexp hint comment
- *
{5} : value of {@link #tableHint()} if the step has a
- * table
- *
+ * Returns a {@link java.text.MessageFormat} template used to generate a snippet.
+ *
+ *
The template can access the following variables:
+ *
+ *
{0} : Step Keyword
+ *
{1} : Value of {@link #escapePattern(String)}
+ *
{2} : Function name
+ *
{3} : Value of {@link #arguments(Map)}
+ *
{4} : Regexp hint comment
+ *
{5} : value of {@link #tableHint()} if the step has a
+ * table
+ *
+ *
+ * @return a template used to generate a snippet.
*/
MessageFormat template();
/**
+ * Returns a hint about alternative ways to declare a table argument
+ *
* @return a hint about alternative ways to declare a table argument
*/
String tableHint();
@@ -45,15 +50,17 @@ default Optional language() {
* should accept. The arguments are provided a map of (suggested) names and
* types. The arguments are ordered by their position.
*
- * @param arguments ordered pairs of names and types
- * @return a string representation of the arguments
+ * @param arguments ordered pairs of names and types
+ * @return a string representation of the arguments
*/
String arguments(Map arguments);
/**
- * @param pattern the computed pattern that will match an undefined step
- * @return an escaped representation of the pattern, if escaping is
- * necessary.
+ * Escape representation of the pattern, if escaping is necessary.
+ *
+ * @param pattern the computed pattern that will match an undefined step
+ * @return an escaped representation of the pattern, if escaping is
+ * necessary.
*/
String escapePattern(String pattern);
diff --git a/cucumber-core/src/main/java/io/cucumber/core/backend/StackTraceElementReference.java b/cucumber-core/src/main/java/io/cucumber/core/backend/StackTraceElementReference.java
index 06219e8778..267c04bb34 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/backend/StackTraceElementReference.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/backend/StackTraceElementReference.java
@@ -5,7 +5,7 @@
import static java.util.Objects.requireNonNull;
-public class StackTraceElementReference implements SourceReference {
+public final class StackTraceElementReference implements SourceReference {
private final String className;
private final String methodName;
diff --git a/cucumber-core/src/main/java/io/cucumber/core/backend/StepDefinition.java b/cucumber-core/src/main/java/io/cucumber/core/backend/StepDefinition.java
index e59cbe381b..1787287987 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/backend/StepDefinition.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/backend/StepDefinition.java
@@ -11,7 +11,7 @@ public interface StepDefinition extends Located {
* Invokes the step definition. The method should raise a Throwable if the
* invocation fails, which will cause the step to fail.
*
- * @param args The arguments for the step
+ * @param args The arguments for the step
* @throws CucumberBackendException of a failure to invoke the step
* @throws CucumberInvocationTargetException in case of a failure in the
* step.
@@ -19,13 +19,16 @@ public interface StepDefinition extends Located {
void execute(Object[] args) throws CucumberBackendException, CucumberInvocationTargetException;
/**
+ * Return parameter information.
+ *
* @return parameter information, may not return null
*/
List parameterInfos();
/**
- * @return the pattern associated with this instance. Used for error
- * reporting only.
+ * Return the pattern associated with this instance. Used for error reporting only.
+ *
+ * @return the pattern associated with this instance.
*/
String getPattern();
diff --git a/cucumber-core/src/main/java/io/cucumber/core/backend/TestCaseState.java b/cucumber-core/src/main/java/io/cucumber/core/backend/TestCaseState.java
index db6e3ae87a..b4d6b26ee1 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/backend/TestCaseState.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/backend/TestCaseState.java
@@ -9,6 +9,8 @@
public interface TestCaseState {
/**
+ * Return tags of this scenario.
+ *
* @return tags of this scenario.
*/
Collection getSourceTagNames();
@@ -16,7 +18,7 @@ public interface TestCaseState {
/**
* Returns the current status of this test case.
*
- * The test case status is calculate as the most severe status of the
+ * The test case status is calculated as the most severe status of the
* executed steps in the testcase so far.
*
* @return the current status of this test case
@@ -24,7 +26,11 @@ public interface TestCaseState {
Status getStatus();
/**
- * @return true if and only if {@link #getStatus()} returns "failed"
+ * Returns true when the scenario has failed.
+ *
+ *
This is implemented as {@code this.getStatus() == Status.FAILED}.
+ *
+ * @return true if the scenario has failed.
*/
boolean isFailed();
@@ -54,6 +60,8 @@ public interface TestCaseState {
void attach(byte[] data, String mediaType, String name);
/**
+ * Attach data to the report(s).
+ *
* @param data what to attach, for example html.
* @param mediaType what is the data?
* @param name attachment name
@@ -70,24 +78,33 @@ public interface TestCaseState {
void log(String text);
/**
+ * Returns the name of the Scenario
+ *
* @return the name of the Scenario
*/
String getName();
/**
+ * Returns the id of the Scenario.
+ *
* @return the id of the Scenario.
*/
String getId();
/**
+ * Returns the uri of the Scenario.
+ *
* @return the uri of the Scenario.
*/
URI getUri();
/**
- * @return the line in the feature file of the Scenario. If this is a
- * Scenario from Scenario Outlines this will return the line of the
- * example row in the Scenario Outline.
+ * Returns the line in the feature file of the Scenario.
+ *
+ *
If this is a Scenario from Scenario Outlines this will
+ * return the line of the example row in the Scenario Outline.
+ *
+ * @return the line in the feature file of the Scenario.
*/
Integer getLine();
diff --git a/cucumber-core/src/main/java/io/cucumber/core/backend/package-info.java b/cucumber-core/src/main/java/io/cucumber/core/backend/package-info.java
new file mode 100644
index 0000000000..17cc474d10
--- /dev/null
+++ b/cucumber-core/src/main/java/io/cucumber/core/backend/package-info.java
@@ -0,0 +1,4 @@
+@NullMarked
+package io.cucumber.core.backend;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/cucumber-core/src/main/java/io/cucumber/core/cli/Main.java b/cucumber-core/src/main/java/io/cucumber/core/cli/Main.java
index 7f4c398932..69f97821c0 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/cli/Main.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/cli/Main.java
@@ -24,8 +24,12 @@
* {@link CommandlineOptions}.
*/
@API(status = API.Status.STABLE)
-public class Main {
+public final class Main {
+ private Main(){
+ /* no-op */
+ }
+
public static void main(String... argv) {
byte exitStatus = run(argv, Thread.currentThread().getContextClassLoader());
System.exit(exitStatus);
diff --git a/cucumber-core/src/main/java/io/cucumber/core/cli/package-info.java b/cucumber-core/src/main/java/io/cucumber/core/cli/package-info.java
new file mode 100644
index 0000000000..dc79e3d780
--- /dev/null
+++ b/cucumber-core/src/main/java/io/cucumber/core/cli/package-info.java
@@ -0,0 +1,4 @@
+@NullMarked
+package io.cucumber.core.cli;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/cucumber-core/src/main/java/io/cucumber/core/eventbus/AbstractEventBus.java b/cucumber-core/src/main/java/io/cucumber/core/eventbus/AbstractEventBus.java
index ba6cee4558..20c7fa54fa 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/eventbus/AbstractEventBus.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/eventbus/AbstractEventBus.java
@@ -2,11 +2,21 @@
public abstract class AbstractEventBus extends AbstractEventPublisher implements EventBus {
+ /**
+ * Send all events.
+ *
+ *
May be overridden, but must be called.
+ */
@Override
public void sendAll(Iterable queue) {
super.sendAll(queue);
}
+ /**
+ * Send a single event.
+ *
+ *
May be overridden, but must be called.
+ */
@Override
public void send(T event) {
super.send(event);
diff --git a/cucumber-core/src/main/java/io/cucumber/core/eventbus/AbstractEventPublisher.java b/cucumber-core/src/main/java/io/cucumber/core/eventbus/AbstractEventPublisher.java
index 0d67fb5f6a..df420df14b 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/eventbus/AbstractEventPublisher.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/eventbus/AbstractEventPublisher.java
@@ -31,12 +31,22 @@ public final void removeHandlerFor(Class eventType, EventHandler handl
}
}
+ /**
+ * Send all events.
+ *
+ *
May be overridden, but must be called.
+ */
protected void sendAll(Iterable events) {
for (T event : events) {
send(event);
}
}
+ /**
+ * Send a single event.
+ *
+ *
May be overridden, but must be called.
+ */
protected void send(T event) {
if (handlers.containsKey(Event.class) && event instanceof Event) {
for (EventHandler handler : handlers.get(Event.class)) {
diff --git a/cucumber-core/src/main/java/io/cucumber/core/eventbus/IncrementingUuidGenerator.java b/cucumber-core/src/main/java/io/cucumber/core/eventbus/IncrementingUuidGenerator.java
index e11d6d07cc..4479455472 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/eventbus/IncrementingUuidGenerator.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/eventbus/IncrementingUuidGenerator.java
@@ -130,7 +130,8 @@ public UUID generateId() {
" capacity. Please generate using a new instance or use another " +
UuidGenerator.class.getSimpleName() + "implementation.");
}
- long leastSigBits = counterValue | 0x8000000000000000L; // set variant
+ // set variant
+ long leastSigBits = counterValue | 0x8000000000000000L;
return new UUID(msb, leastSigBits);
}
}
diff --git a/cucumber-core/src/main/java/io/cucumber/core/eventbus/Options.java b/cucumber-core/src/main/java/io/cucumber/core/eventbus/Options.java
index b14ef7a05e..98b97b2dad 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/eventbus/Options.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/eventbus/Options.java
@@ -1,7 +1,9 @@
package io.cucumber.core.eventbus;
+import org.jspecify.annotations.Nullable;
+
public interface Options {
- Class extends UuidGenerator> getUuidGeneratorClass();
+ @Nullable Class extends UuidGenerator> getUuidGeneratorClass();
}
diff --git a/cucumber-core/src/main/java/io/cucumber/core/eventbus/RandomUuidGenerator.java b/cucumber-core/src/main/java/io/cucumber/core/eventbus/RandomUuidGenerator.java
index 76f34deb39..5df8aa6c8c 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/eventbus/RandomUuidGenerator.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/eventbus/RandomUuidGenerator.java
@@ -6,7 +6,12 @@
* UUID generator based on random numbers. The generator is thread-safe and
* supports multi-jvm usage of Cucumber.
*/
-public class RandomUuidGenerator implements UuidGenerator {
+public final class RandomUuidGenerator implements UuidGenerator {
+
+ public RandomUuidGenerator(){
+ /* no-op */
+ }
+
@Override
public UUID generateId() {
return UUID.randomUUID();
diff --git a/cucumber-core/src/main/java/io/cucumber/core/eventbus/UuidGenerator.java b/cucumber-core/src/main/java/io/cucumber/core/eventbus/UuidGenerator.java
index b698bbf96b..90275729ab 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/eventbus/UuidGenerator.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/eventbus/UuidGenerator.java
@@ -10,8 +10,10 @@
*/
@API(status = API.Status.EXPERIMENTAL)
public interface UuidGenerator extends Supplier {
+
UUID generateId();
+ @Override
default UUID get() {
return generateId();
}
diff --git a/cucumber-core/src/main/java/io/cucumber/core/eventbus/package-info.java b/cucumber-core/src/main/java/io/cucumber/core/eventbus/package-info.java
new file mode 100644
index 0000000000..5bc0cb5209
--- /dev/null
+++ b/cucumber-core/src/main/java/io/cucumber/core/eventbus/package-info.java
@@ -0,0 +1,4 @@
+@NullMarked
+package io.cucumber.core.eventbus;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/cucumber-core/src/main/java/io/cucumber/core/exception/package-info.java b/cucumber-core/src/main/java/io/cucumber/core/exception/package-info.java
new file mode 100644
index 0000000000..999d929df0
--- /dev/null
+++ b/cucumber-core/src/main/java/io/cucumber/core/exception/package-info.java
@@ -0,0 +1,4 @@
+@NullMarked
+package io.cucumber.core.exception;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/cucumber-core/src/main/java/io/cucumber/core/feature/FeatureIdentifier.java b/cucumber-core/src/main/java/io/cucumber/core/feature/FeatureIdentifier.java
index 84d7391f33..33b4f3c298 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/feature/FeatureIdentifier.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/feature/FeatureIdentifier.java
@@ -11,12 +11,12 @@
*
* @see FeatureWithLines
*/
-public class FeatureIdentifier {
+public final class FeatureIdentifier {
private static final String FEATURE_FILE_SUFFIX = ".feature";
private FeatureIdentifier() {
-
+ /* no-op */
}
public static URI parse(String featureIdentifier) {
diff --git a/cucumber-core/src/main/java/io/cucumber/core/feature/FeaturePath.java b/cucumber-core/src/main/java/io/cucumber/core/feature/FeaturePath.java
index 23302be33b..1c0a546754 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/feature/FeaturePath.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/feature/FeaturePath.java
@@ -27,7 +27,7 @@
* @see FeatureIdentifier
* @see FeatureWithLines
*/
-public class FeaturePath {
+public final class FeaturePath {
private FeaturePath() {
diff --git a/cucumber-core/src/main/java/io/cucumber/core/feature/FeatureWithLines.java b/cucumber-core/src/main/java/io/cucumber/core/feature/FeatureWithLines.java
index 4d69ba217e..318f18bfde 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/feature/FeatureWithLines.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/feature/FeatureWithLines.java
@@ -28,7 +28,7 @@
* a {@link FeatureIdentifier} followed by a sequence of line numbers each
* preceded by a colon.
*/
-public class FeatureWithLines implements Serializable {
+public final class FeatureWithLines implements Serializable {
private static final long serialVersionUID = 20190126L;
private static final Pattern FEATURE_WITH_LINES_FILE_FORMAT = Pattern.compile("(?m:^| |)(.*?\\.feature(?::\\d+)*)");
@@ -131,6 +131,7 @@ public boolean equals(Object o) {
return uri.equals(that.uri) && lines.equals(that.lines);
}
+ @Override
public String toString() {
StringBuilder builder = new StringBuilder(uri.toString());
for (Integer line : lines) {
diff --git a/cucumber-core/src/main/java/io/cucumber/core/feature/GluePath.java b/cucumber-core/src/main/java/io/cucumber/core/feature/GluePath.java
index 601fd9c41d..bbc7c45da3 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/feature/GluePath.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/feature/GluePath.java
@@ -32,7 +32,7 @@
*
* It is recommended to always use the package name form.
*/
-public class GluePath {
+public final class GluePath {
private static final Logger log = LoggerFactory.getLogger(GluePath.class);
@@ -118,13 +118,14 @@ private static void warnWhenWellKnownProjectSourceDirectory(String gluePath) {
classPathResource = classPathResource.substring(0, classPathResource.length() - 1);
}
String packageName = classPathResource.replaceAll("/", ".");
- String message = "" +
- "Consider replacing glue path '%s' with '%s'.\n'" +
- "\n" +
- "The current glue path points to a source directory in your project. However " +
- "cucumber looks for glue (i.e. step definitions) on the classpath. By using a " +
- "package name you can avoid this ambiguity.";
- return String.format(message, gluePath, packageName);
+ return """
+ Consider replacing glue path '%s' with '%s'.
+
+ The current glue path points to a source directory in your \
+ project. However cucumber looks for glue (i.e. step \
+ definitions) on the classpath. By using a package name you \
+ can avoid this ambiguity."""
+ .formatted(gluePath, packageName);
});
}
@@ -133,11 +134,11 @@ private static boolean isProbablyPackage(String gluePath) {
&& !gluePath.contains(RESOURCE_SEPARATOR_STRING);
}
+ @SuppressWarnings("UnnecessaryParentheses")
private static boolean isValidIdentifier(String schemeSpecificPart) {
- for (String part : schemeSpecificPart.split("/")) {
+ for (String part : schemeSpecificPart.split("/", 0)) {
for (int i = 0; i < part.length(); i++) {
- if (i == 0 && !isJavaIdentifierStart(part.charAt(i))
- || (i != 0 && !isJavaIdentifierPart(part.charAt(i)))) {
+ if ((i == 0 && !isJavaIdentifierStart(part.charAt(i))) || (i != 0 && !isJavaIdentifierPart(part.charAt(i)))) {
return false;
}
}
diff --git a/cucumber-core/src/main/java/io/cucumber/core/feature/package-info.java b/cucumber-core/src/main/java/io/cucumber/core/feature/package-info.java
new file mode 100644
index 0000000000..dcec86ab24
--- /dev/null
+++ b/cucumber-core/src/main/java/io/cucumber/core/feature/package-info.java
@@ -0,0 +1,4 @@
+@NullMarked
+package io.cucumber.core.feature;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/cucumber-core/src/main/java/io/cucumber/core/filter/package-info.java b/cucumber-core/src/main/java/io/cucumber/core/filter/package-info.java
new file mode 100644
index 0000000000..d9b6789037
--- /dev/null
+++ b/cucumber-core/src/main/java/io/cucumber/core/filter/package-info.java
@@ -0,0 +1,4 @@
+@NullMarked
+package io.cucumber.core.filter;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/cucumber-core/src/main/java/io/cucumber/core/logging/LoggerFactory.java b/cucumber-core/src/main/java/io/cucumber/core/logging/LoggerFactory.java
index 7dade586cb..a6654526fd 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/logging/LoggerFactory.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/logging/LoggerFactory.java
@@ -1,5 +1,7 @@
package io.cucumber.core.logging;
+import org.jspecify.annotations.Nullable;
+
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.function.Supplier;
import java.util.logging.Level;
@@ -114,7 +116,7 @@ public void trace(Throwable throwable, Supplier message) {
log(Level.FINER, throwable, message);
}
- private void log(Level level, Throwable throwable, Supplier message) {
+ private void log(Level level, @Nullable Throwable throwable, Supplier message) {
boolean loggable = julLogger.isLoggable(level);
if (loggable || !listeners.isEmpty()) {
LogRecord logRecord = createLogRecord(level, throwable, message);
@@ -125,7 +127,7 @@ private void log(Level level, Throwable throwable, Supplier message) {
}
}
- private LogRecord createLogRecord(Level level, Throwable throwable, Supplier message) {
+ private LogRecord createLogRecord(Level level, @Nullable Throwable throwable, Supplier message) {
StackTraceElement[] stack = new Throwable().getStackTrace();
String sourceClassName = null;
String sourceMethodName = null;
@@ -133,7 +135,8 @@ private LogRecord createLogRecord(Level level, Throwable throwable, Supplier exitStatus() {
@@ -109,7 +111,7 @@ private RuntimeOptionsBuilder parse(List args) {
String nextArg = removeArgFor(arg, args);
exitCode = printI18nKeywords(nextArg);
return parsedOptions;
- } else if (arg.equals(I18N) || arg.equals(I18N_KEYWORDS)) {
+ } else if (arg.equals(I18N)) {
String nextArg = removeArgFor(arg, args);
exitCode = printI18n(nextArg);
return parsedOptions;
diff --git a/cucumber-core/src/main/java/io/cucumber/core/options/CucumberOptionsAnnotationParser.java b/cucumber-core/src/main/java/io/cucumber/core/options/CucumberOptionsAnnotationParser.java
index 9ad7b1aaa7..fec3626cff 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/options/CucumberOptionsAnnotationParser.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/options/CucumberOptionsAnnotationParser.java
@@ -8,6 +8,7 @@
import io.cucumber.core.snippets.SnippetType;
import io.cucumber.tagexpressions.TagExpressionException;
import io.cucumber.tagexpressions.TagExpressionParser;
+import org.jspecify.annotations.Nullable;
import java.util.regex.Pattern;
@@ -18,7 +19,7 @@ public final class CucumberOptionsAnnotationParser {
private boolean featuresSpecified = false;
private boolean overridingGlueSpecified = false;
- private OptionsProvider optionsProvider;
+ private @Nullable OptionsProvider optionsProvider;
public CucumberOptionsAnnotationParser withOptionsProvider(OptionsProvider optionsProvider) {
this.optionsProvider = optionsProvider;
@@ -128,7 +129,7 @@ private void addGlue(CucumberOptions options, RuntimeOptionsBuilder args) {
}
private void addFeatures(CucumberOptions options, RuntimeOptionsBuilder args) {
- if (options != null && options.features().length != 0) {
+ if (options.features().length != 0) {
for (String feature : options.features()) {
FeatureWithLinesOrRerunPath parsed = FeatureWithLinesOrRerunPath.parse(feature);
parsed.getFeaturesToRerun().ifPresent(args::addRerun);
@@ -181,7 +182,7 @@ private static String packageName(Class> clazz) {
public interface OptionsProvider {
- CucumberOptions getOptions(Class> clazz);
+ @Nullable CucumberOptions getOptions(Class> clazz);
}
@@ -207,9 +208,9 @@ public interface CucumberOptions {
SnippetType snippets();
- Class extends ObjectFactory> objectFactory();
+ @Nullable Class extends ObjectFactory> objectFactory();
- Class extends UuidGenerator> uuidGenerator();
+ @Nullable Class extends UuidGenerator> uuidGenerator();
}
diff --git a/cucumber-core/src/main/java/io/cucumber/core/options/CucumberProperties.java b/cucumber-core/src/main/java/io/cucumber/core/options/CucumberProperties.java
index 0da50f0cd1..dd395d3564 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/options/CucumberProperties.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/options/CucumberProperties.java
@@ -2,6 +2,7 @@
import io.cucumber.core.logging.Logger;
import io.cucumber.core.logging.LoggerFactory;
+import org.jspecify.annotations.Nullable;
import java.io.IOException;
import java.io.InputStream;
@@ -71,10 +72,10 @@ public static Map fromSystemProperties() {
static class CucumberPropertiesMap extends AbstractMap {
- private final CucumberPropertiesMap parent;
+ private final @Nullable CucumberPropertiesMap parent;
private final Map delegate;
- CucumberPropertiesMap(CucumberPropertiesMap parent, Map delegate) {
+ CucumberPropertiesMap(@Nullable CucumberPropertiesMap parent, Map delegate) {
this.delegate = requireNonNull(delegate);
this.parent = parent;
}
@@ -95,20 +96,19 @@ private static CucumberPropertiesMap create(Properties p) {
}
@Override
- public String get(Object key) {
+ public @Nullable String get(Object key) {
String exactMatch = super.get(key);
if (exactMatch != null) {
return exactMatch;
}
- if (!(key instanceof String)) {
+ if (!(key instanceof String keyString)) {
return null;
}
// Support old skool
// Not all environments allow properties to contain dots or dashes.
// So we map the requested property to its underscore case variant.
- String keyString = (String) key;
String uppercase = keyString
.replace(".", "_")
diff --git a/cucumber-core/src/main/java/io/cucumber/core/options/CucumberPropertiesParser.java b/cucumber-core/src/main/java/io/cucumber/core/options/CucumberPropertiesParser.java
index e2c490ecbd..48e37eea29 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/options/CucumberPropertiesParser.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/options/CucumberPropertiesParser.java
@@ -38,6 +38,10 @@ public final class CucumberPropertiesParser {
private static final Logger log = LoggerFactory.getLogger(CucumberPropertiesParser.class);
+ public CucumberPropertiesParser(){
+ /* no-op */
+ }
+
public RuntimeOptionsBuilder parse(Map properties) {
return parse(properties::get);
}
@@ -101,7 +105,7 @@ public RuntimeOptionsBuilder parse(CucumberPropertiesProvider properties) {
parse(properties,
OPTIONS_PROPERTY_NAME,
identity(),
- warnWhenCucumberOptionsIsUsed());
+ CucumberPropertiesParser::warnWhenCucumberOptionsIsUsed);
parseAll(properties,
PLUGIN_PROPERTY_NAME,
@@ -110,7 +114,8 @@ public RuntimeOptionsBuilder parse(CucumberPropertiesProvider properties) {
parse(properties,
PLUGIN_PUBLISH_TOKEN_PROPERTY_NAME,
- identity(), // No validation - validated on server
+ // No validation - validated on server
+ identity(),
builder::setPublishToken);
parse(properties,
@@ -136,10 +141,10 @@ public RuntimeOptionsBuilder parse(CucumberPropertiesProvider properties) {
return builder;
}
- private static Consumer warnWhenCucumberOptionsIsUsed() {
+ private static void warnWhenCucumberOptionsIsUsed(String commandLineOptions) {
// Quite a few old blogs still recommend the use of cucumber.options
// This should take care of recurring question involving this property.
- return commandLineOptions -> log.warn(() -> String.format("" +
+ log.warn(() -> String.format("" +
"Passing commandline options via the property '%s' is no longer supported. " +
"Please use individual properties instead. " +
"See the java doc on %s for details.",
diff --git a/cucumber-core/src/main/java/io/cucumber/core/options/CucumberPropertiesProvider.java b/cucumber-core/src/main/java/io/cucumber/core/options/CucumberPropertiesProvider.java
index 24e5128cf9..825772128c 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/options/CucumberPropertiesProvider.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/options/CucumberPropertiesProvider.java
@@ -1,7 +1,9 @@
package io.cucumber.core.options;
+import org.jspecify.annotations.Nullable;
+
@FunctionalInterface
public interface CucumberPropertiesProvider {
- String get(String key);
+ @Nullable String get(String key);
}
diff --git a/cucumber-core/src/main/java/io/cucumber/core/options/CurlOption.java b/cucumber-core/src/main/java/io/cucumber/core/options/CurlOption.java
index 978fca0560..d6fc597cb6 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/options/CurlOption.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/options/CurlOption.java
@@ -2,7 +2,6 @@
import java.net.InetSocketAddress;
import java.net.Proxy;
-import java.net.Proxy.Type;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.AbstractMap.SimpleEntry;
@@ -11,6 +10,8 @@
import java.util.Map.Entry;
import static java.net.Proxy.NO_PROXY;
+import static java.net.Proxy.Type.HTTP;
+import static java.net.Proxy.Type.SOCKS;
import static java.util.Arrays.asList;
import static java.util.Objects.requireNonNull;
@@ -90,9 +91,9 @@ private static Proxy parseProxy(String arg) {
Proxy.Type type;
if (protocol.equalsIgnoreCase("http") || protocol.equalsIgnoreCase("https")) {
- type = Type.HTTP;
+ type = HTTP;
} else if (protocol.equalsIgnoreCase("socks")) {
- type = Type.SOCKS;
+ type = SOCKS;
} else {
throw new IllegalArgumentException("'" + arg + "' did not have a valid proxy protocol");
}
diff --git a/cucumber-core/src/main/java/io/cucumber/core/options/FeatureWithLinesOrRerunPath.java b/cucumber-core/src/main/java/io/cucumber/core/options/FeatureWithLinesOrRerunPath.java
index 9e3fd3d839..62f6a9943a 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/options/FeatureWithLinesOrRerunPath.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/options/FeatureWithLinesOrRerunPath.java
@@ -1,6 +1,7 @@
package io.cucumber.core.options;
import io.cucumber.core.feature.FeatureWithLines;
+import org.jspecify.annotations.Nullable;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -32,11 +33,11 @@
*/
class FeatureWithLinesOrRerunPath {
- private final FeatureWithLines featureWithLines;
- private final Collection featuresWithLinesToRerun;
+ private final @Nullable FeatureWithLines featureWithLines;
+ private final @Nullable Collection featuresWithLinesToRerun;
FeatureWithLinesOrRerunPath(
- FeatureWithLines featureWithLines, Collection featuresWithLinesToRerun
+ @Nullable FeatureWithLines featureWithLines, @Nullable Collection featuresWithLinesToRerun
) {
this.featureWithLines = featureWithLines;
this.featuresWithLinesToRerun = featuresWithLinesToRerun;
diff --git a/cucumber-core/src/main/java/io/cucumber/core/options/PickleOrderParser.java b/cucumber-core/src/main/java/io/cucumber/core/options/PickleOrderParser.java
index 3c8c8a42b3..883df47c76 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/options/PickleOrderParser.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/options/PickleOrderParser.java
@@ -14,6 +14,10 @@ final class PickleOrderParser {
private static final Logger log = LoggerFactory.getLogger(PickleOrderParser.class);
private static final Pattern RANDOM_AND_SEED_PATTERN = Pattern.compile("random(?::(\\d+))?");
+
+ private PickleOrderParser(){
+ /* no-op */
+ }
static PickleOrder parse(String argument) {
if ("reverse".equals(argument)) {
@@ -34,7 +38,7 @@ static PickleOrder parse(String argument) {
if (seedString != null) {
seed = Long.parseLong(seedString);
} else {
- seed = Math.abs(new Random().nextLong());
+ seed = new Random().nextLong(Long.MAX_VALUE);
log.info(() -> "Using random scenario order. Seed: " + seed);
}
return StandardPickleOrders.random(seed);
diff --git a/cucumber-core/src/main/java/io/cucumber/core/options/PluginOption.java b/cucumber-core/src/main/java/io/cucumber/core/options/PluginOption.java
index f95dbe0f10..b727466e6d 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/options/PluginOption.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/options/PluginOption.java
@@ -20,7 +20,7 @@
import io.cucumber.plugin.ConcurrentEventListener;
import io.cucumber.plugin.EventListener;
import io.cucumber.plugin.Plugin;
-import io.cucumber.plugin.SummaryPrinter;
+import org.jspecify.annotations.Nullable;
import java.util.HashMap;
import java.util.HashSet;
@@ -35,7 +35,7 @@
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.joining;
-public class PluginOption implements Options.Plugin {
+public final class PluginOption implements Options.Plugin {
private static final Logger log = LoggerFactory.getLogger(PluginOption.class);
@@ -88,9 +88,9 @@ public class PluginOption implements Options.Plugin {
private final String pluginString;
private final Class extends Plugin> pluginClass;
- private final String argument;
+ private final @Nullable String argument;
- private PluginOption(String pluginString, Class extends Plugin> pluginClass, String argument) {
+ private PluginOption(String pluginString, Class extends Plugin> pluginClass, @Nullable String argument) {
this.pluginString = requireNonNull(pluginString);
this.pluginClass = requireNonNull(pluginClass);
this.argument = argument;
@@ -190,7 +190,7 @@ public Class extends Plugin> pluginClass() {
}
@Override
- public String argument() {
+ public @Nullable String argument() {
return argument;
}
@@ -204,10 +204,6 @@ boolean isEventListener() {
|| ConcurrentEventListener.class.isAssignableFrom(pluginClass);
}
- boolean isSummaryPrinter() {
- return SummaryPrinter.class.isAssignableFrom(pluginClass);
- }
-
@Override
public boolean equals(Object o) {
if (this == o)
diff --git a/cucumber-core/src/main/java/io/cucumber/core/options/RerunPath.java b/cucumber-core/src/main/java/io/cucumber/core/options/RerunPath.java
index bf88121f56..e5cd7f25a6 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/options/RerunPath.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/options/RerunPath.java
@@ -17,7 +17,11 @@
* Either a path to a rerun file or a directory containing exclusively rerun
* files.
*/
-class RerunPath {
+final class RerunPath {
+
+ private RerunPath() {
+ /* no-op */
+ }
static Collection parse(Path rerunFileOrDirectory) {
return listRerunFiles(rerunFileOrDirectory).stream()
@@ -29,6 +33,7 @@ static Collection parse(Path rerunFileOrDirectory) {
private static Set listRerunFiles(Path path) {
class FileCollector extends SimpleFileVisitor {
final Set paths = new HashSet<>();
+
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
if (!Files.isDirectory(file)) {
diff --git a/cucumber-core/src/main/java/io/cucumber/core/options/RuntimeOptions.java b/cucumber-core/src/main/java/io/cucumber/core/options/RuntimeOptions.java
index 795d74b946..6952b1133b 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/options/RuntimeOptions.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/options/RuntimeOptions.java
@@ -10,6 +10,7 @@
import io.cucumber.core.plugin.PublishFormatter;
import io.cucumber.core.snippets.SnippetType;
import io.cucumber.tagexpressions.Expression;
+import org.jspecify.annotations.Nullable;
import java.net.URI;
import java.util.ArrayList;
@@ -21,7 +22,6 @@
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.regex.Pattern;
-import java.util.stream.Collectors;
import static io.cucumber.core.resource.ClasspathSupport.rootPackageUri;
import static java.lang.Boolean.FALSE;
@@ -51,10 +51,10 @@ public final class RuntimeOptions implements
private int threads = 1;
private PickleOrder pickleOrder = StandardPickleOrders.lexicalUriOrder();
private int count = 0;
- private Class extends ObjectFactory> objectFactoryClass;
- private Class extends UuidGenerator> uuidGeneratorClass;
- private String publishToken;
- private Boolean publish;
+ private @Nullable Class extends ObjectFactory> objectFactoryClass;
+ private @Nullable Class extends UuidGenerator> uuidGeneratorClass;
+ private @Nullable String publishToken;
+ private @Nullable Boolean publish;
// Disable the banner advertising the hosted cucumber reports by default
// until the uncertainty around the projects future is resolved. It would
// not be proper to advertise a service that may be discontinued to new
@@ -133,6 +133,7 @@ public boolean isMonochrome() {
return monochrome;
}
+ @Override
public boolean isWip() {
return wip;
}
@@ -161,7 +162,7 @@ public SnippetType getSnippetType() {
}
@Override
- public Class extends ObjectFactory> getObjectFactoryClass() {
+ public @Nullable Class extends ObjectFactory> getObjectFactoryClass() {
return objectFactoryClass;
}
@@ -170,7 +171,7 @@ void setObjectFactoryClass(Class extends ObjectFactory> objectFactoryClass) {
}
@Override
- public Class extends UuidGenerator> getUuidGeneratorClass() {
+ public @Nullable Class extends UuidGenerator> getUuidGeneratorClass() {
return uuidGeneratorClass;
}
@@ -193,11 +194,11 @@ void setGlue(List parsedGlue) {
@Override
public List getFeaturePaths() {
- return unmodifiableList(featurePaths.stream()
+ return featurePaths.stream()
.map(FeatureWithLines::uri)
.sorted()
.distinct()
- .collect(Collectors.toList()));
+ .toList();
}
void setFeaturePaths(List featurePaths) {
diff --git a/cucumber-core/src/main/java/io/cucumber/core/options/RuntimeOptionsBuilder.java b/cucumber-core/src/main/java/io/cucumber/core/options/RuntimeOptionsBuilder.java
index 58b6792bc5..46fd9404e0 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/options/RuntimeOptionsBuilder.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/options/RuntimeOptionsBuilder.java
@@ -8,6 +8,7 @@
import io.cucumber.core.plugin.Options;
import io.cucumber.core.snippets.SnippetType;
import io.cucumber.tagexpressions.Expression;
+import org.jspecify.annotations.Nullable;
import java.net.URI;
import java.util.ArrayList;
@@ -23,23 +24,23 @@ public final class RuntimeOptionsBuilder {
private final List parsedFeaturePaths = new ArrayList<>();
private final List parsedGlue = new ArrayList<>();
private final List plugins = new ArrayList<>();
- private List parsedRerunPaths = null;
- private Integer parsedThreads = null;
- private Boolean parsedDryRun = null;
- private Boolean parsedMonochrome = null;
- private SnippetType parsedSnippetType = null;
- private Boolean parsedWip = null;
- private PickleOrder parsedPickleOrder = null;
- private Integer parsedCount = null;
- private Class extends ObjectFactory> parsedObjectFactoryClass = null;
- private Class extends UuidGenerator> parsedUuidGeneratorClass = null;
- private Boolean addDefaultSummaryPrinter = null;
+ private @Nullable List parsedRerunPaths = null;
+ private @Nullable Integer parsedThreads = null;
+ private @Nullable Boolean parsedDryRun = null;
+ private @Nullable Boolean parsedMonochrome = null;
+ private @Nullable SnippetType parsedSnippetType = null;
+ private @Nullable Boolean parsedWip = null;
+ private @Nullable PickleOrder parsedPickleOrder = null;
+ private @Nullable Integer parsedCount = null;
+ private @Nullable Class extends ObjectFactory> parsedObjectFactoryClass = null;
+ private @Nullable Class extends UuidGenerator> parsedUuidGeneratorClass = null;
+ private @Nullable Boolean addDefaultSummaryPrinter = null;
private boolean addDefaultGlueIfAbsent;
private boolean addDefaultFeaturePathIfAbsent;
- private String parsedPublishToken = null;
- private Boolean parsedPublish;
- private Boolean parsedPublishQuiet;
- private Boolean parsedEnablePublishPlugin;
+ private @Nullable String parsedPublishToken = null;
+ private @Nullable Boolean parsedPublish;
+ private @Nullable Boolean parsedPublishQuiet;
+ private @Nullable Boolean parsedEnablePublishPlugin;
public RuntimeOptionsBuilder addRerun(Collection featureWithLines) {
if (parsedRerunPaths == null) {
@@ -66,7 +67,7 @@ public RuntimeOptionsBuilder addNameFilter(Pattern pattern) {
public RuntimeOptionsBuilder addPluginName(String pluginSpecification) {
PluginOption pluginOption = PluginOption.parse(pluginSpecification);
- if (pluginOption.isEventListener() || pluginOption.isSummaryPrinter()) {
+ if (pluginOption.isEventListener()) {
plugins.add(pluginOption);
} else {
throw new CucumberException("Unrecognized plugin: " + pluginSpecification);
diff --git a/cucumber-core/src/main/java/io/cucumber/core/options/ShellWords.java b/cucumber-core/src/main/java/io/cucumber/core/options/ShellWords.java
index aed166ee6b..253d585c26 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/options/ShellWords.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/options/ShellWords.java
@@ -5,11 +5,12 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-class ShellWords {
+final class ShellWords {
private static final Pattern SHELLWORDS_PATTERN = Pattern.compile("[^\\s'\"]+|[']([^']*)[']|[\"]([^\"]*)[\"]");
private ShellWords() {
+ /* no-op */
}
static List parse(String cmdline) {
diff --git a/cucumber-core/src/main/java/io/cucumber/core/options/package-info.java b/cucumber-core/src/main/java/io/cucumber/core/options/package-info.java
new file mode 100644
index 0000000000..3b757b67f1
--- /dev/null
+++ b/cucumber-core/src/main/java/io/cucumber/core/options/package-info.java
@@ -0,0 +1,4 @@
+@NullMarked
+package io.cucumber.core.options;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/cucumber-core/src/main/java/io/cucumber/core/order/package-info.java b/cucumber-core/src/main/java/io/cucumber/core/order/package-info.java
new file mode 100644
index 0000000000..9e73eed23d
--- /dev/null
+++ b/cucumber-core/src/main/java/io/cucumber/core/order/package-info.java
@@ -0,0 +1,4 @@
+@NullMarked
+package io.cucumber.core.order;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/cucumber-core/src/main/java/io/cucumber/core/plugin/Banner.java b/cucumber-core/src/main/java/io/cucumber/core/plugin/Banner.java
index ed8a1fe45f..70a7307a94 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/plugin/Banner.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/plugin/Banner.java
@@ -11,45 +11,13 @@
final class Banner {
private final boolean monochrome;
-
- static final class Line {
- private final List spans;
-
- Line(Span... spans) {
- this.spans = asList(spans);
- }
-
- Line(String text, AnsiEscapes... escapes) {
- this(new Span(text, escapes));
- }
-
- int length() {
- return spans.stream().map(span -> span.text.length()).mapToInt(Integer::intValue).sum();
- }
- }
-
- static final class Span {
- private final String text;
- private final AnsiEscapes[] escapes;
-
- Span(String text) {
- this.text = text;
- this.escapes = new AnsiEscapes[0];
- }
-
- Span(String text, AnsiEscapes... escapes) {
- this.text = text;
- this.escapes = escapes;
- }
- }
-
private final PrintStream out;
Banner(PrintStream out, boolean monochrome) {
this.out = out;
this.monochrome = monochrome;
}
-
+
void print(List lines, AnsiEscapes... border) {
int maxLength = lines.stream().map(Line::length).max(comparingInt(a -> a))
.orElseThrow(NoSuchElementException::new);
@@ -82,4 +50,36 @@ void print(List lines, AnsiEscapes... border) {
private String times(char c, int count) {
return new String(new char[count]).replace('\0', c);
}
+
+ static final class Line {
+ private final List spans;
+
+ Line(Span... spans) {
+ this.spans = asList(spans);
+ }
+
+ Line(String text, AnsiEscapes... escapes) {
+ this(new Span(text, escapes));
+ }
+
+ int length() {
+ return spans.stream().map(span -> span.text.length()).mapToInt(Integer::intValue).sum();
+ }
+ }
+
+ static final class Span {
+ private final String text;
+ private final AnsiEscapes[] escapes;
+
+ Span(String text) {
+ this.text = text;
+ this.escapes = new AnsiEscapes[0];
+ }
+
+ Span(String text, AnsiEscapes... escapes) {
+ this.text = text;
+ this.escapes = escapes;
+ }
+ }
+
}
diff --git a/cucumber-core/src/main/java/io/cucumber/core/plugin/CanonicalEventOrder.java b/cucumber-core/src/main/java/io/cucumber/core/plugin/CanonicalEventOrder.java
index cc59646fbf..a9dad526d1 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/plugin/CanonicalEventOrder.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/plugin/CanonicalEventOrder.java
@@ -53,8 +53,8 @@ public int compare(Event a, Event b) {
.thenComparing(Event::getInstant);
private static int testCaseEvents(Event a, Event b) {
- if (a instanceof TestCaseEvent && b instanceof TestCaseEvent) {
- return testCaseOrder.compare((TestCaseEvent) a, (TestCaseEvent) b);
+ if (a instanceof TestCaseEvent testCaseEventA && b instanceof TestCaseEvent testCaseEventB) {
+ return testCaseOrder.compare(testCaseEventA, testCaseEventB);
}
return 0;
}
diff --git a/cucumber-core/src/main/java/io/cucumber/core/plugin/CanonicalOrderEventPublisher.java b/cucumber-core/src/main/java/io/cucumber/core/plugin/CanonicalOrderEventPublisher.java
index 5e687f9604..ea496afcca 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/plugin/CanonicalOrderEventPublisher.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/plugin/CanonicalOrderEventPublisher.java
@@ -4,12 +4,12 @@
import io.cucumber.plugin.event.Event;
import io.cucumber.plugin.event.TestRunFinished;
-import java.util.LinkedList;
+import java.util.ArrayList;
import java.util.List;
final class CanonicalOrderEventPublisher extends AbstractEventPublisher {
- private final List queue = new LinkedList<>();
+ private final List queue = new ArrayList<>();
public void handle(final Event event) {
queue.add(event);
diff --git a/cucumber-core/src/main/java/io/cucumber/core/plugin/Format.java b/cucumber-core/src/main/java/io/cucumber/core/plugin/Format.java
index db8dece806..91438d8b97 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/plugin/Format.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/plugin/Format.java
@@ -20,6 +20,7 @@ private Color(AnsiEscapes... escapes) {
this.escapes = escapes;
}
+ @Override
public String text(String text) {
StringBuilder sb = new StringBuilder();
for (AnsiEscapes escape : escapes) {
@@ -34,10 +35,10 @@ public String text(String text) {
}
- class Monochrome implements Format {
+ final class Monochrome implements Format {
private Monochrome() {
-
+ /* no-op */
}
@Override
diff --git a/cucumber-core/src/main/java/io/cucumber/core/plugin/Formats.java b/cucumber-core/src/main/java/io/cucumber/core/plugin/Formats.java
deleted file mode 100644
index a1be2581bb..0000000000
--- a/cucumber-core/src/main/java/io/cucumber/core/plugin/Formats.java
+++ /dev/null
@@ -1,84 +0,0 @@
-package io.cucumber.core.plugin;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import static io.cucumber.core.plugin.Format.color;
-
-interface Formats {
-
- Format get(String key);
-
- String up(int n);
-
- static Formats monochrome() {
- return new Monochrome();
- }
-
- static Formats ansi() {
- return new Ansi();
- }
-
- final class Monochrome implements Formats {
-
- private Monochrome() {
-
- }
-
- public Format get(String key) {
- return text -> text;
- }
-
- public String up(int n) {
- return "";
- }
-
- }
-
- final class Ansi implements Formats {
-
- private Ansi() {
-
- }
-
- private static final Map formats = new HashMap() {
- {
- // Never used, but avoids NPE in formatters.
- put("undefined", color(AnsiEscapes.YELLOW));
- put("undefined_arg", color(AnsiEscapes.YELLOW, AnsiEscapes.INTENSITY_BOLD));
- put("unused", color(AnsiEscapes.YELLOW));
- put("unused_arg", color(AnsiEscapes.YELLOW, AnsiEscapes.INTENSITY_BOLD));
- put("pending", color(AnsiEscapes.YELLOW));
- put("pending_arg", color(AnsiEscapes.YELLOW, AnsiEscapes.INTENSITY_BOLD));
- put("executing", color(AnsiEscapes.GREY));
- put("executing_arg", color(AnsiEscapes.GREY, AnsiEscapes.INTENSITY_BOLD));
- put("failed", color(AnsiEscapes.RED));
- put("failed_arg", color(AnsiEscapes.RED, AnsiEscapes.INTENSITY_BOLD));
- put("ambiguous", color(AnsiEscapes.RED));
- put("ambiguous_arg", color(AnsiEscapes.RED, AnsiEscapes.INTENSITY_BOLD));
- put("passed", color(AnsiEscapes.GREEN));
- put("passed_arg", color(AnsiEscapes.GREEN, AnsiEscapes.INTENSITY_BOLD));
- put("outline", color(AnsiEscapes.CYAN));
- put("outline_arg", color(AnsiEscapes.CYAN, AnsiEscapes.INTENSITY_BOLD));
- put("skipped", color(AnsiEscapes.CYAN));
- put("skipped_arg", color(AnsiEscapes.CYAN, AnsiEscapes.INTENSITY_BOLD));
- put("comment", color(AnsiEscapes.GREY));
- put("tag", color(AnsiEscapes.CYAN));
- put("output", color(AnsiEscapes.BLUE));
- }
- };
-
- public Format get(String key) {
- Format format = formats.get(key);
- if (format == null)
- throw new NullPointerException("No format for key " + key);
- return format;
- }
-
- public String up(int n) {
- return AnsiEscapes.up(n).toString();
- }
-
- }
-
-}
diff --git a/cucumber-core/src/main/java/io/cucumber/core/plugin/HtmlFormatter.java b/cucumber-core/src/main/java/io/cucumber/core/plugin/HtmlFormatter.java
index 64ca508010..4f8f277688 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/plugin/HtmlFormatter.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/plugin/HtmlFormatter.java
@@ -12,9 +12,12 @@ public final class HtmlFormatter implements ConcurrentEventListener {
private final MessagesToHtmlWriter writer;
- @SuppressWarnings("WeakerAccess") // Used by PluginFactory
- public HtmlFormatter(OutputStream out) throws IOException {
- this.writer = new MessagesToHtmlWriter(out, Jackson.OBJECT_MAPPER::writeValue);
+ // Used by PluginFactory
+ @SuppressWarnings("WeakerAccess")
+ public HtmlFormatter(OutputStream out) {
+ this.writer = MessagesToHtmlWriter //
+ .builder(Jackson.OBJECT_MAPPER::writeValue) //
+ .build(out);
}
@Override
diff --git a/cucumber-core/src/main/java/io/cucumber/core/plugin/JsonFormatter.java b/cucumber-core/src/main/java/io/cucumber/core/plugin/JsonFormatter.java
index dba97e9619..7fc65c339d 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/plugin/JsonFormatter.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/plugin/JsonFormatter.java
@@ -10,15 +10,13 @@
import java.io.OutputStream;
import java.net.URI;
-import static io.cucumber.jsonformatter.MessagesToJsonWriter.builder;
-
public final class JsonFormatter implements ConcurrentEventListener {
private final MessagesToJsonWriter writer;
public JsonFormatter(OutputStream out) {
URI cwdUri = new File("").toPath().toUri();
- this.writer = builder(Jackson.OBJECT_MAPPER::writeValue)
+ this.writer = MessagesToJsonWriter.builder(Jackson.OBJECT_MAPPER::writeValue)
.relativizeAgainst(cwdUri)
.build(out);
}
diff --git a/cucumber-core/src/main/java/io/cucumber/core/plugin/NoPublishFormatter.java b/cucumber-core/src/main/java/io/cucumber/core/plugin/NoPublishFormatter.java
index b7e0d4ac52..ed79d98dc2 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/plugin/NoPublishFormatter.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/plugin/NoPublishFormatter.java
@@ -6,6 +6,7 @@
import io.cucumber.plugin.event.EventPublisher;
import java.io.PrintStream;
+import java.util.Locale;
import static io.cucumber.core.options.Constants.PLUGIN_PUBLISH_ENABLED_PROPERTY_NAME;
import static io.cucumber.core.options.Constants.PLUGIN_PUBLISH_QUIET_PROPERTY_NAME;
@@ -16,6 +17,8 @@ public final class NoPublishFormatter implements ConcurrentEventListener, ColorA
private final PrintStream out;
private boolean monochrome = false;
+ // Used in plugins
+ @SuppressWarnings("unused")
public NoPublishFormatter() {
this(System.err);
}
@@ -62,7 +65,7 @@ private void printBanner() {
new Banner.Span("true", AnsiEscapes.CYAN)),
new Banner.Line(
new Banner.Span("Environment variable: "),
- new Banner.Span(PLUGIN_PUBLISH_ENABLED_PROPERTY_NAME.toUpperCase().replace('.', '_'),
+ new Banner.Span(PLUGIN_PUBLISH_ENABLED_PROPERTY_NAME.toUpperCase(Locale.US).replace('.', '_'),
AnsiEscapes.CYAN),
new Banner.Span("="),
new Banner.Span("true", AnsiEscapes.CYAN)),
diff --git a/cucumber-core/src/main/java/io/cucumber/core/plugin/Options.java b/cucumber-core/src/main/java/io/cucumber/core/plugin/Options.java
index c9ff227716..1f53a96745 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/plugin/Options.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/plugin/Options.java
@@ -1,5 +1,7 @@
package io.cucumber.core.plugin;
+import org.jspecify.annotations.Nullable;
+
public interface Options {
Iterable plugins();
@@ -12,7 +14,7 @@ interface Plugin {
Class extends io.cucumber.plugin.Plugin> pluginClass();
- String argument();
+ @Nullable String argument();
String pluginString();
diff --git a/cucumber-core/src/main/java/io/cucumber/core/plugin/PluginFactory.java b/cucumber-core/src/main/java/io/cucumber/core/plugin/PluginFactory.java
index 4a978c6669..78733dd51f 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/plugin/PluginFactory.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/plugin/PluginFactory.java
@@ -5,6 +5,7 @@
import io.cucumber.core.logging.LoggerFactory;
import io.cucumber.core.options.CurlOption;
import io.cucumber.plugin.Plugin;
+import org.jspecify.annotations.Nullable;
import java.io.File;
import java.io.FileNotFoundException;
@@ -49,9 +50,9 @@ public final class PluginFactory {
Appendable.class
};
- private String pluginUsingDefaultOut = null;
+ private @Nullable String pluginUsingDefaultOut = null;
- private PrintStream defaultOut = new PrintStream(System.out) {
+ private @Nullable PrintStream defaultOut = new PrintStream(System.out) {
@Override
public void close() {
// We have no intention to close System.out
@@ -66,10 +67,11 @@ Plugin create(Options.Plugin plugin) {
}
}
- private T instantiate(String pluginString, Class pluginClass, String argument)
+ private T instantiate(String pluginString, Class pluginClass, @Nullable String argument)
throws IOException, URISyntaxException {
Map, Constructor> singleArgConstructors = findSingleArgConstructors(pluginClass);
- if (argument == null) {// No argument passed
+ // No argument passed
+ if (argument == null) {
Constructor outputStreamConstructor = singleArgConstructors.get(OutputStream.class);
if (outputStreamConstructor != null) {
return newInstance(outputStreamConstructor, defaultOutOrFailIfAlreadyUsed(pluginString));
@@ -105,7 +107,8 @@ private Map, Constructor> findSingleArgConstructors(Class plu
for (Class> ctorArgClass : CTOR_PARAMETERS) {
try {
result.put(ctorArgClass, pluginClass.getConstructor(ctorArgClass));
- } catch (NoSuchMethodException ignore) {
+ } catch (NoSuchMethodException ignored) {
+ /* no-op */
}
}
return result;
@@ -137,7 +140,7 @@ private PrintStream defaultOutOrFailIfAlreadyUsed(String pluginString) {
}
}
- private Constructor findEmptyConstructor(Class pluginClass) {
+ private @Nullable Constructor findEmptyConstructor(Class pluginClass) {
try {
return pluginClass.getConstructor();
} catch (NoSuchMethodException ignore) {
@@ -160,11 +163,7 @@ private Object convert(String arg, Class> ctorArgClass, String pluginString, C
return arg;
}
if (ctorArgClass.equals(OutputStream.class)) {
- if (arg == null) {
- return defaultOutOrFailIfAlreadyUsed(pluginString);
- } else {
- return openStream(arg);
- }
+ return openStream(arg);
}
if (ctorArgClass.equals(Appendable.class)) {
diff --git a/cucumber-core/src/main/java/io/cucumber/core/plugin/Plugins.java b/cucumber-core/src/main/java/io/cucumber/core/plugin/Plugins.java
index 7c13621240..7f9c52b877 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/plugin/Plugins.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/plugin/Plugins.java
@@ -4,9 +4,9 @@
import io.cucumber.plugin.ConcurrentEventListener;
import io.cucumber.plugin.EventListener;
import io.cucumber.plugin.Plugin;
-import io.cucumber.plugin.StrictAware;
import io.cucumber.plugin.event.Event;
import io.cucumber.plugin.event.EventPublisher;
+import org.jspecify.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
@@ -17,7 +17,7 @@ public final class Plugins {
private final PluginFactory pluginFactory;
private final Options pluginOptions;
private boolean pluginNamesInstantiated;
- private EventPublisher orderedEventPublisher;
+ private @Nullable EventPublisher orderedEventPublisher;
public Plugins(PluginFactory pluginFactory, Options pluginOptions) {
this.pluginFactory = pluginFactory;
@@ -40,23 +40,14 @@ private List createPlugins() {
private void addPlugin(List plugins, Plugin plugin) {
plugins.add(plugin);
setMonochromeOnColorAwarePlugins(plugin);
- setStrictOnStrictAwarePlugins(plugin);
}
private void setMonochromeOnColorAwarePlugins(Plugin plugin) {
- if (plugin instanceof ColorAware) {
- ColorAware colorAware = (ColorAware) plugin;
+ if (plugin instanceof ColorAware colorAware) {
colorAware.setMonochrome(pluginOptions.isMonochrome());
}
}
- private void setStrictOnStrictAwarePlugins(Plugin plugin) {
- if (plugin instanceof StrictAware) {
- StrictAware strictAware = (StrictAware) plugin;
- strictAware.setStrict(true);
- }
- }
-
public List getPlugins() {
return plugins;
}
@@ -67,21 +58,21 @@ public void addPlugin(Plugin plugin) {
public void setEventBusOnEventListenerPlugins(EventPublisher eventPublisher) {
for (Plugin plugin : plugins) {
- if (plugin instanceof ConcurrentEventListener) {
- ((ConcurrentEventListener) plugin).setEventPublisher(eventPublisher, false);
- } else if (plugin instanceof EventListener) {
- ((EventListener) plugin).setEventPublisher(eventPublisher);
+ if (plugin instanceof ConcurrentEventListener eventListener) {
+ eventListener.setEventPublisher(eventPublisher, false);
+ } else if (plugin instanceof EventListener eventListener) {
+ eventListener.setEventPublisher(eventPublisher);
}
}
}
public void setSerialEventBusOnEventListenerPlugins(EventPublisher eventPublisher) {
for (Plugin plugin : plugins) {
- if (plugin instanceof ConcurrentEventListener) {
- ((ConcurrentEventListener) plugin).setEventPublisher(eventPublisher, true);
- } else if (plugin instanceof EventListener) {
+ if (plugin instanceof ConcurrentEventListener eventListener) {
+ eventListener.setEventPublisher(eventPublisher, true);
+ } else if (plugin instanceof EventListener eventListener) {
EventPublisher orderedEventPublisher = getOrderedEventPublisher(eventPublisher);
- ((EventListener) plugin).setEventPublisher(orderedEventPublisher);
+ eventListener.setEventPublisher(orderedEventPublisher);
}
}
}
diff --git a/cucumber-core/src/main/java/io/cucumber/core/plugin/PublishFormatter.java b/cucumber-core/src/main/java/io/cucumber/core/plugin/PublishFormatter.java
index 466fd0db60..210dd36d8b 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/plugin/PublishFormatter.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/plugin/PublishFormatter.java
@@ -5,6 +5,7 @@
import io.cucumber.plugin.ColorAware;
import io.cucumber.plugin.ConcurrentEventListener;
import io.cucumber.plugin.event.EventPublisher;
+import org.jspecify.annotations.Nullable;
import java.io.IOException;
import java.util.Map;
@@ -15,7 +16,7 @@
public final class PublishFormatter implements ConcurrentEventListener, ColorAware {
/**
- * Where to publishes messages by default
+ * Where to publish messages by default
*/
public static final String DEFAULT_CUCUMBER_MESSAGE_STORE_URL = "https://messages.cucumber.io/api/reports -X GET";
@@ -45,7 +46,7 @@ public void setMonochrome(boolean monochrome) {
urlReporter.setMonochrome(monochrome);
}
- private static CurlOption createCurlOption(String token) {
+ private static CurlOption createCurlOption(@Nullable String token) {
// Note: This only includes properties from the environment and
// cucumber.properties. It does not include junit-platform.properties
// Fixing this requires an overhaul of the plugin system.
diff --git a/cucumber-core/src/main/java/io/cucumber/core/plugin/RerunFormatter.java b/cucumber-core/src/main/java/io/cucumber/core/plugin/RerunFormatter.java
index f9dc9ee416..26fe126eda 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/plugin/RerunFormatter.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/plugin/RerunFormatter.java
@@ -10,6 +10,7 @@
import io.cucumber.plugin.event.EventPublisher;
import io.cucumber.query.Query;
import io.cucumber.query.Repository;
+import org.jspecify.annotations.Nullable;
import java.io.File;
import java.io.OutputStream;
@@ -18,12 +19,13 @@
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
+import java.util.Map;
import java.util.Optional;
+import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.stream.Collector;
-import static io.cucumber.query.Repository.RepositoryFeature.INCLUDE_GHERKIN_DOCUMENTS;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.mapping;
@@ -36,7 +38,6 @@ public final class RerunFormatter implements ConcurrentEventListener {
private final PrintWriter writer;
private final Repository repository = Repository.builder()
- .feature(INCLUDE_GHERKIN_DOCUMENTS, true)
.build();
private final Query query = new Query(repository);
@@ -46,9 +47,9 @@ public RerunFormatter(OutputStream out) {
private static PrintWriter createPrintWriter(OutputStream out) {
return new PrintWriter(
- new OutputStreamWriter(
- requireNonNull(out),
- StandardCharsets.UTF_8));
+ new OutputStreamWriter(
+ requireNonNull(out),
+ StandardCharsets.UTF_8));
}
static URI relativize(URI uri) {
@@ -77,23 +78,6 @@ public void setEventPublisher(EventPublisher publisher) {
});
}
- private static final class UriAndLine {
- private final String uri;
- private final Long line;
-
- private UriAndLine(String uri, Long line) {
- this.uri = uri;
- this.line = line;
- }
-
- public String getUri() {
- return uri;
- }
-
- public Long getLine() {
- return line;
- }
- }
private void finishReport() {
query.findAllTestCaseStarted().stream()
@@ -107,25 +91,25 @@ private void finishReport() {
writer.close();
}
- private void printUriWithLines(String uri, TreeSet lines) {
+ private void printUriWithLines(String uri, Set lines) {
writer.println(renderFeatureWithLines(uri, lines));
}
- private static Collector>> groupByUriAndThenCollectLines() {
+ private static Collector>> groupByUriAndThenCollectLines() {
return groupingBy(
- UriAndLine::getUri,
- // Sort URIs
- TreeMap::new,
- mapping(
- UriAndLine::getLine,
- // Sort lines
- toCollection(TreeSet::new)));
+ UriAndLine::getUri,
+ // Sort URIs
+ TreeMap::new,
+ mapping(
+ UriAndLine::getLine,
+ // Sort lines
+ toCollection(TreeSet::new)));
}
- private static StringBuilder renderFeatureWithLines(String uri, TreeSet lines) {
+ private static StringBuilder renderFeatureWithLines(String uri, Set lines) {
String path = relativize(URI.create(uri)).toString();
StringBuilder builder = new StringBuilder(path);
- for (Long line : lines) {
+ for (Integer line : lines) {
builder.append(':');
builder.append(line);
}
@@ -134,7 +118,7 @@ private static StringBuilder renderFeatureWithLines(String uri, TreeSet li
private UriAndLine createUriAndLine(Pickle pickle) {
String uri = pickle.getUri();
- Long line = query.findLocationOf(pickle).map(Location::getLine).orElse(null);
+ Integer line = pickle.getLocation().map(Location::getLine).orElse(null);
return new UriAndLine(uri, line);
}
@@ -146,4 +130,22 @@ private boolean isNotPassingOrSkipped(TestCaseStarted event) {
.isPresent();
}
+ private static final class UriAndLine {
+ private final String uri;
+ private final @Nullable Integer line;
+
+ private UriAndLine(String uri, @Nullable Integer line) {
+ this.uri = uri;
+ this.line = line;
+ }
+
+ String getUri() {
+ return uri;
+ }
+
+ @Nullable Integer getLine() {
+ return line;
+ }
+ }
+
}
diff --git a/cucumber-core/src/main/java/io/cucumber/core/plugin/TeamCityPlugin.java b/cucumber-core/src/main/java/io/cucumber/core/plugin/TeamCityPlugin.java
index 9a4102a8cb..a9e3590518 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/plugin/TeamCityPlugin.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/plugin/TeamCityPlugin.java
@@ -18,12 +18,13 @@
* href=https://www.jetbrains.com/help/teamcity/service-messages.html>TeamCity
* - Service Messages
*/
-public class TeamCityPlugin implements ConcurrentEventListener {
+public final class TeamCityPlugin implements ConcurrentEventListener {
private final OutputStream out;
private MessagesToTeamCityWriter writer;
- @SuppressWarnings("unused") // Used by PluginFactory
+ // Used by PluginFactory
+ @SuppressWarnings("unused")
public TeamCityPlugin() {
// This plugin prints markers for Team City and IntelliJ IDEA that
// allows them to associate the output to specific test cases. Printing
@@ -39,6 +40,7 @@ public void close() {
TeamCityPlugin(OutputStream out) {
this.out = out;
+ this.writer = MessagesToTeamCityWriter.builder().build(out);
}
@Override
diff --git a/cucumber-core/src/main/java/io/cucumber/core/plugin/TestSourcesModel.java b/cucumber-core/src/main/java/io/cucumber/core/plugin/TestSourcesModel.java
deleted file mode 100644
index 19d13b8008..0000000000
--- a/cucumber-core/src/main/java/io/cucumber/core/plugin/TestSourcesModel.java
+++ /dev/null
@@ -1,262 +0,0 @@
-package io.cucumber.core.plugin;
-
-import io.cucumber.gherkin.GherkinParser;
-import io.cucumber.messages.types.Background;
-import io.cucumber.messages.types.Envelope;
-import io.cucumber.messages.types.Examples;
-import io.cucumber.messages.types.Feature;
-import io.cucumber.messages.types.FeatureChild;
-import io.cucumber.messages.types.GherkinDocument;
-import io.cucumber.messages.types.Rule;
-import io.cucumber.messages.types.RuleChild;
-import io.cucumber.messages.types.Scenario;
-import io.cucumber.messages.types.Source;
-import io.cucumber.messages.types.SourceMediaType;
-import io.cucumber.messages.types.Step;
-import io.cucumber.messages.types.TableRow;
-import io.cucumber.plugin.event.TestSourceRead;
-
-import java.io.File;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Optional;
-import java.util.regex.Pattern;
-import java.util.stream.Stream;
-
-/**
- * Internal class, still used by Serenity.
- *
- * @see cucumber/cucumber-jvm#3076
- * @deprecated for removal, use messages + query.
- */
-@SuppressWarnings("unused")
-@Deprecated
-final class TestSourcesModel {
-
- private final Map pathToReadEventMap = new HashMap<>();
- private final Map pathToAstMap = new HashMap<>();
- private final Map> pathToNodeMap = new HashMap<>();
-
- static Scenario getScenarioDefinition(AstNode astNode) {
- AstNode candidate = astNode;
- while (candidate != null && !(candidate.node instanceof Scenario)) {
- candidate = candidate.parent;
- }
- return candidate == null ? null : (Scenario) candidate.node;
- }
-
- static boolean isBackgroundStep(AstNode astNode) {
- return astNode.parent.node instanceof Background;
- }
-
- static String calculateId(AstNode astNode) {
- Object node = astNode.node;
- if (node instanceof Rule) {
- return calculateId(astNode.parent) + ";" + convertToId(((Rule) node).getName());
- }
- if (node instanceof Scenario) {
- return calculateId(astNode.parent) + ";" + convertToId(((Scenario) node).getName());
- }
- if (node instanceof ExamplesRowWrapperNode) {
- return calculateId(astNode.parent) + ";" + (((ExamplesRowWrapperNode) node).bodyRowIndex + 2);
- }
- if (node instanceof TableRow) {
- return calculateId(astNode.parent) + ";" + 1;
- }
- if (node instanceof Examples) {
- return calculateId(astNode.parent) + ";" + convertToId(((Examples) node).getName());
- }
- if (node instanceof Feature) {
- return convertToId(((Feature) node).getName());
- }
- return "";
- }
-
- private static final Pattern replacementPattern = Pattern.compile("[\\s'_,!]");
-
- static String convertToId(String name) {
- return replacementPattern.matcher(name).replaceAll("-").toLowerCase();
- }
-
- static URI relativize(URI uri) {
- if (!"file".equals(uri.getScheme())) {
- return uri;
- }
- if (!uri.isAbsolute()) {
- return uri;
- }
-
- try {
- URI root = new File("").toURI();
- URI relative = root.relativize(uri);
- // Scheme is lost by relativize
- return new URI("file", relative.getSchemeSpecificPart(), relative.getFragment());
- } catch (URISyntaxException e) {
- throw new IllegalArgumentException(e.getMessage(), e);
- }
- }
-
- void addTestSourceReadEvent(URI path, TestSourceRead event) {
- pathToReadEventMap.put(path, event);
- }
-
- Feature getFeature(URI path) {
- if (!pathToAstMap.containsKey(path)) {
- parseGherkinSource(path);
- }
- if (pathToAstMap.containsKey(path)) {
- return pathToAstMap.get(path).getFeature().orElse(null);
- }
- return null;
- }
-
- private void parseGherkinSource(URI path) {
- if (!pathToReadEventMap.containsKey(path)) {
- return;
- }
- String source = pathToReadEventMap.get(path).getSource();
-
- GherkinParser parser = GherkinParser.builder()
- .build();
-
- Stream envelopes = parser.parse(
- Envelope.of(new Source(path.toString(), source, SourceMediaType.TEXT_X_CUCUMBER_GHERKIN_PLAIN)));
-
- // TODO: What about empty gherkin docs?
- GherkinDocument gherkinDocument = envelopes
- .map(Envelope::getGherkinDocument)
- .filter(Optional::isPresent)
- .map(Optional::get)
- .findFirst()
- .orElse(null);
-
- pathToAstMap.put(path, gherkinDocument);
- Map nodeMap = new HashMap<>();
- // TODO: What about gherkin docs with no features?
- Feature feature = gherkinDocument.getFeature().get();
- AstNode currentParent = new AstNode(feature, null);
- for (FeatureChild child : feature.getChildren()) {
- processFeatureDefinition(nodeMap, child, currentParent);
- }
- pathToNodeMap.put(path, nodeMap);
-
- }
-
- private void processFeatureDefinition(Map nodeMap, FeatureChild child, AstNode currentParent) {
- child.getBackground().ifPresent(background -> processBackgroundDefinition(nodeMap, background, currentParent));
- child.getScenario().ifPresent(scenario -> processScenarioDefinition(nodeMap, scenario, currentParent));
- child.getRule().ifPresent(rule -> {
- AstNode childNode = new AstNode(rule, currentParent);
- nodeMap.put(rule.getLocation().getLine(), childNode);
- rule.getChildren().forEach(ruleChild -> processRuleDefinition(nodeMap, ruleChild, childNode));
- });
- }
-
- private void processBackgroundDefinition(
- Map nodeMap, Background background, AstNode currentParent
- ) {
- AstNode childNode = new AstNode(background, currentParent);
- nodeMap.put(background.getLocation().getLine(), childNode);
- for (Step step : background.getSteps()) {
- nodeMap.put(step.getLocation().getLine(), new AstNode(step, childNode));
- }
- }
-
- private void processScenarioDefinition(Map nodeMap, Scenario child, AstNode currentParent) {
- AstNode childNode = new AstNode(child, currentParent);
- nodeMap.put(child.getLocation().getLine(), childNode);
- for (io.cucumber.messages.types.Step step : child.getSteps()) {
- nodeMap.put(step.getLocation().getLine(), new AstNode(step, childNode));
- }
- if (!child.getExamples().isEmpty()) {
- processScenarioOutlineExamples(nodeMap, child, childNode);
- }
- }
-
- private void processRuleDefinition(Map nodeMap, RuleChild child, AstNode currentParent) {
- child.getBackground().ifPresent(background -> processBackgroundDefinition(nodeMap, background, currentParent));
- child.getScenario().ifPresent(scenario -> processScenarioDefinition(nodeMap, scenario, currentParent));
- }
-
- private void processScenarioOutlineExamples(
- Map nodeMap, Scenario scenarioOutline, AstNode parent
- ) {
- for (Examples examples : scenarioOutline.getExamples()) {
- AstNode examplesNode = new AstNode(examples, parent);
- // TODO: Can tables without headers even exist?
- TableRow headerRow = examples.getTableHeader().get();
- AstNode headerNode = new AstNode(headerRow, examplesNode);
- nodeMap.put(headerRow.getLocation().getLine(), headerNode);
- for (int i = 0; i < examples.getTableBody().size(); ++i) {
- TableRow examplesRow = examples.getTableBody().get(i);
- Object rowNode = new ExamplesRowWrapperNode(examplesRow, i);
- AstNode expandedScenarioNode = new AstNode(rowNode, examplesNode);
- nodeMap.put(examplesRow.getLocation().getLine(), expandedScenarioNode);
- }
- }
- }
-
- AstNode getAstNode(URI path, int line) {
- if (!pathToNodeMap.containsKey(path)) {
- parseGherkinSource(path);
- }
- if (pathToNodeMap.containsKey(path)) {
- return pathToNodeMap.get(path).get((long) line);
- }
- return null;
- }
-
- boolean hasBackground(URI path, int line) {
- if (!pathToNodeMap.containsKey(path)) {
- parseGherkinSource(path);
- }
- if (pathToNodeMap.containsKey(path)) {
- AstNode astNode = pathToNodeMap.get(path).get((long) line);
- return getBackgroundForTestCase(astNode).isPresent();
- }
- return false;
- }
-
- static Optional getBackgroundForTestCase(AstNode astNode) {
- Feature feature = getFeatureForTestCase(astNode);
- return feature.getChildren()
- .stream()
- .map(FeatureChild::getBackground)
- .filter(Optional::isPresent)
- .map(Optional::get)
- .findFirst();
- }
-
- private static Feature getFeatureForTestCase(AstNode astNode) {
- while (astNode.parent != null) {
- astNode = astNode.parent;
- }
- return (Feature) astNode.node;
- }
-
- static class ExamplesRowWrapperNode {
-
- final int bodyRowIndex;
-
- ExamplesRowWrapperNode(Object examplesRow, int bodyRowIndex) {
- this.bodyRowIndex = bodyRowIndex;
- }
-
- }
-
- static class AstNode {
-
- final Object node;
- final AstNode parent;
-
- AstNode(Object node, AstNode parent) {
- this.node = node;
- this.parent = parent;
- }
-
- }
-
-}
diff --git a/cucumber-core/src/main/java/io/cucumber/core/plugin/TimelineFormatter.java b/cucumber-core/src/main/java/io/cucumber/core/plugin/TimelineFormatter.java
index 40ff501507..527730163a 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/plugin/TimelineFormatter.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/plugin/TimelineFormatter.java
@@ -19,6 +19,7 @@
import io.cucumber.query.Lineage;
import io.cucumber.query.Query;
import io.cucumber.query.Repository;
+import org.jspecify.annotations.Nullable;
import java.io.BufferedWriter;
import java.io.File;
@@ -31,6 +32,7 @@
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
@@ -74,9 +76,10 @@ public final class TimelineFormatter implements ConcurrentEventListener {
.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING)
.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET);
- @SuppressWarnings({ "unused", "RedundantThrows" }) // Used by PluginFactory
+ // Used by PluginFactory
+ @SuppressWarnings({"unused", "RedundantThrows", "ResultOfMethodCallIgnored"})
public TimelineFormatter(File reportDir) throws FileNotFoundException {
- boolean dontCare = reportDir.mkdirs();
+ reportDir.mkdirs();
if (!reportDir.isDirectory()) {
throw new CucumberException(String.format("The %s needs an existing directory. Not a directory: %s",
getClass().getName(), reportDir.getAbsolutePath()));
@@ -110,7 +113,7 @@ private void writeTimeLineReport() throws IOException {
.map(testCaseStarted -> createTestData(
testCaseFinished, //
testCaseStarted, //
- createTimeLineGroup(timeLineGroupsById) //
+ workerId -> timeLineGroupsById.computeIfAbsent(workerId, id -> new TimeLineGroup(id, id)) //
)))
.filter(Optional::isPresent)
.map(Optional::get)
@@ -123,22 +126,6 @@ private void writeTimeLineReport() throws IOException {
writeTimeLineReport(timeLineGroups, timeLineItems);
}
- private Function createTimeLineGroup(
- Map timeLineGroups
- ) {
-
- return workerId -> timeLineGroups.computeIfAbsent(workerId, createTimeLineGroup());
- }
-
- private Function createTimeLineGroup() {
- return workerId -> {
- TimeLineGroup timeLineGroup = new TimeLineGroup();
- timeLineGroup.setContent(workerId);
- timeLineGroup.setId(workerId);
- return timeLineGroup;
- };
- }
-
private TimeLineItem createTestData(
TestCaseFinished testCaseFinished, TestCaseStarted testCaseStarted,
Function timeLineGroupCreator
@@ -190,7 +177,7 @@ private String getTagsValue(TestCaseStarted testCaseStarted) {
.map(pickle -> {
StringBuilder tags = new StringBuilder();
for (PickleTag tag : pickle.getTags()) {
- tags.append(tag.getName().toLowerCase()).append(",");
+ tags.append(tag.getName().toLowerCase(Locale.US)).append(",");
}
return tags.toString();
}).orElse("");
@@ -227,9 +214,6 @@ private void appendAsJsonToJs(
}
private void copyReportFiles() throws IOException {
- if (reportDir == null) {
- return;
- }
File outputDir = new File(reportDir.getPath());
for (String textAsset : TEXT_ASSETS) {
try (InputStream textAssetStream = getClass().getResourceAsStream(textAsset)) {
@@ -254,14 +238,11 @@ private static void copyFile(InputStream source, File dest) throws IOException {
static class TimeLineGroup {
- private String id;
- private String content;
+ private final String id;
+ private final String content;
- public void setId(String id) {
+ TimeLineGroup(String id, String content) {
this.id = id;
- }
-
- public void setContent(String content) {
this.content = content;
}
@@ -269,7 +250,7 @@ public String getId() {
return id;
}
- public String getContent() {
+ public @Nullable String getContent() {
return content;
}
@@ -277,15 +258,16 @@ public String getContent() {
static class TimeLineItem {
- private String id;
- private String feature;
- private String scenario;
+ private @Nullable String id;
+ private @Nullable String feature;
+ private @Nullable String scenario;
private long start;
- private String group;
- private String content = ""; // Replaced in JS file
- private String tags;
+ private @Nullable String group;
+ // Replaced in JS file
+ private String content = "";
+ private @Nullable String tags;
private long end;
- private String className;
+ private @Nullable String className;
public void setId(String id) {
this.id = id;
@@ -323,15 +305,15 @@ public void setClassName(String className) {
this.className = className;
}
- public String getId() {
+ public @Nullable String getId() {
return id;
}
- public String getFeature() {
+ public @Nullable String getFeature() {
return feature;
}
- public String getScenario() {
+ public @Nullable String getScenario() {
return scenario;
}
@@ -339,15 +321,15 @@ public long getStart() {
return start;
}
- public String getGroup() {
+ public @Nullable String getGroup() {
return group;
}
- public String getContent() {
+ public @Nullable String getContent() {
return content;
}
- public String getTags() {
+ public @Nullable String getTags() {
return tags;
}
@@ -355,7 +337,7 @@ public long getEnd() {
return end;
}
- public String getClassName() {
+ public @Nullable String getClassName() {
return className;
}
diff --git a/cucumber-core/src/main/java/io/cucumber/core/plugin/UTF8PrintWriter.java b/cucumber-core/src/main/java/io/cucumber/core/plugin/UTF8PrintWriter.java
index 096fbfcb06..a202446599 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/plugin/UTF8PrintWriter.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/plugin/UTF8PrintWriter.java
@@ -35,6 +35,7 @@ public void println(String s) {
}
}
+ @Override
public void flush() {
try {
out.flush();
diff --git a/cucumber-core/src/main/java/io/cucumber/core/plugin/UnusedStepsSummaryPrinter.java b/cucumber-core/src/main/java/io/cucumber/core/plugin/UnusedStepsSummaryPrinter.java
index 7f348f8931..77dc720db2 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/plugin/UnusedStepsSummaryPrinter.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/plugin/UnusedStepsSummaryPrinter.java
@@ -18,10 +18,10 @@ public final class UnusedStepsSummaryPrinter implements ColorAware, ConcurrentEv
private final MessagesToUsageWriter writer;
- @SuppressWarnings("WeakerAccess") // Used by PluginFactory
+ // Used by PluginFactory
+ @SuppressWarnings("WeakerAccess")
public UnusedStepsSummaryPrinter(OutputStream out) {
- this.writer = MessagesToUsageWriter.builder(new UnusedReportSerializer())
- .build(out);
+ this.writer = MessagesToUsageWriter.builder(new UnusedReportSerializer()).build(out);
}
@Override
diff --git a/cucumber-core/src/main/java/io/cucumber/core/plugin/UrlOutputStream.java b/cucumber-core/src/main/java/io/cucumber/core/plugin/UrlOutputStream.java
index 5a93fa965c..d78e31522f 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/plugin/UrlOutputStream.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/plugin/UrlOutputStream.java
@@ -2,6 +2,7 @@
import io.cucumber.core.options.CurlOption;
import io.cucumber.core.options.CurlOption.HttpMethod;
+import org.jspecify.annotations.Nullable;
import java.io.BufferedReader;
import java.io.IOException;
@@ -26,13 +27,13 @@
class UrlOutputStream extends OutputStream {
- private final UrlReporter urlReporter;
+ private final @Nullable UrlReporter urlReporter;
private final CurlOption option;
private final Path temp;
private final OutputStream tempOutputStream;
- UrlOutputStream(CurlOption option, UrlReporter urlReporter) throws IOException {
+ UrlOutputStream(CurlOption option, @Nullable UrlReporter urlReporter) throws IOException {
this.option = requireNonNull(option);
this.urlReporter = urlReporter;
this.temp = Files.createTempFile("cucumber", null);
@@ -145,7 +146,7 @@ private static IOException createCurlLikeException(
Map> requestHeaders,
Map> responseHeaders,
String responseBody,
- Exception e
+ @Nullable Exception e
) {
return new IOException(String.format(
"%s:\n> %s %s%s%s%s",
@@ -157,7 +158,7 @@ private static IOException createCurlLikeException(
responseBody), e);
}
- private static String headersToString(String prefix, Map> headers) {
+ private static String headersToString(String prefix, Map<@Nullable String, List<@Nullable String>> headers) {
return headers
.entrySet()
.stream()
@@ -167,7 +168,7 @@ private static String headersToString(String prefix, Map> h
.map(value -> {
if (header.getKey() == null) {
return prefix + value;
- } else if (header.getValue() == null) {
+ } else if (value == null) {
return prefix + header.getKey();
} else {
return prefix + header.getKey() + ": " + value;
diff --git a/cucumber-core/src/main/java/io/cucumber/core/plugin/UrlReporter.java b/cucumber-core/src/main/java/io/cucumber/core/plugin/UrlReporter.java
index 04515654ef..aef25ee290 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/plugin/UrlReporter.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/plugin/UrlReporter.java
@@ -8,11 +8,11 @@ final class UrlReporter implements ColorAware {
private final PrintStream out;
private boolean monochrome;
- public UrlReporter(PrintStream out) {
+ UrlReporter(PrintStream out) {
this.out = out;
}
- public void report(String message) {
+ void report(String message) {
if (monochrome) {
message = message.replaceAll("\u001B\\[[;\\d]*m", "");
}
diff --git a/cucumber-core/src/main/java/io/cucumber/core/plugin/UsageJsonFormatter.java b/cucumber-core/src/main/java/io/cucumber/core/plugin/UsageJsonFormatter.java
index cc030ff3b9..1ef929fed9 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/plugin/UsageJsonFormatter.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/plugin/UsageJsonFormatter.java
@@ -16,7 +16,8 @@ public final class UsageJsonFormatter implements Plugin, ConcurrentEventListener
private final MessagesToUsageWriter writer;
- @SuppressWarnings("WeakerAccess") // Used by PluginFactory
+ // Used by PluginFactory
+ @SuppressWarnings("WeakerAccess")
public UsageJsonFormatter(OutputStream out) {
this.writer = MessagesToUsageWriter.builder(Jackson.OBJECT_MAPPER::writeValue)
.build(out);
diff --git a/cucumber-core/src/main/java/io/cucumber/core/plugin/package-info.java b/cucumber-core/src/main/java/io/cucumber/core/plugin/package-info.java
new file mode 100644
index 0000000000..174565c969
--- /dev/null
+++ b/cucumber-core/src/main/java/io/cucumber/core/plugin/package-info.java
@@ -0,0 +1,4 @@
+@NullMarked
+package io.cucumber.core.plugin;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/cucumber-core/src/main/java/io/cucumber/core/resource/ClasspathScanner.java b/cucumber-core/src/main/java/io/cucumber/core/resource/ClasspathScanner.java
index 6d8c1ffe11..3ac7f3ef31 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/resource/ClasspathScanner.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/resource/ClasspathScanner.java
@@ -27,7 +27,6 @@ public final class ClasspathScanner {
private static final String CLASS_FILE_SUFFIX = ".class";
private static final String PACKAGE_INFO_FILE_NAME = "package-info" + CLASS_FILE_SUFFIX;
private static final String MODULE_INFO_FILE_NAME = "module-info" + CLASS_FILE_SUFFIX;
- private static final Predicate> NULL_FILTER = aClass -> true;
private final PathScanner pathScanner = new PathScanner();
@@ -112,7 +111,7 @@ private Optional> safelyLoadClass(String fqn) {
}
public List> scanForClassesInPackage(String packageName) {
- return scanForClassesInPackage(packageName, NULL_FILTER);
+ return scanForClassesInPackage(packageName, aClass -> true);
}
}
diff --git a/cucumber-core/src/main/java/io/cucumber/core/resource/ClasspathSupport.java b/cucumber-core/src/main/java/io/cucumber/core/resource/ClasspathSupport.java
index 42ade80f36..53edd678bc 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/resource/ClasspathSupport.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/resource/ClasspathSupport.java
@@ -11,11 +11,11 @@
import java.util.Enumeration;
import java.util.List;
import java.util.regex.Pattern;
+import java.util.stream.Stream;
import static java.util.Arrays.stream;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.joining;
-import static java.util.stream.Stream.of;
public final class ClasspathSupport {
@@ -69,8 +69,9 @@ public static String resourceNameOfPackageName(String packageName) {
static String determinePackageName(Path baseDir, String basePackageName, Path classFile) {
String subPackageName = determineSubpackageName(baseDir, classFile);
- return of(basePackageName, subPackageName)
- .filter(value -> !value.isEmpty()) // default package
+ return Stream.of(basePackageName, subPackageName)
+ // default package
+ .filter(value -> !value.isEmpty())
.collect(joining(PACKAGE_SEPARATOR_STRING));
}
@@ -83,8 +84,9 @@ private static String determineSubpackageName(Path baseDir, Path resource) {
static URI determineClasspathResourceUri(Path baseDir, String basePackagePath, Path resource) {
String subPackageName = determineSubpackagePath(baseDir, resource);
String resourceName = resource.getFileName().toString();
- String classpathResourcePath = of(basePackagePath, subPackageName, resourceName)
- .filter(value -> !value.isEmpty()) // default package .
+ String classpathResourcePath = Stream.of(basePackagePath, subPackageName, resourceName)
+ // default package .
+ .filter(value -> !value.isEmpty())
.collect(joining(RESOURCE_SEPARATOR_STRING));
return classpathResourceUri(classpathResourcePath);
}
@@ -107,7 +109,7 @@ static URI classpathResourceUri(String classpathResourceName) {
static String determineFullyQualifiedClassName(Path baseDir, String basePackageName, Path classFile) {
String subpackageName = determineSubpackageName(baseDir, classFile);
String simpleClassName = determineSimpleClassName(classFile);
- return of(basePackageName, subpackageName, simpleClassName)
+ return Stream.of(basePackageName, subpackageName, simpleClassName)
.filter(value -> !value.isEmpty()) // default package
.collect(joining(PACKAGE_SEPARATOR_STRING));
}
@@ -154,34 +156,36 @@ public static URI rootPackageUri() {
}
public static String classPathScanningExplanation() {
- return "By default Cucumber scans the entire classpath for step definitions.\n" +
- "You can restrict this by configuring the glue path.\n" +
- "\n" +
- configurationExamples();
+ return """
+ By default Cucumber scans the entire classpath for step definitions.
+ You can restrict this by configuring the glue path.
+
+ %s""".formatted(configurationExamples());
}
static String nestedJarEntriesExplanation(URI uri) {
- return "By default Cucumber scans the entire classpath for step definitions.\n" +
- "However the resource '" + uri + "' is located in a nested jar.\n" +
- "\n" +
- "This typically happens when trying to run Cucumber inside a Spring Boot Executable Jar.\n" +
- "Cucumber currently doesn't support classpath scanning in nested jars.\n" +
- "\n" +
- "You can avoid this error by unpacking your application before executing or upgrading to Spring Boot 3.2 or higher.\n"
- +
- "\n" +
- "Alternatively you can restrict which packages cucumber scans configuring the glue path such that " +
- "Cucumber only scans un-nested jars.\n" +
- "\n" +
- configurationExamples();
+ return """
+ By default Cucumber scans the entire classpath for step definitions.
+ However the resource '%s' is located in a nested jar.
+
+ This typically happens when trying to run Cucumber inside a Spring Boot Executable Jar.
+ Cucumber currently doesn't support classpath scanning in nested jars.
+
+ You can avoid this error by unpacking your application before executing or upgrading to Spring Boot 3.2 or higher.
+
+ Alternatively you can restrict which packages cucumber scans configuring the glue path such that Cucumber only scans un-nested jars.
+
+ %s""".formatted(uri, configurationExamples());
}
public static String configurationExamples() {
- return "Examples:\n" +
- " - @CucumberOptions(glue = \"com.example.application\")\n" +
- " - @ConfigurationParameter(key = GLUE_PROPERTY_NAME, value = \"com.example.application\")\n" +
- " - src/test/resources/junit-platform.properties cucumber.glue=com.example.application\n" +
- " - src/test/resources/cucumber.properties cucumber.glue=com.example.application\n";
+ return """
+ Examples:
+ - @CucumberOptions(glue = "com.example.application")
+ - @ConfigurationParameter(key = GLUE_PROPERTY_NAME, value = "com.example.application")
+ - src/test/resources/junit-platform.properties cucumber.glue=com.example.application
+ - src/test/resources/cucumber.properties cucumber.glue=com.example.application
+ """;
}
}
diff --git a/cucumber-core/src/main/java/io/cucumber/core/resource/CloseablePath.java b/cucumber-core/src/main/java/io/cucumber/core/resource/CloseablePath.java
index 73583f11e4..8eb99b4c1e 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/resource/CloseablePath.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/resource/CloseablePath.java
@@ -6,7 +6,7 @@
import java.nio.file.Path;
import java.nio.file.Paths;
-class CloseablePath implements Closeable {
+final class CloseablePath implements Closeable {
private static final Closeable NULL_CLOSEABLE = () -> {
};
diff --git a/cucumber-core/src/main/java/io/cucumber/core/resource/JarUriFileSystemService.java b/cucumber-core/src/main/java/io/cucumber/core/resource/JarUriFileSystemService.java
index 2af2320d14..db36799758 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/resource/JarUriFileSystemService.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/resource/JarUriFileSystemService.java
@@ -15,8 +15,9 @@
import static io.cucumber.core.resource.ClasspathSupport.nestedJarEntriesExplanation;
import static java.util.Collections.emptyMap;
+import static java.util.Objects.requireNonNull;
-class JarUriFileSystemService {
+final class JarUriFileSystemService {
private static final String FILE_URI_SCHEME = "file";
private static final String JAR_URI_SCHEME = "jar";
@@ -27,6 +28,10 @@ class JarUriFileSystemService {
private static final Map openFiles = new HashMap<>();
private static final Map referenceCount = new HashMap<>();
+ private JarUriFileSystemService(){
+ /* no-op */
+ }
+
private static CloseablePath open(URI jarUri, Function pathProvider)
throws IOException {
FileSystem fileSystem = openFileSystem(jarUri);
@@ -35,7 +40,7 @@ private static CloseablePath open(URI jarUri, Function pathPro
}
private synchronized static void closeFileSystem(URI jarUri) throws IOException {
- int referents = referenceCount.get(jarUri).decrementAndGet();
+ int referents = requireNonNull(referenceCount.get(jarUri)).decrementAndGet();
if (referents == 0) {
openFiles.remove(jarUri).close();
referenceCount.remove(jarUri);
@@ -45,7 +50,7 @@ private synchronized static void closeFileSystem(URI jarUri) throws IOException
private synchronized static FileSystem openFileSystem(URI jarUri) throws IOException {
FileSystem existing = openFiles.get(jarUri);
if (existing != null) {
- referenceCount.get(jarUri).getAndIncrement();
+ requireNonNull(referenceCount.get(jarUri)).getAndIncrement();
return existing;
}
FileSystem fileSystem = FileSystems.newFileSystem(jarUri, emptyMap());
@@ -111,7 +116,7 @@ private static CloseablePath handleSpringBoot31JarUri(URI uri) throws IOExceptio
// Examples:
// jar:file:/home/user/application.jar!/BOOT-INF/lib/dependency.jar!/com/example/dependency/resource.txt
// jar:file:/home/user/application.jar!/BOOT-INF/classes!/com/example/package/resource.txt
- String[] parts = uri.toString().split("!");
+ String[] parts = uri.toString().split("!", 0);
String jarUri = parts[0];
String jarEntry = parts[1];
String subEntry = parts[2];
diff --git a/cucumber-core/src/main/java/io/cucumber/core/resource/PathScanner.java b/cucumber-core/src/main/java/io/cucumber/core/resource/PathScanner.java
index a4ec320864..3543d2d7ae 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/resource/PathScanner.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/resource/PathScanner.java
@@ -2,7 +2,6 @@
import io.cucumber.core.logging.Logger;
import io.cucumber.core.logging.LoggerFactory;
-import org.apiguardian.api.API;
import java.io.IOException;
import java.net.URI;
@@ -21,13 +20,15 @@
import static java.nio.file.FileVisitResult.CONTINUE;
import static java.nio.file.Files.exists;
import static java.nio.file.Files.walkFileTree;
-import static org.apiguardian.api.API.Status.INTERNAL;
-@API(status = INTERNAL)
-public class PathScanner {
+public final class PathScanner {
private static final Logger log = LoggerFactory.getLogger(PathScanner.class);
+ public PathScanner(){
+ /* no-op */
+ }
+
void findResourcesForUri(URI baseUri, Predicate filter, Function> consumer) {
try (CloseablePath closeablePath = open(baseUri)) {
Path baseDir = closeablePath.getPath();
diff --git a/cucumber-core/src/main/java/io/cucumber/core/resource/ResourceScanner.java b/cucumber-core/src/main/java/io/cucumber/core/resource/ResourceScanner.java
index ad0917baf0..00e044b08d 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/resource/ResourceScanner.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/resource/ResourceScanner.java
@@ -28,7 +28,6 @@
public final class ResourceScanner {
- private static final Predicate NULL_FILTER = x -> true;
private final PathScanner pathScanner = new PathScanner();
private final Supplier classLoaderSupplier;
private final Predicate canLoad;
@@ -121,17 +120,17 @@ public List scanForResourcesPath(Path resourcePath) {
pathScanner.findResourcesForPath(
resourcePath,
canLoad,
- processResource(DEFAULT_PACKAGE_NAME, NULL_FILTER, createUriResource(), resources::add));
+ processResource(DEFAULT_PACKAGE_NAME, x -> true, createUriResource(), resources::add));
return resources;
}
public List scanForResourcesUri(URI classpathResourceUri) {
requireNonNull(classpathResourceUri, "classpathResourceUri must not be null");
if (CLASSPATH_SCHEME.equals(classpathResourceUri.getScheme())) {
- return scanForClasspathResource(resourceName(classpathResourceUri), NULL_FILTER);
+ return scanForClasspathResource(resourceName(classpathResourceUri), x -> true);
}
- return findResourcesForUri(classpathResourceUri, DEFAULT_PACKAGE_NAME, NULL_FILTER, createUriResource());
+ return findResourcesForUri(classpathResourceUri, DEFAULT_PACKAGE_NAME, x -> true, createUriResource());
}
}
diff --git a/cucumber-core/src/main/java/io/cucumber/core/resource/Resources.java b/cucumber-core/src/main/java/io/cucumber/core/resource/Resources.java
index ea08d5f424..bad3980892 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/resource/Resources.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/resource/Resources.java
@@ -11,10 +11,10 @@
import static io.cucumber.core.resource.ClasspathSupport.determineClasspathResourceUri;
import static io.cucumber.core.resource.ClasspathSupport.resourceNameOfPackageName;
-class Resources {
+final class Resources {
private Resources() {
-
+ /* no-op */
}
static BiFunction createPackageResource(String packageName) {
diff --git a/cucumber-core/src/main/java/io/cucumber/core/resource/package-info.java b/cucumber-core/src/main/java/io/cucumber/core/resource/package-info.java
new file mode 100644
index 0000000000..2b222a1c4b
--- /dev/null
+++ b/cucumber-core/src/main/java/io/cucumber/core/resource/package-info.java
@@ -0,0 +1,4 @@
+@NullMarked
+package io.cucumber.core.resource;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/cucumber-core/src/main/java/io/cucumber/core/runner/CachingGlue.java b/cucumber-core/src/main/java/io/cucumber/core/runner/CachingGlue.java
index fbdbd66d52..f81d439b20 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/runner/CachingGlue.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/runner/CachingGlue.java
@@ -36,6 +36,7 @@
import io.cucumber.messages.types.StepDefinitionPattern;
import io.cucumber.messages.types.StepDefinitionPatternType;
import io.cucumber.plugin.event.StepDefinedEvent;
+import org.jspecify.annotations.Nullable;
import java.net.URI;
import java.util.ArrayList;
@@ -49,6 +50,8 @@
import java.util.Map;
import java.util.TreeMap;
+import static java.util.Objects.requireNonNull;
+
final class CachingGlue implements Glue {
private static final Comparator HOOK_ORDER_ASCENDING = Comparator
@@ -84,9 +87,9 @@ final class CachingGlue implements Glue {
private final EventBus bus;
- private StepTypeRegistry stepTypeRegistry;
- private Locale locale = null;
- private StepExpressionFactory stepExpressionFactory = null;
+ private @Nullable StepTypeRegistry stepTypeRegistry;
+ private @Nullable Locale locale = null;
+ private @Nullable StepExpressionFactory stepExpressionFactory = null;
private boolean cacheIsDirty = false;
private boolean hasScenarioScopedGlue = false;
@@ -245,7 +248,7 @@ List getDocStringTypeDefinitions() {
}
StepTypeRegistry getStepTypeRegistry() {
- return stepTypeRegistry;
+ return requireNonNull(stepTypeRegistry);
}
void prepareGlue(Locale locale) throws DuplicateStepDefinitionException {
@@ -343,19 +346,11 @@ private void emitHook(CoreHookDefinition coreHook) {
.orElseGet(this::emptySourceReference),
coreHook.getTagExpression(),
coreHook.getHookType()
- .map(hookType -> {
- switch (hookType) {
- case BEFORE:
- return HookType.BEFORE_TEST_CASE;
- case AFTER:
- return HookType.AFTER_TEST_CASE;
- case BEFORE_STEP:
- return HookType.BEFORE_TEST_STEP;
- case AFTER_STEP:
- return HookType.AFTER_TEST_STEP;
- default:
- return null;
- }
+ .map(hookType -> switch (hookType) {
+ case BEFORE -> HookType.BEFORE_TEST_CASE;
+ case AFTER -> HookType.AFTER_TEST_CASE;
+ case BEFORE_STEP -> HookType.BEFORE_TEST_STEP;
+ case AFTER_STEP -> HookType.AFTER_TEST_STEP;
})
.orElse(null));
bus.send(Envelope.of(messagesHook));
@@ -380,23 +375,21 @@ private void emitStepDefined(CoreStepDefinition coreStepDefinition) {
}
private SourceReference createSourceReference(io.cucumber.core.backend.SourceReference reference) {
- if (reference instanceof JavaMethodReference) {
- JavaMethodReference methodReference = (JavaMethodReference) reference;
+ if (reference instanceof JavaMethodReference methodReference) {
return SourceReference.of(new JavaMethod(
methodReference.className(),
methodReference.methodName(),
methodReference.methodParameterTypes()));
}
- if (reference instanceof StackTraceElementReference) {
- StackTraceElementReference stackReference = (StackTraceElementReference) reference;
+ if (reference instanceof StackTraceElementReference stackReference) {
JavaStackTraceElement stackTraceElement = new JavaStackTraceElement(
stackReference.className(),
// TODO: Fix json schema. Stacktrace elements need not have a
// source file
stackReference.fileName().orElse("Unknown"),
stackReference.methodName());
- Location location = new Location((long) stackReference.lineNumber(), null);
+ Location location = new Location(stackReference.lineNumber(), null);
return new SourceReference(null, null, stackTraceElement, location);
}
return emptySourceReference();
@@ -417,7 +410,7 @@ private StepDefinitionPatternType getExpressionType(CoreStepDefinition stepDefin
}
}
- PickleStepDefinitionMatch stepDefinitionMatch(URI uri, Step step) throws AmbiguousStepDefinitionsException {
+ @Nullable PickleStepDefinitionMatch stepDefinitionMatch(URI uri, Step step) throws AmbiguousStepDefinitionsException {
PickleStepDefinitionMatch cachedMatch = cachedStepDefinitionMatch(uri, step);
if (cachedMatch != null) {
return cachedMatch;
@@ -425,7 +418,7 @@ PickleStepDefinitionMatch stepDefinitionMatch(URI uri, Step step) throws Ambiguo
return findStepDefinitionMatch(uri, step);
}
- private PickleStepDefinitionMatch cachedStepDefinitionMatch(URI uri, Step step) {
+ private @Nullable PickleStepDefinitionMatch cachedStepDefinitionMatch(URI uri, Step step) {
String stepDefinitionPattern = stepPatternByStepText.get(step.getText());
if (stepDefinitionPattern == null) {
return null;
@@ -436,18 +429,16 @@ private PickleStepDefinitionMatch cachedStepDefinitionMatch(URI uri, Step step)
return null;
}
- // Step definition arguments consists of parameters included in the step
- // text and
- // gherkin step arguments (doc string and data table) which are not
- // included in
- // the step text. As such the step definition arguments can not be
- // cached and
- // must be recreated each time.
- List arguments = coreStepDefinition.matchedArguments(step);
+ // At this point we know the step matches but:
+ // Step definition arguments consists of parameters included in the
+ // step text and gherkin step arguments (doc string and data table)
+ // which are not included in the step text. As such the step definition
+ // arguments can not be cached and must be recreated each time.
+ List arguments = requireNonNull(coreStepDefinition.matchedArguments(step));
return new PickleStepDefinitionMatch(arguments, coreStepDefinition, uri, step);
}
- private PickleStepDefinitionMatch findStepDefinitionMatch(URI uri, Step step)
+ private @Nullable PickleStepDefinitionMatch findStepDefinitionMatch(URI uri, Step step)
throws AmbiguousStepDefinitionsException {
List matches = stepDefinitionMatches(uri, step);
if (matches.isEmpty()) {
@@ -498,8 +489,7 @@ private void removeScenarioScopedGlue(Iterable> glues) {
Iterator> glueIterator = glues.iterator();
while (glueIterator.hasNext()) {
Object glue = glueIterator.next();
- if (glue instanceof ScenarioScoped) {
- ScenarioScoped scenarioScoped = (ScenarioScoped) glue;
+ if (glue instanceof ScenarioScoped scenarioScoped) {
scenarioScoped.dispose();
glueIterator.remove();
cacheIsDirty = true;
diff --git a/cucumber-core/src/main/java/io/cucumber/core/runner/CoreDefaultDataTableEntryTransformerDefinition.java b/cucumber-core/src/main/java/io/cucumber/core/runner/CoreDefaultDataTableEntryTransformerDefinition.java
index 5272271795..9017b3a354 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/runner/CoreDefaultDataTableEntryTransformerDefinition.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/runner/CoreDefaultDataTableEntryTransformerDefinition.java
@@ -60,8 +60,7 @@ private static class ScenarioCoreDefaultDataTableEntryTransformerDefinition
@Override
public void dispose() {
- if (delegate instanceof ScenarioScoped) {
- ScenarioScoped scenarioScoped = (ScenarioScoped) delegate;
+ if (delegate instanceof ScenarioScoped scenarioScoped) {
scenarioScoped.dispose();
}
}
diff --git a/cucumber-core/src/main/java/io/cucumber/core/runner/CoreHookDefinition.java b/cucumber-core/src/main/java/io/cucumber/core/runner/CoreHookDefinition.java
index 88b3d4c7bd..72752c35a4 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/runner/CoreHookDefinition.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/runner/CoreHookDefinition.java
@@ -74,8 +74,12 @@ String getTagExpression() {
Optional getHookType() {
return delegate.getHookType();
}
-
- static class ScenarioScopedCoreHookDefinition extends CoreHookDefinition implements ScenarioScoped {
+
+ Optional getDefinitionLocation() {
+ return delegate.getSourceReference();
+ }
+
+ static final class ScenarioScopedCoreHookDefinition extends CoreHookDefinition implements ScenarioScoped {
private ScenarioScopedCoreHookDefinition(UUID id, HookDefinition delegate) {
super(id, delegate);
@@ -83,15 +87,12 @@ private ScenarioScopedCoreHookDefinition(UUID id, HookDefinition delegate) {
@Override
public void dispose() {
- if (delegate instanceof ScenarioScoped) {
- ScenarioScoped scenarioScoped = (ScenarioScoped) delegate;
+ if (delegate instanceof ScenarioScoped scenarioScoped) {
scenarioScoped.dispose();
}
}
}
- Optional getDefinitionLocation() {
- return delegate.getSourceReference();
- }
+
}
diff --git a/cucumber-core/src/main/java/io/cucumber/core/runner/CoreStepDefinition.java b/cucumber-core/src/main/java/io/cucumber/core/runner/CoreStepDefinition.java
index f0f55c47cd..9f16131f82 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/runner/CoreStepDefinition.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/runner/CoreStepDefinition.java
@@ -9,6 +9,7 @@
import io.cucumber.core.stepexpression.Argument;
import io.cucumber.core.stepexpression.ArgumentMatcher;
import io.cucumber.core.stepexpression.StepExpression;
+import org.jspecify.annotations.Nullable;
import java.lang.reflect.Type;
import java.util.List;
@@ -53,7 +54,7 @@ StepDefinition getStepDefinition() {
return stepDefinition;
}
- List matchedArguments(Step step) {
+ @Nullable List matchedArguments(Step step) {
return argumentMatcher.argumentsFrom(step, types);
}
diff --git a/cucumber-core/src/main/java/io/cucumber/core/runner/DefinitionArgument.java b/cucumber-core/src/main/java/io/cucumber/core/runner/DefinitionArgument.java
index e9d314add8..7181328d8b 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/runner/DefinitionArgument.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/runner/DefinitionArgument.java
@@ -2,6 +2,7 @@
import io.cucumber.core.stepexpression.ExpressionArgument;
import io.cucumber.plugin.event.Argument;
+import org.jspecify.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collection;
@@ -21,8 +22,7 @@ private DefinitionArgument(ExpressionArgument argument) {
static List createArguments(List match) {
List args = new ArrayList<>();
for (io.cucumber.core.stepexpression.Argument argument : match) {
- if (argument instanceof ExpressionArgument) {
- ExpressionArgument expressionArgument = (ExpressionArgument) argument;
+ if (argument instanceof ExpressionArgument expressionArgument) {
args.add(new DefinitionArgument(expressionArgument));
}
}
@@ -35,23 +35,23 @@ public String getParameterTypeName() {
}
@Override
- public String getValue() {
- return group == null ? null : group.getValue();
+ public @Nullable String getValue() {
+ return group.getValue();
}
@Override
public int getStart() {
- return group == null ? -1 : group.getStart();
+ return group.getStart();
}
@Override
public int getEnd() {
- return group == null ? -1 : group.getEnd();
+ return group.getEnd();
}
@Override
public io.cucumber.plugin.event.Group getGroup() {
- return group == null ? null : new Group(group);
+ return new Group(group);
}
private static final class Group implements io.cucumber.plugin.event.Group {
@@ -72,7 +72,7 @@ public Collection getChildren() {
}
@Override
- public String getValue() {
+ public @Nullable String getValue() {
return group.getValue();
}
diff --git a/cucumber-core/src/main/java/io/cucumber/core/runner/NoStepDefinition.java b/cucumber-core/src/main/java/io/cucumber/core/runner/NoStepDefinition.java
index 1a5f5e403a..32183ebfc5 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/runner/NoStepDefinition.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/runner/NoStepDefinition.java
@@ -3,6 +3,7 @@
import io.cucumber.core.backend.ParameterInfo;
import io.cucumber.core.backend.StepDefinition;
+import java.util.Collections;
import java.util.List;
final class NoStepDefinition implements StepDefinition {
@@ -13,12 +14,12 @@ public void execute(Object[] args) {
@Override
public List parameterInfos() {
- return null;
+ return Collections.emptyList();
}
@Override
public String getPattern() {
- return null;
+ return "";
}
@Override
@@ -28,7 +29,7 @@ public boolean isDefinedAt(StackTraceElement stackTraceElement) {
@Override
public String getLocation() {
- return null;
+ return "no location";
}
}
diff --git a/cucumber-core/src/main/java/io/cucumber/core/runner/Options.java b/cucumber-core/src/main/java/io/cucumber/core/runner/Options.java
index 0fe0ffb807..3092437ba9 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/runner/Options.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/runner/Options.java
@@ -3,6 +3,7 @@
import io.cucumber.core.backend.ObjectFactory;
import io.cucumber.core.eventbus.UuidGenerator;
import io.cucumber.core.snippets.SnippetType;
+import org.jspecify.annotations.Nullable;
import java.net.URI;
import java.util.List;
@@ -15,8 +16,8 @@ public interface Options {
SnippetType getSnippetType();
- Class extends ObjectFactory> getObjectFactoryClass();
+ @Nullable Class extends ObjectFactory> getObjectFactoryClass();
- Class extends UuidGenerator> getUuidGeneratorClass();
+ @Nullable Class extends UuidGenerator> getUuidGeneratorClass();
}
diff --git a/cucumber-core/src/main/java/io/cucumber/core/runner/PickleStepDefinitionMatch.java b/cucumber-core/src/main/java/io/cucumber/core/runner/PickleStepDefinitionMatch.java
index 84ad6b0580..2b413d3695 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/runner/PickleStepDefinitionMatch.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/runner/PickleStepDefinitionMatch.java
@@ -12,6 +12,7 @@
import io.cucumber.datatable.CucumberDataTableException;
import io.cucumber.datatable.UndefinedDataTableTypeException;
import io.cucumber.docstring.CucumberDocStringException;
+import org.jspecify.annotations.Nullable;
import java.net.URI;
import java.util.ArrayList;
@@ -37,10 +38,10 @@ class PickleStepDefinitionMatch extends Match implements StepDefinitionMatch {
public void runStep(TestCaseState state) throws Throwable {
List arguments = getArguments();
List parameterInfos = stepDefinition.parameterInfos();
- if (parameterInfos != null && arguments.size() != parameterInfos.size()) {
+ if (arguments.size() != parameterInfos.size()) {
throw arityMismatch(parameterInfos.size());
}
- List
+
+ org.jspecify
+ jspecify
+ 1.0.0
+ io.cucumbercucumber-plugin
diff --git a/cucumber-gherkin/src/main/java/io/cucumber/core/gherkin/DocStringArgument.java b/cucumber-gherkin/src/main/java/io/cucumber/core/gherkin/DocStringArgument.java
index 8d0344dd10..2082f05f29 100644
--- a/cucumber-gherkin/src/main/java/io/cucumber/core/gherkin/DocStringArgument.java
+++ b/cucumber-gherkin/src/main/java/io/cucumber/core/gherkin/DocStringArgument.java
@@ -1,14 +1,19 @@
package io.cucumber.core.gherkin;
+import org.jspecify.annotations.Nullable;
+
public interface DocStringArgument extends Argument, io.cucumber.plugin.event.DocStringArgument {
@Override
String getContent();
@Override
+ @Deprecated
+ @Nullable
String getContentType();
@Override
+ @Nullable
String getMediaType();
@Override
diff --git a/cucumber-gherkin/src/main/java/io/cucumber/core/gherkin/FeatureParser.java b/cucumber-gherkin/src/main/java/io/cucumber/core/gherkin/FeatureParser.java
index 89230527f2..4e5fe925e9 100644
--- a/cucumber-gherkin/src/main/java/io/cucumber/core/gherkin/FeatureParser.java
+++ b/cucumber-gherkin/src/main/java/io/cucumber/core/gherkin/FeatureParser.java
@@ -16,13 +16,13 @@ public interface FeatureParser {
Optional parse(URI path, String source, Supplier idGenerator);
default Optional parse(URI path, InputStream source, Supplier idGenerator) throws IOException {
- final byte[] buffer = new byte[2 * 1024]; // 2KB
+ final byte[] buffer = new byte[2 * 1024];
int read;
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
while (-1 != (read = source.read(buffer, 0, buffer.length))) {
outputStream.write(buffer, 0, read);
}
- String s = new String(outputStream.toByteArray(), UTF_8);
+ String s = outputStream.toString(UTF_8);
return parse(path, s, idGenerator);
}
}
diff --git a/cucumber-gherkin/src/main/java/io/cucumber/core/gherkin/Step.java b/cucumber-gherkin/src/main/java/io/cucumber/core/gherkin/Step.java
index 20411cbfe7..30959bc485 100644
--- a/cucumber-gherkin/src/main/java/io/cucumber/core/gherkin/Step.java
+++ b/cucumber-gherkin/src/main/java/io/cucumber/core/gherkin/Step.java
@@ -1,5 +1,7 @@
package io.cucumber.core.gherkin;
+import org.jspecify.annotations.Nullable;
+
public interface Step extends io.cucumber.plugin.event.Step {
StepType getType();
@@ -9,6 +11,7 @@ public interface Step extends io.cucumber.plugin.event.Step {
String getId();
@Override
+ @Nullable
Argument getArgument();
}
diff --git a/cucumber-gherkin/src/main/java/io/cucumber/core/gherkin/package-info.java b/cucumber-gherkin/src/main/java/io/cucumber/core/gherkin/package-info.java
new file mode 100644
index 0000000000..b2922374f5
--- /dev/null
+++ b/cucumber-gherkin/src/main/java/io/cucumber/core/gherkin/package-info.java
@@ -0,0 +1,4 @@
+@NullMarked
+package io.cucumber.core.gherkin;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/cucumber-guice/src/main/java/io/cucumber/guice/package-info.java b/cucumber-guice/src/main/java/io/cucumber/guice/package-info.java
index b3a5345566..28847c8136 100644
--- a/cucumber-guice/src/main/java/io/cucumber/guice/package-info.java
+++ b/cucumber-guice/src/main/java/io/cucumber/guice/package-info.java
@@ -24,4 +24,7 @@
* precedence if the same class is also bound using a scope annotation.
*
*/
+@NullMarked
package io.cucumber.guice;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/cucumber-jakarta-cdi/src/main/java/io/cucumber/jakarta/cdi/package-info.java b/cucumber-jakarta-cdi/src/main/java/io/cucumber/jakarta/cdi/package-info.java
new file mode 100644
index 0000000000..04dcf11a46
--- /dev/null
+++ b/cucumber-jakarta-cdi/src/main/java/io/cucumber/jakarta/cdi/package-info.java
@@ -0,0 +1,4 @@
+@NullMarked
+package io.cucumber.jakarta.cdi;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/cucumber-jakarta-openejb/src/main/java/io/cucumber/jakarta/openejb/package-info.java b/cucumber-jakarta-openejb/src/main/java/io/cucumber/jakarta/openejb/package-info.java
index d1dd71e47e..ae06320d46 100644
--- a/cucumber-jakarta-openejb/src/main/java/io/cucumber/jakarta/openejb/package-info.java
+++ b/cucumber-jakarta-openejb/src/main/java/io/cucumber/jakarta/openejb/package-info.java
@@ -4,4 +4,7 @@
* By including the cucumber-jakarta-openejb on your
* CLASSPATH your step definitions will be instantiated by OpenEJB.
*/
+@NullMarked
package io.cucumber.jakarta.openejb;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/cucumber-java/src/main/java/io/cucumber/java/AbstractGlueDefinition.java b/cucumber-java/src/main/java/io/cucumber/java/AbstractGlueDefinition.java
index 9b0eeae1a2..724c479e86 100644
--- a/cucumber-java/src/main/java/io/cucumber/java/AbstractGlueDefinition.java
+++ b/cucumber-java/src/main/java/io/cucumber/java/AbstractGlueDefinition.java
@@ -3,6 +3,7 @@
import io.cucumber.core.backend.Located;
import io.cucumber.core.backend.Lookup;
import io.cucumber.core.backend.SourceReference;
+import org.jspecify.annotations.Nullable;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
@@ -14,8 +15,8 @@ abstract class AbstractGlueDefinition implements Located {
protected final Method method;
private final Lookup lookup;
- private String fullFormat;
- private SourceReference sourceReference;
+ private @Nullable String fullFormat;
+ private @Nullable SourceReference sourceReference;
AbstractGlueDefinition(Method method, Lookup lookup) {
this.method = requireNonNull(method);
diff --git a/cucumber-java/src/main/java/io/cucumber/java/Invoker.java b/cucumber-java/src/main/java/io/cucumber/java/Invoker.java
index 3e0a5d8c9a..714ec64044 100644
--- a/cucumber-java/src/main/java/io/cucumber/java/Invoker.java
+++ b/cucumber-java/src/main/java/io/cucumber/java/Invoker.java
@@ -3,6 +3,7 @@
import io.cucumber.core.backend.CucumberBackendException;
import io.cucumber.core.backend.CucumberInvocationTargetException;
import io.cucumber.core.backend.Located;
+import org.jspecify.annotations.Nullable;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
@@ -19,7 +20,7 @@ static Object invoke(Annotation annotation, Method expressionMethod) {
return invoke(null, annotation, expressionMethod);
}
- static Object invoke(Located located, Object target, Method method, Object... args) {
+ static Object invoke(@Nullable Located located, Object target, Method method, Object... args) {
Method targetMethod = targetMethod(target, method);
return doInvoke(located, target, targetMethod, args);
}
@@ -59,7 +60,7 @@ private static Method targetMethod(Object target, Method method) {
}
}
- private static Object doInvoke(Located located, Object target, Method targetMethod, Object[] args) {
+ private static Object doInvoke(@Nullable Located located, @Nullable Object target, Method targetMethod, Object[] args) {
boolean accessible = targetMethod.isAccessible();
try {
targetMethod.setAccessible(true);
@@ -70,6 +71,9 @@ private static Object doInvoke(Located located, Object target, Method targetMeth
if (located == null) { // Reflecting into annotations
throw new CucumberBackendException("Failed to invoke " + targetMethod, e);
}
+ if (e.getCause() == null) {
+ throw new CucumberBackendException("Failed to invoke " + targetMethod, e);
+ }
throw new CucumberInvocationTargetException(located, e);
} finally {
targetMethod.setAccessible(accessible);
diff --git a/cucumber-java/src/main/java/io/cucumber/java/package-info.java b/cucumber-java/src/main/java/io/cucumber/java/package-info.java
new file mode 100644
index 0000000000..3fccd3fd6c
--- /dev/null
+++ b/cucumber-java/src/main/java/io/cucumber/java/package-info.java
@@ -0,0 +1,4 @@
+@NullMarked
+package io.cucumber.java;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/cucumber-java8/src/main/java/io/cucumber/java8/package-info.java b/cucumber-java8/src/main/java/io/cucumber/java8/package-info.java
new file mode 100644
index 0000000000..b9e2346b81
--- /dev/null
+++ b/cucumber-java8/src/main/java/io/cucumber/java8/package-info.java
@@ -0,0 +1,4 @@
+@NullMarked
+package io.cucumber.java8;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/cucumber-junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/package-info.java b/cucumber-junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/package-info.java
new file mode 100644
index 0000000000..1d165c1931
--- /dev/null
+++ b/cucumber-junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/package-info.java
@@ -0,0 +1,4 @@
+@NullMarked
+package io.cucumber.junit.platform.engine;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/cucumber-junit-platform-engine/src/main/java9/module-info.java b/cucumber-junit-platform-engine/src/main/java/module-info.java
similarity index 100%
rename from cucumber-junit-platform-engine/src/main/java9/module-info.java
rename to cucumber-junit-platform-engine/src/main/java/module-info.java
diff --git a/cucumber-junit/src/main/java/io/cucumber/junit/JUnitCucumberOptionsProvider.java b/cucumber-junit/src/main/java/io/cucumber/junit/JUnitCucumberOptionsProvider.java
index eafd59b962..b4da5078e5 100644
--- a/cucumber-junit/src/main/java/io/cucumber/junit/JUnitCucumberOptionsProvider.java
+++ b/cucumber-junit/src/main/java/io/cucumber/junit/JUnitCucumberOptionsProvider.java
@@ -6,6 +6,7 @@
import io.cucumber.core.logging.LoggerFactory;
import io.cucumber.core.options.CucumberOptionsAnnotationParser;
import io.cucumber.core.snippets.SnippetType;
+import org.jspecify.annotations.Nullable;
import java.lang.annotation.Annotation;
@@ -14,7 +15,7 @@ final class JUnitCucumberOptionsProvider implements CucumberOptionsAnnotationPar
private static final Logger log = LoggerFactory.getLogger(JUnitCucumberOptionsProvider.class);
@Override
- public CucumberOptionsAnnotationParser.CucumberOptions getOptions(Class> clazz) {
+ public CucumberOptionsAnnotationParser.@Nullable CucumberOptions getOptions(Class> clazz) {
CucumberOptions annotation = clazz.getAnnotation(CucumberOptions.class);
if (annotation != null) {
return new JunitCucumberOptions(annotation);
@@ -99,12 +100,12 @@ public SnippetType snippets() {
}
@Override
- public Class extends ObjectFactory> objectFactory() {
+ public @Nullable Class extends ObjectFactory> objectFactory() {
return (annotation.objectFactory() == NoObjectFactory.class) ? null : annotation.objectFactory();
}
@Override
- public Class extends UuidGenerator> uuidGenerator() {
+ public @Nullable Class extends UuidGenerator> uuidGenerator() {
return (annotation.uuidGenerator() == NoUuidGenerator.class) ? null : annotation.uuidGenerator();
}
}
diff --git a/cucumber-junit/src/main/java/io/cucumber/junit/package-info.java b/cucumber-junit/src/main/java/io/cucumber/junit/package-info.java
new file mode 100644
index 0000000000..037955768e
--- /dev/null
+++ b/cucumber-junit/src/main/java/io/cucumber/junit/package-info.java
@@ -0,0 +1,4 @@
+@NullMarked
+package io.cucumber.junit;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/cucumber-openejb/src/main/java/io/cucumber/openejb/package-info.java b/cucumber-openejb/src/main/java/io/cucumber/openejb/package-info.java
index 3bf4ee736d..4f1df1f827 100644
--- a/cucumber-openejb/src/main/java/io/cucumber/openejb/package-info.java
+++ b/cucumber-openejb/src/main/java/io/cucumber/openejb/package-info.java
@@ -4,4 +4,7 @@
* By including the cucumber-openejb on your CLASSPATH
* your step definitions will be instantiated by OpenEJB.
*/
+@NullMarked
package io.cucumber.openejb;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/cucumber-picocontainer/src/main/java/io/cucumber/picocontainer/package-info.java b/cucumber-picocontainer/src/main/java/io/cucumber/picocontainer/package-info.java
index 9812425a59..e59c3f2e85 100644
--- a/cucumber-picocontainer/src/main/java/io/cucumber/picocontainer/package-info.java
+++ b/cucumber-picocontainer/src/main/java/io/cucumber/picocontainer/package-info.java
@@ -5,4 +5,7 @@
* CLASSPATH your step definitions will be instantiated by
* PicoContainer.
*/
+@NullMarked
package io.cucumber.picocontainer;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/cucumber-plugin/pom.xml b/cucumber-plugin/pom.xml
index 2c99ab22ff..e37e0a8f83 100644
--- a/cucumber-plugin/pom.xml
+++ b/cucumber-plugin/pom.xml
@@ -42,6 +42,11 @@
apiguardian-api${apiguardian-api.version}
+
+ org.jspecify
+ jspecify
+ 1.0.0
+ org.junit.jupiterjunit-jupiter
diff --git a/cucumber-plugin/src/main/java/io/cucumber/plugin/StrictAware.java b/cucumber-plugin/src/main/java/io/cucumber/plugin/StrictAware.java
deleted file mode 100755
index 5c8e91ba03..0000000000
--- a/cucumber-plugin/src/main/java/io/cucumber/plugin/StrictAware.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package io.cucumber.plugin;
-
-import org.apiguardian.api.API;
-
-/**
- * Interface for Plugins that need to know if the Runtime is strict.
- *
- * @deprecated strict mode is enabled by default and will be removed.
- */
-@Deprecated
-@API(status = API.Status.STABLE)
-public interface StrictAware extends Plugin {
-
- /**
- * When set to strict the plugin should indicate failure for undefined and
- * pending steps
- *
- * @param strict true if the runtime is in strict mode
- */
- void setStrict(boolean strict);
-
-}
diff --git a/cucumber-plugin/src/main/java/io/cucumber/plugin/SummaryPrinter.java b/cucumber-plugin/src/main/java/io/cucumber/plugin/SummaryPrinter.java
deleted file mode 100644
index b799f4ffff..0000000000
--- a/cucumber-plugin/src/main/java/io/cucumber/plugin/SummaryPrinter.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package io.cucumber.plugin;
-
-import org.apiguardian.api.API;
-
-/**
- * Interface for plugins that print a summary after test execution. Deprecated
- * use the {@link EventListener} or {@link ConcurrentEventListener} interface
- * instead.
- *
- * @see Plugin
- */
-@API(status = API.Status.STABLE)
-@Deprecated
-public interface SummaryPrinter extends Plugin {
-
-}
diff --git a/cucumber-plugin/src/main/java/io/cucumber/plugin/event/Argument.java b/cucumber-plugin/src/main/java/io/cucumber/plugin/event/Argument.java
index 8715eda5cc..1385d0b802 100644
--- a/cucumber-plugin/src/main/java/io/cucumber/plugin/event/Argument.java
+++ b/cucumber-plugin/src/main/java/io/cucumber/plugin/event/Argument.java
@@ -1,6 +1,7 @@
package io.cucumber.plugin.event;
import org.apiguardian.api.API;
+import org.jspecify.annotations.Nullable;
/**
* Represents an argument in a cucumber or regular expressions
@@ -14,12 +15,14 @@ public interface Argument {
String getParameterTypeName();
+ @Nullable
String getValue();
int getStart();
int getEnd();
+ @Nullable
Group getGroup();
}
diff --git a/cucumber-plugin/src/main/java/io/cucumber/plugin/event/DocStringArgument.java b/cucumber-plugin/src/main/java/io/cucumber/plugin/event/DocStringArgument.java
index 0345ecf434..8d4b36acff 100644
--- a/cucumber-plugin/src/main/java/io/cucumber/plugin/event/DocStringArgument.java
+++ b/cucumber-plugin/src/main/java/io/cucumber/plugin/event/DocStringArgument.java
@@ -1,6 +1,7 @@
package io.cucumber.plugin.event;
import org.apiguardian.api.API;
+import org.jspecify.annotations.Nullable;
/**
* Represents a Gherkin doc string argument.
@@ -14,8 +15,10 @@ public interface DocStringArgument extends StepArgument {
* @deprecated use {@link #getMediaType()} instead.
*/
@Deprecated
+ @Nullable
String getContentType();
+ @Nullable
String getMediaType();
int getLine();
diff --git a/cucumber-plugin/src/main/java/io/cucumber/plugin/event/EmbedEvent.java b/cucumber-plugin/src/main/java/io/cucumber/plugin/event/EmbedEvent.java
index 2e2619fc46..8131256903 100644
--- a/cucumber-plugin/src/main/java/io/cucumber/plugin/event/EmbedEvent.java
+++ b/cucumber-plugin/src/main/java/io/cucumber/plugin/event/EmbedEvent.java
@@ -1,6 +1,7 @@
package io.cucumber.plugin.event;
import org.apiguardian.api.API;
+import org.jspecify.annotations.Nullable;
import java.time.Instant;
@@ -9,7 +10,7 @@
@API(status = API.Status.STABLE)
public final class EmbedEvent extends TestCaseEvent {
- public final String name;
+ public final @Nullable String name;
private final byte[] data;
private final String mediaType;
@@ -17,7 +18,7 @@ public EmbedEvent(Instant timeInstant, TestCase testCase, byte[] data, String me
this(timeInstant, testCase, data, mediaType, null);
}
- public EmbedEvent(Instant timeInstant, TestCase testCase, byte[] data, String mediaType, String name) {
+ public EmbedEvent(Instant timeInstant, TestCase testCase, byte[] data, String mediaType, @Nullable String name) {
super(timeInstant, testCase);
this.data = requireNonNull(data);
this.mediaType = requireNonNull(mediaType);
@@ -41,7 +42,7 @@ public String getMimeType() {
return mediaType;
}
- public String getName() {
+ public @Nullable String getName() {
return name;
}
diff --git a/cucumber-plugin/src/main/java/io/cucumber/plugin/event/Group.java b/cucumber-plugin/src/main/java/io/cucumber/plugin/event/Group.java
index 33d0c4658f..4d8ec18ea7 100644
--- a/cucumber-plugin/src/main/java/io/cucumber/plugin/event/Group.java
+++ b/cucumber-plugin/src/main/java/io/cucumber/plugin/event/Group.java
@@ -1,5 +1,7 @@
package io.cucumber.plugin.event;
+import org.jspecify.annotations.Nullable;
+
import java.util.Collection;
/**
@@ -9,6 +11,7 @@ public interface Group {
Collection getChildren();
+ @Nullable
String getValue();
int getStart();
diff --git a/cucumber-plugin/src/main/java/io/cucumber/plugin/event/Location.java b/cucumber-plugin/src/main/java/io/cucumber/plugin/event/Location.java
index b85a6e5db2..180c937a68 100644
--- a/cucumber-plugin/src/main/java/io/cucumber/plugin/event/Location.java
+++ b/cucumber-plugin/src/main/java/io/cucumber/plugin/event/Location.java
@@ -48,4 +48,12 @@ public int compareTo(Location o) {
}
return Integer.compare(column, o.column);
}
+
+ @Override
+ public String toString() {
+ return "Location{" +
+ "line=" + line +
+ ", column=" + column +
+ '}';
+ }
}
diff --git a/cucumber-plugin/src/main/java/io/cucumber/plugin/event/Node.java b/cucumber-plugin/src/main/java/io/cucumber/plugin/event/Node.java
index 79d3a44a06..b9cd91d978 100644
--- a/cucumber-plugin/src/main/java/io/cucumber/plugin/event/Node.java
+++ b/cucumber-plugin/src/main/java/io/cucumber/plugin/event/Node.java
@@ -38,7 +38,7 @@ public interface Node {
default URI getUri() {
throw new UnsupportedOperationException("Not yet implemented");
- };
+ }
Location getLocation();
@@ -76,24 +76,23 @@ default T map(
BiFunction mapExamples,
BiFunction mapExample
) {
- if (this instanceof Scenario) {
- return mapScenario.apply((Scenario) this, parent);
- } else if (this instanceof Example) {
- return mapExample.apply((Example) this, parent);
- } else if (this instanceof Container) {
+ if (this instanceof Scenario scenario) {
+ return mapScenario.apply(scenario, parent);
+ } else if (this instanceof Example example) {
+ return mapExample.apply(example, parent);
+ } else if (this instanceof Container> container) {
final T mapped;
- if (this instanceof Feature) {
- mapped = mapFeature.apply((Feature) this, parent);
- } else if (this instanceof Rule) {
- mapped = mapRule.apply((Rule) this, parent);
- } else if (this instanceof ScenarioOutline) {
- mapped = mapScenarioOutline.apply((ScenarioOutline) this, parent);
- } else if (this instanceof Examples) {
- mapped = mapExamples.apply((Examples) this, parent);
+ if (this instanceof Feature feature) {
+ mapped = mapFeature.apply(feature, parent);
+ } else if (this instanceof Rule rule) {
+ mapped = mapRule.apply(rule, parent);
+ } else if (this instanceof ScenarioOutline scenarioOutline) {
+ mapped = mapScenarioOutline.apply(scenarioOutline, parent);
+ } else if (this instanceof Examples examples) {
+ mapped = mapExamples.apply(examples, parent);
} else {
throw new IllegalArgumentException(this.getClass().getName());
}
- Container> container = (Container>) this;
container.elements().forEach(node -> node.map(mapped, mapFeature, mapRule, mapScenario, mapScenarioOutline,
mapExamples, mapExample));
return mapped;
@@ -142,9 +141,8 @@ default Optional> findPathTo(Predicate predicate) {
path.add(candidate);
return Optional.of(path);
}
- if (candidate instanceof Container) {
+ if (candidate instanceof Container> container) {
path.add(candidate);
- Container> container = (Container>) candidate;
toSearch.addLast(new ArrayDeque<>(container.elements()));
}
}
diff --git a/cucumber-plugin/src/main/java/io/cucumber/plugin/event/PickleStepTestStep.java b/cucumber-plugin/src/main/java/io/cucumber/plugin/event/PickleStepTestStep.java
index 91be638d06..e3e0e0bb40 100644
--- a/cucumber-plugin/src/main/java/io/cucumber/plugin/event/PickleStepTestStep.java
+++ b/cucumber-plugin/src/main/java/io/cucumber/plugin/event/PickleStepTestStep.java
@@ -1,6 +1,7 @@
package io.cucumber.plugin.event;
import org.apiguardian.api.API;
+import org.jspecify.annotations.Nullable;
import java.net.URI;
import java.util.List;
@@ -45,6 +46,7 @@ public interface PickleStepTestStep extends TestStep {
* @deprecated use {@link #getStep()}
*/
@Deprecated
+ @Nullable
StepArgument getStepArgument();
/**
diff --git a/cucumber-plugin/src/main/java/io/cucumber/plugin/event/Result.java b/cucumber-plugin/src/main/java/io/cucumber/plugin/event/Result.java
index 77802e2f18..08fb833e12 100644
--- a/cucumber-plugin/src/main/java/io/cucumber/plugin/event/Result.java
+++ b/cucumber-plugin/src/main/java/io/cucumber/plugin/event/Result.java
@@ -1,6 +1,7 @@
package io.cucumber.plugin.event;
import org.apiguardian.api.API;
+import org.jspecify.annotations.Nullable;
import java.time.Duration;
import java.util.Objects;
@@ -15,7 +16,7 @@ public final class Result {
private final Status status;
private final Duration duration;
- private final Throwable error;
+ private final @Nullable Throwable error;
/**
* Creates a new result.
@@ -24,7 +25,7 @@ public final class Result {
* @param duration the duration
* @param error the error that caused the failure if any
*/
- public Result(Status status, Duration duration, Throwable error) {
+ public Result(Status status, Duration duration, @Nullable Throwable error) {
this.status = requireNonNull(status);
this.duration = requireNonNull(duration);
this.error = error;
@@ -47,7 +48,7 @@ public Duration getDuration() {
*
* @return the error encountered while executing a step or scenario or null.
*/
- public Throwable getError() {
+ public @Nullable Throwable getError() {
return error;
}
@@ -72,7 +73,7 @@ public boolean equals(Object o) {
public String toString() {
return "Result{" +
"status=" + status +
- ", duration=" + duration.getSeconds() +
+ ", duration=" + duration.toSeconds() +
", error=" + error +
'}';
}
diff --git a/cucumber-plugin/src/main/java/io/cucumber/plugin/event/Step.java b/cucumber-plugin/src/main/java/io/cucumber/plugin/event/Step.java
index 5ac6bb5ac7..15783536c2 100644
--- a/cucumber-plugin/src/main/java/io/cucumber/plugin/event/Step.java
+++ b/cucumber-plugin/src/main/java/io/cucumber/plugin/event/Step.java
@@ -1,6 +1,7 @@
package io.cucumber.plugin.event;
import org.apiguardian.api.API;
+import org.jspecify.annotations.Nullable;
/**
* Represents a step in a scenario.
@@ -14,6 +15,7 @@ public interface Step {
*
* @return a step argument, null if absent
*/
+ @Nullable
StepArgument getArgument();
/**
@@ -22,6 +24,7 @@ public interface Step {
* @return step key word
* @deprecated use {@link #getKeyword()} instead
*/
+ @Deprecated
default String getKeyWord() {
return getKeyword();
}
diff --git a/cucumber-plugin/src/main/java/io/cucumber/plugin/event/StepDefinition.java b/cucumber-plugin/src/main/java/io/cucumber/plugin/event/StepDefinition.java
index 1add47b80c..684c90519f 100644
--- a/cucumber-plugin/src/main/java/io/cucumber/plugin/event/StepDefinition.java
+++ b/cucumber-plugin/src/main/java/io/cucumber/plugin/event/StepDefinition.java
@@ -24,8 +24,10 @@ public String getLocation() {
}
/**
- * @return the pattern associated with this instance. Used for error
- * reporting only.
+ * Returns the pattern associated with this instance. Used for error
+ * reporting only.
+ *
+ * @return the pattern associated with this instance.
*/
public String getPattern() {
return pattern;
diff --git a/cucumber-plugin/src/main/java/io/cucumber/plugin/event/TestCase.java b/cucumber-plugin/src/main/java/io/cucumber/plugin/event/TestCase.java
index 490561c482..26fee7c80c 100644
--- a/cucumber-plugin/src/main/java/io/cucumber/plugin/event/TestCase.java
+++ b/cucumber-plugin/src/main/java/io/cucumber/plugin/event/TestCase.java
@@ -14,7 +14,8 @@ public interface TestCase {
* is an example in a Scenario Outline the method wil return the line of the
* example.
*
- * @return the line of this scenario.
+ * @return the line of this scenario.
+ * @deprecated use {@link #getLocation()} instead.
*/
@Deprecated
Integer getLine();
diff --git a/cucumber-plugin/src/main/java/io/cucumber/plugin/event/TestCaseEvent.java b/cucumber-plugin/src/main/java/io/cucumber/plugin/event/TestCaseEvent.java
index 4cfc03c267..947c82125a 100644
--- a/cucumber-plugin/src/main/java/io/cucumber/plugin/event/TestCaseEvent.java
+++ b/cucumber-plugin/src/main/java/io/cucumber/plugin/event/TestCaseEvent.java
@@ -15,6 +15,9 @@ public abstract class TestCaseEvent extends TimeStampedEvent {
this.testCase = Objects.requireNonNull(testCase);
}
+ /**
+ * Returns the test case for this event.
+ */
public TestCase getTestCase() {
return testCase;
}
diff --git a/cucumber-plugin/src/main/java/io/cucumber/plugin/event/TestRunFinished.java b/cucumber-plugin/src/main/java/io/cucumber/plugin/event/TestRunFinished.java
index 6facfa8398..277a64cc6c 100644
--- a/cucumber-plugin/src/main/java/io/cucumber/plugin/event/TestRunFinished.java
+++ b/cucumber-plugin/src/main/java/io/cucumber/plugin/event/TestRunFinished.java
@@ -1,6 +1,7 @@
package io.cucumber.plugin.event;
import org.apiguardian.api.API;
+import org.jspecify.annotations.Nullable;
import java.time.Instant;
import java.util.Objects;
@@ -8,7 +9,7 @@
@API(status = API.Status.STABLE)
public final class TestRunFinished extends TimeStampedEvent {
- private final Result result;
+ private final @Nullable Result result;
@Deprecated
public TestRunFinished(Instant timeInstant) {
@@ -21,7 +22,7 @@ public TestRunFinished(Instant timeInstant, Result result) {
this.result = Objects.requireNonNull(result);
}
- public Result getResult() {
+ public @Nullable Result getResult() {
return result;
}
diff --git a/cucumber-plugin/src/main/java/io/cucumber/plugin/event/package-info.java b/cucumber-plugin/src/main/java/io/cucumber/plugin/event/package-info.java
new file mode 100644
index 0000000000..644055de5d
--- /dev/null
+++ b/cucumber-plugin/src/main/java/io/cucumber/plugin/event/package-info.java
@@ -0,0 +1,4 @@
+@NullMarked
+package io.cucumber.plugin.event;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/cucumber-plugin/src/main/java/io/cucumber/plugin/package-info.java b/cucumber-plugin/src/main/java/io/cucumber/plugin/package-info.java
new file mode 100644
index 0000000000..6f1ad75054
--- /dev/null
+++ b/cucumber-plugin/src/main/java/io/cucumber/plugin/package-info.java
@@ -0,0 +1,4 @@
+@NullMarked
+package io.cucumber.plugin;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/cucumber-plugin/src/test/java/io/cucumber/plugin/event/NodeTest.java b/cucumber-plugin/src/test/java/io/cucumber/plugin/event/NodeTest.java
index b84298de06..dbdd026e90 100644
--- a/cucumber-plugin/src/test/java/io/cucumber/plugin/event/NodeTest.java
+++ b/cucumber-plugin/src/test/java/io/cucumber/plugin/event/NodeTest.java
@@ -1,5 +1,6 @@
package io.cucumber.plugin.event;
+import org.jspecify.annotations.NullUnmarked;
import org.junit.jupiter.api.Test;
import java.net.URI;
@@ -11,6 +12,7 @@
import static java.util.Arrays.asList;
import static org.junit.jupiter.api.Assertions.assertEquals;
+@NullUnmarked
class NodeTest {
private final Node.Example example1 = new Node.Example() {
diff --git a/cucumber-spring/src/main/java/io/cucumber/spring/package-info.java b/cucumber-spring/src/main/java/io/cucumber/spring/package-info.java
index ab6089556a..f02bb4ceb3 100644
--- a/cucumber-spring/src/main/java/io/cucumber/spring/package-info.java
+++ b/cucumber-spring/src/main/java/io/cucumber/spring/package-info.java
@@ -4,4 +4,7 @@
* By including the cucumber-spring on your CLASSPATH
* your step definitions will be instantiated by Spring.
*/
+@NullMarked
package io.cucumber.spring;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/cucumber-testng/src/main/java/io/cucumber/testng/package-info.java b/cucumber-testng/src/main/java/io/cucumber/testng/package-info.java
new file mode 100644
index 0000000000..962934b91e
--- /dev/null
+++ b/cucumber-testng/src/main/java/io/cucumber/testng/package-info.java
@@ -0,0 +1,4 @@
+@NullMarked
+package io.cucumber.testng;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/datatable-matchers/pom.xml b/datatable-matchers/pom.xml
index d1886d0e34..ba1424d7e0 100644
--- a/datatable-matchers/pom.xml
+++ b/datatable-matchers/pom.xml
@@ -45,7 +45,13 @@
apiguardian-api${apiguardian-api.version}
-
+
+
+ org.jspecify
+ jspecify
+ 1.0.0
+
+
io.cucumberdatatable
diff --git a/datatable-matchers/src/main/java/io/cucumber/datatable/matchers/package-info.java b/datatable-matchers/src/main/java/io/cucumber/datatable/matchers/package-info.java
new file mode 100644
index 0000000000..16a45eabfa
--- /dev/null
+++ b/datatable-matchers/src/main/java/io/cucumber/datatable/matchers/package-info.java
@@ -0,0 +1,3 @@
+@NullMarked
+package io.cucumber.datatable.matchers;
+
diff --git a/datatable/pom.xml b/datatable/pom.xml
index a0437bb537..d559a0f0ff 100644
--- a/datatable/pom.xml
+++ b/datatable/pom.xml
@@ -56,6 +56,12 @@
${apiguardian-api.version}
+
+ org.jspecify
+ jspecify
+ 1.0.0
+
+
com.googlecode.java-diff-utilsdiffutils
diff --git a/datatable/src/main/java/io/cucumber/datatable/ConversionRequired.java b/datatable/src/main/java/io/cucumber/datatable/ConversionRequired.java
index 70817c1721..e63b91f507 100644
--- a/datatable/src/main/java/io/cucumber/datatable/ConversionRequired.java
+++ b/datatable/src/main/java/io/cucumber/datatable/ConversionRequired.java
@@ -9,11 +9,13 @@
final class ConversionRequired implements TableConverter {
@Override
+ @SuppressWarnings("TypeParameterUnusedInFormals")
public T convert(DataTable dataTable, Type type) {
return convert(dataTable, type, false);
}
@Override
+ @SuppressWarnings("TypeParameterUnusedInFormals")
public T convert(DataTable dataTable, Type type, boolean transposed) {
throw new CucumberDataTableException(String
.format("Can't convert DataTable to %s. You have to write the conversion for it in this method", type));
diff --git a/datatable/src/main/java/io/cucumber/datatable/CucumberDataTableException.java b/datatable/src/main/java/io/cucumber/datatable/CucumberDataTableException.java
index 3a09d90703..386b344b82 100644
--- a/datatable/src/main/java/io/cucumber/datatable/CucumberDataTableException.java
+++ b/datatable/src/main/java/io/cucumber/datatable/CucumberDataTableException.java
@@ -1,6 +1,7 @@
package io.cucumber.datatable;
import org.apiguardian.api.API;
+import org.jspecify.annotations.Nullable;
import java.lang.reflect.Type;
@@ -13,8 +14,8 @@ public class CucumberDataTableException extends RuntimeException {
super(message);
}
- CucumberDataTableException(String s, Throwable throwable) {
- super(s, throwable);
+ CucumberDataTableException(@Nullable String message, Throwable throwable) {
+ super(message, throwable);
}
static CucumberDataTableException cantConvertTo(Type type, String message) {
@@ -28,7 +29,7 @@ private static CucumberDataTableException cantConvertToMap(Type keyType, Type va
}
static CucumberDataTableException duplicateKeyException(
- Type keyType, Type valueType, K key, V value, V replaced
+ Type keyType, Type valueType, @Nullable K key, @Nullable V value, @Nullable V replaced
) {
return cantConvertToMap(keyType, valueType,
format("Encountered duplicate key %s with values %s and %s", key, replaced, value));
@@ -63,17 +64,17 @@ static CucumberDataTableException keyValueMismatchException(
static CucumberDataTableException keysImplyTableEntryTransformer(Type keyType, Type valueType) {
return cantConvertToMap(keyType, valueType,
- format("The first cell was either blank or you have registered a TableEntryTransformer for the key type.\n"
- +
- "\n" +
- "This requires that there is a TableEntryTransformer for the value type but I couldn't find any.\n"
- +
- "\n" +
- "You can either:\n" +
- "\n" +
- " 1) Use a DataTableType that uses a TableEntryTransformer for %s\n" +
- "\n" +
- " 2) Add a key to the first cell and use a DataTableType that uses a TableEntryTransformer for %s",
+ format(
+ """
+ The first cell was either blank or you have registered a TableEntryTransformer for the key type.
+
+ This requires that there is a TableEntryTransformer for the value type but I couldn't find any.
+
+ You can either:
+
+ 1) Use a DataTableType that uses a TableEntryTransformer for %s
+
+ 2) Add a key to the first cell and use a DataTableType that uses a TableEntryTransformer for %s""",
valueType, keyType));
}
diff --git a/datatable/src/main/java/io/cucumber/datatable/DataTable.java b/datatable/src/main/java/io/cucumber/datatable/DataTable.java
index 67e1e84ed1..d413406339 100644
--- a/datatable/src/main/java/io/cucumber/datatable/DataTable.java
+++ b/datatable/src/main/java/io/cucumber/datatable/DataTable.java
@@ -1,6 +1,7 @@
package io.cucumber.datatable;
import org.apiguardian.api.API;
+import org.jspecify.annotations.Nullable;
import java.io.IOException;
import java.lang.reflect.Type;
@@ -16,6 +17,7 @@
import static java.util.Collections.emptyList;
import static java.util.Collections.unmodifiableList;
import static java.util.Collections.unmodifiableMap;
+import static java.util.Objects.requireNonNull;
/**
* A m-by-n table of string values. For example:
@@ -29,7 +31,7 @@
* A table is either empty or contains one or more values. As such if a table
* has zero height it must have zero width and vice versa.
*
- * The first row of the the table may be referred to as the table header. The
+ * The first row of the table may be referred to as the table header. The
* remaining cells as the table body.
*
* A table can be converted into an object of an arbitrary type by a
@@ -43,7 +45,7 @@
@API(status = API.Status.STABLE)
public final class DataTable {
- private final List> raw;
+ private final List> raw;
private final TableConverter tableConverter;
/**
@@ -56,13 +58,9 @@ public final class DataTable {
* @param tableConverter to transform the table
* @throws NullPointerException if either raw or tableConverter is null
*/
- private DataTable(List> raw, TableConverter tableConverter) {
- if (raw == null)
- throw new NullPointerException("cells can not be null");
- if (tableConverter == null)
- throw new NullPointerException("tableConverter can not be null");
- this.raw = raw;
- this.tableConverter = tableConverter;
+ private DataTable(List> raw, TableConverter tableConverter) {
+ this.raw = requireNonNull(raw);
+ this.tableConverter = requireNonNull(tableConverter);
}
/**
@@ -76,7 +74,7 @@ private DataTable(List> raw, TableConverter tableConverter) {
* @throws IllegalArgumentException when the table is not rectangular or
* contains null values.
*/
- public static DataTable create(List> raw) {
+ public static DataTable create(List> raw) {
return create(raw, new NoConverterDefined());
}
@@ -91,26 +89,26 @@ public static DataTable create(List> raw) {
* @throws IllegalArgumentException when the table is not rectangular or
* contains null values
*/
- public static DataTable create(List> raw, TableConverter tableConverter) {
+ public static DataTable create(List> raw, TableConverter tableConverter) {
return new DataTable(copy(requireRectangularTable(raw)), tableConverter);
}
- private static List> copy(List> balanced) {
- List> rawCopy = new ArrayList<>(balanced.size());
- for (List row : balanced) {
+ private static List> copy(List> balanced) {
+ List> rawCopy = new ArrayList<>(balanced.size());
+ for (List<@Nullable String> row : balanced) {
// A table without columns is an empty table and has no rows.
if (row.isEmpty()) {
return emptyList();
}
- List rowCopy = new ArrayList<>(row.size());
+ List<@Nullable String> rowCopy = new ArrayList<>(row.size());
rowCopy.addAll(row);
rawCopy.add(unmodifiableList(rowCopy));
}
return unmodifiableList(rawCopy);
}
- private static List> requireRectangularTable(List> table) {
+ private static List> requireRectangularTable(List> table) {
int columns = table.isEmpty() ? 0 : table.get(0).size();
for (List row : table) {
if (columns != row.size()) {
@@ -173,7 +171,7 @@ public void unorderedDiff(DataTable actual) throws TableDiffException {
*
* @return the values of the table
*/
- public List values() {
+ public List<@Nullable String> values() {
return new ListView();
}
@@ -183,7 +181,7 @@ public List values() {
* @return a list of strings
* @see TableConverter#toList(DataTable, Type)
*/
- public List asList() {
+ public List<@Nullable String> asList() {
return asList(String.class);
}
@@ -195,7 +193,7 @@ public List asList() {
* @return a list of objects
* @see TableConverter#toList(DataTable, Type)
*/
- public List asList(Class itemType) {
+ public List<@Nullable T> asList(Class itemType) {
return tableConverter.toList(this, itemType);
}
@@ -207,17 +205,17 @@ public List asList(Class itemType) {
* @return a list of objects
* @see TableConverter#toList(DataTable, Type)
*/
- public List asList(Type itemType) {
+ public List<@Nullable T> asList(Type itemType) {
return tableConverter.toList(this, itemType);
}
/**
* Converts the table to a list of lists of {@code String}s.
*
- * @return a list of list of strings
+ * @return a list-of-list-of strings
* @see TableConverter#toLists(DataTable, Type)
*/
- public List> asLists() {
+ public List> asLists() {
return asLists(String.class);
}
@@ -229,7 +227,7 @@ public List> asLists() {
* @return a list of list of objects
* @see TableConverter#toLists(DataTable, Type)
*/
- public List> asLists(Class itemType) {
+ public List> asLists(Class itemType) {
return tableConverter.toLists(this, itemType);
}
@@ -241,7 +239,7 @@ public List> asLists(Class itemType) {
* @return a list of list of objects
* @see TableConverter#toLists(DataTable, Type)
*/
- public List> asLists(Type itemType) {
+ public List> asLists(Type itemType) {
return tableConverter.toLists(this, itemType);
}
@@ -255,7 +253,7 @@ public List> asLists(Type itemType) {
* @return a map
* @see TableConverter#toMap(DataTable, Type, Type)
*/
- public Map asMap() {
+ public Map<@Nullable String, @Nullable String> asMap() {
return asMap(String.class, String.class);
}
@@ -274,7 +272,7 @@ public Map asMap() {
* @return a map
* @see TableConverter#toMap(DataTable, Type, Type)
*/
- public Map asMap(Class keyType, Class valueType) {
+ public Map<@Nullable K, @Nullable V> asMap(Class keyType, Class valueType) {
return tableConverter.toMap(this, keyType, valueType);
}
@@ -293,7 +291,7 @@ public Map asMap(Class keyType, Class valueType) {
* @return a map
* @see TableConverter#toMap(DataTable, Type, Type)
*/
- public Map asMap(Type keyType, Type valueType) {
+ public Map<@Nullable K, @Nullable V> asMap(Type keyType, Type valueType) {
return tableConverter.toMap(this, keyType, valueType);
}
@@ -303,16 +301,16 @@ public Map asMap(Type keyType, Type valueType) {
*
* @return a view of the entries in a table.
*/
- public List