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 4a298b781b5d6b007f39f5ff5661991ed6c9b889 Author: Gary Gregory <garydgreg...@gmail.com> AuthorDate: Mon Jul 10 09:46:18 2023 -0400 Add AbstractStreamBuilder.setBufferSizeMax(int). Add IntToIntFunction Add AbstractStreamBuilder.setBufferSizeChecker(IntToIntFunction) --- src/changes/changes.xml | 9 +++ .../commons/io/build/AbstractStreamBuilder.java | 68 ++++++++++++++++++++-- .../commons/io/function/IntToIntFunction.java | 52 +++++++++++++++++ .../io/build/AbstractStreamBuilderTest.java | 68 ++++++++++++++++++++++ 4 files changed, 191 insertions(+), 6 deletions(-) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 79aa9a53..4e713b8a 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -107,6 +107,15 @@ The <action> type attribute can be add,update,fix,remove. <action dev="ggregory" type="add" due-to="Gary Gregory"> Add DeferredFileOutputStream.Builder.setDirectory(Path). </action> + <action dev="ggregory" type="add" due-to="Gary Gregory"> + Add IntToIntFunction. + </action> + <action dev="ggregory" type="add" due-to="Gary Gregory"> + Add AbstractStreamBuilder.setBufferSizeChecker(IntToIntFunction). + </action> + <action dev="ggregory" type="add" due-to="Gary Gregory"> + Add AbstractStreamBuilder.setBufferSizeMax(int). + </action> <!-- UPDATE --> <action dev="ggregory" type="update" due-to="Dependabot"> Bump jimfs from 1.2 to 1.3.0 #465 (tests). diff --git a/src/main/java/org/apache/commons/io/build/AbstractStreamBuilder.java b/src/main/java/org/apache/commons/io/build/AbstractStreamBuilder.java index 8beccd93..d7c2fc3c 100644 --- a/src/main/java/org/apache/commons/io/build/AbstractStreamBuilder.java +++ b/src/main/java/org/apache/commons/io/build/AbstractStreamBuilder.java @@ -24,10 +24,10 @@ import java.io.Writer; import java.nio.charset.Charset; import java.nio.file.OpenOption; import java.nio.file.Path; - import org.apache.commons.io.Charsets; import org.apache.commons.io.IOUtils; import org.apache.commons.io.file.PathUtils; +import org.apache.commons.io.function.IntToIntFunction; /** * Abstracts building a typed instance of {@code T}. @@ -38,6 +38,8 @@ import org.apache.commons.io.file.PathUtils; */ public abstract class AbstractStreamBuilder<T, B extends AbstractStreamBuilder<T, B>> extends AbstractOriginSupplier<T, B> { + private static final int DEFAULT_MAX_VALUE = Integer.MAX_VALUE; + private static final OpenOption[] DEFAULT_OPEN_OPTIONS = PathUtils.EMPTY_OPEN_OPTION_ARRAY; /** @@ -50,6 +52,11 @@ public abstract class AbstractStreamBuilder<T, B extends AbstractStreamBuilder<T */ private int bufferSizeDefault = IOUtils.DEFAULT_BUFFER_SIZE; + /** + * The maximum buffer size. + */ + private int bufferSizeMax = DEFAULT_MAX_VALUE; + /** * The Charset, defaults to {@link Charset#defaultCharset()}. */ @@ -62,6 +69,26 @@ public abstract class AbstractStreamBuilder<T, B extends AbstractStreamBuilder<T private OpenOption[] openOptions = DEFAULT_OPEN_OPTIONS; + /** + * The default checking behavior for a buffer size request. Throws a {@link IllegalArgumentException} by default. + */ + private final IntToIntFunction defaultSizeChecker = size -> size > bufferSizeMax ? throwIae(size, bufferSizeMax) : size; + + /** + * The checking behavior for a buffer size request. + */ + private IntToIntFunction bufferSizeChecker = defaultSizeChecker; + + /** + * Applies the buffer size request. + * + * @param size the size request. + * @return the size to use, usually the input, or can throw an unchecked exception, like {@link IllegalArgumentException}. + */ + private int checkBufferSize(final int size) { + return bufferSizeChecker.applyAsInt(size); + } + /** * Gets the buffer size, defaults to {@link IOUtils#DEFAULT_BUFFER_SIZE} ({@value IOUtils#DEFAULT_BUFFER_SIZE}). * @@ -86,7 +113,7 @@ public abstract class AbstractStreamBuilder<T, B extends AbstractStreamBuilder<T * @return An input stream * @throws IOException if an I/O error occurs. * @throws UnsupportedOperationException if the origin cannot be converted to a CharSequence. - * @throws IllegalStateException if the {@code origin} is {@code null}. + * @throws IllegalStateException if the {@code origin} is {@code null}. * @see AbstractOrigin#getCharSequence(Charset) * @since 2.13.0 */ @@ -136,7 +163,7 @@ public abstract class AbstractStreamBuilder<T, B extends AbstractStreamBuilder<T * @return An OutputStream * @throws IOException if an I/O error occurs. * @throws UnsupportedOperationException if the origin cannot be converted to an OututStream. - * @throws IllegalStateException if the {@code origin} is {@code null}. + * @throws IllegalStateException if the {@code origin} is {@code null}. * @see AbstractOrigin#getOutputStream(OpenOption...) * @since 2.13.0 */ @@ -149,7 +176,7 @@ public abstract class AbstractStreamBuilder<T, B extends AbstractStreamBuilder<T * * @return A Path * @throws UnsupportedOperationException if the origin cannot be converted to a Path. - * @throws IllegalStateException if the {@code origin} is {@code null}. + * @throws IllegalStateException if the {@code origin} is {@code null}. * @see AbstractOrigin#getPath() * @since 2.13.0 */ @@ -163,7 +190,7 @@ public abstract class AbstractStreamBuilder<T, B extends AbstractStreamBuilder<T * @return An writer. * @throws IOException if an I/O error occurs. * @throws UnsupportedOperationException if the origin cannot be converted to a Writer. - * @throws IllegalStateException if the {@code origin} is {@code null}. + * @throws IllegalStateException if the {@code origin} is {@code null}. * @see AbstractOrigin#getOutputStream(OpenOption...) * @since 2.13.0 */ @@ -181,7 +208,7 @@ public abstract class AbstractStreamBuilder<T, B extends AbstractStreamBuilder<T * @return this. */ public B setBufferSize(final int bufferSize) { - this.bufferSize = bufferSize > 0 ? bufferSize : bufferSizeDefault; + this.bufferSize = checkBufferSize(bufferSize > 0 ? bufferSize : bufferSizeDefault); return asThis(); } @@ -199,6 +226,18 @@ public abstract class AbstractStreamBuilder<T, B extends AbstractStreamBuilder<T return asThis(); } + /** + * Sets the buffer size checker function. Throws a {@link IllegalArgumentException} by default. + * + * @param bufferSizeChecker the buffer size checker function. null resets to the default behavior. + * @return this + * @since 2.14.0 + */ + public B setBufferSizeChecker(final IntToIntFunction bufferSizeChecker) { + this.bufferSizeChecker = bufferSizeChecker != null ? bufferSizeChecker : defaultSizeChecker; + return asThis(); + } + /** * Sets the buffer size for subclasses to initialize. * <p> @@ -213,6 +252,19 @@ public abstract class AbstractStreamBuilder<T, B extends AbstractStreamBuilder<T return asThis(); } + /** + * The maximum buffer size checked by the buffer size checker. Values less or equal to 0, resets to the int max value. By default, if this value is + * exceeded, this methods throws an {@link IllegalArgumentException}. + * + * @param bufferSizeMax maximum buffer size checked by the buffer size checker. + * @return this. + * @since 2.14.0 + */ + public B setBufferSizeMax(final int bufferSizeMax) { + this.bufferSizeMax = bufferSizeMax > 0 ? bufferSizeMax : DEFAULT_MAX_VALUE; + return asThis(); + } + /** * Sets the Charset. * <p> @@ -274,4 +326,8 @@ public abstract class AbstractStreamBuilder<T, B extends AbstractStreamBuilder<T this.openOptions = openOptions != null ? openOptions : DEFAULT_OPEN_OPTIONS; return asThis(); } + + private int throwIae(final int size, final int max) { + throw new IllegalArgumentException(String.format("Request %,d exceeds maximum %,d", size, max)); + } } diff --git a/src/main/java/org/apache/commons/io/function/IntToIntFunction.java b/src/main/java/org/apache/commons/io/function/IntToIntFunction.java new file mode 100644 index 00000000..57cea6c0 --- /dev/null +++ b/src/main/java/org/apache/commons/io/function/IntToIntFunction.java @@ -0,0 +1,52 @@ +/* + * 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.util.function.Function; + +/** + * Represents a function that accepts an int-valued argument and produces an int-valued result. This is the {@code int}-to-{@code int} primitive + * specialization for {@link Function}. + * + * <p> + * This is a <a href="package-summary.html">functional interface</a> whose functional method is {@link #applyAsInt(int)}. + * </p> + * + * @see Function + * @since 2.14.0 + */ +@FunctionalInterface +public interface IntToIntFunction { + + /** + * Returns a function that always returns its input argument. + * + * @return a function that always returns its input argument + */ + static IntToIntFunction identity() { + return i -> i; + } + + /** + * Applies this function to the given argument. + * + * @param value the function argument + * @return the function result + */ + int applyAsInt(int value); +} \ No newline at end of file diff --git a/src/test/java/org/apache/commons/io/build/AbstractStreamBuilderTest.java b/src/test/java/org/apache/commons/io/build/AbstractStreamBuilderTest.java new file mode 100644 index 00000000..425ba4a5 --- /dev/null +++ b/src/test/java/org/apache/commons/io/build/AbstractStreamBuilderTest.java @@ -0,0 +1,68 @@ +/* + * 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.build; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.Arrays; + +import org.junit.jupiter.api.Test; + +/** + * Tests {@link AbstractStreamBuilder}. + */ +public class AbstractStreamBuilderTest { + + public static class Builder extends AbstractStreamBuilder<char[], Builder> { + + @Override + public char[] get() { + final char[] arr = new char[getBufferSize()]; + Arrays.fill(arr, 'a'); + return arr; + } + + } + + private void assertResult(final char[] arr, final int size) { + assertNotNull(arr); + assertEquals(size, arr.length); + for (final char c : arr) { + assertEquals('a', c); + } + } + + protected Builder builder() { + return new Builder(); + } + + @Test + public void testBufferSizeChecker() { + // sanity + final Builder builder = builder(); + assertResult(builder.get(), builder.getBufferSize()); + // basic failure + assertThrows(IllegalArgumentException.class, () -> builder().setBufferSizeMax(2).setBufferSize(3)); + // reset + assertResult(builder.setBufferSizeMax(2).setBufferSizeMax(0).setBufferSize(3).get(), 3); + // resize + assertResult(builder().setBufferSizeMax(2).setBufferSizeChecker(i -> 100).setBufferSize(3).get(), 100); + } +}