This is an automated email from the ASF dual-hosted git repository.

jochen pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-lang.git


The following commit(s) were added to refs/heads/master by this push:
     new 45e8395  Introducing @Nonnull, @Nullable, and the Objects class as a 
helper tool.
45e8395 is described below

commit 45e8395ec7a63f432ce8df03159ee5465b1380ca
Author: Jochen Wiedmann <jochen.wiedm...@gmail.com>
AuthorDate: Sat Feb 6 22:51:47 2021 +0100

    Introducing @Nonnull, @Nullable, and the Objects class as a helper tool.
---
 pom.xml                                            |   7 +-
 spotbugs-exclude-filter.xml                        |   8 +
 src/changes/changes.xml                            |   1 +
 .../org/apache/commons/lang3/function/Objects.java | 176 +++++++++++++++++++++
 .../apache/commons/lang3/function/ObjectsTest.java | 146 +++++++++++++++++
 5 files changed, 337 insertions(+), 1 deletion(-)

diff --git a/pom.xml b/pom.xml
index f8524e8..5135e4c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -566,7 +566,12 @@
       <version>${jmh.version}</version>
       <scope>test</scope>
     </dependency>
-
+    <dependency>
+      <groupId>com.google.code.findbugs</groupId>
+      <artifactId>jsr305</artifactId>
+      <version>3.0.2</version>
+      <scope>provided</scope>
+    </dependency>
   </dependencies>
 
   <distributionManagement>
diff --git a/spotbugs-exclude-filter.xml b/spotbugs-exclude-filter.xml
index 8636890..be00732 100644
--- a/spotbugs-exclude-filter.xml
+++ b/spotbugs-exclude-filter.xml
@@ -153,4 +153,12 @@
     <Method name="compare" />
     <Bug pattern="RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE" />
   </Match>
+
+  <!-- Reason: requireNonNull is supposed to take a nullable parameter,
+       whatever Spotbugs thinks of it. -->
+  <Match>
+    <Class name="org.apache.commons.lang3.function.Objects" />
+    <Method name="requireNonNull" />
+    <Bug pattern="NP_PARAMETER_MUST_BE_NONNULL_BUT_MARKED_AS_NULLABLE" />
+  </Match>
 </FindBugsFilter>
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 1eca750..4908376 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -105,6 +105,7 @@ The <action> type attribute can be add,update,fix,remove.
     <action                   type="update" dev="ggregory" due-to="Ali K. 
Nouri">Processor.java: check enum equality with == instead of .equals() method 
#690.</action>
     <action                   type="update" dev="ggregory" 
due-to="Dependabot">Bump junit-pioneer from 1.1.0 to 1.3.0 #702.</action>
     <action                   type="update" dev="ggregory" 
due-to="Dependabot">Bump maven-checkstyle-plugin from 3.1.1 to 3.1.2 
#705.</action>
+    <action                   type="add"    dev="jochen">Introduce the use of 
@Nonnull, and @Nullable, and the Objects class as a helper tool.</action>
   </release>
   <release version="3.11" date="2020-07-12" description="New features and bug 
fixes.">
     <action                   type="update" dev="chtompki" due-to="Jin 
Xu">Refine test output for FastDateParserTest</action>
diff --git a/src/main/java/org/apache/commons/lang3/function/Objects.java 
b/src/main/java/org/apache/commons/lang3/function/Objects.java
new file mode 100755
index 0000000..02f7ddb
--- /dev/null
+++ b/src/main/java/org/apache/commons/lang3/function/Objects.java
@@ -0,0 +1,176 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.commons.lang3.function;
+
+import java.util.function.Supplier;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.apache.commons.lang3.ObjectUtils;
+
+
+/**
+ * This class provides some replacements for the corresponding methods in
+ * {@link Objects}. The replacements have the advantage, that they are properly
+ * annotated with {@link Nullable}, and/or {@link Nonnull}, so they let the
+ * compiler know, what their respective results are.
+ *
+ * The various {@code requireNonNull} methods are particularly handy, when
+ * dealing with external code, that a) doesn't support the {@link Nonnull}
+ * annotation, or if you know for other reasons, that an object is non-null.
+ * Take for example, a {@link java.util.Map map}, that you have filled with
+ * non-null values. So, in your opinion, the following should be perfectably
+ * valid code:
+ * <pre>
+ *   final Map&lt;String,Object&gt; map = getMapOfNonNullValues();
+ *   final @Nonnull Object o = map.get("SomeKey");
+ * </pre>
+ * However, if your Java compiler *does* null analysis, it will reject this
+ * example as invalid, because {@link java.util.Map#get(Object)} might return
+ * a null value. As a workaround, you can use this:
+ * <pre>
+ *   import static org.apache.commons.lang3.function.Objects.requireNonNull;
+ *
+ *   final Map&lt;String,Object&gt; map = getMapOfNonNullValues();
+ *   final @Nonnull Object o = requireNonNull(map.get("SomeKey"));
+ * </pre>
+ *
+ * This class is somewhat redundant with regards to {@link ObjectUtils}.
+ * For example, {@link #requireNonNull(Object, Object)} is almost equivalent
+ * with {@link ObjectUtils#defaultIfNull(Object, Object)}. However, it isn't
+ * quite the same, because the latter can, in fact, return null. The former
+ * can't, and the Java compiler confirms this.(An alternative to redundancy
+ * would have been to change the {@code ObjectUtils} class. However, that
+ * would mean loosing upwards compatibility, and we don't do that.)
+ */
+public class Objects {
+    /**
+     * Checks, whether the given object is non-null. If so, returns the 
non-null
+     * object as a result value. Otherwise, a NullPointerException is thrown.
+     * @param <O> The type of parameter {@code value}, also the result type.
+     * @param value The value, which is being checked.
+     * @return The given input value, if it was found to be non-null.
+     * @throws NullPointerException The input value was null.
+     * @see java.util.Objects#requireNonNull(Object)
+     */
+    public static <O> @Nonnull O requireNonNull(@Nullable O value) throws 
NullPointerException {
+        if (value == null) {
+            throw new NullPointerException("The value must not be null.");
+        } else {
+            return value;
+        }
+    }
+
+    /**
+     * Checks, whether the given object is non-null. If so, returns the 
non-null
+     * object as a result value. Otherwise, a NullPointerException is thrown.
+     * @param <O> The type of parameter {@code value}, also the result type.
+     * @param value The value, which is being checked.
+     * @param defaultValue The default value, which is being returned, if the
+     *   check fails, and the {@code value} is null.
+     * @throws NullPointerException The input value, and the default value are 
null.
+     * @return The given input value, if it was found to be non-null.
+     * @see java.util.Objects#requireNonNull(Object)
+     */
+    public static <O> @Nonnull O requireNonNull(@Nullable O value, @Nonnull O 
defaultValue) {
+        if (value == null) {
+            if (defaultValue == null) {
+                throw new NullPointerException("The default value must not be 
null.");
+            } else {
+                return defaultValue;
+            }
+        } else {
+            return value;
+        }
+    }
+
+    /**
+     * Checks, whether the given object is non-null. If so, returns the 
non-null
+     * object as a result value. Otherwise, a NullPointerException is thrown.
+     * @param <O> The type of parameter {@code value}, also the result type.
+     * @param value The value, which is being checked.
+     * @param msg A string, which is being used as the exceptions message, if 
the
+     *   check fails.
+     * @return The given input value, if it was found to be non-null.
+     * @throws NullPointerException The input value was null.
+     * @see java.util.Objects#requireNonNull(Object, String)
+     * @see #requireNonNull(Object, Supplier)
+     */
+    public static <O> @Nonnull O requireNonNull(@Nullable O value, @Nonnull 
String msg) throws NullPointerException {
+        if (value == null) {
+            throw new NullPointerException(msg);
+        } else {
+            return value;
+        }
+    }
+
+    /**
+     * Checks, whether the given object is non-null. If so, returns the 
non-null
+     * object as a result value. Otherwise, a NullPointerException is thrown.
+     * @param <O> The type of parameter {@code value}, also the result type.
+     * @param value The value, which is being checked.
+     * @param msgSupplier A supplier, which creates the exception message, if 
the check fails.
+     *     This supplier will only be invoked, if necessary.
+     * @return The given input value, if it was found to be non-null.
+     * @throws NullPointerException The input value was null.
+     * @see java.util.Objects#requireNonNull(Object, String)
+     * @see #requireNonNull(Object, String)
+     */
+    public static <O> @Nonnull O requireNonNull(@Nullable O value, @Nonnull 
Supplier<String> msgSupplier) throws NullPointerException {
+        if (value == null) {
+            throw new NullPointerException(msgSupplier.get());
+        } else {
+            return value;
+        }
+    }
+
+    /** Checks, whether the given object is non-null. If so, returns the 
non-null
+     * object as a result value. Otherwise, invokes the given {@link Supplier},
+     * and returns the suppliers result value.
+     * @param <O> The type of parameter {@code value}, also the result type of
+     *    the default value supplier, and of the method itself.
+     * @param <E> The type of exception, that the {@code default value 
supplier},
+     *    may throw.
+     * @param value The value, which is being checked.
+     * @param defaultValueSupplier The supplier, which returns the default 
value. This default
+     *   value <em>must</em> be non-null. The supplier will only be invoked, if
+     *   necessary. (If the {@code value} parameter is null, that is.)
+     * @return The given input value, if it was found to be non-null. 
Otherwise,
+     *   the value, that has been returned by the default value supplier.
+     * @see #requireNonNull(Object)
+     * @see #requireNonNull(Object, String)
+     * @see #requireNonNull(Object, Supplier)
+     * @throws NullPointerException The default value supplier is null, or the 
default
+     *   value supplier has returned null.
+     */
+    public static <O, E extends Throwable> @Nonnull O requireNonNull(@Nullable 
O value, @Nonnull FailableSupplier<O, E> defaultValueSupplier) {
+        if (value == null) {
+            final FailableSupplier<O, ?> supp = 
requireNonNull(defaultValueSupplier, "The supplier must not be null");
+            final O o;
+            try {
+                o = supp.get();
+            } catch (Throwable t) {
+                throw Failable.rethrow(t);
+            }
+            return requireNonNull(o, "The supplier must not return null.");
+        } else {
+            return value;
+        }
+    }
+}
diff --git a/src/test/java/org/apache/commons/lang3/function/ObjectsTest.java 
b/src/test/java/org/apache/commons/lang3/function/ObjectsTest.java
new file mode 100755
index 0000000..cea1c8e
--- /dev/null
+++ b/src/test/java/org/apache/commons/lang3/function/ObjectsTest.java
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.commons.lang3.function;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.util.function.Supplier;
+
+import org.junit.jupiter.api.Test;
+
+
+class ObjectsTest {
+    @Test
+    void testRequireNonNullObject() {
+        assertSame("foo", Objects.requireNonNull("foo"));
+        try {
+            Objects.requireNonNull(null);
+            fail("Expected Exception");
+        } catch (NullPointerException e) {
+            assertEquals("The value must not be null.", e.getMessage());
+        }
+    }
+
+    @Test
+    void testRequireNonNullObjectString() {
+        assertSame("foo", Objects.requireNonNull("foo", "bar"));
+        try {
+            Objects.requireNonNull(null, "bar");
+            fail("Expected Exception");
+        } catch (NullPointerException e) {
+            assertEquals("bar", e.getMessage());
+        }
+    }
+
+    public static class TestableSupplier<O> implements Supplier<O> {
+        private final Supplier<O> supplier;
+        private boolean invoked;
+
+        TestableSupplier(Supplier<O> pSupplier) {
+            this.supplier = pSupplier;
+        }
+
+        @Override
+        public O get() {
+            invoked = true;
+            return supplier.get();
+        }
+
+        public boolean isInvoked() {
+            return invoked;
+        }
+    }
+
+    @Test
+    void testRequireNonNullObjectSupplierString() {
+        final TestableSupplier<String> supplier = new TestableSupplier<>(() -> 
"bar");
+        assertSame("foo", Objects.requireNonNull("foo", supplier));
+        assertFalse(supplier.isInvoked());
+        try {
+            Objects.requireNonNull(null, supplier);
+            fail("Expected Exception");
+        } catch (NullPointerException e) {
+            assertEquals("bar", e.getMessage());
+            assertTrue(supplier.isInvoked());
+        }
+    }
+
+    public static class TestableFailableSupplier<O, E extends Exception> 
implements FailableSupplier<O, E> {
+        private final FailableSupplier<O, E> supplier;
+        private boolean invoked;
+
+        TestableFailableSupplier(FailableSupplier<O, E> pSupplier) {
+            this.supplier = pSupplier;
+        }
+
+        @Override
+        public O get() throws E {
+            invoked = true;
+            return supplier.get();
+        }
+
+        public boolean isInvoked() {
+            return invoked;
+        }
+    }
+
+    @Test
+    void testRequireNonNullObjectFailableSupplierString() {
+        final TestableFailableSupplier<String, ?> supplier = new 
TestableFailableSupplier<>(() -> {
+            return null;
+        });
+        assertSame("foo", Objects.requireNonNull("foo", supplier));
+        assertFalse(supplier.isInvoked());
+        try {
+            Objects.requireNonNull(null, supplier);
+            fail("Expected Exception");
+        } catch (NullPointerException e) {
+            assertEquals("The supplier must not return null.", e.getMessage());
+            assertTrue(supplier.isInvoked());
+        }
+        final TestableFailableSupplier<String, ?> supplier2 = new 
TestableFailableSupplier<>(() -> {
+            return null;
+        });
+        try {
+            Objects.requireNonNull(null, supplier2);
+            fail("Expected Exception");
+        } catch (NullPointerException e) {
+            assertEquals("The supplier must not return null.", e.getMessage());
+            assertTrue(supplier2.isInvoked());
+        }
+        final TestableFailableSupplier<String, ?> supplier3 = new 
TestableFailableSupplier<>(() -> {
+            return "bar";
+        });
+        assertSame("bar", Objects.requireNonNull(null, supplier3));
+        final RuntimeException rte = new RuntimeException();
+        final TestableFailableSupplier<String, ?> supplier4 = new 
TestableFailableSupplier<>(() -> {
+            throw rte;
+        });
+        try {
+            Objects.requireNonNull(null, supplier4);
+            fail("Expected Exception");
+        } catch (RuntimeException e) {
+            assertSame(rte, e);
+            assertTrue(supplier4.isInvoked());
+        }
+    }
+}

Reply via email to