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 f830a4acdf1d926a251606fdde6fb687c0c9d104 Author: Gary Gregory <garydgreg...@gmail.com> AuthorDate: Sat Nov 9 10:57:24 2024 -0500 Add support to BOMInputStream for setting a consumer for ProxyInputStream.afterRead(int) --- src/changes/changes.xml | 1 + .../apache/commons/io/input/BOMInputStream.java | 21 +++++++++++---- .../commons/io/input/BOMInputStreamTest.java | 31 ++++++++++++++++++++++ 3 files changed, 48 insertions(+), 5 deletions(-) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 1a2c7ec77..73f3885d8 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -67,6 +67,7 @@ The <action> type attribute can be add,update,fix,remove. <action dev="ggregory" type="add" due-to="Gary Gregory">Add IOIntConsumer.</action> <action dev="ggregory" type="add" issue="IO-861" due-to="Gary Gregory">Add ProxyInputStream.AbstractBuilder. Supports setting a consumer for ProxyInputStream.afterRead(int).</action> <action dev="ggregory" type="add" due-to="Gary Gregory">Add support to AutoCloseInputStream for setting a consumer for ProxyInputStream.afterRead(int).</action> + <action dev="ggregory" type="add" due-to="Gary Gregory">Add support to BOMInputStream for setting a consumer for ProxyInputStream.afterRead(int).</action> <!-- UPDATE --> <action dev="ggregory" type="update" due-to="Gary Gregory">Bump org.apache.commons:commons-parent from 74 to 78 #670, #676, #679, #688.</action> <action dev="ggregory" type="update" due-to="Gary Gregory">Bump commons.bytebuddy.version from 1.15.1 to 1.15.10 #672, #673, #685, #686, #694, #696, #698.</action> diff --git a/src/main/java/org/apache/commons/io/input/BOMInputStream.java b/src/main/java/org/apache/commons/io/input/BOMInputStream.java index 73dadaa9a..63c0275e1 100644 --- a/src/main/java/org/apache/commons/io/input/BOMInputStream.java +++ b/src/main/java/org/apache/commons/io/input/BOMInputStream.java @@ -27,7 +27,6 @@ import java.util.Objects; import org.apache.commons.io.ByteOrderMark; import org.apache.commons.io.IOUtils; -import org.apache.commons.io.build.AbstractStreamBuilder; /** * This class is used to wrap a stream that includes an encoded {@link ByteOrderMark} as its first bytes. @@ -125,7 +124,7 @@ public class BOMInputStream extends ProxyInputStream { * @since 2.12.0 */ // @formatter:on - public static class Builder extends AbstractStreamBuilder<BOMInputStream, Builder> { + public static class Builder extends AbstractBuilder<BOMInputStream, Builder> { private static final ByteOrderMark[] DEFAULT = { ByteOrderMark.UTF_8 }; @@ -165,10 +164,9 @@ public class BOMInputStream extends ProxyInputStream { * @throws IOException if an I/O error occurs. * @see #getInputStream() */ - @SuppressWarnings("resource") @Override public BOMInputStream get() throws IOException { - return new BOMInputStream(getInputStream(), include, byteOrderMarks); + return new BOMInputStream(this); } /** @@ -229,6 +227,18 @@ public class BOMInputStream extends ProxyInputStream { private boolean markedAtStart; private int markFbIndex; + private BOMInputStream(final Builder builder) throws IOException { + super(builder); + if (IOUtils.length(builder.byteOrderMarks) == 0) { + throw new IllegalArgumentException("No BOMs specified"); + } + this.include = builder.include; + final List<ByteOrderMark> list = Arrays.asList(builder.byteOrderMarks); + // Sort the BOMs to match the longest BOM first because some BOMs have the same starting two bytes. + list.sort(ByteOrderMarkLengthComparator); + this.boms = list; + } + /** * Constructs a new BOM InputStream that excludes a {@link ByteOrderMark#UTF_8} BOM. * @@ -318,6 +328,7 @@ public class BOMInputStream extends ProxyInputStream { // Read first maxBomSize bytes for (int i = 0; i < firstBytes.length; i++) { firstBytes[i] = in.read(); + afterRead(firstBytes[i]); fbLength++; if (firstBytes[i] < 0) { break; @@ -464,6 +475,7 @@ public class BOMInputStream extends ProxyInputStream { } } final int secondCount = in.read(buf, off, len); + afterRead(secondCount); return secondCount < 0 ? firstCount > 0 ? firstCount : EOF : firstCount + secondCount; } @@ -493,7 +505,6 @@ public class BOMInputStream extends ProxyInputStream { if (markedAtStart) { firstBytes = null; } - in.reset(); } diff --git a/src/test/java/org/apache/commons/io/input/BOMInputStreamTest.java b/src/test/java/org/apache/commons/io/input/BOMInputStreamTest.java index 350d099d4..2bb22d031 100644 --- a/src/test/java/org/apache/commons/io/input/BOMInputStreamTest.java +++ b/src/test/java/org/apache/commons/io/input/BOMInputStreamTest.java @@ -21,6 +21,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertThrowsExactly; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assumptions.assumeFalse; import static org.junit.jupiter.api.Assumptions.assumeTrue; @@ -31,12 +32,15 @@ import java.io.InputStream; import java.io.Reader; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import java.util.concurrent.atomic.AtomicBoolean; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.apache.commons.io.ByteOrderMark; +import org.apache.commons.io.IOUtils; +import org.apache.commons.io.test.CustomIOException; import org.apache.commons.lang3.SystemProperties; import org.junit.jupiter.api.Test; import org.w3c.dom.Document; @@ -206,6 +210,33 @@ public class BOMInputStreamTest { } while (bytes > 0); } + @Test + public void testAfterReadConsumer() throws Exception { + final byte[] data = { 'A', 'B', 'C', 'D' }; + final AtomicBoolean boolRef = new AtomicBoolean(); + // @formatter:off + try (InputStream bounded = BOMInputStream.builder() + .setInputStream(createUtf8Input(data, true)) + .setAfterRead(i -> boolRef.set(true)) + .get()) { + IOUtils.consume(bounded); + } + // @formatter:on + assertTrue(boolRef.get()); + // Throwing + final String message = "test exception message"; + // @formatter:off + try (InputStream bounded = BOMInputStream.builder() + .setInputStream(createUtf8Input(data, true)) + .setAfterRead(i -> { + throw new CustomIOException(message); + }) + .get()) { + assertEquals(message, assertThrowsExactly(CustomIOException.class, () -> IOUtils.consume(bounded)).getMessage()); + } + // @formatter:on + } + @Test public void testAvailableWithBOMAfterClose() throws Exception { final byte[] data = { 'A', 'B', 'C', 'D' };