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 7d198e4bc3c42fc63ac1acfafa6d76c9ed71b1fc Author: Gary Gregory <gardgreg...@gmail.com> AuthorDate: Sun Aug 7 23:28:02 2022 -0400 Add IOBinaryOperator --- src/changes/changes.xml | 2 +- .../commons/io/function/IOBinaryOperator.java | 76 +++++++++++++++++ .../io/function/IOBinaryOperatorStreamTest.java | 99 ++++++++++++++++++++++ .../apache/commons/io/function/TestConstants.java | 2 + .../org/apache/commons/io/function/TestUtils.java | 13 ++- 5 files changed, 187 insertions(+), 5 deletions(-) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index d1a63601..4611da4d 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -360,7 +360,7 @@ The <action> type attribute can be add,update,fix,remove. Add IOExceptionList.checkEmpty(List, Object). </action> <action dev="ggregory" type="add" due-to="Gary Gregory"> - Add IOBiConsumer, IOTriConsumer, IOComparator, IOUnaryOperator. + Add IOBiConsumer, IOTriConsumer, IOComparator, IOUnaryOperator, IOBinaryOperator. </action> <action dev="ggregory" type="add" due-to="Gary Gregory"> Add and reuse IOConsumer forAll(*), forEach(*), and forEachIndexed(*). diff --git a/src/main/java/org/apache/commons/io/function/IOBinaryOperator.java b/src/main/java/org/apache/commons/io/function/IOBinaryOperator.java new file mode 100644 index 00000000..e91d6481 --- /dev/null +++ b/src/main/java/org/apache/commons/io/function/IOBinaryOperator.java @@ -0,0 +1,76 @@ +/* + * 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.BinaryOperator; + +/** + * Like {@link BinaryOperator} but throws {@link IOException}. + * + * @param <T> the type of the operands and result of the operator. + * + * @see IOBiFunction + * @see BinaryOperator + * @since 2.12.0 + */ +@FunctionalInterface +public interface IOBinaryOperator<T> extends IOBiFunction<T, T, T> { + + /** + * Creates a {@link IOBinaryOperator} which returns the lesser of two elements according to the specified + * {@code Comparator}. + * + * @param <T> the type of the input arguments of the comparator + * @param comparator a {@code Comparator} for comparing the two values + * @return a {@code BinaryOperator} which returns the lesser of its operands, according to the supplied + * {@code Comparator} + * @throws NullPointerException if the argument is null + */ + static <T> IOBinaryOperator<T> minBy(final IOComparator<? super T> comparator) { + Objects.requireNonNull(comparator); + return (a, b) -> comparator.compare(a, b) <= 0 ? a : b; + } + + /** + * Creates a {@link IOBinaryOperator} which returns the greater of two elements according to the specified + * {@code Comparator}. + * + * @param <T> the type of the input arguments of the comparator + * @param comparator a {@code Comparator} for comparing the two values + * @return a {@code BinaryOperator} which returns the greater of its operands, according to the supplied + * {@code Comparator} + * @throws NullPointerException if the argument is null + */ + static <T> IOBinaryOperator<T> maxBy(final IOComparator<? super T> comparator) { + Objects.requireNonNull(comparator); + return (a, b) -> comparator.compare(a, b) >= 0 ? a : b; + } + + /** + * Creates a {@link BinaryOperator} for this instance that throws {@link UncheckedIOException} instead of + * {@link IOException}. + * + * @return an unchecked BiFunction. + */ + default BinaryOperator<T> asBinaryOperator() { + return (t, u) -> Uncheck.apply(this, t, u); + } +} diff --git a/src/test/java/org/apache/commons/io/function/IOBinaryOperatorStreamTest.java b/src/test/java/org/apache/commons/io/function/IOBinaryOperatorStreamTest.java new file mode 100644 index 00000000..42b4029c --- /dev/null +++ b/src/test/java/org/apache/commons/io/function/IOBinaryOperatorStreamTest.java @@ -0,0 +1,99 @@ +/* + * 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.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.fail; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.function.BiFunction; +import java.util.function.BinaryOperator; +import java.util.stream.Stream; + +import org.apache.commons.io.file.PathUtils; +import org.junit.jupiter.api.Test; + +/** + * Tests {@link IOBinaryOperator}. + */ +public class IOBinaryOperatorStreamTest { + + private static final IOBinaryOperator<Path> MIN_BY_IO_BO = IOBinaryOperator.minBy(IOComparatorTest.REAL_PATH_COMP); + private static final BinaryOperator<Path> MIN_BY_BO = MIN_BY_IO_BO.asBinaryOperator(); + private static final IOBinaryOperator<Path> MAX_BY_IO_BO = IOBinaryOperator.maxBy(IOComparatorTest.REAL_PATH_COMP); + private static final BinaryOperator<Path> MAX_BY_BO = MAX_BY_IO_BO.asBinaryOperator(); + private static final IOBinaryOperator<Path> REAL_PATH_IO_BO = (t, u) -> t.toRealPath(); + private static final BinaryOperator<Path> REAL_PATH_BO = REAL_PATH_IO_BO.asBinaryOperator(); + + @Test + public void testAsBinaryOperator() { + assertThrows(UncheckedIOException.class, + () -> Stream.of(TestConstants.ABS_PATH_A, TestConstants.ABS_PATH_A).reduce((TestUtils.<Path>throwingIOBinaryOperator()).asBinaryOperator()).get()); + assertEquals(TestConstants.ABS_PATH_A, Stream.of(TestConstants.ABS_PATH_A, TestConstants.ABS_PATH_A).reduce(MAX_BY_BO).get()); + assertEquals(TestConstants.ABS_PATH_A, Stream.of(TestConstants.ABS_PATH_A, TestConstants.ABS_PATH_A).reduce(MIN_BY_BO).get()); + } + + @Test + public void testMaxBy() throws IOException { + assertEquals(TestConstants.ABS_PATH_A, Stream.of(TestConstants.ABS_PATH_A, TestConstants.ABS_PATH_A).reduce(MAX_BY_BO).get()); + // in-line lambda ok: + final IOBinaryOperator<Path> binIoOp = IOBinaryOperator.maxBy((t, u) -> t.toRealPath().compareTo(u)); + final BiFunction<Path, Path, Path> asBiFunction = binIoOp.asBiFunction(); + final BinaryOperator<Path> asBinaryOperator = binIoOp.asBinaryOperator(); + assertEquals(TestConstants.ABS_PATH_B, asBiFunction.apply(TestConstants.ABS_PATH_A, TestConstants.ABS_PATH_B)); + assertEquals(TestConstants.ABS_PATH_B, asBinaryOperator.apply(TestConstants.ABS_PATH_A, TestConstants.ABS_PATH_B)); + // + assertEquals(TestConstants.ABS_PATH_A, Stream.of(TestConstants.ABS_PATH_A, TestConstants.ABS_PATH_A).reduce(asBinaryOperator).get()); + assertEquals(TestConstants.ABS_PATH_B, Stream.of(TestConstants.ABS_PATH_A, TestConstants.ABS_PATH_B).reduce(asBinaryOperator).get()); + assertEquals(TestConstants.ABS_PATH_B, Stream.of(TestConstants.ABS_PATH_B, TestConstants.ABS_PATH_A).reduce(asBinaryOperator).get()); + } + + @Test + public void testMinBy() throws IOException { + assertEquals(TestConstants.ABS_PATH_A, Stream.of(TestConstants.ABS_PATH_A, TestConstants.ABS_PATH_A).reduce(MIN_BY_BO).get()); + // in-line lambda ok: + final IOBinaryOperator<Path> binIoOp = IOBinaryOperator.minBy((t, u) -> t.toRealPath().compareTo(u)); + final BiFunction<Path, Path, Path> asBiFunction = binIoOp.asBiFunction(); + final BinaryOperator<Path> asBinaryOperator = binIoOp.asBinaryOperator(); + assertEquals(TestConstants.ABS_PATH_A, asBiFunction.apply(TestConstants.ABS_PATH_A, TestConstants.ABS_PATH_B)); + assertEquals(TestConstants.ABS_PATH_A, asBinaryOperator.apply(TestConstants.ABS_PATH_A, TestConstants.ABS_PATH_B)); + // + assertEquals(TestConstants.ABS_PATH_A, Stream.of(TestConstants.ABS_PATH_A, TestConstants.ABS_PATH_A).reduce(asBinaryOperator).get()); + assertEquals(TestConstants.ABS_PATH_A, Stream.of(TestConstants.ABS_PATH_A, TestConstants.ABS_PATH_B).reduce(asBinaryOperator).get()); + assertEquals(TestConstants.ABS_PATH_A, Stream.of(TestConstants.ABS_PATH_B, TestConstants.ABS_PATH_A).reduce(asBinaryOperator).get()); + } + + @Test + public void testReduce() throws IOException { + // A silly example to pass in a IOBinaryOperator. + final Path current = PathUtils.current(); + final Path expected = Files.list(current).reduce((t, u) -> { + try { + return t.toRealPath(); + } catch (final IOException e) { + return fail(e); + } + }).get(); + assertEquals(expected, Files.list(current).reduce(REAL_PATH_BO).get()); + } + +} diff --git a/src/test/java/org/apache/commons/io/function/TestConstants.java b/src/test/java/org/apache/commons/io/function/TestConstants.java index 482d8389..1823e216 100644 --- a/src/test/java/org/apache/commons/io/function/TestConstants.java +++ b/src/test/java/org/apache/commons/io/function/TestConstants.java @@ -40,6 +40,8 @@ class TestConstants { throw new IOException("Failure"); }; + static IOBinaryOperator<?> THROWING_IO_BINARY_OPERATOR = (t, u) -> throwIOException(); + static IOComparator<Object> THROWING_IO_COMPARATOR = (t, u) -> throwIOException(); static IOConsumer<Object> THROWING_IO_CONSUMER = t -> { diff --git a/src/test/java/org/apache/commons/io/function/TestUtils.java b/src/test/java/org/apache/commons/io/function/TestUtils.java index 1338ee79..7e62c078 100644 --- a/src/test/java/org/apache/commons/io/function/TestUtils.java +++ b/src/test/java/org/apache/commons/io/function/TestUtils.java @@ -33,9 +33,14 @@ class TestUtils { return ref.get(); // same as update } - @SuppressWarnings("unchecked") - static <T> IOUnaryOperator<T> throwingIOUnaryOperator() { - return (IOUnaryOperator<T>) TestConstants.THROWING_IO_UNARY_OPERATOR; - } + @SuppressWarnings("unchecked") + static <T> IOBinaryOperator<T> throwingIOBinaryOperator() { + return (IOBinaryOperator<T>) TestConstants.THROWING_IO_BINARY_OPERATOR; + } + + @SuppressWarnings("unchecked") + static <T> IOUnaryOperator<T> throwingIOUnaryOperator() { + return (IOUnaryOperator<T>) TestConstants.THROWING_IO_UNARY_OPERATOR; + } }