From 078c17925c525e16772652d0ed243d901194ffdc Mon Sep 17 00:00:00 2001 From: Jens Date: Sun, 24 Jul 2022 22:48:15 +0200 Subject: [PATCH] Add javaapi collection forwarders for 2.12 and 2.11 Adds 2.13 collection compatible forwarders for scala 2.11 and scala 2.12, see #346. 2.11 has converters in traits WrapAsJava and WrapAsScala, while 2.12 has them in AsJavaConverters and AsScalaConverters. The method names are the same, but unfortunately both CollectionConverters objects have to be separately defined for both sources, because of this import. Tests are added in a Java file, to verify that the methods can be called as java api. --- .../jdk/javaapi/CollectionConverters.scala | 65 +++++++++ .../jdk/javaapi/CollectionConverters.scala | 65 +++++++++ .../jdk/javaapi/CollectionConvertersTest.java | 128 ++++++++++++++++++ .../test/scala/jdk/javaapi/TestObjects.scala | 24 ++++ 4 files changed, 282 insertions(+) create mode 100644 compat/src/main/scala-2.11/scala/jdk/javaapi/CollectionConverters.scala create mode 100644 compat/src/main/scala-2.12/scala/jdk/javaapi/CollectionConverters.scala create mode 100644 compat/src/test/scala-jvm/test/scala/jdk/javaapi/CollectionConvertersTest.java create mode 100644 compat/src/test/scala-jvm/test/scala/jdk/javaapi/TestObjects.scala diff --git a/compat/src/main/scala-2.11/scala/jdk/javaapi/CollectionConverters.scala b/compat/src/main/scala-2.11/scala/jdk/javaapi/CollectionConverters.scala new file mode 100644 index 00000000..af0b911b --- /dev/null +++ b/compat/src/main/scala-2.11/scala/jdk/javaapi/CollectionConverters.scala @@ -0,0 +1,65 @@ +/* + * 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.jdk.javaapi + +import java.{lang => jl, util => ju}, java.util.{concurrent => juc} +import scala.collection.convert.{WrapAsJava, WrapAsScala} +import scala.collection._ + +/** This object contains methods that convert between Scala and Java collections. + * + * The explicit conversion methods defined here are intended to be used in Java code. For Scala + * code, it is recommended to use the extension methods defined in + * [[scala.jdk.CollectionConverters]]. + */ +object CollectionConverters extends WrapAsJava with WrapAsScala { + def asJava[A](i: Iterator[A]): ju.Iterator[A] = asJavaIterator(i) + + def asJava[A](i: Iterable[A]): jl.Iterable[A] = asJavaIterable(i) + + def asJava[A](b: mutable.Buffer[A]): ju.List[A] = bufferAsJavaList(b) + + def asJava[A](s: mutable.Seq[A]): ju.List[A] = mutableSeqAsJavaList(s) + + def asJava[A](s: Seq[A]): ju.List[A] = seqAsJavaList(s) + + def asJava[A](s: mutable.Set[A]): ju.Set[A] = mutableSetAsJavaSet(s) + + def asJava[A](s: Set[A]): ju.Set[A] = setAsJavaSet(s) + + def asJava[K, V](m: mutable.Map[K, V]): ju.Map[K, V] = mutableMapAsJavaMap(m) + + def asJava[K, V](m: Map[K, V]): ju.Map[K, V] = mapAsJavaMap(m) + + def asJava[K, V](m: concurrent.Map[K, V]): juc.ConcurrentMap[K, V] = mapAsJavaConcurrentMap(m) + + def asScala[A](i: ju.Iterator[A]): Iterator[A] = asScalaIterator(i) + + def asScala[A](e: ju.Enumeration[A]): Iterator[A] = enumerationAsScalaIterator(e) + + def asScala[A](i: jl.Iterable[A]): Iterable[A] = iterableAsScalaIterable(i) + + def asScala[A](c: ju.Collection[A]): Iterable[A] = collectionAsScalaIterable(c) + + def asScala[A](l: ju.List[A]): mutable.Buffer[A] = asScalaBuffer(l) + + def asScala[A](s: ju.Set[A]): mutable.Set[A] = asScalaSet(s) + + def asScala[A, B](m: ju.Map[A, B]): mutable.Map[A, B] = mapAsScalaMap(m) + + def asScala[A, B](m: juc.ConcurrentMap[A, B]): concurrent.Map[A, B] = mapAsScalaConcurrentMap(m) + + def asScala[A, B](d: ju.Dictionary[A, B]): mutable.Map[A, B] = dictionaryAsScalaMap(d) + + def asScala(p: ju.Properties): mutable.Map[String, String] = propertiesAsScalaMap(p) +} diff --git a/compat/src/main/scala-2.12/scala/jdk/javaapi/CollectionConverters.scala b/compat/src/main/scala-2.12/scala/jdk/javaapi/CollectionConverters.scala new file mode 100644 index 00000000..1266d8b2 --- /dev/null +++ b/compat/src/main/scala-2.12/scala/jdk/javaapi/CollectionConverters.scala @@ -0,0 +1,65 @@ +/* + * 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.jdk.javaapi + +import java.{lang => jl, util => ju}, java.util.{concurrent => juc} +import scala.collection.convert.{AsJavaConverters, AsScalaConverters} +import scala.collection._ + +/** This object contains methods that convert between Scala and Java collections. + * + * The explicit conversion methods defined here are intended to be used in Java code. For Scala + * code, it is recommended to use the extension methods defined in + * [[scala.jdk.CollectionConverters]]. + */ +object CollectionConverters extends AsJavaConverters with AsScalaConverters { + def asJava[A](i: Iterator[A]): ju.Iterator[A] = asJavaIterator(i) + + def asJava[A](i: Iterable[A]): jl.Iterable[A] = asJavaIterable(i) + + def asJava[A](b: mutable.Buffer[A]): ju.List[A] = bufferAsJavaList(b) + + def asJava[A](s: mutable.Seq[A]): ju.List[A] = mutableSeqAsJavaList(s) + + def asJava[A](s: Seq[A]): ju.List[A] = seqAsJavaList(s) + + def asJava[A](s: mutable.Set[A]): ju.Set[A] = mutableSetAsJavaSet(s) + + def asJava[A](s: Set[A]): ju.Set[A] = setAsJavaSet(s) + + def asJava[K, V](m: mutable.Map[K, V]): ju.Map[K, V] = mutableMapAsJavaMap(m) + + def asJava[K, V](m: Map[K, V]): ju.Map[K, V] = mapAsJavaMap(m) + + def asJava[K, V](m: concurrent.Map[K, V]): juc.ConcurrentMap[K, V] = mapAsJavaConcurrentMap(m) + + def asScala[A](i: ju.Iterator[A]): Iterator[A] = asScalaIterator(i) + + def asScala[A](e: ju.Enumeration[A]): Iterator[A] = enumerationAsScalaIterator(e) + + def asScala[A](i: jl.Iterable[A]): Iterable[A] = iterableAsScalaIterable(i) + + def asScala[A](c: ju.Collection[A]): Iterable[A] = collectionAsScalaIterable(c) + + def asScala[A](l: ju.List[A]): mutable.Buffer[A] = asScalaBuffer(l) + + def asScala[A](s: ju.Set[A]): mutable.Set[A] = asScalaSet(s) + + def asScala[A, B](m: ju.Map[A, B]): mutable.Map[A, B] = mapAsScalaMap(m) + + def asScala[A, B](m: juc.ConcurrentMap[A, B]): concurrent.Map[A, B] = mapAsScalaConcurrentMap(m) + + def asScala[A, B](d: ju.Dictionary[A, B]): mutable.Map[A, B] = dictionaryAsScalaMap(d) + + def asScala(p: ju.Properties): mutable.Map[String, String] = propertiesAsScalaMap(p) +} diff --git a/compat/src/test/scala-jvm/test/scala/jdk/javaapi/CollectionConvertersTest.java b/compat/src/test/scala-jvm/test/scala/jdk/javaapi/CollectionConvertersTest.java new file mode 100644 index 00000000..6567fe97 --- /dev/null +++ b/compat/src/test/scala-jvm/test/scala/jdk/javaapi/CollectionConvertersTest.java @@ -0,0 +1,128 @@ +package test.scala.jdk.javaapi; + +import org.junit.Assert; +import org.junit.Test; +import scala.Array; +import scala.Tuple2; +import scala.collection.Iterable; +import scala.collection.Iterator; +import scala.collection.concurrent.TrieMap; +import scala.collection.mutable.*; +import scala.collection.mutable.Map; +import scala.collection.mutable.Set; +import scala.jdk.javaapi.CollectionConverters; + +import java.util.*; +import java.util.concurrent.ConcurrentMap; + +public class CollectionConvertersTest { + + /** + * The following conversions are supported via asScala and asJava: + * + * scala.collection.Iterable <=> java.lang.Iterable + * scala.collection.Iterator <=> java.util.Iterator + * scala.collection.mutable.Buffer <=> java.util.List + * scala.collection.mutable.Set <=> java.util.Set + * scala.collection.mutable.Map <=> java.util.Map + * scala.collection.concurrent.Map <=> java.util.concurrent.ConcurrentMap + */ + @Test + public void shouldConvertAsScala() { + // scala.collection.Iterable <=> java.lang.Iterable + java.lang.Iterable iterable = CollectionConverters.asJava(TestObjects.iterable()); + Assert.assertEquals("A", iterable.iterator().next()); + Iterable scalaIterable = CollectionConverters.asScala(iterable); + Assert.assertEquals(TestObjects.iterable().head(), scalaIterable.head()); + + // scala.collection.Iterator <=> java.util.Iterator + java.util.Iterator iterator = CollectionConverters.asJava(TestObjects.iterator()); + Assert.assertEquals("A", iterator.next()); + Iterator scalaIterator = CollectionConverters.asScala(iterator); + Assert.assertTrue(scalaIterator.contains("B")); + + // scala.collection.mutable.Buffer <=> java.util.List + List list = CollectionConverters.asJava(TestObjects.buffer()); + Assert.assertEquals("A", list.get(0)); + Buffer scalaBuffer = CollectionConverters.asScala(list); + Assert.assertEquals("A", scalaBuffer.head()); + + // scala.collection.mutable.Set <=> java.util.Set + java.util.Set set = CollectionConverters.asJava(TestObjects.mutableSet()); + Assert.assertTrue(set.contains("A")); + Set scalaSet = CollectionConverters.asScala(set); + Assert.assertTrue(scalaSet.contains("A")); + + // scala.collection.mutable.Map <=> java.util.Map + java.util.Map map = CollectionConverters.asJava(TestObjects.mutableMap()); + Assert.assertEquals("B", map.get("A")); + Map scalaMap = CollectionConverters.asScala(map); + Assert.assertEquals("B", scalaMap.get("A").get()); + + // scala.collection.concurrent.Map <=> java.util.concurrent.ConcurrentMap + ConcurrentMap concurrentMap = CollectionConverters.asJava(TestObjects.concurrentMap()); + Assert.assertEquals("B", concurrentMap.get("A")); + scala.collection.concurrent.Map scalaConcurrentMap = CollectionConverters.asScala(concurrentMap); + Assert.assertEquals("B", scalaConcurrentMap.get("A").get()); + } + + /** + * The following conversions are supported via asScala and through specially-named methods to convert to Java collections, as shown: + * + * scala.collection.Iterable <=> java.util.Collection (via asJavaCollection) + * scala.collection.Iterator <=> java.util.Enumeration (via asJavaEnumeration) + * scala.collection.mutable.Map <=> java.util.Dictionary (via asJavaDictionary) + */ + public void convertAsCollection() { + // scala.collection.Iterable <=> java.util.Collection (via asJavaCollection) + Collection collection = CollectionConverters.asJavaCollection(TestObjects.iterable()); + Assert.assertTrue(collection.contains("A")); + Iterable iterable = CollectionConverters.asScala(collection); + Assert.assertEquals("A", iterable.head()); + + // scala.collection.Iterator <=> java.util.Enumeration (via asJavaEnumeration) + Enumeration enumeration = CollectionConverters.asJavaEnumeration(TestObjects.iterator()); + Assert.assertEquals("A", enumeration.nextElement()); + Iterator iterator = CollectionConverters.asScala(enumeration); + Assert.assertEquals("A", iterator.next()); + + // scala.collection.mutable.Map <=> java.util.Dictionary (via asJavaDictionary) + Dictionary dictionary = CollectionConverters.asJavaDictionary(TestObjects.mutableMap()); + Assert.assertEquals("B", dictionary.get("A")); + Map map = CollectionConverters.asScala(dictionary); + Assert.assertEquals("B", map.get("A").get()); + } + + /** In addition, the following one-way conversions are provided via asJava: + * + * scala.collection.Seq => java.util.List + * scala.collection.mutable.Seq => java.util.List + * scala.collection.Set => java.util.Set + * scala.collection.Map => java.util.Map + */ + public void convertsAsJava() { + // scala.collection.Seq => java.util.List + Assert.assertEquals("A", CollectionConverters.asJava(TestObjects.seq()).get(0)); + + // scala.collection.mutable.Seq => java.util.List + Assert.assertEquals("A", CollectionConverters.asJava(TestObjects.mutableSeq()).get(0)); + + // scala.collection.Set => java.util.Set + Assert.assertTrue(CollectionConverters.asJava(TestObjects.set()).contains("A")); + + // scala.collection.Map => java.util.Map + Assert.assertEquals("B", CollectionConverters.asJava(TestObjects.map()).get("A")); + } + + /** + * The following one way conversion is provided via asScala: + * + * java.util.Properties => scala.collection.mutable.Map + */ + public void convertsFromProperties() { + Properties properties = new Properties(); + properties.put("key", "value"); + Map stringStringMap = CollectionConverters.asScala(properties); + Assert.assertEquals("value", stringStringMap.get("key").get()); + } +} diff --git a/compat/src/test/scala-jvm/test/scala/jdk/javaapi/TestObjects.scala b/compat/src/test/scala-jvm/test/scala/jdk/javaapi/TestObjects.scala new file mode 100644 index 00000000..283dd900 --- /dev/null +++ b/compat/src/test/scala-jvm/test/scala/jdk/javaapi/TestObjects.scala @@ -0,0 +1,24 @@ +package test.scala.jdk.javaapi + +import scala.collection.concurrent.TrieMap +import scala.collection.mutable +import scala.collection.mutable.ArrayBuffer +import scala.collection.mutable.Buffer + +/** + * Scala collection objects defined for easy access in a Java class + */ +object TestObjects { + + val seq: scala.collection.Seq[String] = ArrayBuffer("A", "B") + val mutableSeq: scala.collection.mutable.Seq[String] = ArrayBuffer("A", "B") + val set: scala.collection.Set[String] = Set("A", "B") + val map: scala.collection.Map[String, String] = Map("A" -> "B") + + val iterable: scala.collection.Iterable[String] = Iterable("A", "B") + val iterator: scala.collection.Iterator[String] = Iterator("A", "B") + val buffer: scala.collection.mutable.Buffer[String] = mutable.Buffer("A", "B") + val mutableSet: scala.collection.mutable.Set[String] = mutable.Set("A", "B") + val mutableMap: scala.collection.mutable.Map[String, String] = mutable.Map("A" -> "B") + val concurrentMap: scala.collection.concurrent.Map[String, String] = TrieMap("A" -> "B") +}