*/
+@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/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/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/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/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/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..b420fd0f47 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,16 @@ 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..f87b3abc41 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