--- old/src/share/classes/java/nio/file/Files.java 2013-07-10 14:11:40.935135667 -0700 +++ new/src/share/classes/java/nio/file/Files.java 2013-07-10 14:11:40.743135671 -0700 @@ -25,34 +25,57 @@ package java.nio.file; -import java.nio.ByteBuffer; -import java.nio.file.attribute.*; -import java.nio.file.spi.FileSystemProvider; -import java.nio.file.spi.FileTypeDetector; -import java.nio.channels.FileChannel; -import java.nio.channels.SeekableByteChannel; -import java.io.Closeable; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.Reader; -import java.io.Writer; import java.io.BufferedReader; import java.io.BufferedWriter; +import java.io.Closeable; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; import java.io.InputStreamReader; +import java.io.OutputStream; import java.io.OutputStreamWriter; -import java.io.IOException; +import java.io.Reader; import java.io.UncheckedIOException; -import java.util.*; -import java.util.function.BiPredicate; -import java.util.stream.CloseableStream; -import java.util.stream.DelegatingStream; -import java.util.stream.Stream; -import java.util.stream.StreamSupport; -import java.security.AccessController; -import java.security.PrivilegedAction; +import java.io.Writer; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.channels.SeekableByteChannel; import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; import java.nio.charset.CharsetEncoder; +import java.nio.file.attribute.BasicFileAttributeView; +import java.nio.file.attribute.BasicFileAttributes; +import java.nio.file.attribute.DosFileAttributes; +import java.nio.file.attribute.FileAttribute; +import java.nio.file.attribute.FileAttributeView; +import java.nio.file.attribute.FileOwnerAttributeView; +import java.nio.file.attribute.FileStoreAttributeView; +import java.nio.file.attribute.FileTime; +import java.nio.file.attribute.PosixFileAttributeView; +import java.nio.file.attribute.PosixFileAttributes; +import java.nio.file.attribute.PosixFilePermission; +import java.nio.file.attribute.UserPrincipal; +import java.nio.file.spi.FileSystemProvider; +import java.nio.file.spi.FileTypeDetector; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.EnumSet; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.MayHoldCloseableResource; +import java.util.Objects; +import java.util.ServiceLoader; +import java.util.Set; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.function.BiPredicate; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; /** * This class consists exclusively of static methods that operate on files, @@ -74,6 +97,21 @@ return path.getFileSystem().provider(); } + /** + * Convert a Closeable to a Runnable by converting checked IOException + * to UncheckedIOException + */ + private static Runnable asUncheckedRunnable(Closeable c) { + return () -> { + try { + c.close(); + } + catch (IOException e) { + throw new UncheckedIOException(e); + } + }; + } + // -- File contents -- /** @@ -3181,29 +3219,7 @@ // -- Stream APIs -- /** - * Implementation of CloseableStream - */ - private static class DelegatingCloseableStream extends DelegatingStream - implements CloseableStream - { - private final Closeable closeable; - - DelegatingCloseableStream(Closeable c, Stream delegate) { - super(delegate); - this.closeable = c; - } - - public void close() { - try { - closeable.close(); - } catch (IOException ex) { - throw new UncheckedIOException(ex); - } - } - } - - /** - * Return a lazily populated {@code CloseableStream}, the elements of + * Return a lazily populated {@code Stream}, the elements of * which are the entries in the directory. The listing is not recursive. * *

The elements of the stream are {@link Path} objects that are @@ -3218,7 +3234,7 @@ * method. * *

When not using the try-with-resources construct, then the stream's - * {@link CloseableStream#close close} method should be invoked after the + * {@link Stream#close close} method should be invoked after the * operation is completed so as to free any resources held for the open * directory. Operating on a closed stream behaves as if the end of stream * has been reached. Due to read-ahead, one or more elements may be @@ -3231,7 +3247,7 @@ * * @param dir The path to the directory * - * @return The {@code CloseableStream} describing the content of the + * @return The {@code Stream} describing the content of the * directory * * @throws NotDirectoryException @@ -3247,43 +3263,47 @@ * @see #newDirectoryStream(Path) * @since 1.8 */ - public static CloseableStream list(Path dir) throws IOException { + @MayHoldCloseableResource.HoldsResource + public static Stream list(Path dir) throws IOException { DirectoryStream ds = Files.newDirectoryStream(dir); - final Iterator delegate = ds.iterator(); + try { + final Iterator delegate = ds.iterator(); - // Re-wrap DirectoryIteratorException to UncheckedIOException - Iterator it = new Iterator() { - public boolean hasNext() { - try { - return delegate.hasNext(); - } catch (DirectoryIteratorException e) { - throw new UncheckedIOException(e.getCause()); + // Re-wrap DirectoryIteratorException to UncheckedIOException + Iterator it = new Iterator() { + public boolean hasNext() { + try { + return delegate.hasNext(); + } catch (DirectoryIteratorException e) { + throw new UncheckedIOException(e.getCause()); + } } - } - public Path next() { - try { - return delegate.next(); - } catch (DirectoryIteratorException e) { - throw new UncheckedIOException(e.getCause()); + public Path next() { + try { + return delegate.next(); + } catch (DirectoryIteratorException e) { + throw new UncheckedIOException(e.getCause()); + } } - } - }; + }; - Stream s = StreamSupport.stream( - Spliterators.spliteratorUnknownSize(it, Spliterator.DISTINCT), - false); - return new DelegatingCloseableStream<>(ds, s); + return StreamSupport.stream(Spliterators.spliteratorUnknownSize(it, Spliterator.DISTINCT), false) + .onClose(asUncheckedRunnable(ds)); + } catch (Error|RuntimeException e) { + ds.close(); + throw e; + } } /** - * Return a {@code CloseableStream} that is lazily populated with {@code + * Return a {@code Stream} that is lazily populated with {@code * Path} by walking the file tree rooted at a given starting file. The * file tree is traversed depth-first, the elements in the stream * are {@link Path} objects that are obtained as if by {@link * Path#resolve(Path) resolving} the relative path against {@code start}. * *

The {@code stream} walks the file tree as elements are consumed. - * The {@code CloseableStream} returned is guaranteed to have at least one + * The {@code Stream} returned is guaranteed to have at least one * element, the starting file itself. For each file visited, the stream * attempts to read its {@link BasicFileAttributes}. If the file is a * directory and can be opened successfully, entries in the directory, and @@ -3324,7 +3344,7 @@ * (or directory), then it is ignored and not included in the stream. * *

When not using the try-with-resources construct, then the stream's - * {@link CloseableStream#close close} method should be invoked after the + * {@link Stream#close close} method should be invoked after the * operation is completed so as to free any resources held for the open * directory. Operate the stream after it is closed will throw an * {@link java.lang.IllegalStateException}. @@ -3341,7 +3361,7 @@ * @param options * options to configure the traversal * - * @return the {@link CloseableStream} of {@link Path} + * @return the {@link Stream} of {@link Path} * * @throws IllegalArgumentException * if the {@code maxDepth} parameter is negative @@ -3354,21 +3374,23 @@ * if an I/O error is thrown when accessing the starting file. * @since 1.8 */ - public static CloseableStream walk(Path start, int maxDepth, - FileVisitOption... options) - throws IOException - { + @MayHoldCloseableResource.HoldsResource + public static Stream walk(Path start, int maxDepth, + FileVisitOption... options) + throws IOException { FileTreeIterator iterator = new FileTreeIterator(start, maxDepth, options); - - Stream s = StreamSupport.stream( - Spliterators.spliteratorUnknownSize(iterator, Spliterator.DISTINCT), - false). - map(entry -> entry.file()); - return new DelegatingCloseableStream<>(iterator, s); + try { + return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, Spliterator.DISTINCT), false) + .onClose(iterator::close) + .map(entry -> entry.file()); + } catch (Error|RuntimeException e) { + iterator.close(); + throw e; + } } /** - * Return a {@code CloseableStream} that is lazily populated with {@code + * Return a {@code Stream} that is lazily populated with {@code * Path} by walking the file tree rooted at a given starting file. The * file tree is traversed depth-first, the elements in the stream * are {@link Path} objects that are obtained as if by {@link @@ -3386,7 +3408,7 @@ * @param options * options to configure the traversal * - * @return the {@link CloseableStream} of {@link Path} + * @return the {@link Stream} of {@link Path} * * @throws SecurityException * If the security manager denies access to the starting file. @@ -3399,15 +3421,15 @@ * @see #walk(Path, int, FileVisitOption...) * @since 1.8 */ - public static CloseableStream walk(Path start, - FileVisitOption... options) - throws IOException - { + @MayHoldCloseableResource.HoldsResource + public static Stream walk(Path start, + FileVisitOption... options) + throws IOException { return walk(start, Integer.MAX_VALUE, options); } /** - * Return a {@code CloseableStream} that is lazily populated with {@code + * Return a {@code Stream} that is lazily populated with {@code * Path} by searching for files in a file tree rooted at a given starting * file. * @@ -3416,7 +3438,7 @@ * {@link BiPredicate} is invoked with its {@link Path} and {@link * BasicFileAttributes}. The {@code Path} object is obtained as if by * {@link Path#resolve(Path) resolving} the relative path against {@code - * start} and is only included in the returned {@link CloseableStream} if + * start} and is only included in the returned {@link Stream} if * the {@code BiPredicate} returns true. Compare to calling {@link * java.util.stream.Stream#filter filter} on the {@code Stream} * returned by {@code walk} method, this method may be more efficient by @@ -3437,7 +3459,7 @@ * @param options * options to configure the traversal * - * @return the {@link CloseableStream} of {@link Path} + * @return the {@link Stream} of {@link Path} * * @throws IllegalArgumentException * if the {@code maxDepth} parameter is negative @@ -3452,24 +3474,26 @@ * @see #walk(Path, int, FileVisitOption...) * @since 1.8 */ - public static CloseableStream find(Path start, - int maxDepth, - BiPredicate matcher, - FileVisitOption... options) - throws IOException - { + @MayHoldCloseableResource.HoldsResource + public static Stream find(Path start, + int maxDepth, + BiPredicate matcher, + FileVisitOption... options) + throws IOException { FileTreeIterator iterator = new FileTreeIterator(start, maxDepth, options); - - Stream s = StreamSupport.stream( - Spliterators.spliteratorUnknownSize(iterator, Spliterator.DISTINCT), - false). - filter(entry -> matcher.test(entry.file(), entry.attributes())). - map(entry -> entry.file()); - return new DelegatingCloseableStream<>(iterator, s); + try { + return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, Spliterator.DISTINCT), false) + .onClose(iterator::close) + .filter(entry -> matcher.test(entry.file(), entry.attributes())) + .map(entry -> entry.file()); + } catch (Error|RuntimeException e) { + iterator.close(); + throw e; + } } /** - * Read all lines from a file as a {@code CloseableStream}. Unlike {@link + * Read all lines from a file as a {@code Stream}. Unlike {@link * #readAllLines(Path, Charset) readAllLines}, this method does not read * all lines into a {@code List}, but instead populates lazily as the stream * is consumed. @@ -3487,7 +3511,7 @@ * it is also wrapped as an {@code UncheckedIOException}. * *

When not using the try-with-resources construct, then stream's - * {@link CloseableStream#close close} method should be invoked after + * {@link Stream#close close} method should be invoked after * operation is completed so as to free any resources held for the open * file. * @@ -3496,7 +3520,7 @@ * @param cs * the charset to use for decoding * - * @return the lines from the file as a {@code CloseableStream} + * @return the lines from the file as a {@code Stream} * * @throws IOException * if an I/O error occurs opening the file @@ -3510,10 +3534,14 @@ * @see java.io.BufferedReader#lines() * @since 1.8 */ - public static CloseableStream lines(Path path, Charset cs) - throws IOException - { + @MayHoldCloseableResource.HoldsResource + public static Stream lines(Path path, Charset cs) throws IOException { BufferedReader br = Files.newBufferedReader(path, cs); - return new DelegatingCloseableStream<>(br, br.lines()); + try { + return br.lines().onClose(asUncheckedRunnable(br)); + } catch (Error|RuntimeException e) { + br.close(); + throw e; + } } } --- old/src/share/classes/java/util/stream/AbstractPipeline.java 2013-07-10 14:11:41.943135649 -0700 +++ new/src/share/classes/java/util/stream/AbstractPipeline.java 2013-07-10 14:11:41.759135653 -0700 @@ -71,6 +71,9 @@ */ abstract class AbstractPipeline> extends PipelineHelper implements BaseStream { + private static final String MSG_STREAM_LINKED = "stream has already been operated upon or closed"; + private static final String MSG_CONSUMED = "source already consumed or closed"; + /** * Backlink to the head of the pipeline chain (self if this is the source * stage). @@ -134,6 +137,8 @@ */ private boolean sourceAnyStateful; + private Runnable sourceCloseAction; + /** * True if pipeline is parallel, otherwise the pipeline is sequential; only * valid for the source stage. @@ -192,7 +197,7 @@ */ AbstractPipeline(AbstractPipeline previousStage, int opFlags) { if (previousStage.linkedOrConsumed) - throw new IllegalStateException("stream has already been operated upon"); + throw new IllegalStateException(MSG_STREAM_LINKED); previousStage.linkedOrConsumed = true; previousStage.nextStage = this; @@ -218,7 +223,7 @@ final R evaluate(TerminalOp terminalOp) { assert getOutputShape() == terminalOp.inputShape(); if (linkedOrConsumed) - throw new IllegalStateException("stream has already been operated upon"); + throw new IllegalStateException(MSG_STREAM_LINKED); linkedOrConsumed = true; return isParallel() @@ -234,7 +239,7 @@ */ final Node evaluateToArrayNode(IntFunction generator) { if (linkedOrConsumed) - throw new IllegalStateException("stream has already been operated upon"); + throw new IllegalStateException(MSG_STREAM_LINKED); linkedOrConsumed = true; // If the last intermediate operation is stateful then @@ -261,7 +266,7 @@ throw new IllegalStateException(); if (linkedOrConsumed) - throw new IllegalStateException("stream has already been operated upon"); + throw new IllegalStateException(MSG_STREAM_LINKED); linkedOrConsumed = true; if (sourceStage.sourceSpliterator != null) { @@ -275,7 +280,7 @@ return s; } else { - throw new IllegalStateException("source already consumed"); + throw new IllegalStateException(MSG_CONSUMED); } } @@ -293,11 +298,32 @@ return (S) this; } + @Override + public void close() { + linkedOrConsumed = true; + sourceSupplier = null; + sourceSpliterator = null; + if (sourceStage.sourceCloseAction != null) { + sourceStage.sourceCloseAction.run(); + sourceStage.sourceCloseAction = null; + } + } + + @Override + public S onClose(Runnable closeHandler) { + Runnable existingHandler = sourceStage.sourceCloseAction; + sourceStage.sourceCloseAction = + (existingHandler == null) + ? closeHandler + : Streams.composeWithExceptions(existingHandler, closeHandler); + return (S) this; + } + // Primitive specialization use co-variant overrides, hence is not final @Override public Spliterator spliterator() { if (linkedOrConsumed) - throw new IllegalStateException("stream has already been operated upon"); + throw new IllegalStateException(MSG_STREAM_LINKED); linkedOrConsumed = true; if (this == sourceStage) { @@ -312,7 +338,7 @@ return lazySpliterator(s); } else { - throw new IllegalStateException("source already consumed"); + throw new IllegalStateException(MSG_CONSUMED); } } else { @@ -410,7 +436,7 @@ sourceStage.sourceSupplier = null; } else { - throw new IllegalStateException("source already consumed"); + throw new IllegalStateException(MSG_CONSUMED); } if (isParallel()) { --- old/src/share/classes/java/util/stream/BaseStream.java 2013-07-10 14:11:42.811135634 -0700 +++ new/src/share/classes/java/util/stream/BaseStream.java 2013-07-10 14:11:42.623135637 -0700 @@ -24,6 +24,7 @@ */ package java.util.stream; +import java.util.MayHoldCloseableResource; import java.util.Iterator; import java.util.Spliterator; @@ -35,7 +36,8 @@ * @param type of stream implementing {@code BaseStream} * @since 1.8 */ -public interface BaseStream> { +public interface BaseStream> + extends MayHoldCloseableResource { /** * Returns an iterator for the elements of this stream. * @@ -103,4 +105,18 @@ * @return an unordered stream */ S unordered(); + + /** + * Returns an equivalent stream with an additional close handler. Close + * handlers are run when the {@link MayHoldCloseableResource#close()} method + * is called on the stream, and are executed in the order they were + * added. All close handlers are run, even if earlier close handlers throw + * exceptions. If any close handler throws an exception, the first + * exception thrown will be relayed to the caller of {@code close()}, with + * any remaining exceptions added to that exception as suppressed exceptions. + * + * @param closeHandler A task to execute when the stream is closed + * @return a stream with a handler that is run if the stream is closed + */ + S onClose(Runnable closeHandler); } --- old/src/share/classes/java/util/stream/DoublePipeline.java 2013-07-10 14:11:43.667135619 -0700 +++ new/src/share/classes/java/util/stream/DoublePipeline.java 2013-07-10 14:11:43.487135622 -0700 @@ -265,10 +265,11 @@ @Override public void accept(double t) { - // We can do better that this too; optimize for depth=0 case and just grab spliterator and forEach it - DoubleStream result = mapper.apply(t); - if (result != null) - result.sequential().forEach(i -> downstream.accept(i)); + try (DoubleStream result = mapper.apply(t)) { + // We can do better that this too; optimize for depth=0 case and just grab spliterator and forEach it + if (result != null) + result.sequential().forEach(i -> downstream.accept(i)); + } } }; } --- old/src/share/classes/java/util/stream/DoubleStream.java 2013-07-10 14:11:44.531135603 -0700 +++ new/src/share/classes/java/util/stream/DoubleStream.java 2013-07-10 14:11:44.343135607 -0700 @@ -752,7 +752,8 @@ * elements of a first {@code DoubleStream} succeeded by all the elements of the * second {@code DoubleStream}. The resulting stream is ordered if both * of the input streams are ordered, and parallel if either of the input - * streams is parallel. + * streams is parallel. When the resulting stream is closed, the close + * handlers for both input streams is invoked. * * @param a the first stream * @param b the second stream to concatenate on to end of the first stream @@ -764,7 +765,8 @@ Spliterator.OfDouble split = new Streams.ConcatSpliterator.OfDouble( a.spliterator(), b.spliterator()); - return StreamSupport.doubleStream(split, a.isParallel() || b.isParallel()); + DoubleStream stream = StreamSupport.doubleStream(split, a.isParallel() || b.isParallel()); + return stream.onClose(Streams.composedClose(a, b)); } /** --- old/src/share/classes/java/util/stream/IntPipeline.java 2013-07-10 14:11:45.391135588 -0700 +++ new/src/share/classes/java/util/stream/IntPipeline.java 2013-07-10 14:11:45.203135592 -0700 @@ -301,10 +301,11 @@ @Override public void accept(int t) { - // We can do better that this too; optimize for depth=0 case and just grab spliterator and forEach it - IntStream result = mapper.apply(t); - if (result != null) - result.sequential().forEach(i -> downstream.accept(i)); + try (IntStream result = mapper.apply(t)) { + // We can do better that this too; optimize for depth=0 case and just grab spliterator and forEach it + if (result != null) + result.sequential().forEach(i -> downstream.accept(i)); + } } }; } --- old/src/share/classes/java/util/stream/IntStream.java 2013-07-10 14:11:46.251135573 -0700 +++ new/src/share/classes/java/util/stream/IntStream.java 2013-07-10 14:11:46.063135576 -0700 @@ -806,7 +806,8 @@ * elements of a first {@code IntStream} succeeded by all the elements of the * second {@code IntStream}. The resulting stream is ordered if both * of the input streams are ordered, and parallel if either of the input - * streams is parallel. + * streams is parallel. When the resulting stream is closed, the close + * handlers for both input streams is invoked. * * @param a the first stream * @param b the second stream to concatenate on to end of the first stream @@ -818,7 +819,8 @@ Spliterator.OfInt split = new Streams.ConcatSpliterator.OfInt( a.spliterator(), b.spliterator()); - return StreamSupport.intStream(split, a.isParallel() || b.isParallel()); + IntStream stream = StreamSupport.intStream(split, a.isParallel() || b.isParallel()); + return stream.onClose(Streams.composedClose(a, b)); } /** --- old/src/share/classes/java/util/stream/LongPipeline.java 2013-07-10 14:11:47.099135558 -0700 +++ new/src/share/classes/java/util/stream/LongPipeline.java 2013-07-10 14:11:46.919135561 -0700 @@ -282,10 +282,11 @@ @Override public void accept(long t) { - // We can do better that this too; optimize for depth=0 case and just grab spliterator and forEach it - LongStream result = mapper.apply(t); - if (result != null) - result.sequential().forEach(i -> downstream.accept(i)); + try (LongStream result = mapper.apply(t)) { + // We can do better that this too; optimize for depth=0 case and just grab spliterator and forEach it + if (result != null) + result.sequential().forEach(i -> downstream.accept(i)); + } } }; } --- old/src/share/classes/java/util/stream/LongStream.java 2013-07-10 14:11:47.943135543 -0700 +++ new/src/share/classes/java/util/stream/LongStream.java 2013-07-10 14:11:47.755135546 -0700 @@ -812,7 +812,8 @@ * elements of a first {@code LongStream} succeeded by all the elements of the * second {@code LongStream}. The resulting stream is ordered if both * of the input streams are ordered, and parallel if either of the input - * streams is parallel. + * streams is parallel. When the resulting stream is closed, the close + * handlers for both input streams is invoked. * * @param a the first stream * @param b the second stream to concatenate on to end of the first stream @@ -824,7 +825,8 @@ Spliterator.OfLong split = new Streams.ConcatSpliterator.OfLong( a.spliterator(), b.spliterator()); - return StreamSupport.longStream(split, a.isParallel() || b.isParallel()); + LongStream stream = StreamSupport.longStream(split, a.isParallel() || b.isParallel()); + return stream.onClose(Streams.composedClose(a, b)); } /** --- old/src/share/classes/java/util/stream/ReferencePipeline.java 2013-07-10 14:11:48.815135527 -0700 +++ new/src/share/classes/java/util/stream/ReferencePipeline.java 2013-07-10 14:11:48.627135531 -0700 @@ -263,10 +263,11 @@ @Override public void accept(P_OUT u) { - // We can do better that this too; optimize for depth=0 case and just grab spliterator and forEach it - Stream result = mapper.apply(u); - if (result != null) - result.sequential().forEach(downstream); + try (Stream result = mapper.apply(u)) { + // We can do better that this too; optimize for depth=0 case and just grab spliterator and forEach it + if (result != null) + result.sequential().forEach(downstream); + } } }; } @@ -290,10 +291,11 @@ @Override public void accept(P_OUT u) { - // We can do better that this too; optimize for depth=0 case and just grab spliterator and forEach it - IntStream result = mapper.apply(u); - if (result != null) - result.sequential().forEach(downstreamAsInt); + try (IntStream result = mapper.apply(u)) { + // We can do better that this too; optimize for depth=0 case and just grab spliterator and forEach it + if (result != null) + result.sequential().forEach(downstreamAsInt); + } } }; } @@ -317,10 +319,11 @@ @Override public void accept(P_OUT u) { - // We can do better that this too; optimize for depth=0 case and just grab spliterator and forEach it - DoubleStream result = mapper.apply(u); - if (result != null) - result.sequential().forEach(downstreamAsDouble); + try (DoubleStream result = mapper.apply(u)) { + // We can do better that this too; optimize for depth=0 case and just grab spliterator and forEach it + if (result != null) + result.sequential().forEach(downstreamAsDouble); + } } }; } @@ -344,10 +347,11 @@ @Override public void accept(P_OUT u) { - // We can do better that this too; optimize for depth=0 case and just grab spliterator and forEach it - LongStream result = mapper.apply(u); - if (result != null) - result.sequential().forEach(downstreamAsLong); + try (LongStream result = mapper.apply(u)) { + // We can do better that this too; optimize for depth=0 case and just grab spliterator and forEach it + if (result != null) + result.sequential().forEach(downstreamAsLong); + } } }; } --- old/src/share/classes/java/util/stream/Stream.java 2013-07-10 14:11:49.671135512 -0700 +++ new/src/share/classes/java/util/stream/Stream.java 2013-07-10 14:11:49.487135515 -0700 @@ -890,7 +890,8 @@ * elements of a first {@code Stream} succeeded by all the elements of the * second {@code Stream}. The resulting stream is ordered if both * of the input streams are ordered, and parallel if either of the input - * streams is parallel. + * streams is parallel. When the resulting stream is closed, the close + * handlers for both input streams is invoked. * * @param The type of stream elements * @param a the first stream @@ -905,7 +906,8 @@ @SuppressWarnings("unchecked") Spliterator split = new Streams.ConcatSpliterator.OfRef<>( (Spliterator) a.spliterator(), (Spliterator) b.spliterator()); - return StreamSupport.stream(split, a.isParallel() || b.isParallel()); + Stream stream = StreamSupport.stream(split, a.isParallel() || b.isParallel()); + return stream.onClose(Streams.composedClose(a, b)); } /** --- old/src/share/classes/java/util/stream/Streams.java 2013-07-10 14:11:50.563135496 -0700 +++ new/src/share/classes/java/util/stream/Streams.java 2013-07-10 14:11:50.379135500 -0700 @@ -25,6 +25,7 @@ package java.util.stream; import java.util.Comparator; +import java.util.MayHoldCloseableResource; import java.util.Spliterator; import java.util.function.Consumer; import java.util.function.DoubleConsumer; @@ -810,4 +811,61 @@ } } } + + /** + * Given two Runnables, return a Runnable that executes both in sequence, + * even if the first throws an exception, and if both throw exceptions, add + * any exceptions thrown by the second as suppressed exceptions of the first. + */ + static Runnable composeWithExceptions(Runnable a, Runnable b) { + return new Runnable() { + @Override + public void run() { + try { + a.run(); + } + catch (Error|RuntimeException e1) { + try { + b.run(); + } + catch (Error|RuntimeException e2) { + e1.addSuppressed(e2); + } + finally { + throw e1; + } + } + b.run(); + } + }; + } + + /** + * Given two MayHoldCloseableResource objects, return a Runnable that + * executes both of their close methods in sequence, + * even if the first throws an exception, and if both throw exceptions, add + * any exceptions thrown by the second as suppressed exceptions of the first. + */ + static Runnable composedClose(MayHoldCloseableResource a, MayHoldCloseableResource b) { + return new Runnable() { + @Override + public void run() { + try { + a.close(); + } + catch (Error|RuntimeException e1) { + try { + b.close(); + } + catch (Error|RuntimeException e2) { + e1.addSuppressed(e2); + } + finally { + throw e1; + } + } + b.close(); + } + }; + } } --- old/test/java/nio/file/Files/StreamTest.java 2013-07-10 14:11:51.419135481 -0700 +++ new/test/java/nio/file/Files/StreamTest.java 2013-07-10 14:11:51.231135484 -0700 @@ -50,7 +50,7 @@ import java.util.Set; import java.util.TreeSet; import java.util.function.BiPredicate; -import java.util.stream.CloseableStream; +import java.util.stream.Stream; import java.util.stream.Collectors; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; @@ -138,14 +138,14 @@ } public void testBasic() { - try (CloseableStream s = Files.list(testFolder)) { + try (Stream s = Files.list(testFolder)) { Object[] actual = s.sorted(Comparator.naturalOrder()).toArray(); assertEquals(actual, level1); } catch (IOException ioe) { fail("Unexpected IOException"); } - try (CloseableStream s = Files.list(testFolder.resolve("empty"))) { + try (Stream s = Files.list(testFolder.resolve("empty"))) { int count = s.mapToInt(p -> 1).reduce(0, Integer::sum); assertEquals(count, 0, "Expect empty stream."); } catch (IOException ioe) { @@ -154,7 +154,7 @@ } public void testWalk() { - try (CloseableStream s = Files.walk(testFolder)) { + try (Stream s = Files.walk(testFolder)) { Object[] actual = s.sorted(Comparator.naturalOrder()).toArray(); assertEquals(actual, all); } catch (IOException ioe) { @@ -163,7 +163,7 @@ } public void testWalkOneLevel() { - try (CloseableStream s = Files.walk(testFolder, 1)) { + try (Stream s = Files.walk(testFolder, 1)) { Object[] actual = s.filter(path -> ! path.equals(testFolder)) .sorted(Comparator.naturalOrder()) .toArray(); @@ -176,7 +176,7 @@ public void testWalkFollowLink() { // If link is not supported, the directory structure won't have link. // We still want to test the behavior with FOLLOW_LINKS option. - try (CloseableStream s = Files.walk(testFolder, FileVisitOption.FOLLOW_LINKS)) { + try (Stream s = Files.walk(testFolder, FileVisitOption.FOLLOW_LINKS)) { Object[] actual = s.sorted(Comparator.naturalOrder()).toArray(); assertEquals(actual, all_folowLinks); } catch (IOException ioe) { @@ -185,7 +185,7 @@ } private void validateFileSystemLoopException(Path start, Path... causes) { - try (CloseableStream s = Files.walk(start, FileVisitOption.FOLLOW_LINKS)) { + try (Stream s = Files.walk(start, FileVisitOption.FOLLOW_LINKS)) { try { int count = s.mapToInt(p -> 1).reduce(0, Integer::sum); fail("Should got FileSystemLoopException, but got " + count + "elements."); @@ -282,28 +282,28 @@ public void testFind() throws IOException { PathBiPredicate pred = new PathBiPredicate((path, attrs) -> true); - try (CloseableStream s = Files.find(testFolder, Integer.MAX_VALUE, pred)) { + try (Stream s = Files.find(testFolder, Integer.MAX_VALUE, pred)) { Set result = s.collect(Collectors.toCollection(TreeSet::new)); assertEquals(pred.visited(), all); assertEquals(result.toArray(new Path[0]), pred.visited()); } pred = new PathBiPredicate((path, attrs) -> attrs.isSymbolicLink()); - try (CloseableStream s = Files.find(testFolder, Integer.MAX_VALUE, pred)) { + try (Stream s = Files.find(testFolder, Integer.MAX_VALUE, pred)) { s.forEach(path -> assertTrue(Files.isSymbolicLink(path))); assertEquals(pred.visited(), all); } pred = new PathBiPredicate((path, attrs) -> path.getFileName().toString().startsWith("e")); - try (CloseableStream s = Files.find(testFolder, Integer.MAX_VALUE, pred)) { + try (Stream s = Files.find(testFolder, Integer.MAX_VALUE, pred)) { s.forEach(path -> assertEquals(path.getFileName().toString(), "empty")); assertEquals(pred.visited(), all); } pred = new PathBiPredicate((path, attrs) -> path.getFileName().toString().startsWith("l") && attrs.isRegularFile()); - try (CloseableStream s = Files.find(testFolder, Integer.MAX_VALUE, pred)) { + try (Stream s = Files.find(testFolder, Integer.MAX_VALUE, pred)) { s.forEach(path -> fail("Expect empty stream")); assertEquals(pred.visited(), all); } @@ -317,14 +317,14 @@ try { // zero lines assertTrue(Files.size(tmpfile) == 0, "File should be empty"); - try (CloseableStream s = Files.lines(tmpfile, US_ASCII)) { + try (Stream s = Files.lines(tmpfile, US_ASCII)) { assertEquals(s.mapToInt(l -> 1).reduce(0, Integer::sum), 0, "No line expected"); } // one line byte[] hi = { (byte)'h', (byte)'i' }; Files.write(tmpfile, hi); - try (CloseableStream s = Files.lines(tmpfile, US_ASCII)) { + try (Stream s = Files.lines(tmpfile, US_ASCII)) { List lines = s.collect(Collectors.toList()); assertTrue(lines.size() == 1, "One line expected"); assertTrue(lines.get(0).equals("hi"), "'Hi' expected"); @@ -334,7 +334,7 @@ List expected = Arrays.asList("hi", "there"); Files.write(tmpfile, expected, US_ASCII); assertTrue(Files.size(tmpfile) > 0, "File is empty"); - try (CloseableStream s = Files.lines(tmpfile, US_ASCII)) { + try (Stream s = Files.lines(tmpfile, US_ASCII)) { List lines = s.collect(Collectors.toList()); assertTrue(lines.equals(expected), "Unexpected lines"); } @@ -342,7 +342,7 @@ // MalformedInputException byte[] bad = { (byte)0xff, (byte)0xff }; Files.write(tmpfile, bad); - try (CloseableStream s = Files.lines(tmpfile, US_ASCII)) { + try (Stream s = Files.lines(tmpfile, US_ASCII)) { try { List lines = s.collect(Collectors.toList()); throw new RuntimeException("UncheckedIOException expected"); @@ -378,7 +378,7 @@ fsp.setFaultyMode(false); Path fakeRoot = fs.getRoot(); try { - try (CloseableStream s = Files.list(fakeRoot)) { + try (Stream s = Files.list(fakeRoot)) { s.forEach(path -> assertEquals(path.getFileName().toString(), "DirectoryIteratorException")); } } catch (UncheckedIOException uioe) { @@ -398,7 +398,7 @@ } try { - try (CloseableStream s = Files.list(fakeRoot)) { + try (Stream s = Files.list(fakeRoot)) { s.forEach(path -> fail("should not get here")); } } catch (UncheckedIOException uioe) { @@ -427,12 +427,12 @@ try { fsp.setFaultyMode(false); Path fakeRoot = fs.getRoot(); - try (CloseableStream s = Files.list(fakeRoot.resolve("dir2"))) { + try (Stream s = Files.list(fakeRoot.resolve("dir2"))) { // only one file s.forEach(path -> assertEquals(path.getFileName().toString(), "IOException")); } - try (CloseableStream s = Files.walk(fakeRoot.resolve("empty"))) { + try (Stream s = Files.walk(fakeRoot.resolve("empty"))) { String[] result = s.map(path -> path.getFileName().toString()) .toArray(String[]::new); // ordered as depth-first @@ -440,13 +440,13 @@ } fsp.setFaultyMode(true); - try (CloseableStream s = Files.list(fakeRoot.resolve("dir2"))) { + try (Stream s = Files.list(fakeRoot.resolve("dir2"))) { s.forEach(path -> fail("should have caused exception")); } catch (UncheckedIOException uioe) { assertTrue(uioe.getCause() instanceof FaultyFileSystem.FaultyException); } - try (CloseableStream s = Files.walk(fakeRoot.resolve("empty"))) { + try (Stream s = Files.walk(fakeRoot.resolve("empty"))) { String[] result = s.map(path -> path.getFileName().toString()) .toArray(String[]::new); fail("should not reach here due to IOException"); @@ -454,7 +454,7 @@ assertTrue(uioe.getCause() instanceof FaultyFileSystem.FaultyException); } - try (CloseableStream s = Files.walk( + try (Stream s = Files.walk( fakeRoot.resolve("empty").resolve("IOException"))) { String[] result = s.map(path -> path.getFileName().toString()) @@ -502,20 +502,20 @@ fsp.setFaultyMode(false); Path fakeRoot = fs.getRoot(); // validate setting - try (CloseableStream s = Files.list(fakeRoot.resolve("empty"))) { + try (Stream s = Files.list(fakeRoot.resolve("empty"))) { String[] result = s.map(path -> path.getFileName().toString()) .toArray(String[]::new); assertEqualsNoOrder(result, new String[] { "SecurityException", "sample" }); } - try (CloseableStream s = Files.walk(fakeRoot.resolve("dir2"))) { + try (Stream s = Files.walk(fakeRoot.resolve("dir2"))) { String[] result = s.map(path -> path.getFileName().toString()) .toArray(String[]::new); assertEqualsNoOrder(result, new String[] { "dir2", "SecurityException", "fileInSE", "file" }); } if (supportsLinks) { - try (CloseableStream s = Files.list(fakeRoot.resolve("dir"))) { + try (Stream s = Files.list(fakeRoot.resolve("dir"))) { String[] result = s.map(path -> path.getFileName().toString()) .toArray(String[]::new); assertEqualsNoOrder(result, new String[] { "d1", "f1", "lnDir2", "SecurityException", "lnDirSE", "lnFileSE" }); @@ -525,13 +525,13 @@ // execute test fsp.setFaultyMode(true); // ignore file cause SecurityException - try (CloseableStream s = Files.walk(fakeRoot.resolve("empty"))) { + try (Stream s = Files.walk(fakeRoot.resolve("empty"))) { String[] result = s.map(path -> path.getFileName().toString()) .toArray(String[]::new); assertEqualsNoOrder(result, new String[] { "empty", "sample" }); } // skip folder cause SecurityException - try (CloseableStream s = Files.walk(fakeRoot.resolve("dir2"))) { + try (Stream s = Files.walk(fakeRoot.resolve("dir2"))) { String[] result = s.map(path -> path.getFileName().toString()) .toArray(String[]::new); assertEqualsNoOrder(result, new String[] { "dir2", "file" }); @@ -539,14 +539,14 @@ if (supportsLinks) { // not following links - try (CloseableStream s = Files.walk(fakeRoot.resolve("dir"))) { + try (Stream s = Files.walk(fakeRoot.resolve("dir"))) { String[] result = s.map(path -> path.getFileName().toString()) .toArray(String[]::new); assertEqualsNoOrder(result, new String[] { "dir", "d1", "f1", "lnDir2", "lnDirSE", "lnFileSE" }); } // following links - try (CloseableStream s = Files.walk(fakeRoot.resolve("dir"), FileVisitOption.FOLLOW_LINKS)) { + try (Stream s = Files.walk(fakeRoot.resolve("dir"), FileVisitOption.FOLLOW_LINKS)) { String[] result = s.map(path -> path.getFileName().toString()) .toArray(String[]::new); // ?? Should fileInSE show up? @@ -556,19 +556,19 @@ } // list instead of walk - try (CloseableStream s = Files.list(fakeRoot.resolve("empty"))) { + try (Stream s = Files.list(fakeRoot.resolve("empty"))) { String[] result = s.map(path -> path.getFileName().toString()) .toArray(String[]::new); assertEqualsNoOrder(result, new String[] { "sample" }); } - try (CloseableStream s = Files.list(fakeRoot.resolve("dir2"))) { + try (Stream s = Files.list(fakeRoot.resolve("dir2"))) { String[] result = s.map(path -> path.getFileName().toString()) .toArray(String[]::new); assertEqualsNoOrder(result, new String[] { "file" }); } // root cause SecurityException should be reported - try (CloseableStream s = Files.walk( + try (Stream s = Files.walk( fakeRoot.resolve("dir2").resolve("SecurityException"))) { String[] result = s.map(path -> path.getFileName().toString()) @@ -579,7 +579,7 @@ } // Walk a file cause SecurityException, we should get SE - try (CloseableStream s = Files.walk( + try (Stream s = Files.walk( fakeRoot.resolve("dir").resolve("SecurityException"))) { String[] result = s.map(path -> path.getFileName().toString()) @@ -590,7 +590,7 @@ } // List a file cause SecurityException, we should get SE as cannot read attribute - try (CloseableStream s = Files.list( + try (Stream s = Files.list( fakeRoot.resolve("dir2").resolve("SecurityException"))) { String[] result = s.map(path -> path.getFileName().toString()) @@ -600,7 +600,7 @@ assertTrue(se.getCause() instanceof FaultyFileSystem.FaultyException); } - try (CloseableStream s = Files.list( + try (Stream s = Files.list( fakeRoot.resolve("dir").resolve("SecurityException"))) { String[] result = s.map(path -> path.getFileName().toString()) @@ -627,7 +627,7 @@ } public void testConstructException() { - try (CloseableStream s = Files.lines(testFolder.resolve("notExist"), Charset.forName("UTF-8"))) { + try (Stream s = Files.lines(testFolder.resolve("notExist"), Charset.forName("UTF-8"))) { s.forEach(l -> fail("File is not even exist!")); } catch (IOException ioe) { assertTrue(ioe instanceof NoSuchFileException); @@ -635,13 +635,15 @@ } public void testClosedStream() throws IOException { - try (CloseableStream s = Files.list(testFolder)) { + try (Stream s = Files.list(testFolder)) { s.close(); Object[] actual = s.sorted(Comparator.naturalOrder()).toArray(); - assertTrue(actual.length <= level1.length); + fail("Operate on closed stream should throw IllegalStateException"); + } catch (IllegalStateException ex) { + // expected } - try (CloseableStream s = Files.walk(testFolder)) { + try (Stream s = Files.walk(testFolder)) { s.close(); Object[] actual = s.sorted(Comparator.naturalOrder()).toArray(); fail("Operate on closed stream should throw IllegalStateException"); @@ -649,7 +651,7 @@ // expected } - try (CloseableStream s = Files.find(testFolder, Integer.MAX_VALUE, + try (Stream s = Files.find(testFolder, Integer.MAX_VALUE, (p, attr) -> true)) { s.close(); Object[] actual = s.sorted(Comparator.naturalOrder()).toArray(); --- old/test/java/util/Spliterator/SpliteratorCollisions.java 2013-07-10 14:11:52.259135466 -0700 +++ new/test/java/util/Spliterator/SpliteratorCollisions.java 2013-07-10 14:11:52.071135470 -0700 @@ -148,7 +148,6 @@ List data = new ArrayList<>(); for (int size : SIZES) { List exp = listIntRange(size, true); - exp.add(0, null); SpliteratorDataBuilder db = new SpliteratorDataBuilder<>(data, exp); // Maps --- old/test/java/util/stream/bootlib/java/util/stream/DoubleStreamTestScenario.java 2013-07-10 14:11:53.119135451 -0700 +++ new/test/java/util/stream/bootlib/java/util/stream/DoubleStreamTestScenario.java 2013-07-10 14:11:52.931135454 -0700 @@ -40,7 +40,7 @@ @SuppressWarnings({"rawtypes", "unchecked"}) public enum DoubleStreamTestScenario implements OpTestCase.BaseStreamTestScenario { - STREAM_FOR_EACH(false) { + STREAM_FOR_EACH_WITH_CLOSE(false) { > void _run(TestData data, DoubleConsumer b, Function m) { DoubleStream s = m.apply(data.stream()); @@ -48,6 +48,7 @@ s = s.sequential(); } s.forEach(b); + s.close(); } }, --- old/test/java/util/stream/bootlib/java/util/stream/IntStreamTestScenario.java 2013-07-10 14:11:53.951135436 -0700 +++ new/test/java/util/stream/bootlib/java/util/stream/IntStreamTestScenario.java 2013-07-10 14:11:53.759135440 -0700 @@ -40,7 +40,7 @@ @SuppressWarnings({"rawtypes", "unchecked"}) public enum IntStreamTestScenario implements OpTestCase.BaseStreamTestScenario { - STREAM_FOR_EACH(false) { + STREAM_FOR_EACH_WITH_CLOSE(false) { > void _run(TestData data, IntConsumer b, Function m) { IntStream s = m.apply(data.stream()); @@ -48,6 +48,7 @@ s = s.sequential(); } s.forEach(b); + s.close(); } }, --- old/test/java/util/stream/bootlib/java/util/stream/LongStreamTestScenario.java 2013-07-10 14:11:54.815135421 -0700 +++ new/test/java/util/stream/bootlib/java/util/stream/LongStreamTestScenario.java 2013-07-10 14:11:54.623135424 -0700 @@ -40,7 +40,7 @@ @SuppressWarnings({"rawtypes", "unchecked"}) public enum LongStreamTestScenario implements OpTestCase.BaseStreamTestScenario { - STREAM_FOR_EACH(false) { + STREAM_FOR_EACH_WITH_CLOSE(false) { > void _run(TestData data, LongConsumer b, Function m) { LongStream s = m.apply(data.stream()); @@ -48,6 +48,7 @@ s = s.sequential(); } s.forEach(b); + s.close(); } }, --- old/test/java/util/stream/bootlib/java/util/stream/StreamTestScenario.java 2013-07-10 14:11:55.999135400 -0700 +++ new/test/java/util/stream/bootlib/java/util/stream/StreamTestScenario.java 2013-07-10 14:11:55.803135403 -0700 @@ -39,7 +39,7 @@ @SuppressWarnings({"rawtypes", "unchecked"}) public enum StreamTestScenario implements OpTestCase.BaseStreamTestScenario { - STREAM_FOR_EACH(false) { + STREAM_FOR_EACH_WITH_CLOSE(false) { > void _run(TestData data, Consumer b, Function> m) { Stream s = m.apply(data.stream()); @@ -47,6 +47,7 @@ s = s.sequential(); } s.forEach(b); + s.close(); } }, --- /dev/null 2013-07-08 08:32:16.866272774 -0700 +++ new/src/share/classes/java/util/MayHoldCloseableResource.java 2013-07-10 14:11:56.667135388 -0700 @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package java.util; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * An object that may (but need not) hold one or more references to + * resources that will be released when closed. Such objects may be + * used with try-with-resources or related {@code try...finally} + * constructions that ensure they are closed as soon as they are no + * longer needed. Interface {@code MayHoldCloseableResource} indicates that + * only a minority of usages warrant resource control constructions: + * those specialized to known resource-bearing instances, or those + * that must operate in complete generality. + * + *

For example, most usages of the {@link java.util.stream.Stream} + * classes operate on data sources such as an array, {@code + * Collection}, or generator function that do not require or benefit + * from explicit resource control. However, some uses of IO channels + * as data sources do. + * + *

Annotation {@link HoldsResource} may be used to guide users or static + * analysis tools to deciding whether resource-control constructions are + * warranted when using particular instantiations of + * {@code MayHoldCloseableResource}. + * + * @see AutoCloseable + * @see HoldsResource + */ +public interface MayHoldCloseableResource extends AutoCloseable { + /** + * Closes this resource, relinquishing any underlying resources. + * This method is invoked automatically on objects managed by the + * {@code try}-with-resources statement. + * + * Implementers of this interface are strongly encouraged + * to make their {@code close} methods idempotent. + * + * @see AutoCloseable#close() + */ + @Override + void close(); + + /** + * Indicates that a variable holding a {@code MayHoldCloseableResource} or + * a method returning a {@code MayHoldCloseableResource} definitely does + * hold a closeable resource. + */ + @Retention(RetentionPolicy.CLASS) + @Documented + @interface HoldsResource { } +} --- /dev/null 2013-07-08 08:32:16.866272774 -0700 +++ new/test/java/nio/Files/FilesLambdaTest.java 2013-07-10 14:11:57.475135373 -0700 @@ -0,0 +1,336 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @summary function test for Files.list()/find()/walk()/lines() + * @library /sqeutil + * @(#) FilesLambdaTest.java + * @author Tristan Yan + * @run testng/othervm FilesLambdaTest + */ + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.NotDirectoryException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Random; +import java.util.function.Consumer; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import static org.testng.Assert.*; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +public class FilesLambdaTest { + private static final Random rand = new Random(System.nanoTime()); + + private static final String TEST_SRC = System.getProperty("test.src"); + + private static final String ROOT_NAME = "FilesLambdaTest" + System.nanoTime(); + + private static final String LINES_TEST_FILE = "lines" + System.nanoTime(); + + private static final int MAX_FILES_NUMBER = 1 << 6; + + private static final Charset UTF8 = Charset.forName("UTF-8"); + + private final static int MIN_LEN = 1 << 2; + + private final static int MAX_LEN = 1 << 8; + + private final static int LINES_NUM = 1 << 8; + + private static final String[][] folders = { + {"A01"}, + {"A01", "AA02"}, + {"A01", "AB02"}, + {"A01", "AC02"}, + {"B01"}, + {"B01", "BA02"}, + {"B01", "BA02", "BAA03"}, + {"B01", "BA02", "BAA03", "BAAA04", "BAAAA05", "BAAAAA06", "BAAAAAA07", + "BAAAAAAA08", "BAAAAAAAA09", "BAAAAAAAAA10", "BAAAAAAAAAA11"}, + {"C01"}, + {"C01", "CA02"}, + {"C01", "CD02"}, + {"D01", "DA02", "DAA03", "DAAA04", "DAAAA05", "DAAAAA06", "DAAAAAA07"}, + {"E01"}, + {"E01", "EA02"}, + {"E01", "EB02", "EBB03"}, + {"E01", "EC02", "ECC03", "ECCC04"}, + {"E01", "ED02", "EDD03", "EDDD04", "EDDDD05"}, + {"E01", "EE02", "EEE03", "EEEE04", "EEEEE05", "EEEEEE06"}, + {"E01", "EF02", "EFF03", "EFFF04", "EFFFF05", "EFFFFF06"}, + {"E01", "EG02", "EGG03", "EGGG04", "EGGGG05", "EGGGGG06", "EGGGGGG07"}, + {"E01", "EH02", "EHH03", "EHHH04", "EHHHH05", "EHHHHH06", "EHHHHHH07", + "EHHHHHHH08"}, + {"E01", "EI02", "EII03", "EIII04", "EIIII05", "EIIIII06", "EIIIIII07", + "EIIIIIII08", "EIIIIIIII09"}, + {"E01", "EJ02", "EJJ03", "EJJJ04", "EJJJJ05", "EJJJJJ06", "EJJJJJJ07", + "EJJJJJJJ08", "EJJJJJJJJ09", "EJJJJJJJJJ10"}, + {"E01", "EK02", "EKK03", "EKKK04", "EKKKK05", "EKKKKK06", "EKKKKKK07", + "EKKKKKKK08", "EKKKKKKKK09", "EKKKKKKKKK10", "EJJJJJJJJJJ11"} + }; + + private Path root; + + private Path testFile; + + private Path notExistPath; + + final Consumer writeReverseWithLink = path -> { + int filesCount = rand.nextInt(MAX_FILES_NUMBER); + for(int count = 0; count < filesCount; count++) { + String fileName = String.valueOf(rand.nextLong()); + Path file = path.resolve(fileName); + String linkName = String.valueOf(rand.nextLong()); + Path link = path.resolve(linkName); + try (BufferedWriter writer + = Files.newBufferedWriter(Files.createFile(file), UTF8)) { + writer.write(new StringBuilder(fileName).reverse().toString(), + 0, fileName.length()); + Files.createSymbolicLink(link, file); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + }; + + @BeforeClass + public void filesSetUp() throws IOException { + List paths = new ArrayList<>(); + root = Paths.get(TEST_SRC, ROOT_NAME); + paths.add(Files.createDirectory(root)); + for(int i = 0; i < folders.length; i++ ) { + String[] toBeCreated = folders[i]; + Path folder = Paths.get(ROOT_NAME, toBeCreated); + paths.add(Files.createDirectories(folder)); + } + paths.forEach(writeReverseWithLink); + + testFile = Paths.get(LINES_TEST_FILE); + try (BufferedWriter writer + = Files.newBufferedWriter(Files.createFile(testFile), UTF8)) { + for(int i = 0; i< LINES_NUM; i++) { + String line = StringUtilities.randomString(MAX_LEN, MIN_LEN); + writer.write(line, 0, line.length()); + } + } catch (IOException ex) { + throw new RuntimeException(ex); + } + + String NOT_EXIST = "NOT_EXIST"; + notExistPath = Paths.get(TEST_SRC, NOT_EXIST); + } + + @AfterClass + public void filesTearDown() throws IOException { + if(root != null) { + Files.walkFileTree(root, new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) + throws IOException { + Files.delete(file); + return FileVisitResult.CONTINUE; + } + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException exc) + throws IOException{ + Files.delete(dir); + return FileVisitResult.CONTINUE; + } + }); + } + if(testFile != null) { + Files.delete(testFile); + } + } + + @Test + public void testFilesList() throws IOException { + checkFilesList(root); + } + + @Test + public void testWalk() throws IOException{ + String[] dir = folders[rand.nextInt(folders.length)]; + Path walkFolder = Paths.get(ROOT_NAME, dir); + final int maxDepth = rand.nextInt(11); + List expectedFullFileList = new ArrayList<>(); + List expectedMaxDepthFileList = new ArrayList<>(); + + List expectedFullSymList = new ArrayList<>(); + List expectedMaxDepthSymList = new ArrayList<>(); + + List expectedFullDirList = new ArrayList<>(); + List expectedMaxDepthDirList = new ArrayList<>(); + + Files.walkFileTree(walkFolder, new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) + throws IOException { + if (walkFolder.relativize(file).getNameCount() <= maxDepth) { + expectedMaxDepthFileList.add(file); + if(Files.isSymbolicLink(file)) { + expectedMaxDepthSymList.add(file); + } + } + expectedFullFileList.add(file); + if(Files.isSymbolicLink(file)) { + expectedFullSymList.add(file); + } + return FileVisitResult.CONTINUE; + } + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException exc) + throws IOException{ + if (walkFolder.relativize(dir).getNameCount() <= maxDepth + || walkFolder.equals(dir)) { + expectedMaxDepthDirList.add(dir); + } + expectedFullDirList.add(dir); + return FileVisitResult.CONTINUE; + } + }); + Collections.sort(expectedFullFileList); + Collections.sort(expectedMaxDepthFileList); + Collections.sort(expectedFullSymList); + Collections.sort(expectedMaxDepthSymList); + Collections.sort(expectedFullDirList); + Collections.sort(expectedMaxDepthDirList); + + assertEquals(expectedMaxDepthFileList, + Files.walk(walkFolder, maxDepth).filter(p -> !Files.isDirectory(p)). + sorted().collect(Collectors.toList())); + assertEquals(expectedMaxDepthSymList, + Files.walk(walkFolder, maxDepth).filter(p -> Files.isSymbolicLink(p)). + sorted().collect(Collectors.toList())); + assertEquals(expectedMaxDepthDirList, + Files.walk(walkFolder, maxDepth).filter(p -> Files.isDirectory(p)). + sorted().collect(Collectors.toList())); + assertEquals(expectedFullFileList, + Files.walk(walkFolder).filter(p -> !Files.isDirectory(p)). + sorted().collect(Collectors.toList())); + assertEquals(expectedFullSymList, + Files.walk(walkFolder).filter(p -> Files.isSymbolicLink(p)). + sorted().collect(Collectors.toList())); + assertEquals(expectedFullDirList, + Files.walk(walkFolder).filter(p -> Files.isDirectory(p)). + sorted().collect(Collectors.toList())); + } + + @Test + public void testFind() throws IOException{ + String[] dir = folders[rand.nextInt(folders.length)]; + Path walkFolder = Paths.get(ROOT_NAME, dir); + String walkFoderName = dir[dir.length - 1]; + + int walkFolderDepth = Integer.parseInt(walkFoderName.substring(walkFoderName.length() - 2)); + int maxDepth = rand.nextInt(11); + Stream stream + = Files.find(walkFolder, maxDepth, (p,bfa) -> Files.isSymbolicLink(p)); + stream.forEach(p -> { + assertTrue(Files.isSymbolicLink(p)); + assertFalse(Files.isDirectory(p)); + assertFalse(Files.isRegularFile(p, LinkOption.NOFOLLOW_LINKS)); + String parentName = p.getParent().toFile().getName(); + int depth = Integer.parseInt(parentName.substring(parentName.length() - 2)); + assertTrue(depth - walkFolderDepth < maxDepth); + }); + + assertEquals(Files.find(walkFolder, maxDepth, (p,bfa) -> Files.isSymbolicLink(p)).toArray(), + Files.find(walkFolder, maxDepth, (p,bfa) -> bfa.isSymbolicLink()).toArray()); + } + + @Test + public void testLines() throws IOException{ + Stream stream = Files.lines(testFile, UTF8); + List lines = Files.readAllLines(testFile, UTF8); + assertEquals(lines, stream.collect(Collectors.toList())); + } + + @Test(expectedExceptions = IOException.class) + public void testListNoReadAccess() throws IOException{ + Files.list(notExistPath); + } + + @Test(expectedExceptions = NotDirectoryException.class) + public void testListNotDirectory() throws IOException{ + Files.list(testFile); + } + + @Test(expectedExceptions = IOException.class) + public void testFindNoExist() throws IOException{ + Files.find(notExistPath, Integer.MAX_VALUE, (p, bfa) -> true); + } + + @Test(expectedExceptions = IOException.class) + public void testLinesNoExist() throws IOException{ + Files.lines(notExistPath, UTF8); + } + + @Test(expectedExceptions = IOException.class) + public void testWalkNoExist() throws IOException{ + Files.walk(notExistPath); + } + + @Test(expectedExceptions = IOException.class) + public void testWalkNoExistWithDepth() throws IOException{ + Files.walk(notExistPath, Integer.MAX_VALUE); + } + + public void checkFilesList(Path checkPath){ + try { + assert(Files.isDirectory(checkPath)); + Files.list(checkPath).filter(p -> Files.isDirectory(p)).forEach(p -> checkFilesList(p)); + assertEquals(Files.list(checkPath).filter(p -> Files.isRegularFile(p, LinkOption.NOFOLLOW_LINKS)).count(), + Files.list(checkPath).filter(p -> Files.isSymbolicLink(p)).count()); + assertTrue(Files.list(checkPath).filter(p -> Files.isRegularFile(p, LinkOption.NOFOLLOW_LINKS)).allMatch(file -> contentRevered(file))); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + private boolean contentRevered(Path path) { + try (BufferedReader reader = Files.newBufferedReader(path, UTF8)){ + String fileName = path.getName(path.getNameCount() -1).toString(); + String reversed = new StringBuilder(reader.readLine()).reverse().toString(); + return (fileName.equals(reversed)); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } +} \ No newline at end of file --- /dev/null 2013-07-08 08:32:16.866272774 -0700 +++ new/test/java/util/stream/test/org/openjdk/tests/java/util/stream/StreamCloseTest.java 2013-07-10 14:11:58.295135359 -0700 @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.tests.java.util.stream; + +import java.util.Arrays; +import java.util.stream.OpTestCase; +import java.util.stream.Stream; + +import static java.util.stream.LambdaTestHelpers.countTo; + +/** + * StreamCloseTest + * + * @author Brian Goetz + */ +public class StreamCloseTest extends OpTestCase { + public void testEmptyCloseHandler() { + try (Stream ints = countTo(100).stream()) { + ints.forEach(i -> {}); + } + } + + public void testOneCloseHandler() { + final boolean[] holder = new boolean[1]; + Runnable closer = () -> { holder[0] = true; }; + + try (Stream ints = countTo(100).stream()) { + ints.onClose(closer); + ints.forEach(i -> {}); + } + assertTrue(holder[0]); + + Arrays.fill(holder, false); + try (Stream ints = countTo(100).stream().onClose(closer)) { + ints.forEach(i -> {}); + } + assertTrue(holder[0]); + + Arrays.fill(holder, false); + try (Stream ints = countTo(100).stream().filter(e -> true).onClose(closer)) { + ints.forEach(i -> {}); + } + assertTrue(holder[0]); + + Arrays.fill(holder, false); + try (Stream ints = countTo(100).stream().filter(e -> true).onClose(closer).filter(e -> true)) { + ints.forEach(i -> {}); + } + assertTrue(holder[0]); + } + + public void testTwoCloseHandlers() { + final boolean[] holder = new boolean[2]; + Runnable close1 = () -> { holder[0] = true; }; + Runnable close2 = () -> { holder[1] = true; }; + + try (Stream ints = countTo(100).stream()) { + ints.onClose(close1).onClose(close2); + ints.forEach(i -> {}); + } + assertTrue(holder[0] && holder[1]); + + Arrays.fill(holder, false); + try (Stream ints = countTo(100).stream().onClose(close1).onClose(close2)) { + ints.forEach(i -> {}); + } + assertTrue(holder[0] && holder[1]); + + Arrays.fill(holder, false); + try (Stream ints = countTo(100).stream().filter(e -> true).onClose(close1).onClose(close2)) { + ints.forEach(i -> {}); + } + assertTrue(holder[0] && holder[1]); + + Arrays.fill(holder, false); + try (Stream ints = countTo(100).stream().filter(e -> true).onClose(close1).onClose(close2).filter(e -> true)) { + ints.forEach(i -> {}); + } + assertTrue(holder[0] && holder[1]); + } + + public void testCascadedExceptions() { + final boolean[] holder = new boolean[3]; + boolean caught = false; + Runnable close1 = () -> { holder[0] = true; throw new RuntimeException("1"); }; + Runnable close2 = () -> { holder[1] = true; throw new RuntimeException("2"); }; + Runnable close3 = () -> { holder[2] = true; throw new RuntimeException("3"); }; + + try (Stream ints = countTo(100).stream()) { + ints.onClose(close1).onClose(close2).onClose(close3); + ints.forEach(i -> {}); + } + catch (RuntimeException e) { + assertCascaded(e, 3); + assertTrue(holder[0] && holder[1] && holder[2]); + caught = true; + } + assertTrue(caught); + + Arrays.fill(holder, false); + caught = false; + try (Stream ints = countTo(100).stream().onClose(close1).onClose(close2).onClose(close3)) { + ints.forEach(i -> {}); + } + catch (RuntimeException e) { + assertCascaded(e, 3); + assertTrue(holder[0] && holder[1] && holder[2]); + caught = true; + } + assertTrue(caught); + + caught = false; + Arrays.fill(holder, false); + try (Stream ints = countTo(100).stream().filter(e -> true).onClose(close1).onClose(close2).onClose(close3)) { + ints.forEach(i -> {}); + } + catch (RuntimeException e) { + assertCascaded(e, 3); + assertTrue(holder[0] && holder[1] && holder[2]); + caught = true; + } + assertTrue(caught); + + caught = false; + Arrays.fill(holder, false); + try (Stream ints = countTo(100).stream().filter(e -> true).onClose(close1).onClose(close2).filter(e -> true).onClose(close3)) { + ints.forEach(i -> {}); + } + catch (RuntimeException e) { + assertCascaded(e, 3); + assertTrue(holder[0] && holder[1] && holder[2]); + caught = true; + } + assertTrue(caught); + } + + private void assertCascaded(RuntimeException e, int n) { + assertTrue(e.getMessage().equals("1")); + assertTrue(e.getSuppressed().length == n - 1); + for (int i=0; i i >= limit; + } else { + return i -> i < limit; + } + } + + public static Predicate randomIntegerPredicate(boolean isUP, int limit) { + if (isUP) { + return i -> i >= limit; + } else { + return i -> i < limit; + } + } + + public static LongPredicate randomLongPredicate(boolean isUP, long limit) { + if (isUP) { + return i -> i >= limit; + } else { + return i -> i < limit; + } + } + + public static Predicate startPathPredicate(Path start) { + return p -> p.startsWith(start); + } + + public static Predicate randomGenericPredicate(boolean isUp, T value, Comparator c) { + if (isUp) { + return emp -> c.compare(emp, value) >= 0; + }else { + return emp -> c.compare(emp, value) < 0; + } + } + + public static Predicate randomSBPredicate(StringPredicateType type, String value) { + switch (type) { + case START_WTIH: + return sb -> Character.isDigit(sb.charAt(0)); + case NOT_START_WITH: + return sb -> Character.isLowerCase(sb.charAt(0)); + case MORE_THAN_LEN: + return sb -> Character.isUpperCase(sb.charAt(0)); + default: + return sb -> !Character.isLetterOrDigit(sb.charAt(0)); + } + } + + public static Predicate randomSBPredicate(CharType startType, + boolean first) { + switch (startType) { + case DIGIT: + return sb -> Character.isDigit(sb.charAt(first ? 0 : sb.toString().length() - 1)); + case LOWERCASE: + return sb -> Character.isLowerCase(sb.charAt(first ? 0 : sb.toString().length() - 1)); + case UPPERCASE: + return sb -> Character.isUpperCase(sb.charAt(first ? 0 : sb.toString().length() - 1)); + default: + return sb -> !Character.isLetterOrDigit(sb.charAt(first ? 0 : sb.toString().length() - 1)); + } + } + + public static Predicate isDigitCharacterPredicate() { + return c -> Character.isDigit(c); + } + + public static Function posIntegerFunction(boolean isHighest) { + if (isHighest) { + return i -> Integer.valueOf(new StringBuilder().append(i < 0 ? -i : i).reverse().toString()) % 10; + } else { + return i -> i % 10 < 0 ? -i % 10 : i % 10; + } + } + + public static Function sbGenericFunction(boolean isFirst) { + if (isFirst) + return i -> Character.isAlphabetic(i.charAt(0)) ? (Character.isUpperCase(i.charAt(0)) ? CharType.UPPERCASE : CharType.LOWERCASE) : (Character.isDigit(i.charAt(0)) ? CharType.DIGIT : CharType.SPECIAL); + else + return i -> Character.isAlphabetic(i.charAt(i.length() - 1)) ? (Character.isUpperCase(i.charAt(i.length() - 1)) ? CharType.UPPERCASE : CharType.LOWERCASE) : (Character.isDigit(i.charAt(i.length() - 1)) ? CharType.DIGIT : CharType.SPECIAL); + } + + public static Function mappingFunction(Map m, IntOp op, int value) { + switch (op) { + case ADD: + return k -> (value != 0) ? m.get(k) + value : m.get(k); + case SUBTRACT: + return k -> (value != 0) ? m.get(k) - value : m.get(k); + case MULTIPLY: + return k -> (value != 0) ? m.get(k) * value : m.get(k); + case DIVIDE: + return k -> (value != 0) ? m.get(k) / value : m.get(k); + default: + return k -> (value != 0) ? m.get(k) % value : m.get(k); + } + } + + public static IntFunction posIntFunction(boolean isHighest) { + if (isHighest) { + return i -> Integer.valueOf(new StringBuilder().append(i < 0 ? -i : i).reverse().toString()) % 10; + } else { + return i -> i % 10 < 0 ? -i % 10 : i % 10; + } + } + + public static BiFunction randBetweenIntegerFunction() { + return (t1, t2) -> randBetween(t1, t2); + } + + public static int randBetween(int low, int up) { + assert (low < up && low >= 0); + Random rand = new Random(); + int i = rand.nextInt(up); + while (i < low) { + i = rand.nextInt(); + } + return i; + } + + public static ToIntFunction highestPosValueIntFunction() { + return i -> Integer.valueOf(new StringBuilder().append(i < 0 ? -i : i).reverse().toString()) % 10; + } + + public static ToIntFunction lowestPosValueIntFunction() { + return i -> i % 10 < 0 ? -i % 10 : i % 10; + } + + public static Consumer reverseConsumer(Set set) { + return t -> { + set.add(t); + }; + } + + public static Consumer addIntegerConsumer(AtomicInteger ai) { + return t -> { + ai.updateAndGet(t1 -> t1 + t); + }; + } + + public static Consumer appendSBConsumer(StringBuilder sb) { + return t -> { + sb.append(t); + }; + } + + public static IntConsumer addIntConsumer(AtomicInteger ai) { + return t -> { + ai.updateAndGet(t1 -> t1 + t); + }; + } + + public static IntConsumer addLongConsumer(AtomicLong ai) { + return t -> { + ai.updateAndGet(t1 -> t1 + t); + }; + } + + public static Consumer copyConsumer(List list) { + return t -> { + list.add(t); + }; + } + + public static Consumer existsConsumer(Collection in, Collection out) { + return t -> { + if (in.contains(t)) { + out.add(t); + } + }; + } + + public static Supplier sbSupplier(StringBuilder value) { + return () -> value; + } + + public static Supplier integerSupplier(int value) { + return () -> value; + } + + public static Supplier genericSuppiler(T value) { + return () -> value; + } + + public static IntSupplier intSupplier(int value) { + return () -> value; + } + + public static Supplier longSupplier(long value) { + return () -> value; + } + + public static Supplier atomicIntegerSupplier(int value) { + return () -> new AtomicInteger(value); + } + + public static Supplier> atomicGenericSupplier(T value) { + return () -> new AtomicReference<>(value); + } + + public static Supplier> atomicSBSupplier(StringBuilder value) { + return () -> new AtomicReference<>(value); + } + + public static UnaryOperator opIntegerUnaryOperator(IntOp op, int value) { + switch (op) { + case ADD: + return t -> t + value; + case SUBTRACT: + return t -> t - value; + case MULTIPLY: + return t -> t * value; + case DIVIDE: + return t -> t / value; + default: + return t -> t % value; + } + } + + public static IntUnaryOperator opIntUnaryOperator(IntOp op, int value) { + if(value == 0) + return t -> t; + switch (op) { + case ADD: + return t -> t + value; + case SUBTRACT: + return t -> t - value; + case MULTIPLY: + return t -> t * value; + case DIVIDE: + return t -> t / value; + default: + return t -> t % value; + } + } + + public static Function opIntegerFunction(IntOp op, int value) { + if(value == 0) + return t -> t; + switch (op) { + case ADD: + return t -> Integer.valueOf(t + value); + case SUBTRACT: + return t -> Integer.valueOf(t - value); + case MULTIPLY: + return t -> Integer.valueOf(t * value); + case DIVIDE: + return t -> Integer.valueOf(t / value); + default: + return t -> Integer.valueOf(t % value); + } + } + + public static ToIntFunction opToIntFunction(IntOp op, int value) { + if(value == 0) + return t -> t.intValue(); + switch (op) { + case ADD: + return t -> t.intValue() + value; + case SUBTRACT: + return t -> t.intValue() - value; + case MULTIPLY: + return t -> t.intValue() * value; + case DIVIDE: + return t -> t.intValue() / value; + default: + return t -> t.intValue() % value; + } + } + + public static IntFunction opIntFunction(IntOp op, int value) { + if(value == 0) + return t -> t; + switch (op) { + case ADD: + return t -> t + value; + case SUBTRACT: + return t -> t - value; + case MULTIPLY: + return t -> t * value; + case DIVIDE: + return t -> t / value; + default: + return t -> t % value; + } + } + + public static IntUnaryOperator addIntUnaryOperator(int value) { + return t -> t + value; + } + + public static IntUnaryOperator subIntUnaryOperator(int value) { + return t -> t - value; + } + + public static IntUnaryOperator mulIntUnaryOperator(int value) { + return t -> t * value; + } + + public static IntUnaryOperator divIntUnaryOperator(int value) { + return t -> t / value; + } + + public static IntBinaryOperator minIntBinaryOperator() { + return (t1, t2) -> t1< t2 ? t1 : t2; + } + + public static BinaryOperator minIntegerBinaryOperator(Comparator c) { + return (t1, t2) -> c.compare(t1, t2) < 0 ? t1 : t2; + } + + public static BinaryOperator minGenericBinaryOperator(Comparator c) { + return (t1, t2) -> c.compare(t1, t2) < 0 ? t1 : t2; + } + + public static BinaryOperator minSBBinaryOperator(Comparator c) { + return ( t1, t2 ) -> c.compare(t1, t2) < 0 ? t1 : t2; + } + + public static IntBinaryOperator maxIntBinaryOperator() { + return ( t1, t2 ) -> (t1< t2) ? t2: t1; + } + + public static BinaryOperator maxIntegerBinaryOperator(Comparator c) { + return (t1, t2) -> c.compare(t1, t2) < 0 ? t2 : t1; + } + + public static BinaryOperator maxGenericBinaryOperator(Comparator c) { + return (t1, t2) -> c.compare(t1, t2) < 0 ? t2 : t1; + } + + public static IntBinaryOperator maxIntBinaryOperator(Comparator c) { + return ( t1, t2 ) -> c.compare(t1, t2) < 0 ? t2 : t1; + } + + public static BinaryOperator maxSBBinaryOperator(Comparator c) { + return ( t1, t2 ) -> c.compare(t1, t2) < 0 ? t2 : t1; + } + + public static BinaryOperator addIntegerBinaryOperator() { + return ( t1 , t2 ) -> t1 + t2; + } + + public static IntBinaryOperator addIntBinaryOperator() { + return ( t1 , t2 ) -> t1 + t2; + } + + public static BinaryOperator addBigDecimalBinaryOperator() { + return ( t1 , t2 ) -> t1.add(t2); + } + + public static DoubleBinaryOperator addDoubleBinaryOperator() { + return ( t1 , t2 ) -> t1 + t2; + } + + public static BinaryOperator appendSBBinaryOperator() { + return (t1 , t2) -> new StringBuilder().append(t1).append(t2); + } + + public static BinaryOperator subIntegerBinaryOperator() { + return (t1, t2) -> t1 - t2; + } + + public static IntBinaryOperator subIntBinaryOperator() { + return (t1, t2) -> t1 - t2; + } + + public static BinaryOperator deleteSBBinaryOperator() { + return (t1, t2) -> {if (t1.length() >= t2.length()) { + int i1 = t1.indexOf(t2.toString()); + int i2 = i1 + t2.length(); + return new StringBuilder(t1).delete(i1, i2); + }else { + int i1 = t2.indexOf(t1.toString()); + int i2 = i1 + t1.length(); + return new StringBuilder(t2).delete(i1, i2); + } + }; + + } + + public static IntBinaryOperator mulIntBinaryOperator() { + return (t1, t2) -> t1 * t2; + } + + public static IntBinaryOperator divIntBinaryOperator() { + return (t1, t2) -> t1 / t2; + } + + public static LongUnaryOperator addLongUnaryOperator(long value) { + return t -> t + value; + } + + public static UnaryOperator appendSBUnaryOperator(StringBuilder value) { + return t -> t.append(value); + } + + public static LongUnaryOperator subLongUnaryOperator(long value) { + return t -> t - value; + } + + public static LongUnaryOperator mulLongUnaryOperator(long value) { + return t -> t * value; + } + + public static LongUnaryOperator divLongUnaryOperator(long value) { + return t -> t / value; + } + + public static LongBinaryOperator addLongBinaryOperator() { + return (t1, t2) -> t1 + t2; + } + + public static LongBinaryOperator subLongBinaryOperator() { + return (t1, t2) -> t1 - t2; + } + + public static LongBinaryOperator mulLongBinaryOperator() { + return (t1, t2) -> t1 * t2; + } + + public static LongBinaryOperator divLongBinaryOperator() { + return (t1, t2) -> t1 / t2; + } + + public static BiConsumer addIntegerBiConsumer() { + return (t1 , t2 ) -> { t1.addAndGet(t2); }; + } + + public static BiConsumer addAtomicIntegerBiConsumer() { + return (t1 , t2) -> { t1.addAndGet(t2.get()); }; + } + + public static BiConsumer, StringBuilder> appendSBBiConsumer() { + return (t1, t2) -> {t1.updateAndGet(appendSBUnaryOperator(t2));}; + } + + public static BiConsumer, AtomicReference> appendAtomicSBBiConsumer() { + return (t1, t2) -> {t1.updateAndGet(appendSBUnaryOperator(t2.get()));}; + } + + public static BiConsumer maxIntegerBiConsumer(Comparator c) { + return (t1 , t2 ) -> { t1.getAndUpdate(t -> max(t, t2, c)); }; + } + + public static BiConsumer, T> maxGenericBiConsumer(Comparator c) { + return (t1 , t2 ) -> { t1.getAndUpdate(t -> max(t, t2, c)); }; + } + + public static BiConsumer maxAtomicIntegerBiConsumer(Comparator c) { + return (t1 , t2) -> { t1.getAndUpdate(t -> max(t, t2.get(), c)); }; + } + + public static BiConsumer, AtomicReference> maxAtomicGenericBiConsumer(Comparator c) { + return (t1 , t2) -> { t1.getAndUpdate(t -> max(t, t2.get(), c)); }; + } + + public static BiConsumer minIntegerBiConsumer(Comparator c) { + return (t1 , t2) -> { t1.getAndUpdate(t -> min(t, t2, c)); }; + } + + public static BiConsumer, T> minGenericBiConsumer(Comparator c) { + return (t1 , t2) -> { t1.getAndUpdate(t -> min(t, t2, c)); }; + } + + public static BiConsumer minAtomicIntegerBiConsumer(Comparator c) { + return (t1, t2) -> { t1.getAndUpdate(t -> min(t, t2.get(), c)); }; + } + + public static BiConsumer, AtomicReference> minAtomicGenericBiConsumer(Comparator c) { + return (t1, t2) -> { t1.getAndUpdate(t -> min(t, t2.get(), c)); }; + } + + public static BiFunction maxIntegerFunction(Comparator c) { + return (t1, t2) -> max(t1, t2, c); + } + + public static BiFunction deviationSequareFunction(double avg) { + return (bd, t) -> bd.add(new BigDecimal(avg - t).pow(2)); + } + + public static BiFunction maxGenericFunction(Comparator c) { + return (t1, t2) -> max(t1, t2, c); + } + + public static BiFunction maxStringBuilderFunction(Comparator c) { + return (t1, t2) -> max(t1, t2, c); + } + + public static BiFunction minIntegerFunction(Comparator c) { + return (t1, t2) -> min(t1, t2, c); + } + + public static BiFunction minGenericFunction(Comparator c) { + return (t1, t2) -> min(t1, t2, c); + } + + public static BiFunction minStringBuilderFunction(Comparator c) { + return (t1, t2) -> min(t1, t2, c); + } + + public static BiFunction opBiFunction(IntOp op, int value) { + switch (op) { + case ADD: + return (k, v) -> (value != 0) ? v + value : v; + case SUBTRACT: + return (k, v) -> (value != 0) ? v - value : v; + case MULTIPLY: + return (k, v) -> (value != 0) ? v * value : v; + case DIVIDE: + return (k, v) -> (value != 0) ? v / value : v; + default: + return (k, v) -> (value != 0) ? v % value : v; + } + } + + + public static BiFunction opBiFunction(IntOp op) { + switch (op) { + case ADD: + return (oldv, v) -> (v != 0) ? oldv + v : oldv; + case SUBTRACT: + return (oldv, v) -> (v != 0) ? oldv - v : oldv; + case MULTIPLY: + return (oldv, v) -> (v != 0) ? oldv * v : oldv; + case DIVIDE: + return (oldv, v) -> (v != 0) ? oldv / v : oldv; + default: + return (oldv, v) -> (v != 0) ? oldv % v : oldv; + } + } + + private static Integer min(Integer i1, Integer i2, Comparator c) { + return c.compare(i1, i2) < 0 ? i1 : i2; + } + + private static T min(T i1, T i2, Comparator c) { + return c.compare(i1, i2) < 0 ? i1 : i2; + } + + private static StringBuilder min(StringBuilder sb1, StringBuilder sb2, Comparator c) { + return c.compare(sb1, sb2) < 0 ? sb1 : sb2; + } + + private static Integer max(Integer i1, Integer i2, Comparator c) { + return c.compare(i1, i2) < 0 ? i2 : i1; + } + + private static T max(T i1, T i2, Comparator c) { + return c.compare(i1, i2) < 0 ? i2 : i1; + } + + private static StringBuilder max(StringBuilder sb1, StringBuilder sb2, Comparator c) { + return c.compare(sb1, sb2) < 0 ? sb2 : sb1; + } + /* + * Construct a Collection C object based on a C object, using generic type + * instead of Class type can help preventing type error in compilation + */ + @SuppressWarnings({"rawtypes", "unchecked"}) + public static > C create(C c, int... initSize) + throws InstantiationException, IllegalAccessException, + NoSuchMethodException, IllegalArgumentException, + InvocationTargetException { + return create((Class) c.getClass(), initSize); + } + + /* + * Construct a Collection C object based on a C's type, using generic type + * instead of Class type can help preventing type error in compilation + */ + @SuppressWarnings({"rawtypes", "unchecked"}) + public static > T create( + Class> cls, int... initSize) + throws InstantiationException, IllegalAccessException, + NoSuchMethodException, IllegalArgumentException, + InvocationTargetException { + assert (initSize.length <= 1); + Collection c; + if (initSize.length == 0) { + c = cls.newInstance(); + } else { + Constructor con = cls.getConstructor(int.class); + c = (Collection) con.newInstance(initSize[0]); + } + return (T) c; + } + + /* + * Construct a T object based on T's type, using generic type instead of + * Class type can help preventing type error in compilation + */ + @SuppressWarnings({"rawtypes", "unchecked"}) + public static > M createMap(M m, int... initSize) + throws InstantiationException, IllegalAccessException, + NoSuchMethodException, IllegalArgumentException, + InvocationTargetException { + return createMap((Class) m.getClass(), initSize); + } + + /* + * Construct a Map M object based on M's type, using generic type instead of + * Class type can help preventing type error in compilation + */ + @SuppressWarnings({"rawtypes", "unchecked"}) + public static > M createMap(Class> cls, + int... initSize) throws InstantiationException, + IllegalAccessException, NoSuchMethodException, + IllegalArgumentException, InvocationTargetException { + assert (initSize.length <= 1); + Map map; + if (initSize.length == 0) { + map = cls.newInstance(); + } else { + Constructor con = cls.getConstructor(int.class); + map = (Map) con.newInstance(initSize[0]); + } + return (M) map; + } +} --- /dev/null 2013-07-08 08:32:16.866272774 -0700 +++ new/test/sqeutil/StringUtilities.java 2013-07-10 14:11:59.907135330 -0700 @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * + * @summary utilities class supporting common operation for tests. + * @(#)StringUtilities.java + * @author Tristan Yan + * @version 1.0 + */ + +import java.util.Random; + +public class StringUtilities { + private final static Random RANDOM = new Random(System.currentTimeMillis()); + + public static String randomString(int max_length, int min_length){ + return randomAscii(min_length + RANDOM.nextInt(max_length - min_length)); + } + + public static String random(int count, int start, int end, boolean letters, + boolean numbers, char[] chars, Random rnd) { + if (count == 0) { + return ""; + } else if (count < 0) { + throw new IllegalArgumentException("Requested random string length " + count + " is less than 0."); + } + if ((start == 0) && (end == 0)) { + end = 'z' + 1; + start = ' '; + if (!letters && !numbers) { + start = 0; + end = Integer.MAX_VALUE; + } + } + + char[] buffer = new char[count]; + int gap = end - start; + + while (count-- != 0) { + char ch; + if (chars == null) { + ch = (char) (rnd.nextInt(gap) + start); + } else { + ch = chars[rnd.nextInt(gap) + start]; + } + if ((letters && Character.isLetter(ch)) + || (numbers && Character.isDigit(ch)) + || (!letters && !numbers)) + { + if(ch >= 56320 && ch <= 57343) { + if(count == 0) { + count++; + } else { + // low surrogate, insert high surrogate after putting it in + buffer[count] = ch; + count--; + buffer[count] = (char) (55296 + rnd.nextInt(128)); + } + } else if(ch >= 55296 && ch <= 56191) { + if(count == 0) { + count++; + } else { + // high surrogate, insert low surrogate before putting it in + buffer[count] = (char) (56320 + rnd.nextInt(128)); + count--; + buffer[count] = ch; + } + } else if(ch >= 56192 && ch <= 56319) { + // private high surrogate, no effing clue, so skip it + count++; + } else { + buffer[count] = ch; + } + } else { + count++; + } + } + return new String(buffer); + } + public static String random(int count) { + return random(count, false, false); + } + + public static String randomAscii(int count) { + return random(count, 32, 127, false, false); + } + + public static String randomAlphabetic(int count) { + return random(count, true, false); + } + + public static String randomAlphanumeric(int count) { + return random(count, true, true); + } + + public static String randomNumeric(int count) { + return random(count, false, true); + } + + public static String random(int count, boolean letters, boolean numbers) { + return random(count, 0, 0, letters, numbers); + } + + public static String random(int count, int start, int end, boolean letters, boolean numbers) { + return random(count, start, end, letters, numbers, null, RANDOM); + } + + public static String random(int count, int start, int end, boolean letters, boolean numbers, char[] chars) { + return random(count, start, end, letters, numbers, chars, RANDOM); + } +} --- old/src/share/classes/java/util/stream/CloseableStream.java 2013-07-10 14:12:00.891135313 -0700 +++ /dev/null 2013-07-08 08:32:16.866272774 -0700 @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package java.util.stream; - -/** - * A {@code CloseableStream} is a {@code Stream} that can be closed. - * The close method is invoked to release resources that the object is - * holding (such as open files). - * - * @param The type of stream elements - * @since 1.8 - */ -public interface CloseableStream extends Stream, AutoCloseable { - - /** - * Closes this resource, relinquishing any underlying resources. - * This method is invoked automatically on objects managed by the - * {@code try}-with-resources statement. Does nothing if called when - * the resource has already been closed. - * - * This method does not allow throwing checked {@code Exception}s like - * {@link AutoCloseable#close() AutoCloseable.close()}. Cases where the - * close operation may fail require careful attention by implementers. It - * is strongly advised to relinquish the underlying resources and to - * internally mark the resource as closed. The {@code close} - * method is unlikely to be invoked more than once and so this ensures - * that the resources are released in a timely manner. Furthermore it - * reduces problems that could arise when the resource wraps, or is - * wrapped, by another resource. - * - * @see AutoCloseable#close() - */ - void close(); -} --- old/src/share/classes/java/util/stream/DelegatingStream.java 2013-07-10 14:12:01.407135304 -0700 +++ /dev/null 2013-07-08 08:32:16.866272774 -0700 @@ -1,270 +0,0 @@ -/* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package java.util.stream; - -import java.util.Comparator; -import java.util.Iterator; -import java.util.Objects; -import java.util.Optional; -import java.util.Spliterator; -import java.util.function.BiConsumer; -import java.util.function.BiFunction; -import java.util.function.BinaryOperator; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.IntFunction; -import java.util.function.Predicate; -import java.util.function.Supplier; -import java.util.function.ToDoubleFunction; -import java.util.function.ToIntFunction; -import java.util.function.ToLongFunction; - -/** - * A {@code Stream} implementation that delegates operations to another {@code - * Stream}. - * - * @param type of stream elements for this stream and underlying delegate - * stream - * - * @since 1.8 - */ -public class DelegatingStream implements Stream { - final private Stream delegate; - - /** - * Construct a {@code Stream} that delegates operations to another {@code - * Stream}. - * - * @param delegate the underlying {@link Stream} to which we delegate all - * {@code Stream} methods - * @throws NullPointerException if the delegate is null - */ - public DelegatingStream(Stream delegate) { - this.delegate = Objects.requireNonNull(delegate); - } - - // -- BaseStream methods -- - - @Override - public Spliterator spliterator() { - return delegate.spliterator(); - } - - @Override - public boolean isParallel() { - return delegate.isParallel(); - } - - @Override - public Iterator iterator() { - return delegate.iterator(); - } - - // -- Stream methods -- - - @Override - public Stream filter(Predicate predicate) { - return delegate.filter(predicate); - } - - @Override - public Stream map(Function mapper) { - return delegate.map(mapper); - } - - @Override - public IntStream mapToInt(ToIntFunction mapper) { - return delegate.mapToInt(mapper); - } - - @Override - public LongStream mapToLong(ToLongFunction mapper) { - return delegate.mapToLong(mapper); - } - - @Override - public DoubleStream mapToDouble(ToDoubleFunction mapper) { - return delegate.mapToDouble(mapper); - } - - @Override - public Stream flatMap(Function> mapper) { - return delegate.flatMap(mapper); - } - - @Override - public IntStream flatMapToInt(Function mapper) { - return delegate.flatMapToInt(mapper); - } - - @Override - public LongStream flatMapToLong(Function mapper) { - return delegate.flatMapToLong(mapper); - } - - @Override - public DoubleStream flatMapToDouble(Function mapper) { - return delegate.flatMapToDouble(mapper); - } - - @Override - public Stream distinct() { - return delegate.distinct(); - } - - @Override - public Stream sorted() { - return delegate.sorted(); - } - - @Override - public Stream sorted(Comparator comparator) { - return delegate.sorted(comparator); - } - - @Override - public void forEach(Consumer action) { - delegate.forEach(action); - } - - @Override - public void forEachOrdered(Consumer action) { - delegate.forEachOrdered(action); - } - - @Override - public Stream peek(Consumer consumer) { - return delegate.peek(consumer); - } - - @Override - public Stream limit(long maxSize) { - return delegate.limit(maxSize); - } - - @Override - public Stream substream(long startingOffset) { - return delegate.substream(startingOffset); - } - - @Override - public Stream substream(long startingOffset, long endingOffset) { - return delegate.substream(startingOffset, endingOffset); - } - - @Override - public A[] toArray(IntFunction generator) { - return delegate.toArray(generator); - } - - @Override - public Object[] toArray() { - return delegate.toArray(); - } - - @Override - public T reduce(T identity, BinaryOperator accumulator) { - return delegate.reduce(identity, accumulator); - } - - @Override - public Optional reduce(BinaryOperator accumulator) { - return delegate.reduce(accumulator); - } - - @Override - public U reduce(U identity, BiFunction accumulator, - BinaryOperator combiner) { - return delegate.reduce(identity, accumulator, combiner); - } - - @Override - public R collect(Supplier resultFactory, - BiConsumer accumulator, - BiConsumer combiner) { - return delegate.collect(resultFactory, accumulator, combiner); - } - - @Override - public R collect(Collector collector) { - return delegate.collect(collector); - } - - @Override - public Optional max(Comparator comparator) { - return delegate.max(comparator); - } - - @Override - public Optional min(Comparator comparator) { - return delegate.min(comparator); - } - - @Override - public long count() { - return delegate.count(); - } - - @Override - public boolean anyMatch(Predicate predicate) { - return delegate.anyMatch(predicate); - } - - @Override - public boolean allMatch(Predicate predicate) { - return delegate.allMatch(predicate); - } - - @Override - public boolean noneMatch(Predicate predicate) { - return delegate.noneMatch(predicate); - } - - @Override - public Optional findFirst() { - return delegate.findFirst(); - } - - @Override - public Optional findAny() { - return delegate.findAny(); - } - - @Override - public Stream unordered() { - return delegate.unordered(); - } - - @Override - public Stream sequential() { - return delegate.sequential(); - } - - @Override - public Stream parallel() { - return delegate.parallel(); - } -}