From e54bef96ff42eb735130cb7db33abfefd1c5eb55 Mon Sep 17 00:00:00 2001 From: Roman Proshin Date: Tue, 1 May 2018 13:55:14 +0300 Subject: [PATCH 1/3] #764: Implement HeadInput --- src/main/java/org/cactoos/io/HeadInput.java | 63 +++++++++ .../java/org/cactoos/io/HeadInputStream.java | 122 ++++++++++++++++++ .../org/cactoos/io/HeadInputStreamTest.java | 98 ++++++++++++++ .../java/org/cactoos/io/HeadInputTest.java | 70 ++++++++++ 4 files changed, 353 insertions(+) create mode 100644 src/main/java/org/cactoos/io/HeadInput.java create mode 100644 src/main/java/org/cactoos/io/HeadInputStream.java create mode 100644 src/test/java/org/cactoos/io/HeadInputStreamTest.java create mode 100644 src/test/java/org/cactoos/io/HeadInputTest.java diff --git a/src/main/java/org/cactoos/io/HeadInput.java b/src/main/java/org/cactoos/io/HeadInput.java new file mode 100644 index 0000000000..220f07f0b4 --- /dev/null +++ b/src/main/java/org/cactoos/io/HeadInput.java @@ -0,0 +1,63 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2017-2018 Yegor Bugayenko + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.cactoos.io; + +import java.io.IOException; +import java.io.InputStream; +import org.cactoos.Input; + +/** + * Input that only shows the first N bytes of the original input. + * + * @author Roman Proshin (roman@proshin.org) + * @version $Id$ + * @since 0.31 + */ +public final class HeadInput implements Input { + + /** + * The original input. + */ + private final Input origin; + + /** + * Limit of bytes that can be read from the beginning. + */ + private final int length; + + /** + * Ctor. + * @param orig The original input. + * @param len Limit of bytes that can be read from the beginning. + */ + public HeadInput(final Input orig, final int len) { + this.origin = orig; + this.length = len; + } + + @Override + public InputStream stream() throws IOException { + return new HeadInputStream(this.origin.stream(), this.length); + } +} diff --git a/src/main/java/org/cactoos/io/HeadInputStream.java b/src/main/java/org/cactoos/io/HeadInputStream.java new file mode 100644 index 0000000000..0fa6bbb744 --- /dev/null +++ b/src/main/java/org/cactoos/io/HeadInputStream.java @@ -0,0 +1,122 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2017-2018 Yegor Bugayenko + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.cactoos.io; + +import java.io.IOException; +import java.io.InputStream; + +/** + * Input stream that only shows the first N bytes of the original stream. + * + *

There is no thread-safety guarantee. + * + * @author Roman Proshin (roman@proshin.org) + * @version $Id$ + * @since 0.31 + */ +public final class HeadInputStream extends InputStream { + + /** + * Original input stream. + */ + private final InputStream origin; + + /** + * A number of bytes that can be read from the beginning. + */ + private final long length; + + /** + * Current number or read bytes. + */ + private long processed; + + /** + * Ctor. + * @param orig The original input stream. + * @param len A number of bytes that can be read from the beginning. + */ + public HeadInputStream(final InputStream orig, final int len) { + super(); + this.origin = orig; + this.length = len; + } + + @Override + public int read() throws IOException { + final int adjusted; + if (this.processed >= this.length) { + adjusted = -1; + } else { + this.processed = this.processed + 1; + adjusted = this.origin.read(); + } + return adjusted; + } + + @Override + public long skip(final long skip) throws IOException { + final long adjusted; + if (this.processed + skip > this.length) { + adjusted = this.length - this.processed; + } else { + adjusted = skip; + } + this.processed = this.processed + adjusted; + return this.origin.skip(adjusted); + } + + @Override + public void reset() throws IOException { + this.processed = 0L; + this.origin.reset(); + } + + @Override + public int available() throws IOException { + final int available = this.origin.available(); + final int adjusted; + if (this.processed + available > this.length) { + adjusted = (int) (this.length - this.processed); + } else { + adjusted = available; + } + return adjusted; + } + + @Override + public void close() throws IOException { + this.origin.close(); + } + + @Override + public boolean markSupported() { + return this.origin.markSupported(); + } + + @Override + public void mark(final int readlimit) { + this.origin.mark(readlimit); + } +} diff --git a/src/test/java/org/cactoos/io/HeadInputStreamTest.java b/src/test/java/org/cactoos/io/HeadInputStreamTest.java new file mode 100644 index 0000000000..96824b88fb --- /dev/null +++ b/src/test/java/org/cactoos/io/HeadInputStreamTest.java @@ -0,0 +1,98 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2017-2018 Yegor Bugayenko + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.cactoos.io; + +import org.cactoos.text.TextOf; +import org.hamcrest.MatcherAssert; +import org.hamcrest.core.IsEqual; +import org.hamcrest.text.IsEmptyString; +import org.junit.Test; + +/** + * Test cases for {@link HeadInputStream}. + * + * @author Roman Proshin (roman@proshin.org) + * @version $Id$ + * @since 0.31 + * @checkstyle JavadocMethodCheck (500 lines) + * @checkstyle MagicNumberCheck (500 lines) + */ +public final class HeadInputStreamTest { + + @Test + public void testSkippingLessThanTotal() throws Exception { + final HeadInputStream stream = new HeadInputStream( + new InputOf("testSkippingLessThanTotal").stream(), + 5 + ); + stream.skip(3L); + MatcherAssert.assertThat( + "Incorrect head of the input stream has been read", + new TextOf(stream).asString(), + new IsEqual<>("tS") + ); + } + + @Test + public void testSkippingMoreThanTotal() throws Exception { + final HeadInputStream stream = new HeadInputStream( + new InputOf("testSkippingMoreThanTotal").stream(), + 5 + ); + stream.skip(7L); + MatcherAssert.assertThat( + "The result text wasn't empty", + new TextOf(stream).asString(), + new IsEmptyString() + ); + } + + @Test + public void testResetting() throws Exception { + final HeadInputStream stream = new HeadInputStream( + new InputOf("testResetting").stream(), + 5 + ); + stream.skip(7L); + stream.reset(); + MatcherAssert.assertThat( + "Reset didn't change the state", + new TextOf(stream).asString(), + new IsEqual<>("testR") + ); + } + + @Test + public void testAvailableLessThanTotal() throws Exception { + final HeadInputStream stream = new HeadInputStream( + new InputOf("testAvailableLessThanTotal").stream(), + 5 + ); + MatcherAssert.assertThat( + "Count of available bytes is incorrect", + stream.available(), + new IsEqual<>(5) + ); + } +} diff --git a/src/test/java/org/cactoos/io/HeadInputTest.java b/src/test/java/org/cactoos/io/HeadInputTest.java new file mode 100644 index 0000000000..9664a0f970 --- /dev/null +++ b/src/test/java/org/cactoos/io/HeadInputTest.java @@ -0,0 +1,70 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2017-2018 Yegor Bugayenko + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.cactoos.io; + +import org.cactoos.text.TextOf; +import org.hamcrest.MatcherAssert; +import org.hamcrest.core.IsEqual; +import org.junit.Test; + +/** + * Test cases for {@link HeadInput}. + * + * @author Roman Proshin (roman@proshin.org) + * @version $Id$ + * @since 0.31 + * @checkstyle JavadocMethodCheck (500 lines) + * @checkstyle MagicNumberCheck (500 lines) + */ +public final class HeadInputTest { + + @Test + public void readsHeadOfLongerInput() throws Exception { + MatcherAssert.assertThat( + "HeadInput couldn't limit a number of read bytes", + new TextOf( + new HeadInput( + new InputOf("readsHeadOfLongerInput"), + 5 + ) + ).asString(), + new IsEqual<>("reads") + ); + } + + @Test + public void readsHeadOfShorterInput() throws Exception { + final String input = "readsHeadOfShorterInput"; + MatcherAssert.assertThat( + "HeadInput incorrectly limited a number of read bytes", + new TextOf( + new HeadInput( + new InputOf(input), + 35 + ) + ).asString(), + new IsEqual<>(input) + ); + } +} From fa73936e6aace0bdba22046424fa62570d89d57c Mon Sep 17 00:00:00 2001 From: Roman Proshin Date: Fri, 4 May 2018 00:22:08 +0300 Subject: [PATCH 2/3] #764: Implement HeadInput - fix PR comments --- src/main/java/org/cactoos/io/HeadInputStream.java | 5 +++-- .../java/org/cactoos/io/HeadInputStreamTest.java | 14 +++++++------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/cactoos/io/HeadInputStream.java b/src/main/java/org/cactoos/io/HeadInputStream.java index 0fa6bbb744..2278de7b6a 100644 --- a/src/main/java/org/cactoos/io/HeadInputStream.java +++ b/src/main/java/org/cactoos/io/HeadInputStream.java @@ -83,8 +83,9 @@ public long skip(final long skip) throws IOException { } else { adjusted = skip; } - this.processed = this.processed + adjusted; - return this.origin.skip(adjusted); + final long skipped = this.origin.skip(adjusted); + this.processed = this.processed + skipped; + return skipped; } @Override diff --git a/src/test/java/org/cactoos/io/HeadInputStreamTest.java b/src/test/java/org/cactoos/io/HeadInputStreamTest.java index 96824b88fb..f7b2829063 100644 --- a/src/test/java/org/cactoos/io/HeadInputStreamTest.java +++ b/src/test/java/org/cactoos/io/HeadInputStreamTest.java @@ -23,10 +23,10 @@ */ package org.cactoos.io; +import org.cactoos.matchers.TextHasString; import org.cactoos.text.TextOf; import org.hamcrest.MatcherAssert; import org.hamcrest.core.IsEqual; -import org.hamcrest.text.IsEmptyString; import org.junit.Test; /** @@ -49,8 +49,8 @@ public void testSkippingLessThanTotal() throws Exception { stream.skip(3L); MatcherAssert.assertThat( "Incorrect head of the input stream has been read", - new TextOf(stream).asString(), - new IsEqual<>("tS") + new TextOf(stream), + new TextHasString("tS") ); } @@ -63,8 +63,8 @@ public void testSkippingMoreThanTotal() throws Exception { stream.skip(7L); MatcherAssert.assertThat( "The result text wasn't empty", - new TextOf(stream).asString(), - new IsEmptyString() + new TextOf(stream), + new TextHasString("") ); } @@ -78,8 +78,8 @@ public void testResetting() throws Exception { stream.reset(); MatcherAssert.assertThat( "Reset didn't change the state", - new TextOf(stream).asString(), - new IsEqual<>("testR") + new TextOf(stream), + new TextHasString("testR") ); } From 1ec662740afb7b83f4a290baa1ee99768f923ba9 Mon Sep 17 00:00:00 2001 From: Roman Proshin Date: Fri, 4 May 2018 00:23:31 +0300 Subject: [PATCH 3/3] #764: Implement HeadInput - fix PR comments --- src/test/java/org/cactoos/io/HeadInputTest.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/test/java/org/cactoos/io/HeadInputTest.java b/src/test/java/org/cactoos/io/HeadInputTest.java index 9664a0f970..1f3574333f 100644 --- a/src/test/java/org/cactoos/io/HeadInputTest.java +++ b/src/test/java/org/cactoos/io/HeadInputTest.java @@ -23,9 +23,9 @@ */ package org.cactoos.io; +import org.cactoos.matchers.TextHasString; import org.cactoos.text.TextOf; import org.hamcrest.MatcherAssert; -import org.hamcrest.core.IsEqual; import org.junit.Test; /** @@ -48,8 +48,8 @@ public void readsHeadOfLongerInput() throws Exception { new InputOf("readsHeadOfLongerInput"), 5 ) - ).asString(), - new IsEqual<>("reads") + ), + new TextHasString("reads") ); } @@ -63,8 +63,8 @@ public void readsHeadOfShorterInput() throws Exception { new InputOf(input), 35 ) - ).asString(), - new IsEqual<>(input) + ), + new TextHasString(input) ); } }