diff --git a/README.md b/README.md index df273711c2..9b8a21a6ad 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ Java version required: 1.8+. ## Input/Output -To read a file: +To read a text file in UTF-8: ```java String text = new BytesAsText( @@ -86,7 +86,7 @@ To read a binary file from classpath: ```java byte[] data = new InputAsBytes( - new ResourceAsInput("/foo/img.jpg") + new ResourceAsInput("foo/img.jpg") ).asBytes(); ``` @@ -145,12 +145,11 @@ To iterate a collection: new AllOf( new TransformedIterable<>( new ArrayAsIterable<>("how", "are", "you"), - new Func.Quiet() { - @Override - public void exec(final String input) throws Exception { + new ProcAsFunc<>( + input -> { System.out.printf("Item: %s\n", input); } - } + ) ) ).asValue(); ``` @@ -160,7 +159,9 @@ Or even more compact: ```java new IterableAsBoolean( new ArrayAsIterable<>("how", "are", "you"), - (Func.Quiet) i -> System.out.printf("Item: %s\n", i) + new ProcAsFunc<>( + input -> System.out.printf("Item: %s\n", i) + ) ).asValue(); ``` @@ -246,13 +247,15 @@ Note: [Checkstyle](https://en.wikipedia.org/wiki/Checkstyle) is used as a static ## Contributors - - [Yegor Bugayenko](https://github.com/yegor256) - - [Kirill Che.](https://github.com/g4s8) g4s8.public@gmail.com - - [Fabrício Cabral](https://github.com/fabriciofx) - - [Andriy Kryvtsun](https://github.com/englishman) - - [Vseslav Sekorin](https://github.com/VsSekorin) - - [Andrey Valyaev](https://github.com/DronMDF) - - [Dušan Rychnovský](https://github.com/dusan-rychnovsky) [Blog](http://blog.dusanrychnovsky.cz/) + - [@yegor256](https://github.com/yegor256) as Yegor Bugayenko ([Blog](http://www.yegor256.com)) + - [@g4s8](https://github.com/g4s8) as Kirill Che. (g4s8.public@gmail.com) + - [@fabriciofx](https://github.com/fabriciofx) as Fabrício Cabral + - [@englishman](https://github.com/englishman) as Andriy Kryvtsun + - [@VsSekorin](https://github.com/VsSekorin) as Vseslav Sekorin + - [@DronMDF](https://github.com/DronMDF) as Andrey Valyaev + - [@dusan-rychnovsky](https://github.com/dusan-rychnovsky) as Dušan Rychnovský ([Blog](http://blog.dusanrychnovsky.cz/)) + - [@timmeey](https://github.com/timmeey) as Tim Hinkes ([Blog](https://blog.timmeey.de)) + ## License (MIT) diff --git a/src/main/java/org/cactoos/func/StickyScalar.java b/src/main/java/org/cactoos/func/StickyScalar.java index 512d5aaf08..981a569883 100644 --- a/src/main/java/org/cactoos/func/StickyScalar.java +++ b/src/main/java/org/cactoos/func/StickyScalar.java @@ -23,8 +23,7 @@ */ package org.cactoos.func; -import java.util.ArrayList; -import java.util.List; +import org.cactoos.Func; import org.cactoos.Scalar; /** @@ -33,6 +32,7 @@ *

There is no thread-safety guarantee. * * @author Tim Hinkes (timmeey@timmeey.de) + * @author Yegor Bugayenko (yegor256@gmail.com) * @version $Id$ * @param Type of result * @since 0.3 @@ -40,29 +40,22 @@ public final class StickyScalar implements Scalar { /** - * The scalar to cache. + * Func. */ - private final Scalar source; - - /** - * The list used as a optional value. - */ - private final List cache; + private final Func func; /** * Ctor. * @param src The Scalar to cache */ public StickyScalar(final Scalar src) { - this.source = src; - this.cache = new ArrayList<>(1); + this.func = new StickyFunc<>( + input -> src.asValue() + ); } @Override public T asValue() throws Exception { - if (this.cache.isEmpty()) { - this.cache.add(this.source.asValue()); - } - return this.cache.get(0); + return this.func.apply(true); } } diff --git a/src/main/java/org/cactoos/io/InputAsBytes.java b/src/main/java/org/cactoos/io/InputAsBytes.java index 667edddc5f..0ea6e9b340 100644 --- a/src/main/java/org/cactoos/io/InputAsBytes.java +++ b/src/main/java/org/cactoos/io/InputAsBytes.java @@ -71,11 +71,11 @@ public InputAsBytes(final Input input, final int max) { @Override public byte[] asBytes() throws IOException { - try (final ByteArrayOutputStream baos = new ByteArrayOutputStream()) { + try (final ByteArrayOutputStream baos = new ByteArrayOutputStream(); final InputStream stream = new TeeInput( this.source, new OutputStreamAsOutput(baos) - ).stream(); + ).stream()) { final byte[] buf = new byte[this.size]; while (true) { if (stream.read(buf) != buf.length) { diff --git a/src/main/java/org/cactoos/io/TeeInputStream.java b/src/main/java/org/cactoos/io/TeeInputStream.java index bd31b29de9..9ea6a68425 100644 --- a/src/main/java/org/cactoos/io/TeeInputStream.java +++ b/src/main/java/org/cactoos/io/TeeInputStream.java @@ -37,14 +37,17 @@ * @since 0.1 */ public final class TeeInputStream extends InputStream { + /** * Input. */ private final InputStream input; + /** * Output. */ private final OutputStream output; + /** * Ctor. * @param src Source of data @@ -59,7 +62,9 @@ public TeeInputStream(final InputStream src, final OutputStream tgt) { @Override public int read() throws IOException { final int data = this.input.read(); - this.output.write(data); + if (data >= 0) { + this.output.write(data); + } return data; } diff --git a/src/main/java/org/cactoos/list/IterableAsList.java b/src/main/java/org/cactoos/list/IterableAsList.java index a76c176eb9..8dd9c642d1 100644 --- a/src/main/java/org/cactoos/list/IterableAsList.java +++ b/src/main/java/org/cactoos/list/IterableAsList.java @@ -24,9 +24,7 @@ package org.cactoos.list; import java.util.AbstractList; -import java.util.ArrayList; import java.util.List; -import org.cactoos.func.StickyScalar; import org.cactoos.func.UncheckedScalar; import org.cactoos.text.FormattedText; import org.cactoos.text.UncheckedText; @@ -48,11 +46,6 @@ public final class IterableAsList extends AbstractList { */ private final Iterable source; - /** - * Cache for source. - */ - private final List cache; - /** * Iterable length. */ @@ -66,29 +59,30 @@ public final class IterableAsList extends AbstractList { public IterableAsList(final Iterable iterable) { super(); this.source = iterable; - this.cache = new ArrayList<>(0); this.length = new UncheckedScalar<>( - new StickyScalar<>( - new LengthOfIterable(iterable) - ) + new LengthOfIterable(iterable) ); } @Override public T get(final int index) { - if (index < 0 || index >= this.size()) { - throw new IndexOutOfBoundsException( - new UncheckedText( - new FormattedText( - "index=%d, bounds=[%d; %d]", - index, - 0, - this.size() - ) - ).asString() - ); + int position = 0; + for (final T elem : this.source) { + if (position == index) { + return elem; + } + position += 1; } - return this.cachedItem(index); + throw new IndexOutOfBoundsException( + new UncheckedText( + new FormattedText( + "index=%d, bounds=[%d; %d]", + index, + 0, + this.size() + ) + ).asString() + ); } @Override @@ -96,18 +90,4 @@ public int size() { return this.length.asValue(); } - /** - * Find item in cache by index. - * - * @param index Item index - * @return Cached item - */ - private T cachedItem(final int index) { - if (this.cache.size() != this.size()) { - for (final T item : this.source) { - this.cache.add(item); - } - } - return this.cache.get(index); - } } diff --git a/src/main/java/org/cactoos/list/IterableAsMap.java b/src/main/java/org/cactoos/list/IterableAsMap.java new file mode 100644 index 0000000000..b1f316434c --- /dev/null +++ b/src/main/java/org/cactoos/list/IterableAsMap.java @@ -0,0 +1,147 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2017 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.list; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import org.cactoos.func.StickyScalar; +import org.cactoos.func.UncheckedScalar; + +/** + * Iterable as {@link Map}. + * + *

There is no thread-safety guarantee. + * + * @author Yegor Bugayenko (yegor256@gmail.com) + * @version $Id$ + * @param Type of key + * @param Type of value + * @since 0.4 + */ +public final class IterableAsMap implements Map { + + /** + * The map. + */ + private final UncheckedScalar> map; + + /** + * Ctor. + * @param entries Entries for the map + */ + @SafeVarargs + @SuppressWarnings("varargs") + public IterableAsMap(final Map.Entry... entries) { + this(new ArrayAsIterable<>(entries)); + } + + /** + * Ctor. + * @param entries Entries for the map + */ + public IterableAsMap(final Iterable> entries) { + this.map = new UncheckedScalar<>( + new StickyScalar<>( + () -> { + final Map temp = new HashMap<>(0); + for (final Map.Entry entry : entries) { + temp.put(entry.getKey(), entry.getValue()); + } + return temp; + } + ) + ); + } + + @Override + public int size() { + return this.map.asValue().size(); + } + + @Override + public boolean isEmpty() { + return this.map.asValue().isEmpty(); + } + + @Override + public boolean containsKey(final Object key) { + return this.map.asValue().containsKey(key); + } + + @Override + public boolean containsValue(final Object value) { + return this.map.asValue().containsValue(value); + } + + @Override + public Y get(final Object key) { + return this.map.asValue().get(key); + } + + @Override + public Y put(final X key, final Y value) { + throw new UnsupportedOperationException( + "#put() is not supported" + ); + } + + @Override + public Y remove(final Object key) { + throw new UnsupportedOperationException( + "#remove() is not supported" + ); + } + + @Override + public void putAll(final Map entries) { + throw new UnsupportedOperationException( + "#putAll() is not supported" + ); + } + + @Override + public void clear() { + throw new UnsupportedOperationException( + "#clear() is not supported" + ); + } + + @Override + public Set keySet() { + return this.map.asValue().keySet(); + } + + @Override + public Collection values() { + return this.map.asValue().values(); + } + + @Override + public Set> entrySet() { + return this.map.asValue().entrySet(); + } + +} diff --git a/src/test/java/org/cactoos/func/StickyFuncTest.java b/src/test/java/org/cactoos/func/StickyFuncTest.java new file mode 100644 index 0000000000..94da814cbd --- /dev/null +++ b/src/test/java/org/cactoos/func/StickyFuncTest.java @@ -0,0 +1,53 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2017 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.func; + +import java.security.SecureRandom; +import org.cactoos.Func; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.Test; + +/** + * Test case for {@link StickyFunc}. + * + * @author Yegor Bugayenko (yegor256@gmail.com) + * @version $Id$ + * @since 0.4 + * @checkstyle JavadocMethodCheck (500 lines) + */ +public final class StickyFuncTest { + + @Test + public void cachesFuncResults() throws Exception { + final Func func = new StickyFunc<>( + input -> new SecureRandom().nextInt() + ); + MatcherAssert.assertThat( + func.apply(true) + func.apply(true), + Matchers.equalTo(func.apply(true) + func.apply(true)) + ); + } + +} diff --git a/src/test/java/org/cactoos/func/StickyScalarTest.java b/src/test/java/org/cactoos/func/StickyScalarTest.java new file mode 100644 index 0000000000..0e16476e24 --- /dev/null +++ b/src/test/java/org/cactoos/func/StickyScalarTest.java @@ -0,0 +1,53 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2017 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.func; + +import java.security.SecureRandom; +import org.cactoos.Scalar; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.Test; + +/** + * Test case for {@link StickyScalar}. + * + * @author Yegor Bugayenko (yegor256@gmail.com) + * @version $Id$ + * @since 0.4 + * @checkstyle JavadocMethodCheck (500 lines) + */ +public final class StickyScalarTest { + + @Test + public void cachesScalarResults() throws Exception { + final Scalar scalar = new StickyScalar<>( + () -> new SecureRandom().nextInt() + ); + MatcherAssert.assertThat( + scalar.asValue() + scalar.asValue(), + Matchers.equalTo(scalar.asValue() + scalar.asValue()) + ); + } + +} diff --git a/src/test/java/org/cactoos/io/InputAsBytesTest.java b/src/test/java/org/cactoos/io/InputAsBytesTest.java index 9268b1740f..202807517d 100644 --- a/src/test/java/org/cactoos/io/InputAsBytesTest.java +++ b/src/test/java/org/cactoos/io/InputAsBytesTest.java @@ -23,8 +23,13 @@ */ package org.cactoos.io; +import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; import java.nio.charset.StandardCharsets; +import java.util.concurrent.atomic.AtomicBoolean; +import org.cactoos.func.FuncAsMatcher; +import org.cactoos.text.BytesAsText; import org.cactoos.text.StringAsText; import org.cactoos.text.TextAsBytes; import org.hamcrest.MatcherAssert; @@ -38,6 +43,7 @@ * @version $Id$ * @since 0.1 * @checkstyle JavadocMethodCheck (500 lines) + * @checkstyle ClassDataAbstractionCouplingCheck (500 lines) */ public final class InputAsBytesTest { @@ -84,4 +90,36 @@ public void readsInputIntoBytesWithSmallBuffer() throws IOException { ); } + @Test + public void closesInputStream() throws IOException { + final AtomicBoolean closed = new AtomicBoolean(); + final InputStream input = new ByteArrayInputStream( + "how are you?".getBytes() + ); + MatcherAssert.assertThat( + "Can't close InputStream correctly", + new BytesAsText( + new InputAsBytes( + new InputStreamAsInput( + new InputStream() { + @Override + public int read() throws IOException { + return input.read(); + } + @Override + public void close() throws IOException { + input.close(); + closed.set(true); + } + } + ) + ).asBytes(), + StandardCharsets.UTF_8 + ).asString(), + new FuncAsMatcher<>( + text -> closed.get() + ) + ); + } + } diff --git a/src/test/java/org/cactoos/io/TeeInputStreamTest.java b/src/test/java/org/cactoos/io/TeeInputStreamTest.java new file mode 100644 index 0000000000..11f12c4376 --- /dev/null +++ b/src/test/java/org/cactoos/io/TeeInputStreamTest.java @@ -0,0 +1,81 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2017 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.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.Test; + +/** + * Test case for {@link TeeInputStream}. + * @author Yegor Bugayenko (yegor256@gmail.com) + * @version $Id$ + * @since 0.1 + * @checkstyle JavadocMethodCheck (500 lines) + * @checkstyle ClassDataAbstractionCouplingCheck (500 lines) + */ +public final class TeeInputStreamTest { + + @Test + public void copiesContentByteByByte() throws IOException { + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + final String content = "Hello, товарищ!"; + MatcherAssert.assertThat( + "Can't copy InputStream to OutputStream byte by byte", + TeeInputStreamTest.asString( + new TeeInputStream( + new ByteArrayInputStream( + content.getBytes(StandardCharsets.UTF_8) + ), + baos + ) + ), + Matchers.allOf( + Matchers.equalTo(content), + Matchers.equalTo( + new String(baos.toByteArray(), StandardCharsets.UTF_8) + ) + ) + ); + } + + private static String asString(final InputStream input) throws IOException { + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + while (true) { + final int data = input.read(); + if (data < 0) { + break; + } + baos.write(data); + } + input.close(); + return new String(baos.toByteArray(), StandardCharsets.UTF_8); + } + +} diff --git a/src/test/java/org/cactoos/list/IterableAsMapTest.java b/src/test/java/org/cactoos/list/IterableAsMapTest.java new file mode 100644 index 0000000000..57bb9a4c49 --- /dev/null +++ b/src/test/java/org/cactoos/list/IterableAsMapTest.java @@ -0,0 +1,56 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2017 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.list; + +import java.util.AbstractMap; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.Test; + +/** + * Test case for {@link IterableAsMap}. + * + * @author Yegor Bugayenko (yegor256@gmail.com) + * @version $Id$ + * @since 0.4 + * @checkstyle JavadocMethodCheck (500 lines) + */ +public final class IterableAsMapTest { + + @Test + @SuppressWarnings("unchecked") + public void convertsIterableToMap() { + MatcherAssert.assertThat( + "Can't convert iterable to map", + new IterableAsMap<>( + new AbstractMap.SimpleEntry<>(0, "hello, "), + new AbstractMap.SimpleEntry<>(1, "world!") + ), + Matchers.hasEntry( + Matchers.equalTo(0), + Matchers.startsWith("hello") + ) + ); + } +}