diff --git a/.gitignore b/.gitignore
index a2653f2..1835283 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,4 +21,7 @@ target
*.iws
.idea/
-*.gpg
\ No newline at end of file
+# VS Code
+.vscode
+
+*.gpg
diff --git a/pom.xml b/pom.xml
index 7ded3b9..0960f3e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -40,6 +40,8 @@
2.0.1.Final3.1.15.13.4
+ 1.3
+ 4.11.0
@@ -102,7 +104,25 @@
junit-jupiter-apitest
+
+ org.junit.jupiter
+ junit-jupiter-params
+ test
+
+
+ org.hamcrest
+ hamcrest-all
+ ${hamcrest.version}
+ test
+
+
+ org.mockito
+ mockito-core
+ ${mockito.version}
+ test
+
+
@@ -126,6 +146,32 @@
${java.version}
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 3.5.4
+
+
+ false
+ 3.0
+ false
+ true
+ true
+ true
+
+
+ false
+ UTF-8
+ false
+
+
+ false
+ false
+ true
+ true
+
+
+ org.apache.maven.pluginsmaven-jar-plugin
diff --git a/src/main/java/org/openapitools/jackson/nullable/JsonNullable.java b/src/main/java/org/openapitools/jackson/nullable/JsonNullable.java
index 43a521b..53c4173 100644
--- a/src/main/java/org/openapitools/jackson/nullable/JsonNullable.java
+++ b/src/main/java/org/openapitools/jackson/nullable/JsonNullable.java
@@ -4,6 +4,10 @@
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import java.util.stream.Stream;
public class JsonNullable implements Serializable {
@@ -66,15 +70,86 @@ public T orElse(T other) {
return this.isPresent ? this.value : other;
}
+ /**
+ * If a value is present, returns the value, otherwise returns the result
+ * produced by the supplying function.
+ *
+ * @param supplier the supplying function that produces a value to be returned
+ * @return the value, if present, otherwise the result produced by the supplying function
+ * @throws NullPointerException if no value is present and the supplying function is null
+ *
+ * @since 0.2.8
+ */
+ public T orElseGet(Supplier extends T> supplier) {
+ return this.isPresent ? this.value : supplier.get();
+ }
+
+ /**
+ * If a value is present, returns the value, otherwise throws
+ * NoSuchElementException.
+ *
+ * @return the value of this JsonNullable
+ * @throws NoSuchElementException if no value if present
+ *
+ * @since 0.2.8
+ */
+ public T orElseThrow() {
+ if (!isPresent) {
+ throw new NoSuchElementException("Value is undefined");
+ }
+ return value;
+ }
+
+ /**
+ * If a value is present, returns the value, otherwise throws an exception
+ * produced by the exception supplying function.
+ *
+ * @param type of the exception to be thrown
+ * @param supplier the supplying function that produces an exception to be
+ * thrown
+ * @return the value, if present
+ * @throws X if no value is present
+ * @throws NullPointerException if no value is present and the exception
+ * supplying function is {@code null}
+ *
+ * @since 0.2.8
+ */
+ public T orElseThrow(Supplier extends X> supplier)
+ throws X
+ {
+ if( this.isPresent ) {
+ return this.value;
+ }
+ throw supplier.get();
+ }
+
+ /**
+ * If a value is present, returns true, otherwise false.
+ *
+ * @return true if a value is present, otherwise false
+ */
public boolean isPresent() {
return isPresent;
}
+ /**
+ * If a value is not present, returns true, otherwise false.
+ *
+ * @return true if a value is not present, otherwise false
+ *
+ * @since 0.2.8
+ */
+ public boolean isUndefined() {
+ return !isPresent;
+ }
+
/**
* If a value is present, performs the given action with the value,
* otherwise does nothing.
*
* @param action the action to be performed, if a value is present
+ * @throws NullPointerException if a value is present and the given action
+ * is null
*/
public void ifPresent(
Consumer super T> action) {
@@ -84,6 +159,153 @@ public void ifPresent(
}
}
+ /**
+ * If a value is present, performs the given action with the value,
+ * otherwise performs the given empty-based action.
+ *
+ * @param action the action to be performed, if a value is present
+ * @param undefinedAction the empty-based action to be performed, if no
+ * value is present
+ * @throws NullPointerException if a value is present and the given action
+ * is null, or no value is present and the given empty-based action
+ * is null
+ *
+ * @since 0.2.8
+ */
+ public void ifPresentOrElse( Consumer super T> action, Runnable undefinedAction ) {
+ if (this.isPresent) {
+ action.accept(value);
+ }
+ else {
+ undefinedAction.run();
+ }
+ }
+
+ /**
+ * If a value is present, and the value matches the given predicate, returns
+ * a JsonNullable describing the value, otherwise returns an undefined
+ * JsonNullable.
+ *
+ * @param predicate the predicate to apply to a value, if present
+ * @return a JsonNullable describing the value of this JsonNullable,
+ * if a value is present and the value matches the given predicate,
+ * otherwise an undefined JsonNullable
+ * @throws NullPointerException if the predicate is null
+ *
+ * @since 0.2.8
+ */
+ public JsonNullable filter( Predicate predicate ) {
+ if (predicate == null) {
+ throw new NullPointerException("filter predicate is null");
+ }
+ if (this.isPresent && predicate.test(value)) {
+ return this;
+ }
+ else {
+ return undefined();
+ }
+ }
+
+ /**
+ * If a value is present, returns a JsonNullable describing the result of
+ * applying the given mapping function to the value, otherwise returns an
+ * undeined JsonNullable.
+ *
+ * @param the type of the value returned from the mapping function
+ * @param mapper the mapping function to apply to a value, if present
+ * @return a JsonNullable describing the result of applying a mapping
+ * function to the value of this JsonNullable, if a value is
+ * present, otherwise an undefined JsonNullable
+ * @throws NullPointerException if the mapping function is null
+ *
+ * @since 0.2.8
+ */
+ public JsonNullable map( Function mapper) {
+ if (mapper == null) {
+ throw new NullPointerException("mapping function is null");
+ }
+ if (this.isPresent) {
+ return new JsonNullable(mapper.apply(value), true);
+ }
+ return undefined();
+ }
+
+ /**
+ * If a value is present, returns the result of applying the given
+ * JsonNullable-bearing mapping function to the value, otherwise returns an
+ * undefined JsonNullable.
+ *
+ * @param the type of value of the JsonNullable returned by the mapping
+ * function
+ * @param mapper the mapping function to apply to a value, if present
+ * @return the result of applying a JsonNullable-bearing mapping function to
+ * the value of this JsonNullable, if a value is present, otherwise
+ * an undefined JsonNullable
+ * @throws NullPointerException if the mapping function is null or returns a
+ * null result
+ *
+ * @since 0.2.8
+ */
+ @SuppressWarnings("unchecked")
+ public JsonNullable flatMap( Function super T, ? extends JsonNullable extends U>> mapper ) {
+ if (mapper == null) {
+ throw new NullPointerException("mapping function is null");
+ }
+ if (!this.isPresent) {
+ return undefined();
+ }
+
+ JsonNullable mapped = (JsonNullable)mapper.apply(value);
+ if (mapped == null) {
+ throw new NullPointerException("mapped value is null");
+ }
+ return mapped;
+ }
+
+ /**
+ * If a value is present, returns a JsonNullable describing the value,
+ * otherwise returns a JsonNullable produced by the supplying function.
+ *
+ * @param supplier the supplying function that produces a JsonNullable to be
+ * returned
+ * @return returns a JsonNullable describing the value of this JsonNullable,
+ * if a value is present, otherwise a JsonNullable produced by the
+ * supplying function.
+ * @throws NullPointerException if the supplying function is null or
+ * produces a null result
+ *
+ * @since 0.2.8
+ */
+ @SuppressWarnings("unchecked")
+ public JsonNullable or( Supplier extends JsonNullable extends T>> supplier ) {
+ if( supplier == null ) {
+ throw new NullPointerException("or supplier is null");
+ }
+ if (this.isPresent) {
+ return this;
+ }
+ JsonNullable supplied = (JsonNullable)supplier.get();
+ if (supplied == null) {
+ throw new NullPointerException("supplied value is null");
+ }
+ return supplied;
+ }
+
+ /**
+ * If a value is present, returns a sequential Stream containing only that
+ * value, otherwise returns an empty Stream.
+ *
+ * @return the JsonNullable value as a Stream
+ *
+ * @since 0.2.8
+ */
+ public Stream stream() {
+ if (this.isPresent) {
+ return Stream.of(value);
+ }
+ return Stream.empty();
+ }
+
@Override
public boolean equals(Object obj) {
if (this == obj) {
diff --git a/src/test/java/org/openapitools/jackson/nullable/StreamingApiTest.java b/src/test/java/org/openapitools/jackson/nullable/StreamingApiTest.java
new file mode 100644
index 0000000..f4b7a73
--- /dev/null
+++ b/src/test/java/org/openapitools/jackson/nullable/StreamingApiTest.java
@@ -0,0 +1,1023 @@
+/*
+ * Copyright 2025 Christian Trimble
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openapitools.jackson.nullable;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.NoSuchElementException;
+import java.util.Optional;
+import java.util.concurrent.Callable;
+import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.hamcrest.Matcher;
+import org.junit.jupiter.api.Assumptions;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.mockito.AdditionalAnswers;
+
+import static org.openapitools.jackson.nullable.JsonNullable.of;
+import static org.openapitools.jackson.nullable.JsonNullable.undefined;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.nullValue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.empty;
+
+/**
+ * Tests for Stream API methods patterned after java.util.Optional.
+ *
+ * @author Christian Trimble
+ */
+//@DisplayName("Streaming API Tests")
+public class StreamingApiTest {
+
+ static ReflectiveService>> OPTIONAL = new ReflectiveService>>(Optional.class);
+
+ static String VALUE = "value";
+ static String OTHER = "other";
+ static String NULL = null;
+ static JsonNullable JSON_VALUE = of(VALUE);
+ static JsonNullable JSON_OTHER = of(OTHER);
+ static JsonNullable JSON_NULL = of(null);
+ static JsonNullable UNDEFINED = undefined();
+ static Class NPE = NullPointerException.class;
+ static Class TE = TestException.class;
+
+ @TestInstance(TestInstance.Lifecycle.PER_CLASS)
+ public abstract class BaseTest {
+
+ public abstract Stream