Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add buildable for java.util.HashMap #1023

Merged
merged 3 commits into from
May 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

package org.scalacheck.util

import java.util.ArrayList
import java.util.{ArrayList, HashMap}

import collection.{Map => _, _}
import generic.CanBuildFrom
Expand All @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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._`)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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]] =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ package util

import scala.collection._

import Buildable._
import ScalaVersionSpecific._

object BuildableSpecification {
Expand All @@ -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]
Expand Down Expand Up @@ -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]
}
}
34 changes: 20 additions & 14 deletions doc/UserGuide.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down