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")); + } + +}