diff --git a/core/shared/src/main/scala-2.12-/org/scalacheck/util/BuildableVersionSpecific.scala b/core/shared/src/main/scala-2.12-/org/scalacheck/util/BuildableVersionSpecific.scala index 43ffcb9a..7f67cf23 100644 --- a/core/shared/src/main/scala-2.12-/org/scalacheck/util/BuildableVersionSpecific.scala +++ b/core/shared/src/main/scala-2.12-/org/scalacheck/util/BuildableVersionSpecific.scala @@ -9,7 +9,7 @@ package org.scalacheck.util -import java.util.ArrayList +import java.util.{ArrayList, HashMap} import collection.{Map => _, _} import generic.CanBuildFrom @@ -32,6 +32,18 @@ private[util] class ArrayListBuilder[T] extends Builder[T, ArrayList[T]] { def result(): ArrayList[T] = al } +private[util] class HashMapBuilder[K, V] extends Builder[(K, V), HashMap[K, V]] { + private val hm = new HashMap[K, V] + + def +=(x: (K, V)): this.type = { + val (k, v) = x + hm.put(k, v) + this + } + def clear(): Unit = hm.clear() + def result(): HashMap[K, V] = hm +} + /** CanBuildFrom instances implementing Serializable, so that the objects capturing those can be serializable too. */ object SerializableCanBuildFroms { diff --git a/core/shared/src/main/scala-2.13+/org/scalacheck/util/BuildableVersionSpecific.scala b/core/shared/src/main/scala-2.13+/org/scalacheck/util/BuildableVersionSpecific.scala index 1e95b7e5..ea694ecf 100644 --- a/core/shared/src/main/scala-2.13+/org/scalacheck/util/BuildableVersionSpecific.scala +++ b/core/shared/src/main/scala-2.13+/org/scalacheck/util/BuildableVersionSpecific.scala @@ -9,11 +9,12 @@ package org.scalacheck.util -import java.util.ArrayList +import java.util.{ArrayList, HashMap} import scala.collection.mutable.Builder import scala.collection.{Map => _, _} private[util] trait BuildableVersionSpecific { + implicit def buildableFactory[T, C](implicit f: Factory[T, C]): Buildable[T, C] = new Buildable[T, C] { def builder = f.newBuilder @@ -30,6 +31,17 @@ private[util] class ArrayListBuilder[T] extends Builder[T, ArrayList[T]] { def result(): ArrayList[T] = al } +private[util] class HashMapBuilder[K, V] extends Builder[(K, V), HashMap[K, V]] { + private val hm = new HashMap[K, V] + def addOne(x: (K, V)): this.type = { + val (k, v) = x + hm.put(k, v) + this + } + def clear(): Unit = hm.clear() + def result(): HashMap[K, V] = hm +} + /** Factory instances implementing Serializable, so that the objects capturing those can be serializable too. */ // Named `...CanBuildFroms` for 2.12 source compatibility (`import SerializableCanBuildFroms._`) diff --git a/core/shared/src/main/scala/org/scalacheck/util/Buildable.scala b/core/shared/src/main/scala/org/scalacheck/util/Buildable.scala index e78b7ed7..fee467e7 100644 --- a/core/shared/src/main/scala/org/scalacheck/util/Buildable.scala +++ b/core/shared/src/main/scala/org/scalacheck/util/Buildable.scala @@ -22,12 +22,18 @@ trait Buildable[T, C] extends Serializable { } object Buildable extends BuildableVersionSpecific { - import java.util.ArrayList + import java.util.{ArrayList, HashMap} + implicit def buildableArrayList[T]: Buildable[T, ArrayList[T]] = new Buildable[T, ArrayList[T]] { def builder = new ArrayListBuilder[T] } + implicit def buildableHashMap[K, V]: Buildable[(K, V), HashMap[K, V]] = + new Buildable[(K, V), HashMap[K, V]] { + def builder = new HashMapBuilder[K, V] + } + def buildableSeq[T]: Buildable[T, Seq[T]] = new Buildable[T, Seq[T]] { def builder: mutable.Builder[T, Seq[T]] = diff --git a/core/shared/src/test/scala/org/scalacheck/util/BuildableSpecification.scala b/core/shared/src/test/scala/org/scalacheck/util/BuildableSpecification.scala index 98a599ff..760f6450 100644 --- a/core/shared/src/test/scala/org/scalacheck/util/BuildableSpecification.scala +++ b/core/shared/src/test/scala/org/scalacheck/util/BuildableSpecification.scala @@ -12,6 +12,7 @@ package util import scala.collection._ +import Buildable._ import ScalaVersionSpecific._ object BuildableSpecification { @@ -20,6 +21,11 @@ object BuildableSpecification { evt: C[String] => Traversable[String] ) = Gen.containerOf[C, String](Gen.alphaStr) + def buildable[C[_, _]](implicit + evb: Buildable[(String, Long), C[String, Long]], + evt: C[String, Long] => Traversable[(String, Long)] + ) = Gen.buildableOf[C[String, Long], (String, Long)](Gen.zip(Gen.alphaStr, Gen.long)) + implicit val listGen: Gen[List[String]] = container[List] implicit val streamGen: Gen[Stream[String]] = container[Stream] @@ -47,4 +53,17 @@ object BuildableSpecification { implicit val iterableGen: Gen[immutable.Iterable[String]] = container[immutable.Iterable] implicit val trieIteratorGen: Gen[immutable.Queue[String]] = container[immutable.Queue] + + implicit val mapGen: Gen[Map[String, Long]] = buildable[Map] + + Gen.buildableOf[Map[String, Int], (String, Int)](Gen.zip(Gen.alphaStr, Gen.choose(0, 100))) + + // java containers + { + import scala.collection.convert.ImplicitConversionsToScala._ + + implicit val arrayListGen: Gen[java.util.ArrayList[String]] = container[java.util.ArrayList] + + implicit val hashMapGen: Gen[java.util.HashMap[String, Long]] = buildable[java.util.HashMap] + } } diff --git a/doc/UserGuide.md b/doc/UserGuide.md index 10289e9b..40a399d0 100644 --- a/doc/UserGuide.md +++ b/doc/UserGuide.md @@ -468,32 +468,38 @@ value ultimately reported as failing might not satisfy the condition given to case that does. To avoid confusion, the corresponding shrink for the type can use `suchThat` method too. -#### Generating Containers +#### Generating Buildables and Containers -There is a special generator, `Gen.containerOf`, that generates containers such -as lists and arrays. They take another generator as argument, that is -responsible for generating the individual items. You can use it in the -following way: +There are special generators +* `Gen.buildableOf`, that generates any buildable collection +* `Gen.containerOf`, a convenience method for single parameter buildable like lists and arrays -```scala -val genIntList = Gen.containerOf[List,Int](Gen.oneOf(1, 3, 5)) +They take another generator as argument, that is responsible for generating the individual items. +You can use it in the following way: -val genStringLazyList = Gen.containerOf[LazyList,String](Gen.alphaStr) +```scala +val genInt: Gen[Int] = Gen.oneOf(1, 3, 5) +val genIntList = Gen.containerOf[List, Int](genInt) +val genStringLazyList = Gen.containerOf[LazyList, String](Gen.alphaStr) +val genBoolArray = Gen.containerOf[Array, Boolean](Gen.const(true)) -val genBoolArray = Gen.containerOf[Array,Boolean](true) +val genTuple: Gen[(String, Int)] = Gen.zip(Gen.alphaStr, Gen.choose(0, 100)) +val genMap = Gen.buildableOf[Map[String, Int], (String, Int)](genTuple) ``` By default, ScalaCheck supports generation of `List`, `Stream` (Scala 2.10 - -2.12, deprecated in 2.13), `LazyList` (Scala 2.13), `Set`, `Array`, and -`ArrayList` (from `java.util`). You can add support for additional containers -by adding implicit `Buildable` instances. See `Buildable.scala` for examples. +2.12, deprecated in 2.13), `LazyList` (Scala 2.13), `Set`, `Array` and `Map`. +Additionally, ScalaCheck can generate `java.util.ArrayList` and `java.util.HashMap` when an implicit +`Traversable` conversion evidence is in scope. + +You can add support for additional containers by adding implicit `Buildable` instances. +See `Buildable.scala` for examples. There is also `Gen.nonEmptyContainerOf` for generating non-empty containers, and `Gen.containerOfN` for generating containers of a given size. To generate a container by picking an arbitrary number of elements use -`Gen.someOf`, or by picking one or more elements with -`Gen.atLeastOne`. +`Gen.someOf`, or by picking one or more elements with `Gen.atLeastOne`. ```scala val zeroOrMoreDigits = Gen.someOf(1 to 9)