From a30b2fdf38b1cbb243bdbb9c3cdc4c7894b4505a Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Tue, 16 Jul 2019 14:12:35 +0200 Subject: [PATCH] Integrate 2.13.x branch into master Use version-specific source directories where necessary. --- .travis.yml | 4 +- README.md | 2 +- build.sbt | 26 +- fnGen/WrapFnGen.scala | 4 +- project/CodeGen.scala | 4 +- .../compat/java8/ScalaStreamSupport.java | 384 ++++++++++++ .../compat/java8/ScalaStreamSupport.java | 0 .../java8/runtime/CollectionInternals.java | 0 .../java8/runtime/LambdaDeserializer.scala | 5 +- .../scala/compat/java8/StreamConverters.scala | 506 ++++++++++++++++ .../compat/java8/collectionImpl/package.scala | 30 + .../java8/converterImpl/Accumulates.scala | 47 ++ .../converterImpl/AccumulatorConverters.scala | 32 + .../converterImpl/StepperExtensions.scala | 77 +++ .../java8/FuturesConvertersImplCompat.scala | 8 + .../compat/java8/SpliteratorConverters.scala | 0 .../scala/compat/java8/StreamConverters.scala | 0 .../java8/collectionImpl/Accumulator.scala | 0 .../collectionImpl/AccumulatorLike.scala | 0 .../collectionImpl/DoubleAccumulator.scala | 0 .../java8/collectionImpl/IntAccumulator.scala | 0 .../collectionImpl/LongAccumulator.scala | 0 .../compat/java8/collectionImpl/Stepper.scala | 0 .../java8/converterImpl/Accumulates.scala | 0 .../converterImpl/AccumulatorConverters.scala | 0 .../java8/converterImpl/MakesSteppers.scala | 0 .../java8/converterImpl/StepConverters.scala | 0 .../java8/converterImpl/StepsArray.scala | 0 .../java8/converterImpl/StepsBitSet.scala | 0 .../converterImpl/StepsFlatHashTable.scala | 0 .../java8/converterImpl/StepsHashTable.scala | 0 .../java8/converterImpl/StepsImmHashMap.scala | 0 .../java8/converterImpl/StepsImmHashSet.scala | 0 .../java8/converterImpl/StepsIndexedSeq.scala | 0 .../java8/converterImpl/StepsIterable.scala | 0 .../java8/converterImpl/StepsIterator.scala | 0 .../java8/converterImpl/StepsLikeGapped.scala | 0 .../converterImpl/StepsLikeImmHashMap.scala | 0 .../converterImpl/StepsLikeIndexed.scala | 0 .../converterImpl/StepsLikeIterator.scala | 0 .../java8/converterImpl/StepsLikeSliced.scala | 0 .../converterImpl/StepsLikeTrieIterator.scala | 0 .../java8/converterImpl/StepsLinearSeq.scala | 0 .../compat/java8/converterImpl/StepsMap.scala | 0 .../java8/converterImpl/StepsRange.scala | 0 .../java8/converterImpl/StepsString.scala | 0 .../java8/converterImpl/StepsVector.scala | 0 .../java8/converterImpl/StepsWithTail.scala | 0 .../java8/FuturesConvertersImplCompat.scala | 8 + .../scala/compat/java8/FutureConverters.scala | 1 + .../java8/PrimitiveIteratorConversions.scala | 12 +- .../java8/FutureConvertersImpl.scala | 15 +- .../java8/runtime/LambdaDeserializerTest.java | 0 .../java/scala/compat/java8/BoxingTest.java | 6 +- .../compat/java8/StepConvertersTest.scala | 565 ++++++++++++++++++ .../scala/compat/java8/StepperTest.scala | 319 ++++++++++ .../compat/java8/StreamConvertersTest.scala | 303 ++++++++++ .../compat/java8/StepConvertersTest.scala | 0 .../scala/compat/java8/StepperTest.scala | 0 .../compat/java8/StreamConvertersTest.scala | 0 60 files changed, 2329 insertions(+), 29 deletions(-) create mode 100644 src/main/java-2.13+/scala/compat/java8/ScalaStreamSupport.java rename src/main/{java => java-2.13-}/scala/compat/java8/ScalaStreamSupport.java (100%) rename src/main/{java => java-2.13-}/scala/compat/java8/runtime/CollectionInternals.java (100%) rename src/main/{java => scala-2.11}/scala/compat/java8/runtime/LambdaDeserializer.scala (97%) create mode 100644 src/main/scala-2.13+/scala/compat/java8/StreamConverters.scala create mode 100644 src/main/scala-2.13+/scala/compat/java8/collectionImpl/package.scala create mode 100644 src/main/scala-2.13+/scala/compat/java8/converterImpl/Accumulates.scala create mode 100644 src/main/scala-2.13+/scala/compat/java8/converterImpl/AccumulatorConverters.scala create mode 100644 src/main/scala-2.13+/scala/compat/java8/converterImpl/StepperExtensions.scala create mode 100644 src/main/scala-2.13+/scala/concurrent/java8/FuturesConvertersImplCompat.scala rename src/main/{scala => scala-2.13-}/scala/compat/java8/SpliteratorConverters.scala (100%) rename src/main/{scala => scala-2.13-}/scala/compat/java8/StreamConverters.scala (100%) rename src/main/{scala => scala-2.13-}/scala/compat/java8/collectionImpl/Accumulator.scala (100%) rename src/main/{scala => scala-2.13-}/scala/compat/java8/collectionImpl/AccumulatorLike.scala (100%) rename src/main/{scala => scala-2.13-}/scala/compat/java8/collectionImpl/DoubleAccumulator.scala (100%) rename src/main/{scala => scala-2.13-}/scala/compat/java8/collectionImpl/IntAccumulator.scala (100%) rename src/main/{scala => scala-2.13-}/scala/compat/java8/collectionImpl/LongAccumulator.scala (100%) rename src/main/{scala => scala-2.13-}/scala/compat/java8/collectionImpl/Stepper.scala (100%) rename src/main/{scala => scala-2.13-}/scala/compat/java8/converterImpl/Accumulates.scala (100%) rename src/main/{scala => scala-2.13-}/scala/compat/java8/converterImpl/AccumulatorConverters.scala (100%) rename src/main/{scala => scala-2.13-}/scala/compat/java8/converterImpl/MakesSteppers.scala (100%) rename src/main/{scala => scala-2.13-}/scala/compat/java8/converterImpl/StepConverters.scala (100%) rename src/main/{scala => scala-2.13-}/scala/compat/java8/converterImpl/StepsArray.scala (100%) rename src/main/{scala => scala-2.13-}/scala/compat/java8/converterImpl/StepsBitSet.scala (100%) rename src/main/{scala => scala-2.13-}/scala/compat/java8/converterImpl/StepsFlatHashTable.scala (100%) rename src/main/{scala => scala-2.13-}/scala/compat/java8/converterImpl/StepsHashTable.scala (100%) rename src/main/{scala => scala-2.13-}/scala/compat/java8/converterImpl/StepsImmHashMap.scala (100%) rename src/main/{scala => scala-2.13-}/scala/compat/java8/converterImpl/StepsImmHashSet.scala (100%) rename src/main/{scala => scala-2.13-}/scala/compat/java8/converterImpl/StepsIndexedSeq.scala (100%) rename src/main/{scala => scala-2.13-}/scala/compat/java8/converterImpl/StepsIterable.scala (100%) rename src/main/{scala => scala-2.13-}/scala/compat/java8/converterImpl/StepsIterator.scala (100%) rename src/main/{scala => scala-2.13-}/scala/compat/java8/converterImpl/StepsLikeGapped.scala (100%) rename src/main/{scala => scala-2.13-}/scala/compat/java8/converterImpl/StepsLikeImmHashMap.scala (100%) rename src/main/{scala => scala-2.13-}/scala/compat/java8/converterImpl/StepsLikeIndexed.scala (100%) rename src/main/{scala => scala-2.13-}/scala/compat/java8/converterImpl/StepsLikeIterator.scala (100%) rename src/main/{scala => scala-2.13-}/scala/compat/java8/converterImpl/StepsLikeSliced.scala (100%) rename src/main/{scala => scala-2.13-}/scala/compat/java8/converterImpl/StepsLikeTrieIterator.scala (100%) rename src/main/{scala => scala-2.13-}/scala/compat/java8/converterImpl/StepsLinearSeq.scala (100%) rename src/main/{scala => scala-2.13-}/scala/compat/java8/converterImpl/StepsMap.scala (100%) rename src/main/{scala => scala-2.13-}/scala/compat/java8/converterImpl/StepsRange.scala (100%) rename src/main/{scala => scala-2.13-}/scala/compat/java8/converterImpl/StepsString.scala (100%) rename src/main/{scala => scala-2.13-}/scala/compat/java8/converterImpl/StepsVector.scala (100%) rename src/main/{scala => scala-2.13-}/scala/compat/java8/converterImpl/StepsWithTail.scala (100%) create mode 100644 src/main/scala-2.13-/scala/concurrent/java8/FuturesConvertersImplCompat.scala rename src/test/{java => java-2.11}/scala/compat/java8/runtime/LambdaDeserializerTest.java (100%) create mode 100644 src/test/scala-2.13+/scala/compat/java8/StepConvertersTest.scala create mode 100644 src/test/scala-2.13+/scala/compat/java8/StepperTest.scala create mode 100644 src/test/scala-2.13+/scala/compat/java8/StreamConvertersTest.scala rename src/test/{scala => scala-2.13-}/scala/compat/java8/StepConvertersTest.scala (100%) rename src/test/{scala => scala-2.13-}/scala/compat/java8/StepperTest.scala (100%) rename src/test/{scala => scala-2.13-}/scala/compat/java8/StreamConvertersTest.scala (100%) diff --git a/.travis.yml b/.travis.yml index 591b290..a0e622d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,9 @@ language: scala scala: - # note that 2.13 is on the 2.13.x branch instead - - 2.11.12 + - 2.13.0 - 2.12.8 + - 2.11.12 env: global: diff --git a/README.md b/README.md index 5d0800c..f5bb5b5 100644 --- a/README.md +++ b/README.md @@ -161,7 +161,7 @@ as the collection can (in some cases) be built in parallel. Because the wrappers are invoked based on the static type of the collection, there are also cases where parallelization is inefficient when interfacing with Java 8 Streams (e.g. when a collection is typed as `Seq[String]` so might have linear -access like `List`, but actually is a `WrappedArray[String]` that can be efficiently parallelized) but can be efficient +access like `List`, but actually is a `WrappedArray[String]` (`ArraySeq` on 2.13) that can be efficiently parallelized) but can be efficient with Scala parallel collections. The `parStream` method is only available when the static type is known to be compatible with rapid parallel operation; `seqStream` can be parallelized by using `.parallel`, but may or may not be efficient. diff --git a/build.sbt b/build.sbt index 2817ffc..3a2b463 100644 --- a/build.sbt +++ b/build.sbt @@ -1,8 +1,6 @@ import ScalaModulePlugin._ -// no 2.13 for now in cross-build because of -// https://github.com/scala/scala-java8-compat/issues/97 -crossScalaVersions in ThisBuild := List("2.12.8", "2.11.12") +crossScalaVersions in ThisBuild := List("2.13.0", "2.12.8", "2.11.12") val disableDocs = sys.props("nodocs") == "true" || @@ -28,7 +26,27 @@ lazy val commonSettings = Seq( organization := "org.scala-lang.modules", version := "0.9.1-SNAPSHOT", - scalacOptions ++= Seq("-feature", "-deprecation", "-unchecked") + scalacOptions ++= Seq("-feature", "-deprecation", "-unchecked"), + + unmanagedSourceDirectories in Compile ++= { + (unmanagedSourceDirectories in Compile).value.flatMap { dir => + CrossVersion.partialVersion(scalaVersion.value) match { + case Some((2, 13)) => Seq(file(dir.getPath ++ "-2.13+")) + case Some((2, 11)) => Seq(file(dir.getPath ++ "-2.13-"), file(dir.getPath ++ "-2.11")) + case _ => Seq(file(dir.getPath ++ "-2.13-")) + } + } + }, + + unmanagedSourceDirectories in Test ++= { + (unmanagedSourceDirectories in Test).value.flatMap { dir => + CrossVersion.partialVersion(scalaVersion.value) match { + case Some((2, 13)) => Seq(file(dir.getPath ++ "-2.13+")) + case Some((2, 11)) => Seq(file(dir.getPath ++ "-2.13-"), file(dir.getPath ++ "-2.11")) + case _ => Seq(file(dir.getPath ++ "-2.13-")) + } + } + }, ) lazy val fnGen = (project in file("fnGen")). diff --git a/fnGen/WrapFnGen.scala b/fnGen/WrapFnGen.scala index 26acee7..c3e2e86 100644 --- a/fnGen/WrapFnGen.scala +++ b/fnGen/WrapFnGen.scala @@ -245,7 +245,7 @@ object WrapFnGen { numberedA ++= scalaTargs.map(_.toString).collect{ case An(digits) if (digits.length < 10) => digits.toInt } val scalafnTnames = (jfn.pTypes :+ jfn.rType).zipWithIndex.map{ case (pt, i) if (i < jfn.pTypes.length && pt.isFinalType) || (!pt.isFinalType && jfn.pTypes.take(i).exists(_ == pt)) => - val j = Iterator.from(i).dropWhile(numberedA).next + val j = Iterator.from(i).dropWhile(numberedA).next() val genericName = TypeName(s"A$j") numberedA += j evidences += ((genericName, pt.typeSymbol.name.toTypeName)) @@ -309,6 +309,6 @@ object WrapFnGen { def main(args: Array[String]): Unit = { val names = args.iterator.map(x => new java.io.File(x)) - write(names.next, converterContents) + write(names.next(), converterContents) } } diff --git a/project/CodeGen.scala b/project/CodeGen.scala index 4289973..b3f3f11 100644 --- a/project/CodeGen.scala +++ b/project/CodeGen.scala @@ -519,9 +519,9 @@ object CodeGen { val specialized = List("V", "V,IJFD", "V,IJD,IJD").flatMap(specialize).map { case (i, a, sp) => - s" public static scala.Function$i<$a> procSpecialized(JFunction$i$sp f) { return f; }" } ++ + s" public static scala.Function$i<$a> procSpecialized(JFunction$i$sp f) { return (scala.Function$i<$a>)(Object)f; }" } ++ List("BSIJCFDZ", "ZIFJD,IJFD", "ZIFJD,IJD,IJD").flatMap(specialize).map { case (i, a, sp) => - s" public static scala.Function$i<$a> funcSpecialized(JFunction$i$sp f) { return f; }" } + s" public static scala.Function$i<$a> funcSpecialized(JFunction$i$sp f) { return (scala.Function$i<$a>)(Object)f; }" } (blocks.map(_._1) ++ blocks.map(_._2)) :+ ( "JFunction", diff --git a/src/main/java-2.13+/scala/compat/java8/ScalaStreamSupport.java b/src/main/java-2.13+/scala/compat/java8/ScalaStreamSupport.java new file mode 100644 index 0000000..af78ecd --- /dev/null +++ b/src/main/java-2.13+/scala/compat/java8/ScalaStreamSupport.java @@ -0,0 +1,384 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.compat.java8; + +import java.util.stream.*; + +import scala.collection.*; +import scala.jdk.javaapi.StreamConverters; + +/** + * This class contains static utility methods for creating Java Streams from Scala Collections, similar + * to the methods in {@code java.util.stream.StreamSupport} for other Java types. It is intended for + * use from Java code. In Scala code, you can use the extension methods provided by + * {@code scala.compat.java8.StreamConverters} instead. + * + * Streams created from immutable Scala collections are also immutable. Mutable collections should + * not be modified concurrently. There are no guarantees for success or failure modes of existing + * streams in case of concurrent modifications. + */ +public class ScalaStreamSupport { + ///////////////////// + // Generic Streams // + ///////////////////// + + /** + * Generates a Stream that traverses a Scala collection. + *

+ * Parallel processing is only efficient for collections that have a Stepper implementation + * which supports efficient splitting. For collections where this is the case, the stepper + * method has a return type marked with EfficientSplit. + * + * @param coll The IterableOnce to traverse + * @return A Stream view of the collection which, by default, executes sequentially. + */ + public static Stream stream(IterableOnce coll) { + return StreamConverters.asJavaSeqStream(coll); + } + + /** + * Generates a Stream that traverses the keys of a scala.collection.Map. + *

+ * Parallel processing is only efficient for Maps that have a keyStepper implementation + * which supports efficient splitting. For collections where this is the case, the keyStepper + * method has a return type marked with EfficientSplit. + * + * @param coll The Map to traverse + * @return A Stream view of the collection which, by default, executes sequentially. + */ + public static Stream streamKeys(Map coll) { + return StreamSupport.stream(coll.keyStepper(StepperShape.anyStepperShape()).spliterator(), false); + } + + /** + * Generates a Stream that traverses the values of a scala.collection.Map. + *

+ * Parallel processing is only efficient for Maps that have a valueStepper implementation + * which supports efficient splitting. For collections where this is the case, the valueStepper + * method has a return type marked with EfficientSplit. + * + * @param coll The Map to traverse + * @return A Stream view of the collection which, by default, executes sequentially. + */ + public static Stream streamValues(Map coll) { + return StreamSupport.stream(coll.>valueStepper(StepperShape.anyStepperShape()).spliterator(), false); + } + + /** + * Generates a Stream that traverses the key-value pairs of a scala.collection.Map. + *

+ * Parallel processing is only efficient for collections that have a Stepper implementation + * which supports efficient splitting. For collections where this is the case, the stepper + * method has a return type marked with EfficientSplit. + * + * @param coll The Map to traverse + * @return A Stream view of the collection which, by default, executes sequentially. + */ + public static Stream< scala.Tuple2 > stream(Map coll) { + return StreamConverters.asJavaSeqStream(coll); + } + + /** + * Generates a Stream that traverses any Scala collection by accumulating its entries + * into a buffer class (Accumulator). + *

+ * Both sequential and parallel operations will be efficient. + * + * @param coll The collection to traverse + * @return A Stream view of the collection which, by default, executes sequentially. + */ + public static Stream streamAccumulated(IterableOnce coll) { + return StreamConverters.asJavaSeqStream(scala.jdk.AnyAccumulator.from(coll)); + } + + /** + * Generates a Stream that traverses the keys of any Scala map by + * accumulating those keys into a buffer class (Accumulator). + *

+ * Both sequential and parallel operations will be efficient. + * + * @param coll The map containing keys to traverse + * @return A Stream view of the collection which, by default, executes sequentially. + */ + public static Stream streamAccumulatedKeys(Map coll) { + return StreamConverters.asJavaSeqStream(scala.jdk.AnyAccumulator.from(coll.keysIterator())); + } + + /** + * Generates a Stream that traverses the values of any Scala map by + * accumulating those values into a buffer class (Accumulator). + *

+ * Both sequential and parallel operations will be efficient. + * + * @param coll The map containing values to traverse + * @return A Stream view of the collection which, by default, executes sequentially. + */ + public static Stream streamAccumulatedValues(Map coll) { + return StreamConverters.asJavaSeqStream(scala.jdk.AnyAccumulator.from(coll.valuesIterator())); + } + + //////////////////// + // Double Streams // + //////////////////// + + /** + * Generates a DoubleStream that traverses a Scala collection. + *

+ * Parallel processing is only efficient for collections that have a Stepper implementation + * which supports efficient splitting. For collections where this is the case, the stepper + * method has a return type marked with EfficientSplit. + * + * @param coll The IterableOnce to traverse + * @return A DoubleStream view of the collection which, by default, executes sequentially. + */ + public static DoubleStream doubleStream(IterableOnce coll) { + return StreamConverters.asJavaSeqDoubleStream(coll); + } + + /** + * Generates a DoubleStream that traverses the keys of a scala.collection.Map. + *

+ * Parallel processing is only efficient for Maps that have a keyStepper implementation + * which supports efficient splitting. For collections where this is the case, the keyStepper + * method has a return type marked with EfficientSplit. + * + * @param coll The Map to traverse + * @return A DoubleStream view of the collection which, by default, executes sequentially. + */ + public static DoubleStream doubleStreamKeys(Map coll) { + return StreamSupport.doubleStream(coll.keyStepper((StepperShape)(Object)StepperShape.doubleStepperShape()).spliterator(), false); + } + + /** + * Generates a DoubleStream that traverses the values of a scala.collection.Map. + *

+ * Parallel processing is only efficient for Maps that have a valueStepper implementation + * which supports efficient splitting. For collections where this is the case, the valueStepper + * method has a return type marked with EfficientSplit. + * + * @param coll The Map to traverse + * @return A DoubleStream view of the collection which, by default, executes sequentially. + */ + public static DoubleStream doubleStreamValues(Map coll) { + return StreamSupport.doubleStream(coll.valueStepper((StepperShape)(Object)StepperShape.doubleStepperShape()).spliterator(), false); + } + + /** + * Generates a DoubleStream that traverses any Scala collection by accumulating its entries + * into a buffer class (Accumulator). + *

+ * Both sequential and parallel operations will be efficient. + * + * @param coll The collection to traverse + * @return A DoubleStream view of the collection which, by default, executes sequentially. + */ + public static DoubleStream doubleStreamAccumulated(IterableOnce coll) { + return StreamConverters.asJavaSeqDoubleStream((IterableOnce)(Object)scala.jdk.DoubleAccumulator$.MODULE$.fromSpecific((IterableOnce)(Object)coll)); + } + + /** + * Generates a DoubleStream that traverses the keys of any Scala map by + * accumulating those keys into a buffer class (Accumulator). + *

+ * Both sequential and parallel operations will be efficient. + * + * @param coll The map containing keys to traverse + * @return A DoubleStream view of the collection which, by default, executes sequentially. + */ + public static DoubleStream doubleStreamAccumulatedKeys(Map coll) { + return StreamConverters.asJavaSeqDoubleStream((IterableOnce)(Object)scala.jdk.DoubleAccumulator$.MODULE$.fromSpecific((IterableOnce)(Object)coll.keysIterator())); + } + + /** + * Generates a DoubleStream that traverses the values of any Scala map by + * accumulating those values into a buffer class (Accumulator). + *

+ * Both sequential and parallel operations will be efficient. + * + * @param coll The map containing values to traverse + * @return A DoubleStream view of the collection which, by default, executes sequentially. + */ + public static DoubleStream doubleStreamAccumulatedValues(Map coll) { + return StreamConverters.asJavaSeqDoubleStream((IterableOnce)(Object)scala.jdk.DoubleAccumulator$.MODULE$.fromSpecific((IterableOnce)(Object)coll.valuesIterator())); + } + + ///////////////// + // Int Streams // + ///////////////// + + /** + * Generates a IntStream that traverses a Scala collection. + *

+ * Parallel processing is only efficient for collections that have a Stepper implementation + * which supports efficient splitting. For collections where this is the case, the stepper + * method has a return type marked with EfficientSplit. + * + * @param coll The IterableOnce to traverse + * @return A IntStream view of the collection which, by default, executes sequentially. + */ + public static IntStream intStream(IterableOnce coll) { + return StreamConverters.asJavaSeqIntStream(coll); + } + + /** + * Generates a IntStream that traverses the keys of a scala.collection.Map. + *

+ * Parallel processing is only efficient for Maps that have a keyStepper implementation + * which supports efficient splitting. For collections where this is the case, the keyStepper + * method has a return type marked with EfficientSplit. + * + * @param coll The Map to traverse + * @return A IntStream view of the collection which, by default, executes sequentially. + */ + public static IntStream intStreamKeys(Map coll) { + return StreamSupport.intStream(coll.keyStepper((StepperShape)(Object)StepperShape.intStepperShape()).spliterator(), false); + } + + /** + * Generates a IntStream that traverses the values of a scala.collection.Map. + *

+ * Parallel processing is only efficient for Maps that have a valueStepper implementation + * which supports efficient splitting. For collections where this is the case, the valueStepper + * method has a return type marked with EfficientSplit. + * + * @param coll The Map to traverse + * @return A IntStream view of the collection which, by default, executes sequentially. + */ + public static IntStream intStreamValues(Map coll) { + return StreamSupport.intStream(coll.valueStepper((StepperShape)(Object)StepperShape.intStepperShape()).spliterator(), false); + } + + /** + * Generates a IntStream that traverses any Scala collection by accumulating its entries + * into a buffer class (Accumulator). + *

+ * Both sequential and parallel operations will be efficient. + * + * @param coll The collection to traverse + * @return A IntStream view of the collection which, by default, executes sequentially. + */ + public static IntStream intStreamAccumulated(IterableOnce coll) { + return StreamConverters.asJavaSeqIntStream((IterableOnce)(Object)scala.jdk.IntAccumulator$.MODULE$.fromSpecific((IterableOnce)(Object)coll)); + } + + /** + * Generates a IntStream that traverses the keys of any Scala map by + * accumulating those keys into a buffer class (Accumulator). + *

+ * Both sequential and parallel operations will be efficient. + * + * @param coll The map containing keys to traverse + * @return A IntStream view of the collection which, by default, executes sequentially. + */ + public static IntStream intStreamAccumulatedKeys(Map coll) { + return StreamConverters.asJavaSeqIntStream((IterableOnce)(Object)scala.jdk.IntAccumulator$.MODULE$.fromSpecific((IterableOnce)(Object)coll.keysIterator())); + } + + /** + * Generates a IntStream that traverses the values of any Scala map by + * accumulating those values into a buffer class (Accumulator). + *

+ * Both sequential and parallel operations will be efficient. + * + * @param coll The map containing values to traverse + * @return A IntStream view of the collection which, by default, executes sequentially. + */ + public static IntStream intStreamAccumulatedValues(Map coll) { + return StreamConverters.asJavaSeqIntStream((IterableOnce)(Object)scala.jdk.IntAccumulator$.MODULE$.fromSpecific((IterableOnce)(Object)coll.valuesIterator())); + } + + ////////////////// + // Long Streams // + ////////////////// + + /** + * Generates a LongStream that traverses a Scala collection. + *

+ * Parallel processing is only efficient for collections that have a Stepper implementation + * which supports efficient splitting. For collections where this is the case, the stepper + * method has a return type marked with EfficientSplit. + * + * @param coll The IterableOnce to traverse + * @return A LongStream view of the collection which, by default, executes sequentially. + */ + public static LongStream longStream(IterableOnce coll) { + return StreamConverters.asJavaSeqLongStream(coll); + } + + /** + * Generates a LongStream that traverses the keys of a scala.collection.Map. + *

+ * Parallel processing is only efficient for Maps that have a keyStepper implementation + * which supports efficient splitting. For collections where this is the case, the keyStepper + * method has a return type marked with EfficientSplit. + * + * @param coll The Map to traverse + * @return A LongStream view of the collection which, by default, executes sequentially. + */ + public static LongStream longStreamKeys(Map coll) { + return StreamSupport.longStream(coll.keyStepper((StepperShape)(Object)StepperShape.doubleStepperShape()).spliterator(), false); + } + + /** + * Generates a LongStream that traverses the values of a scala.collection.Map. + *

+ * Parallel processing is only efficient for Maps that have a valueStepper implementation + * which supports efficient splitting. For collections where this is the case, the valueStepper + * method has a return type marked with EfficientSplit. + * + * @param coll The Map to traverse + * @return A LongStream view of the collection which, by default, executes sequentially. + */ + public static LongStream longStreamValues(Map coll) { + return StreamSupport.longStream(coll.valueStepper((StepperShape)(Object)StepperShape.doubleStepperShape()).spliterator(), false); + } + + /** + * Generates a LongStream that traverses any Scala collection by accumulating its entries + * into a buffer class (Accumulator). + *

+ * Both sequential and parallel operations will be efficient. + * + * @param coll The collection to traverse + * @return A LongStream view of the collection which, by default, executes sequentially. + */ + public static LongStream longStreamAccumulated(IterableOnce coll) { + return StreamConverters.asJavaSeqLongStream((IterableOnce)(Object)scala.jdk.LongAccumulator$.MODULE$.fromSpecific((IterableOnce)(Object)coll)); + } + + /** + * Generates a LongStream that traverses the keys of any Scala map by + * accumulating those keys into a buffer class (Accumulator). + *

+ * Both sequential and parallel operations will be efficient. + * + * @param coll The map containing keys to traverse + * @return A LongStream view of the collection which, by default, executes sequentially. + */ + public static LongStream longStreamAccumulatedKeys(Map coll) { + return StreamConverters.asJavaSeqLongStream((IterableOnce)(Object)scala.jdk.LongAccumulator$.MODULE$.fromSpecific((IterableOnce)(Object)coll.keysIterator())); + } + + /** + * Generates a LongStream that traverses the values of any Scala map by + * accumulating those values into a buffer class (Accumulator). + *

+ * Both sequential and parallel operations will be efficient. + * + * @param coll The map containing values to traverse + * @return A LongStream view of the collection which, by default, executes sequentially. + */ + public static LongStream longStreamAccumulatedValues(Map coll) { + return StreamConverters.asJavaSeqLongStream((IterableOnce)(Object)scala.jdk.LongAccumulator$.MODULE$.fromSpecific((IterableOnce)(Object)coll.valuesIterator())); + } +} diff --git a/src/main/java/scala/compat/java8/ScalaStreamSupport.java b/src/main/java-2.13-/scala/compat/java8/ScalaStreamSupport.java similarity index 100% rename from src/main/java/scala/compat/java8/ScalaStreamSupport.java rename to src/main/java-2.13-/scala/compat/java8/ScalaStreamSupport.java diff --git a/src/main/java/scala/compat/java8/runtime/CollectionInternals.java b/src/main/java-2.13-/scala/compat/java8/runtime/CollectionInternals.java similarity index 100% rename from src/main/java/scala/compat/java8/runtime/CollectionInternals.java rename to src/main/java-2.13-/scala/compat/java8/runtime/CollectionInternals.java diff --git a/src/main/java/scala/compat/java8/runtime/LambdaDeserializer.scala b/src/main/scala-2.11/scala/compat/java8/runtime/LambdaDeserializer.scala similarity index 97% rename from src/main/java/scala/compat/java8/runtime/LambdaDeserializer.scala rename to src/main/scala-2.11/scala/compat/java8/runtime/LambdaDeserializer.scala index cdda56a..551e649 100644 --- a/src/main/java/scala/compat/java8/runtime/LambdaDeserializer.scala +++ b/src/main/scala-2.11/scala/compat/java8/runtime/LambdaDeserializer.scala @@ -15,8 +15,9 @@ package scala.compat.java8.runtime import java.lang.invoke._ /** - * This class is only intended to be called by synthetic `$deserializeLambda$` method that the Scala 2.12 - * compiler will add to classes hosting lambdas. + * This class is only intended to be called by synthetic `$deserializeLambda$` method that the + * Scala 2.11 compiler will add to classes hosting lambdas. In Scala 2.12+, it's part of the + * standard library. * * It is not intended to be consumed directly. */ diff --git a/src/main/scala-2.13+/scala/compat/java8/StreamConverters.scala b/src/main/scala-2.13+/scala/compat/java8/StreamConverters.scala new file mode 100644 index 0000000..8adea5f --- /dev/null +++ b/src/main/scala-2.13+/scala/compat/java8/StreamConverters.scala @@ -0,0 +1,506 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.compat.java8 + +import java.util.stream._ + +import scala.annotation.{implicitNotFound, unused} +import scala.collection.Stepper.EfficientSplit +import scala.collection.convert.StreamExtensions.{AccumulatorFactoryInfo, StreamShape, StreamUnboxer} +import scala.collection.{IterableOnce, Stepper, StepperShape} +import scala.compat.java8.converterImpl._ +import scala.jdk.CollectionConverters._ +import scala.jdk._ +import scala.language.{higherKinds, implicitConversions} + +/** Defines extension methods to create Java Streams for Scala collections, available through + * [[scala.compat.java8.StreamConverters]]. + */ +trait StreamExtensions { + implicit def richStepper[A](s: Stepper[A]): StepperExtensions[A] = new StepperExtensions[A](s) + + // collections + + implicit class IterableHasSeqStream[A](cc: IterableOnce[A]) { + /** Create a sequential [[java.util.stream.Stream Java Stream]] for this collection. If the + * collection contains primitive values, a corresponding specialized Stream is returned (e.g., + * [[java.util.stream.IntStream `IntStream`]]). + */ + def seqStream[S <: BaseStream[_, _], St <: Stepper[_]](implicit s: StreamShape[A, S, St], st: StepperShape[A, St]): S = + s.fromStepper(cc.stepper, par = false) + } + + // Not `CC[X] <: IterableOnce[X]`, but `C` with an extra constraint, to support non-parametric classes like IntAccumulator + implicit class IterableNonGenericHasParStream[A, C <: IterableOnce[_]](c: C)(implicit ev: C <:< IterableOnce[A]) { + private type IterableOnceWithEfficientStepper = IterableOnce[A] { + def stepper[S <: Stepper[_]](implicit shape : StepperShape[A, S]) : S with EfficientSplit + } + + /** Create a parallel [[java.util.stream.Stream Java Stream]] for this collection. If the + * collection contains primitive values, a corresponding specialized Stream is returned (e.g., + * [[java.util.stream.IntStream `IntStream`]]). + */ + def parStream[S <: BaseStream[_, _], St <: Stepper[_]](implicit + s: StreamShape[A, S, St], + st: StepperShape[A, St], + @implicitNotFound("`parStream` can only be called on collections where `stepper` returns a `Stepper with EfficientSplit`") + isEfficient: C <:< IterableOnceWithEfficientStepper): S = + s.fromStepper(ev(c).stepper, par = true) + } + + // maps + + implicit class MapHasSeqKeyValueStream[K, V, CC[X, Y] <: collection.MapOps[X, Y, collection.Map, _]](cc: CC[K, V]) { + /** Create a sequential [[java.util.stream.Stream Java Stream]] for the keys of this map. If + * the keys are primitive values, a corresponding specialized Stream is returned (e.g., + * [[java.util.stream.IntStream `IntStream`]]). + */ + def seqKeyStream[S <: BaseStream[_, _], St <: Stepper[_]](implicit s: StreamShape[K, S, St], st: StepperShape[K, St]): S = + s.fromStepper(cc.keyStepper, par = false) + + /** Create a sequential [[java.util.stream.Stream Java Stream]] for the values of this map. If + * the values are primitives, a corresponding specialized Stream is returned (e.g., + * [[java.util.stream.IntStream `IntStream`]]). + */ + def seqValueStream[S <: BaseStream[_, _], St <: Stepper[_]](implicit s: StreamShape[V, S, St], st: StepperShape[V, St]): S = + s.fromStepper(cc.valueStepper, par = false) + + // The seqStream extension method for IterableOnce doesn't apply because its `CC` takes a single type parameter, whereas the one here takes two + /** Create a sequential [[java.util.stream.Stream Java Stream]] for the `(key, value)` pairs of + * this map. + */ + def seqStream[S <: BaseStream[_, _], St <: Stepper[_]](implicit s: StreamShape[(K, V), S, St], st: StepperShape[(K, V), St]): S = + s.fromStepper(cc.stepper, par = false) + } + + + implicit class MapHasParKeyValueStream[K, V, CC[X, Y] <: collection.MapOps[X, Y, collection.Map, _]](cc: CC[K, V]) { + private type MapOpsWithEfficientKeyStepper = collection.MapOps[K, V, collection.Map, _] { def keyStepper[S <: Stepper[_]](implicit shape : StepperShape[K, S]) : S with EfficientSplit } + private type MapOpsWithEfficientValueStepper = collection.MapOps[K, V, collection.Map, _] { def valueStepper[V1 >: V, S <: Stepper[_]](implicit shape : StepperShape[V1, S]) : S with EfficientSplit } + private type MapOpsWithEfficientStepper = collection.MapOps[K, V, collection.Map, _] { def stepper[S <: Stepper[_]](implicit shape : StepperShape[(K, V), S]) : S with EfficientSplit } + + /** Create a parallel [[java.util.stream.Stream Java Stream]] for the keys of this map. If + * the keys are primitive values, a corresponding specialized Stream is returned (e.g., + * [[java.util.stream.IntStream `IntStream`]]). + */ + def parKeyStream[S <: BaseStream[_, _], St <: Stepper[_]](implicit + s: StreamShape[K, S, St], + st: StepperShape[K, St], + @implicitNotFound("parKeyStream can only be called on maps where `keyStepper` returns a `Stepper with EfficientSplit`") + isEfficient: CC[K, V] <:< MapOpsWithEfficientKeyStepper): S = + s.fromStepper(cc.keyStepper, par = true) + + /** Create a parallel [[java.util.stream.Stream Java Stream]] for the values of this map. If + * the values are primitives, a corresponding specialized Stream is returned (e.g., + * [[java.util.stream.IntStream `IntStream`]]). + */ + def parValueStream[S <: BaseStream[_, _], St <: Stepper[_]](implicit + s: StreamShape[V, S, St], + st: StepperShape[V, St], + @implicitNotFound("parValueStream can only be called on maps where `valueStepper` returns a `Stepper with EfficientSplit`") + isEfficient: CC[K, V] <:< MapOpsWithEfficientValueStepper): S = + s.fromStepper(cc.valueStepper, par = true) + + // The parStream extension method for IterableOnce doesn't apply because its `CC` takes a single type parameter, whereas the one here takes two + /** Create a parallel [[java.util.stream.Stream Java Stream]] for the `(key, value)` pairs of + * this map. + */ + def parStream[S <: BaseStream[_, _], St <: Stepper[_]](implicit + s: StreamShape[(K, V), S, St], + st: StepperShape[(K, V), St], + @implicitNotFound("parStream can only be called on maps where `stepper` returns a `Stepper with EfficientSplit`") + isEfficient: CC[K, V] <:< MapOpsWithEfficientStepper): S = + s.fromStepper(cc.stepper, par = true) + } + + // steppers + + implicit class StepperHasSeqStream[A](stepper: Stepper[A]) { + /** Create a sequential [[java.util.stream.Stream Java Stream]] for this stepper. If the + * stepper yields primitive values, a corresponding specialized Stream is returned (e.g., + * [[java.util.stream.IntStream `IntStream`]]). + */ + def seqStream[S <: BaseStream[_, _], St <: Stepper[_]](implicit s: StreamShape[A, S, St], @unused st: StepperShape[A, St]): S = + s.fromStepper(stepper.asInstanceOf[St], par = false) + } + + implicit class StepperHasParStream[A](stepper: Stepper[A] with EfficientSplit) { + /** Create a parallel [[java.util.stream.Stream Java Stream]] for this stepper. If the + * stepper yields primitive values, a corresponding specialized Stream is returned (e.g., + * [[java.util.stream.IntStream `IntStream`]]). + */ + def parStream[S <: BaseStream[_, _], St <: Stepper[_]](implicit s: StreamShape[A, S, St], @unused st: StepperShape[A, St]): S = + s.fromStepper(stepper.asInstanceOf[St], par = true) + } + + // arrays + // uses the JDK array spliterators (`DoubleArraySpliterator`). users can also call + // `array.stepper.seqStream`, which then uses the Scala steppers (`DoubleArrayStepper`). the + // steppers are also available on byte/short/char/float arrays (`WidenedByteArrayStepper`), + // JDK spliterators only for double/int/long/reference. + + implicit class DoubleArrayHasSeqParStream(a: Array[Double]) { + /** Create a sequential [[java.util.stream.DoubleStream Java DoubleStream]] for this array. */ + def seqStream: DoubleStream = java.util.Arrays.stream(a) + /** Create a parallel [[java.util.stream.DoubleStream Java DoubleStream]] for this array. */ + def parStream: DoubleStream = seqStream.parallel + } + + implicit class IntArrayHasSeqParStream(a: Array[Int]) { + /** Create a sequential [[java.util.stream.IntStream Java IntStream]] for this array. */ + def seqStream: IntStream = java.util.Arrays.stream(a) + /** Create a parallel [[java.util.stream.IntStream Java IntStream]] for this array. */ + def parStream: IntStream = seqStream.parallel + } + + implicit class LongArrayHasSeqParStream(a: Array[Long]) { + /** Create a sequential [[java.util.stream.LongStream Java LongStream]] for this array. */ + def seqStream: LongStream = java.util.Arrays.stream(a) + /** Create a parallel [[java.util.stream.LongStream Java LongStream]] for this array. */ + def parStream: LongStream = seqStream.parallel + } + + implicit class AnyArrayHasSeqParStream[A <: AnyRef](a: Array[A]) { + /** Create a sequential [[java.util.stream.Stream Java Stream]] for this array. */ + def seqStream: Stream[A] = java.util.Arrays.stream(a) + /** Create a parallel [[java.util.stream.Stream Java Stream]] for this array. */ + def parStream: Stream[A] = seqStream.parallel + } + + implicit class ByteArrayHasSeqParStream(a: Array[Byte]) { + /** Create a sequential [[java.util.stream.IntStream Java IntStream]] for this array. */ + def seqStream: IntStream = a.stepper.seqStream + /** Create a parallel [[java.util.stream.IntStream Java IntStream]] for this array. */ + def parStream: IntStream = a.stepper.parStream + } + + implicit class ShortArrayHasSeqParStream(a: Array[Short]) { + /** Create a sequential [[java.util.stream.IntStream Java IntStream]] for this array. */ + def seqStream: IntStream = a.stepper.seqStream + /** Create a parallel [[java.util.stream.IntStream Java IntStream]] for this array. */ + def parStream: IntStream = a.stepper.parStream + } + + implicit class CharArrayHasSeqParStream(a: Array[Char]) { + /** Create a sequential [[java.util.stream.IntStream Java IntStream]] for this array. */ + def seqStream: IntStream = a.stepper.seqStream + /** Create a parallel [[java.util.stream.IntStream Java IntStream]] for this array. */ + def parStream: IntStream = a.stepper.parStream + } + + implicit class FloatArrayHasSeqParStream(a: Array[Float]) { + /** Create a sequential [[java.util.stream.DoubleStream Java DoubleStream]] for this array. */ + def seqStream: DoubleStream = a.stepper.seqStream + + /** Create a parallel [[java.util.stream.DoubleStream Java DoubleStream]] for this array. */ + def parStream: DoubleStream = a.stepper.parStream + } + + // toScala for streams + + implicit class StreamHasToScala[A](stream: Stream[A]) { + def accumulate: AnyAccumulator[A] = toScalaFactory(Accumulator) + + + /** + * Copy the elements of this stream into a Scala collection. + * + * Converting a parallel streams to an [[scala.jdk.Accumulator]] using `stream.toScalaFactory(Accumulator)` + * builds the result in parallel. + * + * A `toScalaFactory(Accumulator)` call automatically converts streams of boxed integers, longs or + * doubles are converted to the primitive accumulators ([[scala.jdk.IntAccumulator]], etc.). + * + * When converting a parallel stream to a different Scala collection, the stream is first + * converted into an [[scala.jdk.Accumulator]], which supports parallel building. The accumulator is + * then converted to the target collection. Note that the stream is processed eagerly while + * building the accumulator, even if the target collection is lazy. + * + * Sequential streams are directly converted to the target collection. If the target collection + * is lazy, the conversion is lazy as well. + */ + private[java8] def toScalaFactory[C](factory: collection.Factory[A, C])(implicit info: AccumulatorFactoryInfo[A, C]): C = { + def anyAcc = stream.collect(AnyAccumulator.supplier[A], AnyAccumulator.adder[A], AnyAccumulator.merger[A]) + if (info.companion == AnyAccumulator) anyAcc.asInstanceOf[C] + else if (info.companion == IntAccumulator) stream.asInstanceOf[Stream[Int]].collect(IntAccumulator.supplier, IntAccumulator.boxedAdder, IntAccumulator.merger).asInstanceOf[C] + else if (info.companion == LongAccumulator) stream.asInstanceOf[Stream[Long]].collect(LongAccumulator.supplier, LongAccumulator.boxedAdder, LongAccumulator.merger).asInstanceOf[C] + else if (info.companion == DoubleAccumulator) stream.asInstanceOf[Stream[Double]].collect(DoubleAccumulator.supplier, DoubleAccumulator.boxedAdder, DoubleAccumulator.merger).asInstanceOf[C] + else if (stream.isParallel) anyAcc.to(factory) + else factory.fromSpecific(stream.iterator.asScala) + } + + /** + * Copy the elements of this stream into a Scala collection. + * + * For parallel streams, using [[accumulate]] is recommended as it builds the [[scala.jdk.Accumulator]] + * in parallel. + * + * When converting a parallel stream to a different Scala collection, the stream is first + * converted into an [[scala.jdk.Accumulator]], which supports parallel building. The accumulator is + * then converted to the target collection. Note that the stream is processed eagerly while + * building the accumulator, even if the target collection is lazy. + * + * Sequential streams are directly converted to the target collection. If the target collection + * is lazy, the conversion is lazy as well. + */ + def toScala[CC[_]](implicit factory: collection.Factory[A, CC[A]]): CC[A] = { + if (stream.isParallel) toScalaFactory(Accumulator).to(factory) + else factory.fromSpecific(stream.iterator.asScala) + } + + /** Convert a generic Java Stream wrapping a primitive type to a corresponding primitive + * Stream. + */ + def unboxed[S](implicit unboxer: StreamUnboxer[A, S]): S = unboxer(stream) + } + + implicit class StreamIntHasAccumulatePrimitive(s: Stream[Int]) { + def accumulatePrimitive: IntAccumulator = s.toScalaFactory(Accumulator) + } + + implicit class StreamLongHasAccumulatePrimitive(s: Stream[Long]) { + def accumulatePrimitive: LongAccumulator = s.toScalaFactory(Accumulator) + } + + implicit class StreamDoubleHasAccumulatePrimitive(s: Stream[Double]) { + def accumulatePrimitive: DoubleAccumulator = s.toScalaFactory(Accumulator) + } + + implicit class StreamJIntegerHasAccumulatePrimitive(s: Stream[java.lang.Integer]) { + def accumulatePrimitive: IntAccumulator = s.toScalaFactory(Accumulator) + } + + implicit class StreamJLongHasAccumulatePrimitive(s: Stream[java.lang.Long]) { + def accumulatePrimitive: LongAccumulator = s.toScalaFactory(Accumulator) + } + + implicit class StreamJDoubleHasAccumulatePrimitive(s: Stream[java.lang.Double]) { + def accumulatePrimitive: DoubleAccumulator = s.toScalaFactory(Accumulator) + } + + implicit class IntStreamHasToScala(stream: IntStream) { + def accumulate: IntAccumulator = toScalaFactory(IntAccumulator) + + /** + * Copy the elements of this stream into a Scala collection. + * + * Converting a parallel streams to an [[scala.jdk.Accumulator]] using `stream.toScalaFactory(Accumulator)` + * builds the result in parallel. + * + * A `toScalaFactory(Accumulator)` call automatically converts the `IntStream` to a primitive + * [[scala.jdk.IntAccumulator]]. + * + * When converting a parallel stream to a different Scala collection, the stream is first + * converted into an [[scala.jdk.Accumulator]], which supports parallel building. The accumulator is + * then converted to the target collection. Note that the stream is processed eagerly while + * building the accumulator, even if the target collection is lazy. + * + * Sequential streams are directly converted to the target collection. If the target collection + * is lazy, the conversion is lazy as well. + */ + private[java8] def toScalaFactory[C](factory: collection.Factory[Int, C])(implicit info: AccumulatorFactoryInfo[Int, C]): C = { + def intAcc = stream.collect(IntAccumulator.supplier, IntAccumulator.adder, IntAccumulator.merger) + if (info.companion == AnyAccumulator) stream.collect(AnyAccumulator.supplier[Int], AnyAccumulator.unboxedIntAdder, AnyAccumulator.merger[Int]).asInstanceOf[C] + else if (info.companion == IntAccumulator) intAcc.asInstanceOf[C] + else if (stream.isParallel) intAcc.to(factory) + else factory.fromSpecific(stream.iterator.asInstanceOf[java.util.Iterator[Int]].asScala) + } + + /** + * Copy the elements of this stream into a Scala collection. + * + * For parallel streams, using [[accumulate]] is recommended as it builds the [[scala.jdk.IntAccumulator]] + * in parallel. + * + * When converting a parallel stream to a different Scala collection, the stream is first + * converted into an [[scala.jdk.Accumulator]], which supports parallel building. The accumulator is + * then converted to the target collection. Note that the stream is processed eagerly while + * building the accumulator, even if the target collection is lazy. + * + * Sequential streams are directly converted to the target collection. If the target collection + * is lazy, the conversion is lazy as well. + */ + def toScala[CC[_]](implicit factory: collection.Factory[Int, CC[Int]]): CC[Int] = { + if (stream.isParallel) toScalaFactory(IntAccumulator).to(factory) + else factory.fromSpecific(stream.iterator.asInstanceOf[java.util.Iterator[Int]].asScala) + } + } + + implicit class LongStreamHasToScala(stream: LongStream) { + def accumulate: LongAccumulator = toScalaFactory(LongAccumulator) + + /** + * Copy the elements of this stream into a Scala collection. + * + * Converting a parallel streams to an [[scala.jdk.Accumulator]] using `stream.toScalaFactory(Accumulator)` + * builds the result in parallel. + * + * A `toScalaFactory(Accumulator)` call automatically converts the `LongStream` to a primitive + * [[scala.jdk.LongAccumulator]]. + * + * When converting a parallel stream to a different Scala collection, the stream is first + * converted into an [[scala.jdk.Accumulator]], which supports parallel building. The accumulator is + * then converted to the target collection. Note that the stream is processed eagerly while + * building the accumulator, even if the target collection is lazy. + * + * Sequential streams are directly converted to the target collection. If the target collection + * is lazy, the conversion is lazy as well. + */ + private[java8] def toScalaFactory[C](factory: collection.Factory[Long, C])(implicit info: AccumulatorFactoryInfo[Long, C]): C = { + def longAcc = stream.collect(LongAccumulator.supplier, LongAccumulator.adder, LongAccumulator.merger) + if (info.companion == AnyAccumulator) stream.collect(AnyAccumulator.supplier[Long], AnyAccumulator.unboxedLongAdder, AnyAccumulator.merger[Long]).asInstanceOf[C] + else if (info.companion == LongAccumulator) longAcc.asInstanceOf[C] + else if (stream.isParallel) longAcc.to(factory) + else factory.fromSpecific(stream.iterator.asInstanceOf[java.util.Iterator[Long]].asScala) + } + + /** + * Copy the elements of this stream into a Scala collection. + * + * For parallel streams, using [[accumulate]] is recommended as it builds the [[scala.jdk.LongAccumulator]] + * in parallel. + * + * When converting a parallel stream to a different Scala collection, the stream is first + * converted into an [[scala.jdk.Accumulator]], which supports parallel building. The accumulator is + * then converted to the target collection. Note that the stream is processed eagerly while + * building the accumulator, even if the target collection is lazy. + * + * Sequential streams are directly converted to the target collection. If the target collection + * is lazy, the conversion is lazy as well. + */ + def toScala[CC[_]](implicit factory: collection.Factory[Long, CC[Long]]): CC[Long] = { + if (stream.isParallel) toScalaFactory(LongAccumulator).to(factory) + else factory.fromSpecific(stream.iterator.asInstanceOf[java.util.Iterator[Long]].asScala) + } + } + + implicit class DoubleStreamHasToScala(stream: DoubleStream) { + def accumulate: DoubleAccumulator = toScalaFactory(DoubleAccumulator) + + /** + * Copy the elements of this stream into a Scala collection. + * + * Converting a parallel streams to an [[scala.jdk.Accumulator]] using `stream.toScalaFactory(Accumulator)` + * builds the result in parallel. + * + * A `toScalaFactory(Accumulator)` call automatically converts the `DoubleStream` to a primitive + * [[scala.jdk.DoubleAccumulator]]. + * + * When converting a parallel stream to a different Scala collection, the stream is first + * converted into an [[scala.jdk.Accumulator]], which supports parallel building. The accumulator is + * then converted to the target collection. Note that the stream is processed eagerly while + * building the accumulator, even if the target collection is lazy. + * + * Sequential streams are directly converted to the target collection. If the target collection + * is lazy, the conversion is lazy as well. + */ + private[java8] def toScalaFactory[C](factory: collection.Factory[Double, C])(implicit info: AccumulatorFactoryInfo[Double, C]): C = { + def doubleAcc = stream.collect(DoubleAccumulator.supplier, DoubleAccumulator.adder, DoubleAccumulator.merger) + if (info.companion == AnyAccumulator) stream.collect(AnyAccumulator.supplier[Double], AnyAccumulator.unboxedDoubleAdder, AnyAccumulator.merger[Double]).asInstanceOf[C] + else if (info.companion == DoubleAccumulator) doubleAcc.asInstanceOf[C] + else if (stream.isParallel) doubleAcc.to(factory) + else factory.fromSpecific(stream.iterator.asInstanceOf[java.util.Iterator[Double]].asScala) + } + + /** + * Copy the elements of this stream into a Scala collection. + * + * For parallel streams, using [[accumulate]] is recommended as it builds the [[scala.jdk.DoubleAccumulator]] + * in parallel. + * + * When converting a parallel stream to a different Scala collection, the stream is first + * converted into an [[scala.jdk.Accumulator]], which supports parallel building. The accumulator is + * then converted to the target collection. Note that the stream is processed eagerly while + * building the accumulator, even if the target collection is lazy. + * + * Sequential streams are directly converted to the target collection. If the target collection + * is lazy, the conversion is lazy as well. + */ + def toScala[CC[_]](implicit factory: collection.Factory[Double, CC[Double]]): CC[Double] = { + if (stream.isParallel) toScalaFactory(DoubleAccumulator).to(factory) + else factory.fromSpecific(stream.iterator.asInstanceOf[java.util.Iterator[Double]].asScala) + } + } +} + +/** `StreamConverters` provides extension methods and other functionality to + * ease interoperability of Scala collections with `java.util.stream` classes. + * + * Scala collections gain extension methods `seqStream` and + * `parStream` that allow them to be used as the source of a `Stream`. + * Some collections either intrinsically cannot be paralellized, or + * could be but an efficient implementation is missing. It this case, + * only `seqStream` is provided. If a collection cannot be stepped over + * at all (e.g. `Traversable`), then it gains neither method. + * + * `Array` also gains `seqStream` and `parStream` methods, and calling those + * on `Array[Double]`, `Array[Int]`, or `Array[Long]` will produce the + * corresponding primitive stream. + * + * Streams gain `accumulate` and `toScala[_]` methods, which collect the stream + * into a custom high-performance `scala.collection.mutable.java8.Accumulator`, + * which is not part of the standard collections hierarchy, or into a named + * Scala collection, respectively. + * + * Generic streams also gain an `unboxed` method that will convert to the + * corresponding unboxed primitive stream, if appropriate. Unboxed streams + * have custom accumulators with improved performance. + * + * Accumulators have `toArray`, `toList`, `iterator`, and `to[_]` methods + * to convert to standard Scala collections. Note that if you wish to + * create an array from a `Stream`, going through an `Accumulator` is + * not the most efficient option: just create the `Array` directly. + * + * Internally, Scala collections implement a hybrid of `Iterator` and + * `java.util.Spliterator` to implement `Stream` compatibility; these + * are called `Stepper`s. In particular, they can test for the presence + * of a next element using `hasStep`, can retrieve the next value with + * `nextStep`, or can optionally retrieve and operate on a value if present + * with `tryStep`, which works like `tryAdvance` in `java.util.Spliterator`. + * + * Every Scala collection that can be stepped + * through has a `stepper` method implicitly provided. In addition, + * maps have `keyStepper` and `valueStepper` methods. A limited number + * of collections operations are defined on `Stepper`s, including conversion + * to Scala collections with `to` or accumulation via `accumulate`. + * `Stepper`s also implement `seqStream` and `parStream` to generate `Stream`s. + * These are provided regardless of whether a `Stepper` can efficiently + * subdivide itself for parallel processing (though one can check for the + * presence of the `EfficientSubstep` trait to know that parallel execution will + * not be limited by long sequential searching steps, and one can call + * `anticipateParallelism` to warn a `Stepper` that it will be used in a parallel + * context and thus may wish to make different tradeoffs). + * + * Examples: + * {{{ + * import scala.compat.java8.StreamConverters._ + * + * val s = Vector(1,2,3,4).parStream // Stream[Int] + * val si = s.unboxed // Stream.OfInt + * val ai = si.accumulate // IntAccumulator + * val v = ai.to[Vector] // Vector[Int] again + * + * val t = Array(2.0, 3.0, 4.0).parStream // DoubleStream + * val q = t.toScala[scala.collection.immutable.Queue] // Queue[Double] + * + * val x = List(1L, 2L, 3L, 4L).stepper.parStream.sum // 10, potentially computed in parallel + * }}} + */ +object StreamConverters +extends StreamExtensions +with converterImpl.Priority1AccumulatorConverters +{ + implicit def richIntStepper(s: Stepper[Int]): StepperExtensions[Int] = new StepperExtensions[Int](s) + implicit def richLongStepper(s: Stepper[Long]): StepperExtensions[Long] = new StepperExtensions[Long](s) + implicit def richDoubleStepper(s: Stepper[Double]): StepperExtensions[Double] = new StepperExtensions[Double](s) +} diff --git a/src/main/scala-2.13+/scala/compat/java8/collectionImpl/package.scala b/src/main/scala-2.13+/scala/compat/java8/collectionImpl/package.scala new file mode 100644 index 0000000..ab5cfca --- /dev/null +++ b/src/main/scala-2.13+/scala/compat/java8/collectionImpl/package.scala @@ -0,0 +1,30 @@ +package scala.compat.java8 + +package object collectionImpl { + type Accumulator[A] = scala.jdk.AnyAccumulator[A] + val Accumulator = scala.jdk.AnyAccumulator + + type IntAccumulator = scala.jdk.IntAccumulator + val IntAccumulator = scala.jdk.IntAccumulator + + type LongAccumulator = scala.jdk.LongAccumulator + val LongAccumulator = scala.jdk.LongAccumulator + + type DoubleAccumulator = scala.jdk.DoubleAccumulator + val DoubleAccumulator = scala.jdk.DoubleAccumulator + + type Stepper[A] = scala.collection.Stepper[A] + val Stepper = scala.collection.Stepper + + type AnyStepper[A] = scala.collection.AnyStepper[A] + val AnyStepper = scala.collection.AnyStepper + + type IntStepper = scala.collection.IntStepper + val IntStepper = scala.collection.IntStepper + + type LongStepper = scala.collection.LongStepper + val LongStepper = scala.collection.LongStepper + + type DoubleStepper = scala.collection.DoubleStepper + val DoubleStepper = scala.collection.DoubleStepper +} diff --git a/src/main/scala-2.13+/scala/compat/java8/converterImpl/Accumulates.scala b/src/main/scala-2.13+/scala/compat/java8/converterImpl/Accumulates.scala new file mode 100644 index 0000000..fa28114 --- /dev/null +++ b/src/main/scala-2.13+/scala/compat/java8/converterImpl/Accumulates.scala @@ -0,0 +1,47 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.compat.java8.converterImpl + +import scala.compat.java8.collectionImpl._ + +final class CollectionCanAccumulate[A](private val underlying: IterableOnce[A]) extends AnyVal { + def accumulate: Accumulator[A] = underlying.iterator.to(Accumulator) +} + +final class AccumulateDoubleCollection(private val underlying: IterableOnce[Double]) extends AnyVal { + def accumulate: DoubleAccumulator = underlying.iterator.to(DoubleAccumulator) +} + +final class AccumulateIntCollection(private val underlying: IterableOnce[Int]) extends AnyVal { + def accumulate: IntAccumulator = underlying.iterator.to(IntAccumulator) +} + +final class AccumulateLongCollection(private val underlying: IterableOnce[Long]) extends AnyVal { + def accumulate: LongAccumulator = underlying.iterator.to(LongAccumulator) +} + +final class AccumulateAnyArray[A](private val underlying: Array[A]) extends AnyVal { + def accumulate: Accumulator[A] = underlying.to(Accumulator) +} + +final class AccumulateDoubleArray(private val underlying: Array[Double]) extends AnyVal { + def accumulate: DoubleAccumulator = underlying.to(DoubleAccumulator) +} + +final class AccumulateIntArray(private val underlying: Array[Int]) extends AnyVal { + def accumulate: IntAccumulator = underlying.to(IntAccumulator) +} + +final class AccumulateLongArray(private val underlying: Array[Long]) extends AnyVal { + def accumulate: LongAccumulator = underlying.to(LongAccumulator) +} diff --git a/src/main/scala-2.13+/scala/compat/java8/converterImpl/AccumulatorConverters.scala b/src/main/scala-2.13+/scala/compat/java8/converterImpl/AccumulatorConverters.scala new file mode 100644 index 0000000..4ff943a --- /dev/null +++ b/src/main/scala-2.13+/scala/compat/java8/converterImpl/AccumulatorConverters.scala @@ -0,0 +1,32 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.compat.java8.converterImpl + +import scala.language.implicitConversions + +trait Priority3AccumulatorConverters { + implicit def collectionCanAccumulate[A](underlying: IterableOnce[A]) = new CollectionCanAccumulate[A](underlying) +} + +trait Priority2AccumulatorConverters extends Priority3AccumulatorConverters { + implicit def accumulateDoubleCollection(underlying: IterableOnce[Double]) = new AccumulateDoubleCollection(underlying) + implicit def accumulateIntCollection(underlying: IterableOnce[Int]) = new AccumulateIntCollection(underlying) + implicit def accumulateLongCollection(underlying: IterableOnce[Long]) = new AccumulateLongCollection(underlying) + implicit def accumulateAnyArray[A](underlying: Array[A]) = new AccumulateAnyArray(underlying) +} + +trait Priority1AccumulatorConverters extends Priority2AccumulatorConverters { + implicit def accumulateDoubleArray(underlying: Array[Double]) = new AccumulateDoubleArray(underlying) + implicit def accumulateIntArray(underlying: Array[Int]) = new AccumulateIntArray(underlying) + implicit def accumulateLongArray(underlying: Array[Long]) = new AccumulateLongArray(underlying) +} diff --git a/src/main/scala-2.13+/scala/compat/java8/converterImpl/StepperExtensions.scala b/src/main/scala-2.13+/scala/compat/java8/converterImpl/StepperExtensions.scala new file mode 100644 index 0000000..15f384a --- /dev/null +++ b/src/main/scala-2.13+/scala/compat/java8/converterImpl/StepperExtensions.scala @@ -0,0 +1,77 @@ +package scala.compat.java8.converterImpl + +import scala.collection.convert.StreamExtensions.AccumulatorFactoryInfo +import scala.compat.java8.collectionImpl.{DoubleAccumulator, IntAccumulator, LongAccumulator, Stepper} +import scala.jdk.AnyAccumulator + +class StepperExtensions[@specialized(Double, Int, Long) A](private val s: Stepper[A]) { + def accumulate[C](implicit info: AccumulatorFactoryInfo[A, C]): C = { + info.companion match { + case IntAccumulator => + val a = new IntAccumulator() + val is = s.asInstanceOf[Stepper[Int]] + while (is.hasStep) a += is.nextStep() + a.asInstanceOf[C] + case LongAccumulator => + val a = new LongAccumulator() + val is = s.asInstanceOf[Stepper[Long]] + while (is.hasStep) a += is.nextStep() + a.asInstanceOf[C] + case DoubleAccumulator => + val a = new DoubleAccumulator() + val is = s.asInstanceOf[Stepper[Double]] + while (is.hasStep) a += is.nextStep() + a.asInstanceOf[C] + case AnyAccumulator | null => + val a = new AnyAccumulator[A] + while (s.hasStep) a += s.nextStep() + a.asInstanceOf[C] + } + } + + def substep(): Stepper[A] = s.trySplit() + + /** Consumes all remaining elements in this `Stepper` and counts how many there are. + * This is a terminal operation. + */ + def count(): Long = { var n = 0L; while (s.hasStep) { s.nextStep(); n += 1 }; n } + + /** Consumes all remaining elements in this `Stepper` and counts how many satisfy condition `p`. + * This is a terminal operation. + */ + def count(p: A => Boolean): Long = { var n = 0L; while (s.hasStep) { if (p(s.nextStep())) n += 1 }; n } + + /** Searches for an element that satisfies condition `p`. If none are found, it returns `false`. + * This is a terminal operation. + */ + def exists(p: A => Boolean): Boolean = { while(s.hasStep) { if (p(s.nextStep())) return true }; false } + + /** Searches for an element that satisifes condition `p`, returning it wrapped in `Some` if one is found, or `None` otherwise. + * This is a terminal operation. + */ + def find(p: A => Boolean): Option[A] = { while (s.hasStep) { val a = s.nextStep(); if (p(a)) return Some(a) }; None } + + /** Repeatedly applies `op` to propagate an initial value `zero` through all elements of the collection. + * Traversal order is left-to-right. + * This is a terminal operation. + */ + def fold[@specialized(Double, Int, Long) B](zero: B)(op: (B, A) => B) = { var b = zero; while (s.hasStep) { b = op(b, s.nextStep()) }; b } + + /** Repeatedly applies `op` to propagate an initial value `zero` through the collection until a condition `p` is met. + * If `p` is never met, the result of the last operation is returned. + * This is a terminal operation. + */ + def foldTo[@specialized(Double, Int, Long) B](zero: B)(op: (B, A) => B)(p: B => Boolean) = { var b = zero; while (!p(b) && s.hasStep) { b = op(b, s.nextStep()) }; b } + + /** Applies `f` to every remaining element in the collection. + * This is a terminal operation. + */ + def foreach(f: A => Unit): Unit = { while (s.hasStep) f(s.nextStep()) } + + /** Repeatedly merges elements with `op` until only a single element remains. + * Throws an exception if the `Stepper` is empty. + * Merging occurs from left to right. + * This is a terminal operation. + */ + def reduce(op: (A, A) => A): A = { var a = s.nextStep(); while (s.hasStep) { a = op(a, s.nextStep()) }; a } +} diff --git a/src/main/scala-2.13+/scala/concurrent/java8/FuturesConvertersImplCompat.scala b/src/main/scala-2.13+/scala/concurrent/java8/FuturesConvertersImplCompat.scala new file mode 100644 index 0000000..ea4cf4d --- /dev/null +++ b/src/main/scala-2.13+/scala/concurrent/java8/FuturesConvertersImplCompat.scala @@ -0,0 +1,8 @@ +package scala.concurrent.java8 + +import scala.concurrent.ExecutionContext + +// TODO: make this private[scala] when genjavadoc allows for that. +object FuturesConvertersImplCompat { + def InternalCallbackExecutor = ExecutionContext.parasitic +} diff --git a/src/main/scala/scala/compat/java8/SpliteratorConverters.scala b/src/main/scala-2.13-/scala/compat/java8/SpliteratorConverters.scala similarity index 100% rename from src/main/scala/scala/compat/java8/SpliteratorConverters.scala rename to src/main/scala-2.13-/scala/compat/java8/SpliteratorConverters.scala diff --git a/src/main/scala/scala/compat/java8/StreamConverters.scala b/src/main/scala-2.13-/scala/compat/java8/StreamConverters.scala similarity index 100% rename from src/main/scala/scala/compat/java8/StreamConverters.scala rename to src/main/scala-2.13-/scala/compat/java8/StreamConverters.scala diff --git a/src/main/scala/scala/compat/java8/collectionImpl/Accumulator.scala b/src/main/scala-2.13-/scala/compat/java8/collectionImpl/Accumulator.scala similarity index 100% rename from src/main/scala/scala/compat/java8/collectionImpl/Accumulator.scala rename to src/main/scala-2.13-/scala/compat/java8/collectionImpl/Accumulator.scala diff --git a/src/main/scala/scala/compat/java8/collectionImpl/AccumulatorLike.scala b/src/main/scala-2.13-/scala/compat/java8/collectionImpl/AccumulatorLike.scala similarity index 100% rename from src/main/scala/scala/compat/java8/collectionImpl/AccumulatorLike.scala rename to src/main/scala-2.13-/scala/compat/java8/collectionImpl/AccumulatorLike.scala diff --git a/src/main/scala/scala/compat/java8/collectionImpl/DoubleAccumulator.scala b/src/main/scala-2.13-/scala/compat/java8/collectionImpl/DoubleAccumulator.scala similarity index 100% rename from src/main/scala/scala/compat/java8/collectionImpl/DoubleAccumulator.scala rename to src/main/scala-2.13-/scala/compat/java8/collectionImpl/DoubleAccumulator.scala diff --git a/src/main/scala/scala/compat/java8/collectionImpl/IntAccumulator.scala b/src/main/scala-2.13-/scala/compat/java8/collectionImpl/IntAccumulator.scala similarity index 100% rename from src/main/scala/scala/compat/java8/collectionImpl/IntAccumulator.scala rename to src/main/scala-2.13-/scala/compat/java8/collectionImpl/IntAccumulator.scala diff --git a/src/main/scala/scala/compat/java8/collectionImpl/LongAccumulator.scala b/src/main/scala-2.13-/scala/compat/java8/collectionImpl/LongAccumulator.scala similarity index 100% rename from src/main/scala/scala/compat/java8/collectionImpl/LongAccumulator.scala rename to src/main/scala-2.13-/scala/compat/java8/collectionImpl/LongAccumulator.scala diff --git a/src/main/scala/scala/compat/java8/collectionImpl/Stepper.scala b/src/main/scala-2.13-/scala/compat/java8/collectionImpl/Stepper.scala similarity index 100% rename from src/main/scala/scala/compat/java8/collectionImpl/Stepper.scala rename to src/main/scala-2.13-/scala/compat/java8/collectionImpl/Stepper.scala diff --git a/src/main/scala/scala/compat/java8/converterImpl/Accumulates.scala b/src/main/scala-2.13-/scala/compat/java8/converterImpl/Accumulates.scala similarity index 100% rename from src/main/scala/scala/compat/java8/converterImpl/Accumulates.scala rename to src/main/scala-2.13-/scala/compat/java8/converterImpl/Accumulates.scala diff --git a/src/main/scala/scala/compat/java8/converterImpl/AccumulatorConverters.scala b/src/main/scala-2.13-/scala/compat/java8/converterImpl/AccumulatorConverters.scala similarity index 100% rename from src/main/scala/scala/compat/java8/converterImpl/AccumulatorConverters.scala rename to src/main/scala-2.13-/scala/compat/java8/converterImpl/AccumulatorConverters.scala diff --git a/src/main/scala/scala/compat/java8/converterImpl/MakesSteppers.scala b/src/main/scala-2.13-/scala/compat/java8/converterImpl/MakesSteppers.scala similarity index 100% rename from src/main/scala/scala/compat/java8/converterImpl/MakesSteppers.scala rename to src/main/scala-2.13-/scala/compat/java8/converterImpl/MakesSteppers.scala diff --git a/src/main/scala/scala/compat/java8/converterImpl/StepConverters.scala b/src/main/scala-2.13-/scala/compat/java8/converterImpl/StepConverters.scala similarity index 100% rename from src/main/scala/scala/compat/java8/converterImpl/StepConverters.scala rename to src/main/scala-2.13-/scala/compat/java8/converterImpl/StepConverters.scala diff --git a/src/main/scala/scala/compat/java8/converterImpl/StepsArray.scala b/src/main/scala-2.13-/scala/compat/java8/converterImpl/StepsArray.scala similarity index 100% rename from src/main/scala/scala/compat/java8/converterImpl/StepsArray.scala rename to src/main/scala-2.13-/scala/compat/java8/converterImpl/StepsArray.scala diff --git a/src/main/scala/scala/compat/java8/converterImpl/StepsBitSet.scala b/src/main/scala-2.13-/scala/compat/java8/converterImpl/StepsBitSet.scala similarity index 100% rename from src/main/scala/scala/compat/java8/converterImpl/StepsBitSet.scala rename to src/main/scala-2.13-/scala/compat/java8/converterImpl/StepsBitSet.scala diff --git a/src/main/scala/scala/compat/java8/converterImpl/StepsFlatHashTable.scala b/src/main/scala-2.13-/scala/compat/java8/converterImpl/StepsFlatHashTable.scala similarity index 100% rename from src/main/scala/scala/compat/java8/converterImpl/StepsFlatHashTable.scala rename to src/main/scala-2.13-/scala/compat/java8/converterImpl/StepsFlatHashTable.scala diff --git a/src/main/scala/scala/compat/java8/converterImpl/StepsHashTable.scala b/src/main/scala-2.13-/scala/compat/java8/converterImpl/StepsHashTable.scala similarity index 100% rename from src/main/scala/scala/compat/java8/converterImpl/StepsHashTable.scala rename to src/main/scala-2.13-/scala/compat/java8/converterImpl/StepsHashTable.scala diff --git a/src/main/scala/scala/compat/java8/converterImpl/StepsImmHashMap.scala b/src/main/scala-2.13-/scala/compat/java8/converterImpl/StepsImmHashMap.scala similarity index 100% rename from src/main/scala/scala/compat/java8/converterImpl/StepsImmHashMap.scala rename to src/main/scala-2.13-/scala/compat/java8/converterImpl/StepsImmHashMap.scala diff --git a/src/main/scala/scala/compat/java8/converterImpl/StepsImmHashSet.scala b/src/main/scala-2.13-/scala/compat/java8/converterImpl/StepsImmHashSet.scala similarity index 100% rename from src/main/scala/scala/compat/java8/converterImpl/StepsImmHashSet.scala rename to src/main/scala-2.13-/scala/compat/java8/converterImpl/StepsImmHashSet.scala diff --git a/src/main/scala/scala/compat/java8/converterImpl/StepsIndexedSeq.scala b/src/main/scala-2.13-/scala/compat/java8/converterImpl/StepsIndexedSeq.scala similarity index 100% rename from src/main/scala/scala/compat/java8/converterImpl/StepsIndexedSeq.scala rename to src/main/scala-2.13-/scala/compat/java8/converterImpl/StepsIndexedSeq.scala diff --git a/src/main/scala/scala/compat/java8/converterImpl/StepsIterable.scala b/src/main/scala-2.13-/scala/compat/java8/converterImpl/StepsIterable.scala similarity index 100% rename from src/main/scala/scala/compat/java8/converterImpl/StepsIterable.scala rename to src/main/scala-2.13-/scala/compat/java8/converterImpl/StepsIterable.scala diff --git a/src/main/scala/scala/compat/java8/converterImpl/StepsIterator.scala b/src/main/scala-2.13-/scala/compat/java8/converterImpl/StepsIterator.scala similarity index 100% rename from src/main/scala/scala/compat/java8/converterImpl/StepsIterator.scala rename to src/main/scala-2.13-/scala/compat/java8/converterImpl/StepsIterator.scala diff --git a/src/main/scala/scala/compat/java8/converterImpl/StepsLikeGapped.scala b/src/main/scala-2.13-/scala/compat/java8/converterImpl/StepsLikeGapped.scala similarity index 100% rename from src/main/scala/scala/compat/java8/converterImpl/StepsLikeGapped.scala rename to src/main/scala-2.13-/scala/compat/java8/converterImpl/StepsLikeGapped.scala diff --git a/src/main/scala/scala/compat/java8/converterImpl/StepsLikeImmHashMap.scala b/src/main/scala-2.13-/scala/compat/java8/converterImpl/StepsLikeImmHashMap.scala similarity index 100% rename from src/main/scala/scala/compat/java8/converterImpl/StepsLikeImmHashMap.scala rename to src/main/scala-2.13-/scala/compat/java8/converterImpl/StepsLikeImmHashMap.scala diff --git a/src/main/scala/scala/compat/java8/converterImpl/StepsLikeIndexed.scala b/src/main/scala-2.13-/scala/compat/java8/converterImpl/StepsLikeIndexed.scala similarity index 100% rename from src/main/scala/scala/compat/java8/converterImpl/StepsLikeIndexed.scala rename to src/main/scala-2.13-/scala/compat/java8/converterImpl/StepsLikeIndexed.scala diff --git a/src/main/scala/scala/compat/java8/converterImpl/StepsLikeIterator.scala b/src/main/scala-2.13-/scala/compat/java8/converterImpl/StepsLikeIterator.scala similarity index 100% rename from src/main/scala/scala/compat/java8/converterImpl/StepsLikeIterator.scala rename to src/main/scala-2.13-/scala/compat/java8/converterImpl/StepsLikeIterator.scala diff --git a/src/main/scala/scala/compat/java8/converterImpl/StepsLikeSliced.scala b/src/main/scala-2.13-/scala/compat/java8/converterImpl/StepsLikeSliced.scala similarity index 100% rename from src/main/scala/scala/compat/java8/converterImpl/StepsLikeSliced.scala rename to src/main/scala-2.13-/scala/compat/java8/converterImpl/StepsLikeSliced.scala diff --git a/src/main/scala/scala/compat/java8/converterImpl/StepsLikeTrieIterator.scala b/src/main/scala-2.13-/scala/compat/java8/converterImpl/StepsLikeTrieIterator.scala similarity index 100% rename from src/main/scala/scala/compat/java8/converterImpl/StepsLikeTrieIterator.scala rename to src/main/scala-2.13-/scala/compat/java8/converterImpl/StepsLikeTrieIterator.scala diff --git a/src/main/scala/scala/compat/java8/converterImpl/StepsLinearSeq.scala b/src/main/scala-2.13-/scala/compat/java8/converterImpl/StepsLinearSeq.scala similarity index 100% rename from src/main/scala/scala/compat/java8/converterImpl/StepsLinearSeq.scala rename to src/main/scala-2.13-/scala/compat/java8/converterImpl/StepsLinearSeq.scala diff --git a/src/main/scala/scala/compat/java8/converterImpl/StepsMap.scala b/src/main/scala-2.13-/scala/compat/java8/converterImpl/StepsMap.scala similarity index 100% rename from src/main/scala/scala/compat/java8/converterImpl/StepsMap.scala rename to src/main/scala-2.13-/scala/compat/java8/converterImpl/StepsMap.scala diff --git a/src/main/scala/scala/compat/java8/converterImpl/StepsRange.scala b/src/main/scala-2.13-/scala/compat/java8/converterImpl/StepsRange.scala similarity index 100% rename from src/main/scala/scala/compat/java8/converterImpl/StepsRange.scala rename to src/main/scala-2.13-/scala/compat/java8/converterImpl/StepsRange.scala diff --git a/src/main/scala/scala/compat/java8/converterImpl/StepsString.scala b/src/main/scala-2.13-/scala/compat/java8/converterImpl/StepsString.scala similarity index 100% rename from src/main/scala/scala/compat/java8/converterImpl/StepsString.scala rename to src/main/scala-2.13-/scala/compat/java8/converterImpl/StepsString.scala diff --git a/src/main/scala/scala/compat/java8/converterImpl/StepsVector.scala b/src/main/scala-2.13-/scala/compat/java8/converterImpl/StepsVector.scala similarity index 100% rename from src/main/scala/scala/compat/java8/converterImpl/StepsVector.scala rename to src/main/scala-2.13-/scala/compat/java8/converterImpl/StepsVector.scala diff --git a/src/main/scala/scala/compat/java8/converterImpl/StepsWithTail.scala b/src/main/scala-2.13-/scala/compat/java8/converterImpl/StepsWithTail.scala similarity index 100% rename from src/main/scala/scala/compat/java8/converterImpl/StepsWithTail.scala rename to src/main/scala-2.13-/scala/compat/java8/converterImpl/StepsWithTail.scala diff --git a/src/main/scala-2.13-/scala/concurrent/java8/FuturesConvertersImplCompat.scala b/src/main/scala-2.13-/scala/concurrent/java8/FuturesConvertersImplCompat.scala new file mode 100644 index 0000000..4262f7a --- /dev/null +++ b/src/main/scala-2.13-/scala/concurrent/java8/FuturesConvertersImplCompat.scala @@ -0,0 +1,8 @@ +package scala.concurrent.java8 + +import scala.concurrent.Future + +// TODO: make this private[scala] when genjavadoc allows for that. +object FuturesConvertersImplCompat { + def InternalCallbackExecutor = Future.InternalCallbackExecutor +} diff --git a/src/main/scala/scala/compat/java8/FutureConverters.scala b/src/main/scala/scala/compat/java8/FutureConverters.scala index 341f400..794ae7e 100644 --- a/src/main/scala/scala/compat/java8/FutureConverters.scala +++ b/src/main/scala/scala/compat/java8/FutureConverters.scala @@ -15,6 +15,7 @@ package scala.compat.java8 import scala.language.implicitConversions import scala.concurrent.java8.FuturesConvertersImpl._ +import scala.concurrent.java8.FuturesConvertersImplCompat._ import scala.concurrent.{ Future, Promise, ExecutionContext, ExecutionContextExecutorService, ExecutionContextExecutor } import java.util.concurrent.{ CompletionStage, Executor, ExecutorService } import java.util.function.Consumer diff --git a/src/main/scala/scala/compat/java8/PrimitiveIteratorConversions.scala b/src/main/scala/scala/compat/java8/PrimitiveIteratorConversions.scala index 2d80ba6..d0abb4d 100644 --- a/src/main/scala/scala/compat/java8/PrimitiveIteratorConversions.scala +++ b/src/main/scala/scala/compat/java8/PrimitiveIteratorConversions.scala @@ -53,10 +53,10 @@ object PrimitiveIteratorConverters { def nextDouble() = it.next() override def remove(): Unit = { throw new UnsupportedOperationException("remove on scala.collection.Iterator") } override def forEachRemaining(c: java.util.function.Consumer[_ >: java.lang.Double]): Unit = { - while (it.hasNext) c.accept(it.next) + while (it.hasNext) c.accept(it.next()) } override def forEachRemaining(c: java.util.function.DoubleConsumer): Unit = { - while (it.hasNext) c.accept(it.next) + while (it.hasNext) c.accept(it.next()) } } } @@ -74,10 +74,10 @@ object PrimitiveIteratorConverters { def nextInt() = it.next() override def remove(): Unit = { throw new UnsupportedOperationException("remove on scala.collection.Iterator") } override def forEachRemaining(c: java.util.function.Consumer[_ >: java.lang.Integer]): Unit = { - while (it.hasNext) c.accept(it.next) + while (it.hasNext) c.accept(it.next()) } override def forEachRemaining(c: java.util.function.IntConsumer): Unit = { - while (it.hasNext) c.accept(it.next) + while (it.hasNext) c.accept(it.next()) } } } @@ -95,10 +95,10 @@ object PrimitiveIteratorConverters { def nextLong() = it.next() override def remove(): Unit = { throw new UnsupportedOperationException("remove on scala.collection.Iterator") } override def forEachRemaining(c: java.util.function.Consumer[_ >: java.lang.Long]): Unit = { - while (it.hasNext) c.accept(it.next) + while (it.hasNext) c.accept(it.next()) } override def forEachRemaining(c: java.util.function.LongConsumer): Unit = { - while (it.hasNext) c.accept(it.next) + while (it.hasNext) c.accept(it.next()) } } } diff --git a/src/main/scala/scala/concurrent/java8/FutureConvertersImpl.scala b/src/main/scala/scala/concurrent/java8/FutureConvertersImpl.scala index 31a6523..3099d6e 100644 --- a/src/main/scala/scala/concurrent/java8/FutureConvertersImpl.scala +++ b/src/main/scala/scala/concurrent/java8/FutureConvertersImpl.scala @@ -14,20 +14,19 @@ package scala.concurrent.java8 // Located in this package to access private[concurrent] members -import scala.concurrent.{ Future, ExecutionContext } import java.util.concurrent._ +import java.util.function.{BiConsumer, BiFunction, Consumer, Function => JF} + +import scala.concurrent.Future import scala.concurrent.impl.Promise.DefaultPromise -import scala.util.{ Try, Success, Failure } -import java.util.function.{ BiConsumer, Function ⇒ JF, Consumer, BiFunction } +import scala.util.{Failure, Success, Try} // TODO: make this private[scala] when genjavadoc allows for that. object FuturesConvertersImpl { - def InternalCallbackExecutor = Future.InternalCallbackExecutor - class CF[T](val wrapped: Future[T]) extends CompletableFuture[T] with (Try[T] => Unit) { override def apply(t: Try[T]): Unit = t match { - case Success(v) ⇒ complete(v) - case Failure(e) ⇒ completeExceptionally(e) + case Success(v) => complete(v) + case Failure(e) => completeExceptionally(e) } /* @@ -67,7 +66,7 @@ object FuturesConvertersImpl { try { fn(e).asInstanceOf[AnyRef] } catch { - case thr: Throwable ⇒ cf.completeExceptionally(thr); this + case thr: Throwable => cf.completeExceptionally(thr); this } if (n ne this) cf.complete(n.asInstanceOf[T]) } diff --git a/src/test/java/scala/compat/java8/runtime/LambdaDeserializerTest.java b/src/test/java-2.11/scala/compat/java8/runtime/LambdaDeserializerTest.java similarity index 100% rename from src/test/java/scala/compat/java8/runtime/LambdaDeserializerTest.java rename to src/test/java-2.11/scala/compat/java8/runtime/LambdaDeserializerTest.java diff --git a/src/test/java/scala/compat/java8/BoxingTest.java b/src/test/java/scala/compat/java8/BoxingTest.java index e0a2c03..7f798ff 100644 --- a/src/test/java/scala/compat/java8/BoxingTest.java +++ b/src/test/java/scala/compat/java8/BoxingTest.java @@ -18,24 +18,26 @@ public class BoxingTest { @Test public void nullBoxesInterpretedAsZeroF1() { - scala.Function1 jFunction1 = new JFunction1$mcII$sp() { + Object o = new JFunction1$mcII$sp() { @Override public int apply$mcII$sp(int v1) { return v1 + 1; } }; + scala.Function1 jFunction1 = (scala.Function1)o; Integer result = (Integer) jFunction1.apply(null); assert (result.intValue() == 1); } @Test public void nullBoxesInterpretedAsZeroF2() { - scala.Function2 jFunction2 = new JFunction2$mcIII$sp() { + Object o = new JFunction2$mcIII$sp() { @Override public int apply$mcIII$sp(int v1, int v2) { return v1 + v2 + 1; } }; + scala.Function2 jFunction2 = (scala.Function2)o; Integer result = (Integer) jFunction2.apply(null, null); assert (result.intValue() == 1); } diff --git a/src/test/scala-2.13+/scala/compat/java8/StepConvertersTest.scala b/src/test/scala-2.13+/scala/compat/java8/StepConvertersTest.scala new file mode 100644 index 0000000..667e13c --- /dev/null +++ b/src/test/scala-2.13+/scala/compat/java8/StepConvertersTest.scala @@ -0,0 +1,565 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.compat.java8 + +import org.junit.Test +import org.junit.Assert._ + +class StepConvertersTest { + import collectionImpl._ + import converterImpl._ + import StreamConverters._ // Includes StepConverters! + import scala.{ collection => co } + import collection.{ mutable => cm, immutable => ci, concurrent => cc } + + def isAcc[X](x: X): Boolean = x.getClass.getSimpleName.contains("AccumulatorStepper") + + trait SpecCheck { + def check[X](x: X): Boolean + def msg[X](x: X): String + def assert(x: Any): Unit = + if(!check(x)) assertTrue(msg(x), false) + } + object SpecCheck { + def apply(f: Any => Boolean, err: Any => String = (_ => "SpecCheck failed")) = new SpecCheck { + def check[X](x: X): Boolean = f(x) + def msg[X](x: X): String = err(x) + } + } + + def _eh_[X](x: => X)(implicit correctSpec: SpecCheck): Unit = { + assertTrue(x.isInstanceOf[Stepper[_]]) + correctSpec.assert(x) + } + + def IFFY[X](x: => X)(implicit correctSpec: SpecCheck): Unit = { + assertTrue(x.isInstanceOf[Stepper[_]]) + correctSpec.assert(x) + assertTrue(isAcc(x)) + } + + def Okay[X](x: => X)(implicit correctSpec: SpecCheck): Unit = { + assertTrue(x.isInstanceOf[Stepper[_]]) + correctSpec.assert(x) + assertTrue(!isAcc(x)) + } + + def Fine[X](x: => X)(implicit correctSpec: SpecCheck): Unit = { + assertTrue(x.isInstanceOf[Stepper[_]]) + correctSpec.assert(x) + assertTrue(!isAcc(x)) + } + + def good[X](x: => X)(implicit correctSpec: SpecCheck): Unit = { + assertTrue(x.isInstanceOf[Stepper[_]]) + correctSpec.assert(x) + assertTrue(!isAcc(x)) + } + + def Tell[X](x: => X)(implicit correctSpec: SpecCheck): Unit = { + println(x.getClass.getName + " -> " + isAcc(x)) + assertTrue(x.isInstanceOf[Stepper[_]]) + correctSpec.assert(x) + } + + @Test + def comprehensivelyGeneric(): Unit = { + implicit val spec = SpecCheck(_.isInstanceOf[AnyStepper[_]]) + + // Collection section + Okay( co.Iterator[String]("salmon").buffered.stepper ) + good( co.IndexedSeq[String]("salmon").stepper ) + Okay( co.Iterable[String]("salmon").stepper ) + Okay( co.Iterable[String]("salmon").view.stepper ) + Okay( co.Iterator[String]("salmon").stepper ) + Okay( co.LinearSeq[String]("salmon").stepper ) + Okay( co.Map[String, String]("fish" -> "salmon").stepper ) + Okay( co.Map[String, String]("fish" -> "salmon").keyStepper ) + Okay( co.Map[String, String]("fish" -> "salmon").valueStepper ) + Okay( co.Seq[String]("salmon").stepper ) + Okay( co.Seq[String]("salmon").view.stepper ) + Okay( co.Set[String]("salmon").stepper ) + Okay( co.SortedMap[String, String]("fish" -> "salmon").stepper ) + Okay( co.SortedMap[String, String]("fish" -> "salmon").keyStepper ) + Okay( co.SortedMap[String, String]("fish" -> "salmon").valueStepper ) + Okay( co.SortedSet[String]("salmon").stepper ) + IFFY( co.Iterable[String]("salmon").accumulate.stepper ) + IFFY( (co.Iterator[String]("salmon"): co.IterableOnce[String]).accumulate.stepper ) + IFFY( co.Iterable[String]("salmon").view.accumulate.stepper ) + + // Immutable section + Okay( ci.::("salmon", Nil).stepper ) + Okay( (ci.HashMap[String, String]("fish" -> "salmon"): ci.AbstractMap[String, String]).stepper ) + Okay( (ci.HashMap[String, String]("fish" -> "salmon"): ci.AbstractMap[String, String]).keyStepper ) + Okay( (ci.HashMap[String, String]("fish" -> "salmon"): ci.AbstractMap[String, String]).valueStepper ) + good( ci.HashSet[String]("salmon").stepper ) + good( ci.IndexedSeq[String]("salmon").stepper ) + Okay( ci.IntMap[String](123456 -> "salmon").stepper ) + Okay( ci.IntMap[String](123456 -> "salmon").valueStepper ) + Okay( ci.Iterable[String]("salmon").stepper ) + Okay( ci.LinearSeq[String]("salmon").stepper ) + Okay( ci.List[String]("salmon").stepper ) + Okay( ci.ListMap[String, String]("fish" -> "salmon").stepper ) + Okay( ci.ListMap[String, String]("fish" -> "salmon").keyStepper ) + Okay( ci.ListMap[String, String]("fish" -> "salmon").valueStepper ) + Okay( ci.ListSet[String]("salmon").stepper ) + Okay( ci.LongMap[String](9876543210L -> "salmon").stepper ) + Okay( ci.LongMap[String](9876543210L -> "salmon").valueStepper ) + Okay( ci.Map[String, String]("fish" -> "salmon").stepper ) + Okay( ci.Map[String, String]("fish" -> "salmon").keyStepper ) + Okay( ci.Map[String, String]("fish" -> "salmon").valueStepper ) + Okay( ci.Queue[String]("salmon").stepper ) + Okay( ci.Seq[String]("salmon").stepper ) + Okay( ci.Set[String]("salmon").stepper ) + Okay( ci.SortedMap[String, String]("fish" -> "salmon").stepper ) + Okay( ci.SortedMap[String, String]("fish" -> "salmon").keyStepper ) + Okay( ci.SortedMap[String, String]("fish" -> "salmon").valueStepper ) + Okay( ci.SortedSet[String]("salmon").stepper ) + Okay( ci.Stream[String]("salmon").stepper ) + _eh_( ci.Stream[String]("salmon").view.stepper ) + Okay( ci.LazyList[String]("salmon").stepper ) + _eh_( ci.LazyList[String]("salmon").view.stepper ) + IFFY( ci.Iterable[String]("salmon").accumulate.stepper ) + Okay( ci.TreeMap[String, String]("fish" -> "salmon").stepper ) + Okay( ci.TreeMap[String, String]("fish" -> "salmon").keyStepper ) + Okay( ci.TreeMap[String, String]("fish" -> "salmon").valueStepper ) + Okay( ci.TreeSet[String]("salmon").stepper ) + good( ci.Vector[String]("salmon").stepper ) + + // Mutable section + Okay( (cm.ArrayBuffer[String]("salmon"): cm.AbstractBuffer[String]).stepper ) + Okay( (cm.PriorityQueue[String]("salmon"): cm.AbstractIterable[String]).stepper ) + Okay( (cm.HashMap[String, String]("fish" -> "salmon"): cm.AbstractMap[String, String]).stepper ) + Okay( (cm.HashMap[String, String]("fish" -> "salmon"): cm.AbstractMap[String, String]).keyStepper ) + Okay( (cm.HashMap[String, String]("fish" -> "salmon"): cm.AbstractMap[String, String]).valueStepper ) + Okay( (cm.ArrayBuffer[String]("salmon"): cm.AbstractSeq[String]).stepper ) + Okay( (cm.HashSet[String]("salmon"): cm.AbstractSet[String]).stepper ) + Okay( cm.AnyRefMap[String,String]("fish" -> "salmon").stepper ) + Okay( cm.AnyRefMap[String,String]("fish" -> "salmon").keyStepper ) + Okay( cm.AnyRefMap[String,String]("fish" -> "salmon").valueStepper ) + good( cm.ArrayBuffer[String]("salmon").stepper ) + good( (Array("salmon"): cm.ArraySeq[String]).stepper ) + good( cm.ArraySeq[String]("salmon").stepper ) + _eh_( cm.ArrayStack[String]("salmon").stepper ) + Okay( (cm.ArrayBuffer[String]("salmon"): cm.Buffer[String]).stepper ) + good( cm.HashMap[String, String]("fish" -> "salmon").stepper ) + good( cm.HashMap[String, String]("fish" -> "salmon").keyStepper ) + good( cm.HashMap[String, String]("fish" -> "salmon").valueStepper ) + good( cm.HashSet[String]("salmon").stepper ) + good( cm.IndexedSeq[String]("salmon").stepper ) + good( cm.IndexedSeq[String]("salmon").view.stepper ) + Okay( cm.Iterable[String]("salmon").stepper ) + good( cm.LinkedHashMap[String, String]("fish" -> "salmon").stepper ) + good( cm.LinkedHashMap[String, String]("fish" -> "salmon").keyStepper ) + good( cm.LinkedHashMap[String, String]("fish" -> "salmon").valueStepper ) + Okay( cm.LinkedHashSet[String]("salmon").stepper ) + Okay( cm.ListBuffer[String]("salmon").stepper ) + Okay( cm.ListMap[String, String]("fish" -> "salmon").stepper ) + Okay( cm.ListMap[String, String]("fish" -> "salmon").keyStepper ) + Okay( cm.ListMap[String, String]("fish" -> "salmon").keyStepper ) + Okay( cm.LongMap[String](9876543210L -> "salmon").stepper ) + Okay( cm.LongMap[String](9876543210L -> "salmon").valueStepper ) + Okay( cm.Map[String, String]("fish" -> "salmon").stepper ) + Okay( cm.Map[String, String]("fish" -> "salmon").keyStepper ) + Okay( cm.Map[String, String]("fish" -> "salmon").valueStepper ) + Okay( cm.OpenHashMap[String, String]("fish" -> "salmon").stepper ) + Okay( cm.OpenHashMap[String, String]("fish" -> "salmon").keyStepper ) + Okay( cm.OpenHashMap[String, String]("fish" -> "salmon").valueStepper ) + Okay( cm.PriorityQueue[String]("salmon").stepper ) + Fine( cm.Queue[String]("salmon").stepper ) // Used to be `Good` in 2.12, in 2.13 `Queue` is no longer a `LinearSeq` + Okay( cm.Seq[String]("salmon").stepper ) + Okay( cm.Set[String]("salmon").stepper ) + Okay( cm.SortedSet[String]("salmon").stepper ) + Fine( cm.Stack[String]("salmon").stepper ) // Used to be `Good` in 2.12, in 2.13 `Stack` is no longer a `LinearSeq` + IFFY( cm.Iterable[String]("salmon").accumulate.stepper ) + Okay( cm.TreeSet[String]("salmon").stepper ) + Okay( cm.UnrolledBuffer[String]("salmon").stepper ) + Okay( cm.WeakHashMap[String, String]("fish" -> "salmon").stepper ) + Okay( cm.WeakHashMap[String, String]("fish" -> "salmon").keyStepper ) + Okay( cm.WeakHashMap[String, String]("fish" -> "salmon").valueStepper ) + + // Java 6 converters section + + // Concurrent section + Okay( cc.TrieMap[String, String]("fish" -> "salmon").stepper ) + Okay( cc.TrieMap[String, String]("fish" -> "salmon").keyStepper ) + Okay( cc.TrieMap[String, String]("fish" -> "salmon").keyStepper ) + Okay( (cc.TrieMap[String, String]("fish" -> "salmon"): cc.Map[String, String]).stepper ) + Okay( (cc.TrieMap[String, String]("fish" -> "salmon"): cc.Map[String, String]).keyStepper ) + Okay( (cc.TrieMap[String, String]("fish" -> "salmon"): cc.Map[String, String]).valueStepper ) + } + + @Test + def comprehensivelyDouble(): Unit = { + implicit val spec = SpecCheck(_.isInstanceOf[DoubleStepper]) + //Double-specific tests + + // Collection section + Okay( co.Iterator[Double](3.14159).buffered.stepper ) + good( co.IndexedSeq[Double](3.14159).stepper ) + Okay( co.Iterable[Double](3.14159).stepper ) + Okay( co.Iterable[Double](3.14159).view.stepper ) + Okay( co.Iterator[Double](3.14159).stepper ) + Okay( co.LinearSeq[Double](3.14159).stepper ) + Okay( co.Map[Double, Double](2.718281828 -> 3.14159).keyStepper ) + Okay( co.Map[Double, Double](2.718281828 -> 3.14159).valueStepper ) + Okay( co.Seq[Double](3.14159).stepper ) + Okay( co.Seq[Double](3.14159).view.stepper ) + Okay( co.Set[Double](3.14159).stepper ) + Okay( co.SortedMap[Double, Double](2.718281828 -> 3.14159).keyStepper ) + Okay( co.SortedMap[Double, Double](2.718281828 -> 3.14159).valueStepper ) + Okay( co.SortedSet[Double](3.14159).stepper ) + IFFY( co.Iterable[Double](3.14159).accumulate.stepper ) + IFFY( (co.Iterator[Double](3.14159): co.IterableOnce[Double]).accumulate.stepper ) + IFFY( co.Iterable[Double](3.14159).view.accumulate.stepper ) + + // Immutable section + Okay( ci.::(3.14159, Nil).stepper ) + Okay( (ci.HashMap[Double, Double](2.718281828 -> 3.14159): ci.AbstractMap[Double, Double]).keyStepper ) + Okay( (ci.HashMap[Double, Double](2.718281828 -> 3.14159): ci.AbstractMap[Double, Double]).valueStepper ) + good( ci.HashSet[Double](3.14159).stepper ) + good( ci.IndexedSeq[Double](3.14159).stepper ) + Okay( ci.IntMap[Double](123456 -> 3.14159).valueStepper ) + Okay( ci.Iterable[Double](3.14159).stepper ) + Okay( ci.LinearSeq[Double](3.14159).stepper ) + Okay( ci.List[Double](3.14159).stepper ) + Okay( ci.ListMap[Double, Double](2.718281828 -> 3.14159).keyStepper ) + Okay( ci.ListMap[Double, Double](2.718281828 -> 3.14159).valueStepper ) + Okay( ci.ListSet[Double](3.14159).stepper ) + Okay( ci.LongMap[Double](9876543210L -> 3.14159).valueStepper ) + Okay( ci.Map[Double, Double](2.718281828 -> 3.14159).keyStepper ) + Okay( ci.Map[Double, Double](2.718281828 -> 3.14159).valueStepper ) + Okay( ci.Queue[Double](3.14159).stepper ) + Okay( ci.Seq[Double](3.14159).stepper ) + Okay( ci.Set[Double](3.14159).stepper ) + Okay( ci.SortedMap[Double, Double](2.718281828 -> 3.14159).keyStepper ) + Okay( ci.SortedMap[Double, Double](2.718281828 -> 3.14159).valueStepper ) + Okay( ci.SortedSet[Double](3.14159).stepper ) + Okay( ci.Stream[Double](3.14159).stepper ) + _eh_( ci.Stream[Double](3.14159).view.stepper ) + Okay( ci.LazyList[Double](3.14159).stepper ) + _eh_( ci.LazyList[Double](3.14159).view.stepper ) + IFFY( ci.Iterable[Double](3.14159).accumulate.stepper ) + Okay( ci.TreeMap[Double, Double](2.718281828 -> 3.14159).keyStepper ) + Okay( ci.TreeMap[Double, Double](2.718281828 -> 3.14159).valueStepper ) + Okay( ci.TreeSet[Double](3.14159).stepper ) + good( ci.Vector[Double](3.14159).stepper ) + + // Mutable section + Okay( (cm.ArrayBuffer[Double](3.14159): cm.AbstractBuffer[Double]).stepper ) + Okay( (cm.PriorityQueue[Double](3.14159): cm.AbstractIterable[Double]).stepper ) + Okay( (cm.HashMap[Double, Double](2.718281828 -> 3.14159): cm.AbstractMap[Double, Double]).keyStepper ) + Okay( (cm.HashMap[Double, Double](2.718281828 -> 3.14159): cm.AbstractMap[Double, Double]).valueStepper ) + Okay( (cm.ArrayBuffer[Double](3.14159): cm.AbstractSeq[Double]).stepper ) + Okay( (cm.HashSet[Double](3.14159): cm.AbstractSet[Double]).stepper ) + Okay( cm.AnyRefMap[String,Double]("fish" -> 3.14159).valueStepper ) + good( cm.ArrayBuffer[Double](3.14159).stepper ) + good( (Array(3.14159): cm.ArraySeq[Double]).stepper ) + good( cm.ArraySeq[Double](3.14159).stepper ) + _eh_( cm.ArrayStack[Double](3.14159).stepper ) + Okay( (cm.ArrayBuffer[Double](3.14159): cm.Buffer[Double]).stepper ) + good( cm.HashMap[Double, Double](2.718281828 -> 3.14159).keyStepper ) + good( cm.HashMap[Double, Double](2.718281828 -> 3.14159).valueStepper ) + good( cm.HashSet[Double](3.14159).stepper ) + good( cm.IndexedSeq[Double](3.14159).stepper ) + good( cm.IndexedSeq[Double](3.14159).view.stepper ) + Okay( cm.Iterable[Double](3.14159).stepper ) + good( cm.LinkedHashMap[Double, Double](2.718281828 -> 3.14159).keyStepper ) + good( cm.LinkedHashMap[Double, Double](2.718281828 -> 3.14159).valueStepper ) + Okay( cm.LinkedHashSet[Double](3.14159).stepper ) + Okay( cm.ListBuffer[Double](3.14159).stepper ) + Okay( cm.ListMap[Double, Double](2.718281828 -> 3.14159).keyStepper ) + Okay( cm.ListMap[Double, Double](2.718281828 -> 3.14159).valueStepper ) + Okay( cm.LongMap[Double](9876543210L -> 3.14159).valueStepper ) + Okay( cm.Map[Double, Double](2.718281828 -> 3.14159).keyStepper ) + Okay( cm.Map[Double, Double](2.718281828 -> 3.14159).valueStepper ) + Okay( cm.OpenHashMap[Double, Double](2.718281828 -> 3.14159).keyStepper ) + Okay( cm.OpenHashMap[Double, Double](2.718281828 -> 3.14159).valueStepper ) + Okay( cm.PriorityQueue[Double](3.14159).stepper ) + Fine( cm.Queue[Double](3.14159).stepper ) // Used to be `Good` in 2.12, in 2.13 `Queue` is no longer a `LinearSeq` + Okay( cm.Seq[Double](3.14159).stepper ) + Okay( cm.Set[Double](3.14159).stepper ) + Okay( cm.SortedSet[Double](3.14159).stepper ) + Fine( cm.Stack[Double](3.14159).stepper ) // Used to be `Good` in 2.12, in 2.13 `Stack` is no longer a `LinearSeq` + IFFY( cm.Iterable[Double](3.14159).accumulate.stepper ) + Okay( cm.TreeSet[Double](3.14159).stepper ) + Okay( cm.UnrolledBuffer[Double](3.14159).stepper ) + Okay( cm.WeakHashMap[Double, Double](2.718281828 -> 3.14159).keyStepper ) + Okay( cm.WeakHashMap[Double, Double](2.718281828 -> 3.14159).valueStepper ) + + // Java 6 converters section + + // Concurrent section + Okay( cc.TrieMap[Double, Double](2.718281828 -> 3.14159).keyStepper ) + Okay( cc.TrieMap[Double, Double](2.718281828 -> 3.14159).valueStepper ) + Okay( (cc.TrieMap[Double, Double](2.718281828 -> 3.14159): cc.Map[Double, Double]).keyStepper ) + Okay( (cc.TrieMap[Double, Double](2.718281828 -> 3.14159): cc.Map[Double, Double]).valueStepper ) + } + + @Test + def comprehensivelyInt(): Unit = { + implicit val spec = SpecCheck(_.isInstanceOf[IntStepper], x => s"$x should be an IntStepper") + + // Int-specific tests + good( co.BitSet(42).stepper ) + good( ci.BitSet(42).stepper ) + good( ci.NumericRange(123456, 123458, 1).stepper ) + good( cm.BitSet(42).stepper ) + good( (1 until 2).stepper ) + Okay( ci.IntMap[String](123456 -> "salmon").keyStepper ) + Okay( ci.IntMap[Double](123456 -> 3.14159).keyStepper ) + Okay( ci.IntMap[Long](123456 -> 0x123456789L).keyStepper ) + + // Collection section + Okay( co.Iterator[Int](654321).buffered.stepper ) + good( co.IndexedSeq[Int](654321).stepper ) + Okay( co.Iterable[Int](654321).stepper ) + Okay( co.Iterable[Int](654321).view.stepper ) + Okay( co.Iterator[Int](654321).stepper ) + Okay( co.LinearSeq[Int](654321).stepper ) + Okay( co.Map[Int, Int](0xDEEDED -> 654321).keyStepper ) + Okay( co.Map[Int, Int](0xDEEDED -> 654321).valueStepper ) + Okay( co.Seq[Int](654321).stepper ) + Okay( co.Seq[Int](654321).view.stepper ) + Okay( co.Set[Int](654321).stepper ) + Okay( co.SortedMap[Int, Int](0xDEEDED -> 654321).keyStepper ) + Okay( co.SortedMap[Int, Int](0xDEEDED -> 654321).valueStepper ) + Okay( co.SortedSet[Int](654321).stepper ) + IFFY( co.Iterable[Int](654321).accumulate.stepper ) + IFFY( (co.Iterator[Int](654321): co.IterableOnce[Int]).accumulate.stepper ) + IFFY( co.Iterable[Int](654321).view.accumulate.stepper ) + + // Immutable section + Okay( ci.::(654321, Nil).stepper ) + Okay( (ci.HashMap[Int, Int](0xDEEDED -> 654321): ci.AbstractMap[Int, Int]).keyStepper ) + Okay( (ci.HashMap[Int, Int](0xDEEDED -> 654321): ci.AbstractMap[Int, Int]).valueStepper ) + good( ci.HashSet[Int](654321).stepper ) + good( ci.IndexedSeq[Int](654321).stepper ) + Okay( ci.IntMap[Int](123456 -> 654321).keyStepper ) + Okay( ci.IntMap[Int](123456 -> 654321).valueStepper ) + Okay( ci.Iterable[Int](654321).stepper ) + Okay( ci.LinearSeq[Int](654321).stepper ) + Okay( ci.List[Int](654321).stepper ) + Okay( ci.ListMap[Int, Int](0xDEEDED -> 654321).keyStepper ) + Okay( ci.ListMap[Int, Int](0xDEEDED -> 654321).valueStepper ) + Okay( ci.ListSet[Int](654321).stepper ) + Okay( ci.LongMap[Int](9876543210L -> 654321).valueStepper ) + Okay( ci.Map[Int, Int](0xDEEDED -> 654321).keyStepper ) + Okay( ci.Map[Int, Int](0xDEEDED -> 654321).valueStepper ) + Okay( ci.Queue[Int](654321).stepper ) + Okay( ci.Seq[Int](654321).stepper ) + Okay( ci.Set[Int](654321).stepper ) + Okay( ci.SortedMap[Int, Int](0xDEEDED -> 654321).keyStepper ) + Okay( ci.SortedMap[Int, Int](0xDEEDED -> 654321).valueStepper ) + Okay( ci.SortedSet[Int](654321).stepper ) + Okay( ci.Stream[Int](654321).stepper ) + _eh_( ci.Stream[Int](654321).view.stepper ) + Okay( ci.LazyList[Int](654321).stepper ) + _eh_( ci.LazyList[Int](654321).view.stepper ) + IFFY( ci.Iterable[Int](654321).accumulate.stepper ) + Okay( ci.TreeMap[Int, Int](0xDEEDED -> 654321).keyStepper ) + Okay( ci.TreeMap[Int, Int](0xDEEDED -> 654321).valueStepper ) + Okay( ci.TreeSet[Int](654321).stepper ) + good( ci.Vector[Int](654321).stepper ) + + // Mutable section + Okay( (cm.ArrayBuffer[Int](654321): cm.AbstractBuffer[Int]).stepper ) + Okay( (cm.PriorityQueue[Int](654321): cm.AbstractIterable[Int]).stepper ) + Okay( (cm.HashMap[Int, Int](0xDEEDED -> 654321): cm.AbstractMap[Int, Int]).keyStepper ) + Okay( (cm.HashMap[Int, Int](0xDEEDED -> 654321): cm.AbstractMap[Int, Int]).valueStepper ) + Okay( (cm.ArrayBuffer[Int](654321): cm.AbstractSeq[Int]).stepper ) + Okay( (cm.HashSet[Int](654321): cm.AbstractSet[Int]).stepper ) + Okay( cm.AnyRefMap[String, Int]("fish" -> 654321).valueStepper ) + good( cm.ArrayBuffer[Int](654321).stepper ) + good( (Array(654321): cm.ArraySeq[Int]).stepper ) + good( cm.ArraySeq[Int](654321).stepper ) + _eh_( cm.ArrayStack[Int](654321).stepper ) + Okay( (cm.ArrayBuffer[Int](654321): cm.Buffer[Int]).stepper ) + good( cm.HashMap[Int, Int](0xDEEDED -> 654321).keyStepper ) + good( cm.HashMap[Int, Int](0xDEEDED -> 654321).valueStepper ) + good( cm.HashSet[Int](654321).stepper ) + good( cm.IndexedSeq[Int](654321).stepper ) + good( cm.IndexedSeq[Int](654321).view.stepper ) + Okay( cm.Iterable[Int](654321).stepper ) + good( cm.LinkedHashMap[Int, Int](0xDEEDED -> 654321).keyStepper ) + good( cm.LinkedHashMap[Int, Int](0xDEEDED -> 654321).valueStepper ) + Okay( cm.LinkedHashSet[Int](654321).stepper ) + Okay( cm.ListBuffer[Int](654321).stepper ) + Okay( cm.ListMap[Int, Int](0xDEEDED -> 654321).keyStepper ) + Okay( cm.ListMap[Int, Int](0xDEEDED -> 654321).valueStepper ) + Okay( cm.LongMap[Int](9876543210L -> 654321).valueStepper ) + Okay( cm.Map[Int, Int](0xDEEDED -> 654321).keyStepper ) + Okay( cm.Map[Int, Int](0xDEEDED -> 654321).valueStepper ) + Okay( cm.OpenHashMap[Int, Int](0xDEEDED -> 654321).keyStepper ) + Okay( cm.OpenHashMap[Int, Int](0xDEEDED -> 654321).valueStepper ) + Okay( cm.PriorityQueue[Int](654321).stepper ) + Fine( cm.Queue[Int](654321).stepper ) // Used to be `Good` in 2.12, in 2.13 `Queue` is no longer a `LinearSeq` + Okay( cm.Seq[Int](654321).stepper ) + Okay( cm.Set[Int](654321).stepper ) + Okay( cm.SortedSet[Int](654321).stepper ) + Fine( cm.Stack[Int](654321).stepper ) // Used to be `Good` in 2.12, in 2.13 `Stack` is no longer a `LinearSeq` + IFFY( cm.Iterable[Int](654321).accumulate.stepper ) + Okay( cm.TreeSet[Int](654321).stepper ) + Okay( cm.UnrolledBuffer[Int](654321).stepper ) + Okay( cm.WeakHashMap[Int, Int](0xDEEDED -> 654321).keyStepper ) + Okay( cm.WeakHashMap[Int, Int](0xDEEDED -> 654321).valueStepper ) + + // Java 6 converters section + + // Concurrent section + Okay( cc.TrieMap[Int, Int](0xDEEDED -> 654321).keyStepper ) + Okay( cc.TrieMap[Int, Int](0xDEEDED -> 654321).valueStepper ) + Okay( (cc.TrieMap[Int, Int](0xDEEDED -> 654321): cc.Map[Int, Int]).keyStepper ) + Okay( (cc.TrieMap[Int, Int](0xDEEDED -> 654321): cc.Map[Int, Int]).valueStepper ) + } + + @Test + def shortWidening(): Unit = { + implicit val spec = SpecCheck(_.isInstanceOf[IntStepper], x => s"$x should be an IntStepper") + + good( Array[Short](654321.toShort).stepper ) + good( (Array[Short](654321.toShort): cm.ArraySeq[Short]).stepper ) + + //TODO: None of these currently work because there are no native Stepper implementations: + + //good( ci.NumericRange(123456.toShort, 123458.toShort, 1.toShort).stepper ) + //good( ((Array[Short](654321.toShort): cm.ArraySeq[Short]): cm.ArrayLike[Short, cm.ArraySeq[Short]]).stepper ) + //good( (Array[Short](654321.toShort): cm.ArrayOps[Short]).stepper ) + //good( cm.ResizableArray[Short](654321.toShort).stepper ) + } + + @Test + def comprehensivelyLong(): Unit = { + implicit val spec = SpecCheck(_.isInstanceOf[LongStepper]) + + // Long-specific tests + good( ci.NumericRange(9876543210L, 9876543212L, 1L).stepper ) + Okay( ci.LongMap[String](9876543210L -> "salmon").keyStepper ) + Okay( cm.LongMap[String](9876543210L -> "salmon").keyStepper ) + Okay( ci.LongMap[Double](9876543210L -> 3.14159).keyStepper ) + Okay( cm.LongMap[Double](9876543210L -> 3.14159).keyStepper ) + Okay( ci.LongMap[Int](9876543210L -> 654321).keyStepper ) + Okay( cm.LongMap[Int](9876543210L -> 654321).keyStepper ) + + // Collection section + Okay( co.Iterator[Long](0x123456789L).buffered.stepper ) + good( co.IndexedSeq[Long](0x123456789L).stepper ) + Okay( co.Iterable[Long](0x123456789L).stepper ) + Okay( co.Iterable[Long](0x123456789L).view.stepper ) + Okay( co.Iterator[Long](0x123456789L).stepper ) + Okay( co.LinearSeq[Long](0x123456789L).stepper ) + Okay( co.Map[Long, Long](1234567654321L -> 0x123456789L).keyStepper ) + Okay( co.Map[Long, Long](1234567654321L -> 0x123456789L).valueStepper ) + Okay( co.Seq[Long](0x123456789L).stepper ) + Okay( co.Seq[Long](0x123456789L).view.stepper ) + Okay( co.Set[Long](0x123456789L).stepper ) + Okay( co.SortedMap[Long, Long](1234567654321L -> 0x123456789L).keyStepper ) + Okay( co.SortedMap[Long, Long](1234567654321L -> 0x123456789L).valueStepper ) + Okay( co.SortedSet[Long](0x123456789L).stepper ) + IFFY( co.Iterable[Long](0x123456789L).accumulate.stepper ) + IFFY( (co.Iterator[Long](0x123456789L): co.IterableOnce[Long]).accumulate.stepper ) + IFFY( co.Iterable[Long](0x123456789L).view.accumulate.stepper ) + + // Immutable section + Okay( ci.::(0x123456789L, Nil).stepper ) + Okay( (ci.HashMap[Long, Long](1234567654321L -> 0x123456789L): ci.AbstractMap[Long, Long]).keyStepper ) + Okay( (ci.HashMap[Long, Long](1234567654321L -> 0x123456789L): ci.AbstractMap[Long, Long]).valueStepper ) + good( ci.HashSet[Long](0x123456789L).stepper ) + good( ci.IndexedSeq[Long](0x123456789L).stepper ) + Okay( ci.IntMap[Long](123456 -> 0x123456789L).valueStepper ) + Okay( ci.Iterable[Long](0x123456789L).stepper ) + Okay( ci.LinearSeq[Long](0x123456789L).stepper ) + Okay( ci.List[Long](0x123456789L).stepper ) + Okay( ci.ListMap[Long, Long](1234567654321L -> 0x123456789L).keyStepper ) + Okay( ci.ListMap[Long, Long](1234567654321L -> 0x123456789L).valueStepper ) + Okay( ci.ListSet[Long](0x123456789L).stepper ) + Okay( ci.LongMap[Long](9876543210L -> 0x123456789L).keyStepper ) + Okay( ci.LongMap[Long](9876543210L -> 0x123456789L).valueStepper ) + Okay( ci.Map[Long, Long](1234567654321L -> 0x123456789L).keyStepper ) + Okay( ci.Map[Long, Long](1234567654321L -> 0x123456789L).valueStepper ) + Okay( ci.Queue[Long](0x123456789L).stepper ) + Okay( ci.Seq[Long](0x123456789L).stepper ) + Okay( ci.Set[Long](0x123456789L).stepper ) + Okay( ci.SortedMap[Long, Long](1234567654321L -> 0x123456789L).keyStepper ) + Okay( ci.SortedMap[Long, Long](1234567654321L -> 0x123456789L).valueStepper ) + Okay( ci.SortedSet[Long](0x123456789L).stepper ) + Okay( ci.Stream[Long](0x123456789L).stepper ) + _eh_( ci.Stream[Long](0x123456789L).view.stepper ) + Okay( ci.LazyList[Long](0x123456789L).stepper ) + _eh_( ci.LazyList[Long](0x123456789L).view.stepper ) + IFFY( ci.Iterable[Long](0x123456789L).accumulate.stepper ) + Okay( ci.TreeMap[Long, Long](1234567654321L -> 0x123456789L).keyStepper ) + Okay( ci.TreeMap[Long, Long](1234567654321L -> 0x123456789L).valueStepper ) + Okay( ci.TreeSet[Long](0x123456789L).stepper ) + good( ci.Vector[Long](0x123456789L).stepper ) + + // Mutable section + Okay( (cm.ArrayBuffer[Long](0x123456789L): cm.AbstractBuffer[Long]).stepper ) + Okay( (cm.PriorityQueue[Long](0x123456789L): cm.AbstractIterable[Long]).stepper ) + Okay( (cm.HashMap[Long, Long](1234567654321L -> 0x123456789L): cm.AbstractMap[Long, Long]).keyStepper ) + Okay( (cm.HashMap[Long, Long](1234567654321L -> 0x123456789L): cm.AbstractMap[Long, Long]).valueStepper ) + Okay( (cm.ArrayBuffer[Long](0x123456789L): cm.AbstractSeq[Long]).stepper ) + Okay( (cm.HashSet[Long](0x123456789L): cm.AbstractSet[Long]).stepper ) + Okay( cm.AnyRefMap[String,Long]("fish" -> 0x123456789L).valueStepper ) + good( cm.ArrayBuffer[Long](0x123456789L).stepper ) + good( (Array(0x123456789L): cm.ArraySeq[Long]).stepper ) + good( cm.ArraySeq[Long](0x123456789L).stepper ) + _eh_( cm.ArrayStack[Long](0x123456789L).stepper ) + Okay( (cm.ArrayBuffer[Long](0x123456789L): cm.Buffer[Long]).stepper ) + good( cm.HashMap[Long, Long](1234567654321L -> 0x123456789L).keyStepper ) + good( cm.HashMap[Long, Long](1234567654321L -> 0x123456789L).valueStepper ) + good( cm.HashSet[Long](0x123456789L).stepper ) + good( cm.IndexedSeq[Long](0x123456789L).stepper ) + good( cm.IndexedSeq[Long](0x123456789L).view.stepper ) + Okay( cm.Iterable[Long](0x123456789L).stepper ) + good( cm.LinkedHashMap[Long, Long](1234567654321L -> 0x123456789L).keyStepper ) + good( cm.LinkedHashMap[Long, Long](1234567654321L -> 0x123456789L).valueStepper ) + Okay( cm.LinkedHashSet[Long](0x123456789L).stepper ) + Okay( cm.ListBuffer[Long](0x123456789L).stepper ) + Okay( cm.ListMap[Long, Long](1234567654321L -> 0x123456789L).keyStepper ) + Okay( cm.ListMap[Long, Long](1234567654321L -> 0x123456789L).valueStepper ) + Okay( cm.LongMap[Long](9876543210L -> 0x123456789L).keyStepper ) + Okay( cm.LongMap[Long](9876543210L -> 0x123456789L).valueStepper ) + Okay( cm.Map[Long, Long](1234567654321L -> 0x123456789L).keyStepper ) + Okay( cm.Map[Long, Long](1234567654321L -> 0x123456789L).valueStepper ) + Okay( cm.OpenHashMap[Long, Long](1234567654321L -> 0x123456789L).keyStepper ) + Okay( cm.OpenHashMap[Long, Long](1234567654321L -> 0x123456789L).valueStepper ) + Okay( cm.PriorityQueue[Long](0x123456789L).stepper ) + Fine( cm.Queue[Long](0x123456789L).stepper ) // Used to be `Good` in 2.12, in 2.13 `Queue` is no longer a `LinearSeq` + Okay( cm.Seq[Long](0x123456789L).stepper ) + Okay( cm.Set[Long](0x123456789L).stepper ) + Okay( cm.SortedSet[Long](0x123456789L).stepper ) + Fine( cm.Stack[Long](0x123456789L).stepper ) // Used to be `Good` in 2.12, in 2.13 `Stack` is no longer a `LinearSeq` + IFFY( cm.Iterable[Long](0x123456789L).accumulate.stepper ) + Okay( cm.TreeSet[Long](0x123456789L).stepper ) + Okay( cm.UnrolledBuffer[Long](0x123456789L).stepper ) + Okay( cm.WeakHashMap[Long, Long](1234567654321L -> 0x123456789L).keyStepper ) + Okay( cm.WeakHashMap[Long, Long](1234567654321L -> 0x123456789L).valueStepper ) + + // Java 6 converters section + + // Concurrent section + Okay( cc.TrieMap[Long, Long](1234567654321L -> 0x123456789L).keyStepper ) + Okay( cc.TrieMap[Long, Long](1234567654321L -> 0x123456789L).valueStepper ) + Okay( (cc.TrieMap[Long, Long](1234567654321L -> 0x123456789L): cc.Map[Long, Long]).keyStepper ) + Okay( (cc.TrieMap[Long, Long](1234567654321L -> 0x123456789L): cc.Map[Long, Long]).valueStepper ) + } + + @Test + def comprehensivelySpecific(): Unit = { + implicit val spec = SpecCheck(_.isInstanceOf[IntStepper], x => s"$x should be an IntStepper") + + good( ci.NumericRange(277: Short, 279: Short, 1: Short).stepper ) + good( ("salmon": ci.WrappedString).stepper ) + } +} diff --git a/src/test/scala-2.13+/scala/compat/java8/StepperTest.scala b/src/test/scala-2.13+/scala/compat/java8/StepperTest.scala new file mode 100644 index 0000000..e8e51ec --- /dev/null +++ b/src/test/scala-2.13+/scala/compat/java8/StepperTest.scala @@ -0,0 +1,319 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.compat.java8 + +import java.util + +import org.junit.Test +import org.junit.Assert._ +import java.util.Spliterator + +import collectionImpl._ +import StreamConverters._ +import scala.collection.{AnyStepper, IntStepper} + + +class IncStepperA(private val size0: Long) extends IntStepper { + if (size0 < 0) throw new IllegalArgumentException("Size must be >= 0L") + private var i = 0L + def characteristics = Spliterator.SIZED | Spliterator.SUBSIZED | Spliterator.ORDERED + override def estimateSize: Long = math.max(0L, size0 - i) + def hasStep = i < size0 + def nextStep() = { i += 1; (i - 1).toInt } + def trySplit() = if (estimateSize <= 1) null else { + val sub = new IncStepperA(size0 - (size0 - i)/2) + sub.i = i + i = sub.size0 + sub + } +} + +class IncSpliterator(private val size0: Long) extends Spliterator.OfInt { + if (size0 < 0) throw new IllegalArgumentException("Size must be >= 0L") + private var i = 0L + def characteristics = Spliterator.SIZED | Spliterator.SUBSIZED | Spliterator.ORDERED + def estimateSize() = math.max(0L, size0 - i) + def tryAdvance(f: java.util.function.IntConsumer): Boolean = if (i >= size0) false else { f.accept(i.toInt); i += 1; true } + def trySplit(): Spliterator.OfInt = if (i+1 >= size0) null else { + val sub = new IncSpliterator(size0 - (size0 - i)/2) + sub.i = i + i = sub.size0 + sub + } + override def forEachRemaining(f: java.util.function.IntConsumer): Unit = { while (i < size0) { f.accept(i.toInt); i += 1 } } +} + +class MappingStepper[@specialized (Double, Int, Long) A, @specialized(Double, Int, Long) B](underlying: Stepper[A], mapping: A => B) extends Stepper[B] { + def characteristics = underlying.characteristics + def hasStep = underlying.hasStep + def nextStep() = mapping(underlying.nextStep()) + + override def trySplit(): Stepper[B] = { + val r = underlying.trySplit() + if (r == null) null else new MappingStepper[A, B](r, mapping) + } + + override def estimateSize: Long = underlying.estimateSize + + override def javaIterator[C >: B]: util.Iterator[_] = new util.Iterator[B] { + override def hasNext: Boolean = underlying.hasStep + override def next(): B = mapping(underlying.nextStep()) + } + def substep() = { + val undersub = underlying.substep() + if (undersub == null) null + else new MappingStepper(undersub, mapping) + } + def spliterator[C >: B]: Spliterator[_] = new MappingSpliterator[A, B](underlying.spliterator.asInstanceOf[Spliterator[A]], mapping) +} + +class MappingSpliterator[A, B](private val underlying: Spliterator[A], mapping: A => B) extends Spliterator[B] { + def characteristics = underlying.characteristics + def estimateSize() = underlying.estimateSize() + def tryAdvance(f: java.util.function.Consumer[_ >: B]): Boolean = underlying.tryAdvance(new java.util.function.Consumer[A]{ def accept(a: A): Unit = { f.accept(mapping(a)) } }) + def trySplit(): Spliterator[B] = { + val undersplit = underlying.trySplit() + if (undersplit == null) null + else new MappingSpliterator(undersplit, mapping) + } +} +class IntToGenericSpliterator[A](private val underlying: Spliterator.OfInt, mapping: Int => A) extends Spliterator[A] { + def characteristics = underlying.characteristics + def estimateSize() = underlying.estimateSize() + def tryAdvance(f: java.util.function.Consumer[_ >: A]): Boolean = underlying.tryAdvance(new java.util.function.IntConsumer{ def accept(a: Int): Unit = { f.accept(mapping(a)) } }) + def trySplit(): Spliterator[A] = { + val undersplit = underlying.trySplit() + if (undersplit == null) null + else new IntToGenericSpliterator[A](undersplit, mapping) + } +} +class IntToDoubleSpliterator(private val underlying: Spliterator.OfInt, mapping: Int => Double) extends Spliterator.OfDouble { + def characteristics = underlying.characteristics + def estimateSize() = underlying.estimateSize() + def tryAdvance(f: java.util.function.DoubleConsumer): Boolean = underlying.tryAdvance(new java.util.function.IntConsumer{ def accept(a: Int): Unit = { f.accept(mapping(a)) } }) + def trySplit(): Spliterator.OfDouble = { + val undersplit = underlying.trySplit() + if (undersplit == null) null + else new IntToDoubleSpliterator(undersplit, mapping) + } +} +class IntToLongSpliterator(private val underlying: Spliterator.OfInt, mapping: Int => Long) extends Spliterator.OfLong { + def characteristics = underlying.characteristics + def estimateSize() = underlying.estimateSize() + def tryAdvance(f: java.util.function.LongConsumer): Boolean = underlying.tryAdvance(new java.util.function.IntConsumer{ def accept(a: Int): Unit = { f.accept(mapping(a)) } }) + def trySplit(): Spliterator.OfLong = { + val undersplit = underlying.trySplit() + if (undersplit == null) null + else new IntToLongSpliterator(undersplit, mapping) + } +} + +class SpliteratorStepper[A](sp: Spliterator[A]) extends AnyStepper[A] { + override def trySplit(): AnyStepper[A] = { + val r = sp.trySplit() + if (r == null) null else new SpliteratorStepper(r) + } + + var cache: AnyRef = null + + override def hasStep: Boolean = cache != null || sp.tryAdvance(x => cache = x.asInstanceOf[AnyRef]) + + override def nextStep(): A = if (hasStep) { + val r = cache + cache = null + r.asInstanceOf[A] + } else throw new NoSuchElementException("") + + override def estimateSize: Long = sp.estimateSize() + + override def characteristics: Int = sp.characteristics() +} + +class StepperTest { + def subs[Z, A, CC <: Stepper[A]](zero: Z)(s: Stepper[A])(f: Stepper[A] => Z, op: (Z, Z) => Z): Z = { + val ss = s.substep() + if (ss == null) op(zero, f(s)) + else { + val left = subs(zero)(ss)(f, op) + subs(left)(s)(f, op) + } + } + + val sizes = Vector(0, 1, 2, 4, 15, 17, 2512) + def sources: Vector[(Int, Stepper[Int])] = sizes.flatMap{ i => + Vector( + i -> new IncStepperA(i), + i -> new SpliteratorStepper(new IncSpliterator(i).asInstanceOf[Spliterator[Int]]), + i -> new MappingStepper[Int,Int](new IncStepperA(i), x => x), + i -> new MappingStepper[Long, Int](new SpliteratorStepper(new IntToLongSpliterator(new IncSpliterator(i), _.toLong).asInstanceOf[Spliterator[Long]]), _.toInt), + i -> new MappingStepper[Double, Int](new SpliteratorStepper(new IntToDoubleSpliterator(new IncSpliterator(i), _.toDouble).asInstanceOf[Spliterator[Double]]), _.toInt), + i -> new MappingStepper[String, Int](new SpliteratorStepper(new IntToGenericSpliterator[String](new IncSpliterator(i), _.toString)), _.toInt) + ) + } + + @Test + def stepping(): Unit = { + sources.foreach{ case (i, s) => assert((0 until i).forall{ j => s.hasStep && s.nextStep() == j } && !s.hasStep) } + sources.foreach{ case (i, s) => + val set = collection.mutable.BitSet.empty + subs(0)(s)( + { x => + while (x.hasStep) { val y = x.nextStep(); assert(!(set contains y)); set += y } + 0 + }, + _ + _ + ) + assert((0 until i).toSet == set) + } + } + + @Test + def trying(): Unit = { + sources.foreach{ case (i,s) => + val set = collection.mutable.BitSet.empty + while (s.hasStep) { val y = s.nextStep(); assert(!(set contains y)); set += y } + assert((0 until i).toSet == set) + } + sources.foreach{ case (i,s) => + val set = collection.mutable.BitSet.empty + subs(0)(s)( + { x => + while (x.hasStep) { val y = x.nextStep(); assert(!(set contains y)); set += y } + 0 + }, + _ + _ + ) + assertTrue(s.getClass.getName + s" said [0, $i) was " + set.mkString("{", " ", "}"), (0 until i).toSet == set) + } + } + + @Test + def substepping(): Unit = { + sources.foreach{ case (i,s) => + val ss = s.substep() + assertEquals(ss == null, i < 2) + if (ss != null) { + assertTrue(s.hasStep) + assertTrue(ss.hasStep) + val c1 = s.count() + val c2 = ss.count() + assertEquals(s"$i != $c1 + $c2 from ${s.getClass.getName}", i, c1 + c2) + } + else assertEquals(i, s.count()) + } + } + + @Test + def characteristically(): Unit = { + val expected = Spliterator.SIZED | Spliterator.SUBSIZED | Spliterator.ORDERED + sources.foreach{ case (_,s) => assertEquals(s.characteristics, expected)} + sources.foreach{ case (_,s) => subs(0)(s)(x => { assertEquals(x.characteristics, expected); 0 }, _ + _) } + } + + @Test + def count_only(): Unit = { + sources.foreach{ case (i, s) => assertEquals(i, s.count()) } + sources.foreach{ case (i, s) => assertEquals(i, subs(0)(s)(_.count().toInt, _ + _)) } + } + + @Test + def count_conditionally(): Unit = { + sources.foreach{ case (i, s) => assertEquals((0 until i).count(_ % 3 == 0), s.count(_ % 3 == 0)) } + sources.foreach{ case (i, s) => assertEquals((0 until i).count(_ % 3 == 0), subs(0)(s)(_.count(_ % 3 == 0).toInt, _ + _)) } + } + + @Test + def existence(): Unit = { + sources.foreach{ case (i, s) => assert(i > 0 == s.exists(_ >= 0)) } + sources.foreach{ case (i, s) => assert(i > 16 == s.exists(_ % 17 == 16)) } + sources.foreach{ case (i, s) => assert(i > 0 == subs(false)(s)(_.exists(_ >= 0), _ || _)) } + sources.foreach{ case (i, s) => assert(i > 16 == subs(false)(s)(_.exists(_ % 17 == 16), _ || _)) } + } + + @Test + def finding(): Unit = { + for (k <- 0 until 100) { + (sources zip sources).foreach{ case ((i,s), (j,t)) => + val x = scala.util.Random.nextInt(math.min(i,j)+3) + val a = s.find(_ == x) + val b = subs(None: Option[Int])(t)(_.find(_ == x), _ orElse _) + assertEquals(a, b) + assertEquals(a.isDefined, x < math.min(i,j)) + } + } + } + + @Test + def folding(): Unit = { + sources.foreach{ case (i,s) => assertEquals((0 until i).mkString, s.fold("")(_ + _.toString)) } + sources.foreach{ case (i,s) => assertEquals((0 until i).mkString, subs("")(s)(_.fold("")(_ + _.toString), _ + _)) } + sources.foreach{ case (i,s) => assertEquals((0 until i).map(_.toDouble).sum, s.fold(0.0)(_ + _), 1e-10) } + sources.foreach{ case (i,s) => assertEquals((0 until i).map(_.toDouble).sum, subs(0.0)(s)(_.fold(0.0)(_ + _), _ + _), 1e-10) } + } + + @Test + def foldingUntil(): Unit = { + def expected(i: Int) = (0 until i).scan(0)(_ + _).dropWhile(_ < 6*i).headOption.getOrElse((0 until i).sum) + sources.foreach{ case (i,s) => assertEquals(expected(i), s.foldTo(0)(_ + _)(_ >= 6*i)) } + sources.foreach{ case (_,s) => assertEquals(-1, s.foldTo(-1)(_ * _)(_ => true)) } + sources.foreach{ case (i,s) => + val ss = s.substep() + val x = s.foldTo( if (ss == null) 0 else ss.foldTo(0)(_ + _)(_ >= 6*i) )(_ + _)(_ >= 6*i) + assertEquals(expected(i), x) + } + } + + @Test + def foreaching(): Unit = { + sources.foreach{ case (i,s) => + val clq = new java.util.concurrent.ConcurrentLinkedQueue[String] + s.foreach( clq add _.toString ) + assertEquals((0 until i).map(_.toString).toSet, Iterator.continually(if (!clq.isEmpty) Some(clq.poll) else None).takeWhile(_.isDefined).toSet.flatten) + } + sources.foreach{ case (i,s) => + val clq = new java.util.concurrent.ConcurrentLinkedQueue[String] + subs(())(s)(_.foreach( clq add _.toString ), (_, _) => ()) + assertEquals((0 until i).map(_.toString).toSet, Iterator.continually(if (!clq.isEmpty) Some(clq.poll) else None).takeWhile(_.isDefined).toSet.flatten) + } + } + + @Test + def reducing(): Unit = { + sources.foreach{ case (i,s) => + if (i==0) assertEquals(s.hasStep, false) + else assertEquals((0 until i).sum, s.reduce(_ + _)) + } + sources.foreach{ case (i,s) => + assertEquals((0 until i).sum, subs(0)(s)(x => if (!x.hasStep) 0 else x.reduce(_ + _), _ + _)) + } + } + + @Test + def iterating(): Unit = { + sources.foreach{ case (i, s) => assert(Iterator.range(0,i) sameElements s.iterator) } + } + + @Test + def spliterating(): Unit = { + sources.foreach{ case (i,s) => + var sum = 0 + s.spliterator.asInstanceOf[Spliterator[Int]].forEachRemaining(new java.util.function.Consumer[Int]{ def accept(i: Int): Unit = { sum += i } }) + assertEquals(sum, (0 until i).sum) + } + sources.foreach{ case (i,s) => + val sum = subs(0)(s)(x => { var sm = 0; x.spliterator.asInstanceOf[Spliterator[Int]].forEachRemaining(new java.util.function.Consumer[Int]{ def accept(i: Int): Unit = { sm += i } }); sm }, _ + _) + assertEquals(sum, (0 until i).sum) + } + } +} + diff --git a/src/test/scala-2.13+/scala/compat/java8/StreamConvertersTest.scala b/src/test/scala-2.13+/scala/compat/java8/StreamConvertersTest.scala new file mode 100644 index 0000000..0beb9ae --- /dev/null +++ b/src/test/scala-2.13+/scala/compat/java8/StreamConvertersTest.scala @@ -0,0 +1,303 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.compat.java8 + +import scala.language.higherKinds + +import org.junit.Test +import org.junit.Assert._ + +import java.util.stream._ +import StreamConverters._ + +class StreamConvertersTest { + + def assertEq[A](a1: A, a2: A, s: String): Unit = { assertEquals(s, a1, a2) } // Weird order normally! + def assertEq[A](a1: A, a2: A): Unit = { assertEq(a1, a2, "not equal") } + def assert(b: Boolean): Unit = { assertTrue(b) } + def assert(b: Boolean, s: String): Unit = { assertTrue(s, b) } + + def arrayO(n: Int) = (1 to n).map(_.toString).toArray + def arrayD(n: Int) = (1 to n).map(_.toDouble).toArray + def arrayI(n: Int) = (1 to n).toArray + def arrayL(n: Int) = (1 to n).map(_.toLong).toArray + + def newStream(n: Int) = java.util.Arrays.stream(arrayO(n)) + def newDoubleStream(n: Int) = java.util.Arrays.stream(arrayD(n)) + def newIntStream(n: Int) = java.util.Arrays.stream(arrayI(n)) + def newLongStream(n: Int) = java.util.Arrays.stream(arrayL(n)) + + val ns = Vector(0, 1, 2, 12, 15, 16, 17, 31, 32, 33, 151, 1298, 7159) + + @Test + def streamAccumulate(): Unit = { + for (n <- ns) { + val vecO = arrayO(n).toVector + val accO = newStream(n).parallel.accumulate + assertEq(vecO, newStream(n).accumulate.to(Vector), s"stream $n to vector") + assertEq(vecO, accO.to(Vector), s"stream $n to vector in parallel") + assertEq(vecO, accO.toArray.toVector, s"stream $n to vector via array in parallel") + assertEq(vecO, accO.iterator.toVector, s"stream $n to vector via iterator in parallel") + assertEq(vecO, accO.toList.toVector, s"stream $n to vector via list in parallel") + assert((0 until accO.size.toInt).forall(i => vecO(i) == accO(i)), s"stream $n indexed via accumulator") + assert(accO.isInstanceOf[scala.compat.java8.collectionImpl.Accumulator[_]], s"stream $n to generic accumulator") + + for (boxless <- Seq(false, true)) { + val sbox = (if (boxless) "" else "(boxed)") + val vecD = arrayD(n).toVector + val accD = + if (boxless) newDoubleStream(n).parallel.accumulate + else newDoubleStream(n).boxed.parallel.accumulatePrimitive + assertEq(vecD, newDoubleStream(n).accumulate.to(Vector), s"double stream $n to vector $sbox") + assertEq(vecD, accD.to(Vector), s"double stream $n to vector in parallel $sbox") + assertEq(vecD, accD.toArray.toVector, s"double stream $n to vector via array in parallel $sbox") + assertEq(vecD, accD.iterator.toVector, s"double stream $n to vector via iterator in parallel $sbox") + assertEq(vecD, accD.toList.toVector, s"double stream $n to vector via list in parallel $sbox") + assert((0 until accD.size.toInt).forall(i => vecD(i) == accD(i)), s"double stream $n indexed via accumulator $sbox") + assert(accD.isInstanceOf[scala.compat.java8.collectionImpl.DoubleAccumulator], s"double stream $n to generic accumulator $sbox") + + val vecI = arrayI(n).toVector + val accI = + if (boxless) newIntStream(n).parallel.accumulate + else newIntStream(n).boxed.parallel.accumulatePrimitive + assertEq(vecI, newIntStream(n).accumulate.to(Vector), s"int stream $n to vector $sbox") + assertEq(vecI, accI.to(Vector), s"int stream $n to vector in parallel $sbox") + assertEq(vecI, accI.toArray.toVector, s"int stream $n to vector via array in parallel $sbox") + assertEq(vecI, accI.iterator.toVector, s"int stream $n to vector via iterator in parallel $sbox") + assertEq(vecI, accI.toList.toVector, s"int stream $n to vector via list in parallel $sbox") + assert((0 until accI.size.toInt).forall(i => vecI(i) == accI(i)), s"int stream $n indexed via accumulator $sbox") + assert(accI.isInstanceOf[scala.compat.java8.collectionImpl.IntAccumulator], s"int stream $n to generic accumulator $sbox") + + val vecL = arrayL(n).toVector + val accL = + if (boxless) newLongStream(n).parallel.accumulate + else newLongStream(n).boxed.parallel.accumulatePrimitive + assertEq(vecL, newLongStream(n).accumulate.to(Vector), s"long stream $n to vector $sbox") + assertEq(vecL, accL.to(Vector), s"long stream $n to vector in parallel $sbox") + assertEq(vecL, accL.toArray.toVector, s"long stream $n to vector via array in parallel $sbox") + assertEq(vecL, accL.iterator.toVector, s"long stream $n to vector via iterator in parallel $sbox") + assertEq(vecL, accL.toList.toVector, s"long stream $n to vector via list in parallel $sbox") + assert((0 until accL.size.toInt).forall(i => vecL(i) == accL(i)), s"long stream $n indexed via accumulator $sbox") + assert(accL.isInstanceOf[scala.compat.java8.collectionImpl.LongAccumulator], s"long stream $n to generic accumulator $sbox") + } + } + } + + @Test + def streamToScala(): Unit = { + for (n <- ns) { + val vecO = arrayO(n).toVector + assertEq(vecO, newStream(n).toScala(Vector)) + assertEq(vecO, newStream(n).parallel.toScala(Vector)) + assertEq(vecO, newStream(n).toScala[Vector]) + assertEq(vecO, newStream(n).parallel.toScala[Vector]) + + val vecD = arrayD(n).toVector + assertEq(vecD, newDoubleStream(n).toScala(Vector)) + assertEq(vecD, newDoubleStream(n).parallel.toScala(Vector)) + assertEq(vecD, newDoubleStream(n).toScala[Vector]) + assertEq(vecD, newDoubleStream(n).parallel.toScala[Vector]) + + val vecI = arrayI(n).toVector + assertEq(vecI, newIntStream(n).toScala(Vector)) + assertEq(vecI, newIntStream(n).parallel.toScala(Vector)) + assertEq(vecI, newIntStream(n).toScala[Vector]) + assertEq(vecI, newIntStream(n).parallel.toScala[Vector]) + + val vecL = arrayL(n).toVector + assertEq(vecL, newLongStream(n).toScala(Vector)) + assertEq(vecL, newLongStream(n).parallel.toScala(Vector)) + assertEq(vecL, newLongStream(n).toScala[Vector]) + assertEq(vecL, newLongStream(n).parallel.toScala[Vector]) + } + } + + @Test + def streamUnbox(): Unit = { + assert(newDoubleStream(1).boxed.unboxed.isInstanceOf[DoubleStream]) + assert(newIntStream(1).boxed.unboxed.isInstanceOf[IntStream]) + assert(newLongStream(1).boxed.unboxed.isInstanceOf[LongStream]) + } + + import collection.mutable.{ ArrayBuffer, ArraySeq } + def abufO(n: Int) = { val ab = new ArrayBuffer[String]; arrayO(n).foreach(ab += _); ab } + def abufD(n: Int) = { val ab = new ArrayBuffer[Double]; arrayD(n).foreach(ab += _); ab } + def abufI(n: Int) = { val ab = new ArrayBuffer[Int]; arrayI(n).foreach(ab += _); ab } + def abufL(n: Int) = { val ab = new ArrayBuffer[Long]; arrayL(n).foreach(ab += _); ab } + def wrapO(n: Int): ArraySeq[String] = arrayO(n) + def wrapD(n: Int): ArraySeq[Double] = arrayD(n) + def wrapI(n: Int): ArraySeq[Int] = arrayI(n) + def wrapL(n: Int): ArraySeq[Long] = arrayL(n) + def vectO(n: Int) = arrayO(n).toVector + def vectD(n: Int) = arrayD(n).toVector + def vectI(n: Int) = arrayI(n).toVector + def vectL(n: Int) = arrayL(n).toVector + def genhset[A](aa: Array[A]) = { val hs = new collection.mutable.HashSet[A]; aa.foreach(hs += _); hs } + def hsetO(n: Int) = genhset(arrayO(n)) + def hsetD(n: Int) = genhset(arrayD(n)) + def hsetI(n: Int) = genhset(arrayI(n)) + def hsetL(n: Int) = genhset(arrayL(n)) + + @Test + def scalaToStream(): Unit = { + for (n <- ns) { + val arrO = arrayO(n) + val seqO = arrO.toSeq + val abO = abufO(n) + val wrO = wrapO(n) + val vecO = vectO(n) + val hsO = hsetO(n) + // Seems like a lot of boilerplate, but we need it to test implicit resolution + assertEq(seqO, seqO.seqStream.toScala[Seq]) +// assertEq(seqO, seqO.stepper.parStream.toScala[Seq]) // Must go through stepper if we're unsure whether we can parallelize well + assertEq(seqO, arrO.seqStream.toScala[Seq]) + assertEq(seqO, arrO.parStream.toScala[Seq]) + assertEq(seqO, abO.seqStream.toScala[Seq]) + assertEq(seqO, abO.parStream.toScala[Seq]) + assertEq(seqO, wrO.seqStream.toScala[Seq]) + assertEq(seqO, wrO.parStream.toScala[Seq]) + assertEq(seqO, vecO.seqStream.toScala[Seq]) + assertEq(seqO, vecO.parStream.toScala[Seq]) +// assertEq(seqO, hsO.seqStream.toScala[Seq].sortBy(_.toInt)) +// assertEq(seqO, hsO.parStream.toScala[Seq].sortBy(_.toInt)) + + val arrD = arrayD(n) + val seqD = arrD.toSeq + val abD = abufD(n) + val wrD = wrapD(n) + val vecD = vectD(n) + val hsD = hsetD(n) + assertEq(seqD, seqD.seqStream.toScala[Seq]) +// assertEq(seqD, seqD.stepper.parStream.toScala[Seq]) + assertEq(seqD, arrD.seqStream.toScala[Seq]) + assertEq(seqD, arrD.parStream.toScala[Seq]) + assert(arrD.seqStream.isInstanceOf[DoubleStream]) + assert(arrD.parStream.isInstanceOf[DoubleStream]) + assertEq(seqD, abD.seqStream.toScala[Seq]) + assertEq(seqD, abD.parStream.toScala[Seq]) + assert(abD.seqStream.isInstanceOf[DoubleStream]) + assert(abD.parStream.isInstanceOf[DoubleStream]) + assertEq(seqD, wrD.seqStream.toScala[Seq]) + assertEq(seqD, wrD.parStream.toScala[Seq]) + assert(wrD.seqStream.isInstanceOf[DoubleStream]) + assert(wrD.parStream.isInstanceOf[DoubleStream]) + assertEq(seqD, vecD.seqStream.toScala[Seq]) + assertEq(seqD, vecD.parStream.toScala[Seq]) + assert(vecD.seqStream.isInstanceOf[DoubleStream]) + assert(vecD.parStream.isInstanceOf[DoubleStream]) +// assertEq(seqD, hsD.seqStream.toScala[Seq].sorted) +// assertEq(seqD, hsD.parStream.toScala[Seq].sorted) +// assert(hsD.seqStream.isInstanceOf[DoubleStream]) +// assert(hsD.parStream.isInstanceOf[DoubleStream]) + + val arrI = arrayI(n) + val seqI = arrI.toSeq + val abI = abufI(n) + val wrI = wrapI(n) + val vecI = vectI(n) + val hsI = hsetI(n) + assertEq(seqI, seqI.seqStream.toScala[Seq]) +// assertEq(seqI, seqI.stepper.parStream.toScala[Seq]) + assertEq(seqI, arrI.seqStream.toScala[Seq]) + assertEq(seqI, arrI.parStream.toScala[Seq]) + assert(arrI.seqStream.isInstanceOf[IntStream]) + assert(arrI.parStream.isInstanceOf[IntStream]) + assertEq(seqI, abI.seqStream.toScala[Seq]) + assertEq(seqI, abI.parStream.toScala[Seq]) + assert(abI.seqStream.isInstanceOf[IntStream]) + assert(abI.parStream.isInstanceOf[IntStream]) + assertEq(seqI, wrI.seqStream.toScala[Seq]) + assertEq(seqI, wrI.parStream.toScala[Seq]) + assert(wrI.seqStream.isInstanceOf[IntStream]) + assert(wrI.parStream.isInstanceOf[IntStream]) + assertEq(seqI, vecI.seqStream.toScala[Seq]) + assertEq(seqI, vecI.parStream.toScala[Seq]) + assert(vecI.seqStream.isInstanceOf[IntStream]) + assert(vecI.parStream.isInstanceOf[IntStream]) +// assertEq(seqI, hsI.seqStream.toScala[Seq].sorted) +// assertEq(seqI, hsI.parStream.toScala[Seq].sorted) +// assert(hsI.seqStream.isInstanceOf[IntStream]) +// assert(hsI.parStream.isInstanceOf[IntStream]) + + val arrL = arrayL(n) + val seqL = arrL.toSeq + val abL = abufL(n) + val wrL = wrapL(n) + val vecL = vectL(n) + val hsL = hsetL(n) + assertEq(seqL, seqL.seqStream.toScala[Seq]) +// assertEq(seqL, seqL.stepper.parStream.toScala[Seq]) + assertEq(seqL, arrL.seqStream.toScala[Seq]) + assertEq(seqL, arrL.parStream.toScala[Seq]) + assert(arrL.seqStream.isInstanceOf[LongStream]) + assert(arrL.parStream.isInstanceOf[LongStream]) + assertEq(seqL, abL.seqStream.toScala[Seq]) + assertEq(seqL, abL.parStream.toScala[Seq]) + assert(abL.seqStream.isInstanceOf[LongStream]) + assert(abL.parStream.isInstanceOf[LongStream]) + assertEq(seqD, wrD.seqStream.toScala[Seq]) + assertEq(seqD, wrD.parStream.toScala[Seq]) + assert(wrL.seqStream.isInstanceOf[LongStream]) + assert(wrL.parStream.isInstanceOf[LongStream]) + assertEq(seqD, wrD.seqStream.toScala[Seq]) + assertEq(seqD, wrD.parStream.toScala[Seq]) + assert(vecL.seqStream.isInstanceOf[LongStream]) + assert(vecL.parStream.isInstanceOf[LongStream]) +// assertEq(seqL, hsL.seqStream.toScala[Seq].sorted) +// assertEq(seqL, hsL.parStream.toScala[Seq].sorted) +// assert(hsL.seqStream.isInstanceOf[LongStream]) +// assert(hsL.parStream.isInstanceOf[LongStream]) + } + } + + @Test + def primitiveStreamTypes(): Unit = { + // Unboxed native + widening Steppers available: + assertEquals(Vector[Int](1, 2, 3), (Array[Int](1, 2, 3).seqStream: IntStream).toScala[Vector]) + assertEquals(Vector[Short](1.toShort, 2.toShort, 3.toShort), (Array[Short](1.toShort, 2.toShort, 3.toShort).seqStream: IntStream).toScala[Vector]) + assertEquals(Vector[String]("a", "b"), (Array[String]("a", "b").seqStream: Stream[String]).toScala[Vector]) + + // Boxed collections, widening via boxed AnySteppers: + assertEquals(Vector[Int](1, 2, 3), (Vector[Int](1, 2, 3).seqStream: IntStream).toScala[Vector]) + assertEquals(Vector[Short](1.toShort, 2.toShort, 3.toShort), (Vector[Short](1.toShort, 2.toShort, 3.toShort).seqStream: IntStream).toScala[Vector]) + assertEquals(Vector[String]("a", "b"), (Vector[String]("a", "b").seqStream: Stream[String]).toScala[Vector]) + } + + @Test + def issue_87(): Unit = { + // Vectors that are generated from other vectors tend _not_ to + // have all their display vectors consistent; the cached vectors + // are correct, but the higher-level vector does _not_ contain + // the cached vector in the correct place (for efficiency)! This + // is called being "dirty", and needs to be handled specially. + val dirtyDisplayVector = Vector.fill(120)("a").slice(0, 40) + val shouldNotNPE = + dirtyDisplayVector.seqStream.collect(Collectors.toList()) + assertEq(shouldNotNPE.toArray(new Array[String](0)).toVector, dirtyDisplayVector, "Vector[Any].seqStream (with dirty display)") + + val dirtyDisplayVectorInt = Vector.fill(120)(999).slice(0, 40) + val shouldNotNPEInt = + dirtyDisplayVectorInt.seqStream.sum() + assertEq(shouldNotNPEInt, dirtyDisplayVectorInt.sum, "Vector[Int].seqStream (with dirty display)") + + val dirtyDisplayVectorLong = Vector.fill(120)(99999999999L).slice(0, 40) + val shouldNotNPELong = + dirtyDisplayVectorLong.seqStream.sum() + assertEq(shouldNotNPELong, dirtyDisplayVectorLong.sum, "Vector[Long].seqStream (with dirty display)") + + val dirtyDisplayVectorDouble = Vector.fill(120)(0.1).slice(0, 40) + val shouldNotNPEDouble = + math.rint(dirtyDisplayVectorDouble.seqStream.sum() * 10) + assertEq(shouldNotNPEDouble, math.rint(dirtyDisplayVectorDouble.sum * 10), "Vector[Double].seqStream (with dirty display)") + } +} diff --git a/src/test/scala/scala/compat/java8/StepConvertersTest.scala b/src/test/scala-2.13-/scala/compat/java8/StepConvertersTest.scala similarity index 100% rename from src/test/scala/scala/compat/java8/StepConvertersTest.scala rename to src/test/scala-2.13-/scala/compat/java8/StepConvertersTest.scala diff --git a/src/test/scala/scala/compat/java8/StepperTest.scala b/src/test/scala-2.13-/scala/compat/java8/StepperTest.scala similarity index 100% rename from src/test/scala/scala/compat/java8/StepperTest.scala rename to src/test/scala-2.13-/scala/compat/java8/StepperTest.scala diff --git a/src/test/scala/scala/compat/java8/StreamConvertersTest.scala b/src/test/scala-2.13-/scala/compat/java8/StreamConvertersTest.scala similarity index 100% rename from src/test/scala/scala/compat/java8/StreamConvertersTest.scala rename to src/test/scala-2.13-/scala/compat/java8/StreamConvertersTest.scala