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..2278de7b6a --- /dev/null +++ b/src/main/java/org/cactoos/io/HeadInputStream.java @@ -0,0 +1,123 @@ +/** + * 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; + } + final long skipped = this.origin.skip(adjusted); + this.processed = this.processed + skipped; + return skipped; + } + + @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..f7b2829063 --- /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.matchers.TextHasString; +import org.cactoos.text.TextOf; +import org.hamcrest.MatcherAssert; +import org.hamcrest.core.IsEqual; +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), + new TextHasString("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), + new TextHasString("") + ); + } + + @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), + new TextHasString("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..1f3574333f --- /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.matchers.TextHasString; +import org.cactoos.text.TextOf; +import org.hamcrest.MatcherAssert; +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 + ) + ), + new TextHasString("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 + ) + ), + new TextHasString(input) + ); + } +}