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

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

commit d012dff1ef6dcdfc4b852d6a249e692442bfbe8a
Author: Gary Gregory <gardgreg...@gmail.com>
AuthorDate: Sun Jul 24 11:13:43 2022 -0400

    Add IOPredicate
---
 src/changes/changes.xml                            |   2 +-
 .../org/apache/commons/io/function/Constants.java  |  10 ++
 .../apache/commons/io/function/IOPredicate.java    | 136 ++++++++++++++++++
 .../commons/io/function/IOPredicateTest.java       | 153 +++++++++++++++++++++
 4 files changed, 300 insertions(+), 1 deletion(-)

diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 50c957b5..4892f35b 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -410,7 +410,7 @@ The <action> type attribute can be add,update,fix,remove.
         Add PathUtils.getLastModifiedFileTime(*).
       </action>
       <action dev="ggregory" type="add" due-to="Gary Gregory">
-        Add IOBiFunction, IOTriFunction, IOQuadFunction.
+        Add IOBiFunction, IOTriFunction, IOQuadFunction, IOPredicate.
       </action>
       <action dev="ggregory" type="add" due-to="Gary Gregory">
         Add IOUtils.consume(Reader).
diff --git a/src/main/java/org/apache/commons/io/function/Constants.java 
b/src/main/java/org/apache/commons/io/function/Constants.java
index 7d9ab52b..55386e58 100644
--- a/src/main/java/org/apache/commons/io/function/Constants.java
+++ b/src/main/java/org/apache/commons/io/function/Constants.java
@@ -40,6 +40,16 @@ class Constants {
     @SuppressWarnings("rawtypes")
     static final IOFunction IO_FUNCTION_ID = t -> t;
 
+    /**
+     * Always false.
+     */
+    static final IOPredicate<Object> IO_PREDICATE_FALSE = t -> false;
+
+    /**
+     * Always true.
+     */
+    static final IOPredicate<Object> IO_PREDICATE_TRUE = t -> true;
+
     private Constants() {
         // We don't want instances
     }
diff --git a/src/main/java/org/apache/commons/io/function/IOPredicate.java 
b/src/main/java/org/apache/commons/io/function/IOPredicate.java
new file mode 100644
index 00000000..f94aba96
--- /dev/null
+++ b/src/main/java/org/apache/commons/io/function/IOPredicate.java
@@ -0,0 +1,136 @@
+/*
+ * 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.io.function;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.util.Objects;
+import java.util.function.Predicate;
+
+import org.apache.commons.io.UncheckedIO;
+
+/**
+ * Like {@link Predicate} but throws {@link IOException}.
+ *
+ * @param <T> the type of the input to the predicate
+ * @since 2.12.0
+ */
+@FunctionalInterface
+public interface IOPredicate<T> {
+
+    /**
+     * Always false.
+     *
+     * @param <T> the type of the input to the predicate
+     * @return a constant predicate that tests always false.
+     */
+    @SuppressWarnings("unchecked")
+    static <T> IOPredicate<T> alwaysFalse() {
+        return (IOPredicate<T>) Constants.IO_PREDICATE_FALSE;
+    }
+
+    /**
+     * Always true.
+     *
+     * @param <T> the type of the input to the predicate
+     * @return a constant predicate that tests always true.
+     */
+    @SuppressWarnings("unchecked")
+    static <T> IOPredicate<T> alwaysTrue() {
+        return (IOPredicate<T>) Constants.IO_PREDICATE_TRUE;
+    }
+
+    /**
+     * Returns a predicate that tests if two arguments are equal using {@link 
Objects#equals(Object, Object)}.
+     *
+     * @param <T> the type of arguments to the predicate
+     * @param target the object to compare for equality, may be {@code null}
+     * @return a predicate that tests if two arguments are equal using {@link 
Objects#equals(Object, Object)}
+     */
+    static <T> IOPredicate<T> isEqual(final Object target) {
+        return null == target ? Objects::isNull : object -> 
target.equals(object);
+    }
+
+    /**
+     * Returns a composed predicate that represents a short-circuiting logical 
AND of this predicate and another. When
+     * evaluating the composed predicate, if this predicate is {@code false}, 
then the {@code other} predicate is not
+     * evaluated.
+     *
+     * <p>
+     * Any exceptions thrown during evaluation of either predicate are relayed 
to the caller; if evaluation of this
+     * predicate throws an exception, the {@code other} predicate will not be 
evaluated.
+     * </p>
+     *
+     * @param other a predicate that will be logically-ANDed with this 
predicate
+     * @return a composed predicate that represents the short-circuiting 
logical AND of this predicate and the {@code other}
+     *         predicate
+     * @throws NullPointerException if other is null
+     */
+    default IOPredicate<T> and(final IOPredicate<? super T> other) {
+        Objects.requireNonNull(other);
+        return t -> test(t) && other.test(t);
+    }
+
+    /**
+     * Converts this predicate to a Predicate that throws {@link 
UncheckedIOException} instead of {@link IOException}.
+     *
+     * @return an unchecked Predicate.
+     */
+    default Predicate<T> asPredicate() {
+        return t -> UncheckedIO.test(this, t);
+    }
+
+    /**
+     * Returns a predicate that represents the logical negation of this 
predicate.
+     *
+     * @return a predicate that represents the logical negation of this 
predicate
+     */
+    default IOPredicate<T> negate() {
+        return t -> !test(t);
+    }
+
+    /**
+     * Returns a composed predicate that represents a short-circuiting logical 
OR of this predicate and another. When
+     * evaluating the composed predicate, if this predicate is {@code true}, 
then the {@code other} predicate is not
+     * evaluated.
+     *
+     * <p>
+     * Any exceptions thrown during evaluation of either predicate are relayed 
to the caller; if evaluation of this
+     * predicate throws an exception, the {@code other} predicate will not be 
evaluated.
+     * </p>
+     *
+     * @param other a predicate that will be logically-ORed with this predicate
+     * @return a composed predicate that represents the short-circuiting 
logical OR of this predicate and the {@code other}
+     *         predicate
+     * @throws NullPointerException if other is null
+     */
+    default IOPredicate<T> or(final IOPredicate<? super T> other) {
+        Objects.requireNonNull(other);
+        return t -> test(t) || other.test(t);
+    }
+
+    /**
+     * Evaluates this predicate on the given argument.
+     *
+     * @param t the input argument
+     * @return {@code true} if the input argument matches the predicate, 
otherwise {@code false}
+     * @throws IOException if an I/O error occurs.
+     */
+    boolean test(T t) throws IOException;
+
+}
diff --git a/src/test/java/org/apache/commons/io/function/IOPredicateTest.java 
b/src/test/java/org/apache/commons/io/function/IOPredicateTest.java
new file mode 100644
index 00000000..fa4e0b09
--- /dev/null
+++ b/src/test/java/org/apache/commons/io/function/IOPredicateTest.java
@@ -0,0 +1,153 @@
+/*
+ * 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.io.function;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Predicate;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.function.Executable;
+
+/**
+ * Tests {@link IOPredicate}.
+ */
+public class IOPredicateTest {
+
+    /** Files::isHidden throws IOException. */
+    private static final IOPredicate<Path> IS_HIDDEN = Files::isHidden;
+
+    private static final Path PATH_FIXTURE = 
Paths.get("src/test/resources/org/apache/commons/io/abitmorethan16k.txt");
+
+    private static final Object THROWING_EQUALS = new Object() {
+        @Override
+        public boolean equals(final Object obj) {
+            throw IOStream.rethrow(new IOException("Expected"));
+        }
+    };
+
+    private static IOPredicate<Object> THROWING_IOPREDICATE = e -> {
+        throw new IOException("Failure");
+    };
+
+    private static final Predicate<Object> THROWING_UNCHECKED_PREDICATE = 
THROWING_IOPREDICATE.asPredicate();
+
+    private void assertThrowsChecked(final Executable executable) {
+        assertThrows(IOException.class, executable);
+    }
+
+    private void assertThrowsUnchecked(final Executable executable) {
+        assertThrows(UncheckedIOException.class, executable);
+    }
+
+    @Test
+    public void testAndChecked() throws IOException {
+        assertFalse(IS_HIDDEN.and(IS_HIDDEN).test(PATH_FIXTURE));
+    }
+
+    @Test
+    public void testAndUnchecked() {
+        assertThrowsUnchecked(() -> 
THROWING_UNCHECKED_PREDICATE.and(THROWING_UNCHECKED_PREDICATE).test(PATH_FIXTURE));
+    }
+
+    @Test
+    public void testAsPredicate() throws IOException {
+        new ArrayList<>().removeIf(THROWING_UNCHECKED_PREDICATE);
+        final List<String> list = new ArrayList<>();
+        list.add("A");
+        list.add("B");
+        list.removeIf(Predicate.isEqual("A"));
+        assertFalse(list.contains("A"));
+        list.removeIf(IOPredicate.isEqual("B").asPredicate());
+        assertFalse(list.contains("B"));
+        assertFalse(IS_HIDDEN.test(PATH_FIXTURE));
+    }
+
+    @Test
+    public void testIsEqualChecked() throws IOException {
+        assertThrowsChecked(() -> 
IOPredicate.isEqual(THROWING_EQUALS).test("B"));
+        assertTrue(IOPredicate.isEqual("B").test("B"));
+    }
+
+    @Test
+    public void testIsEqualUnchecked() throws IOException {
+        assertThrowsUnchecked(() -> 
IOPredicate.isEqual(THROWING_EQUALS).asPredicate().test("B"));
+        assertTrue(IOPredicate.isEqual("B").asPredicate().test("B"));
+    }
+
+    @Test
+    public void testNegateChecked() throws IOException {
+        assertTrue(IS_HIDDEN.negate().test(PATH_FIXTURE));
+    }
+
+    @Test
+    public void testNegateUnchecked() {
+        assertTrue(IS_HIDDEN.negate().asPredicate().test(PATH_FIXTURE));
+        assertTrue(IS_HIDDEN.asPredicate().negate().test(PATH_FIXTURE));
+        assertThrowsUnchecked(() -> 
THROWING_UNCHECKED_PREDICATE.negate().test(PATH_FIXTURE));
+    }
+
+    @Test
+    public void testOrChecked() throws IOException {
+        assertFalse(IS_HIDDEN.or(IS_HIDDEN).test(PATH_FIXTURE));
+    }
+
+    @Test
+    public void testOrUnchecked() {
+        assertFalse(IS_HIDDEN.asPredicate().or(e -> false).test(PATH_FIXTURE));
+        assertThrowsUnchecked(() -> 
THROWING_UNCHECKED_PREDICATE.or(THROWING_UNCHECKED_PREDICATE).test(PATH_FIXTURE));
+    }
+
+    @Test
+    public void testTestChecked() throws IOException {
+        assertThrowsChecked(() -> THROWING_IOPREDICATE.test(null));
+        assertTrue(Constants.IO_PREDICATE_TRUE.test("A"));
+    }
+
+    @Test
+    public void testTestUnchecked() {
+        assertThrowsUnchecked(() -> THROWING_UNCHECKED_PREDICATE.test(null));
+        assertTrue(Constants.IO_PREDICATE_TRUE.asPredicate().test("A"));
+    }
+
+    @Test
+    public void testFalse() throws IOException {
+        assertFalse(Constants.IO_PREDICATE_FALSE.test("A"));
+        // Make sure we keep the argument type
+        final IOPredicate<String> alwaysFalse = IOPredicate.alwaysFalse();
+        assertFalse(alwaysFalse.test("A"));
+    }
+
+    @Test
+    public void testTrue() throws IOException {
+        assertTrue(Constants.IO_PREDICATE_TRUE.test("A"));
+        // Make sure we keep the argument type
+        final IOPredicate<String> alwaysTrue = IOPredicate.alwaysTrue();
+        assertTrue(alwaysTrue.test("A"));
+    }
+
+}

Reply via email to