Skip to content

Commit 3a3a582

Browse files
committed
TypeString improvements, JavaClassName introduced
1 parent bd17cb3 commit 3a3a582

File tree

4 files changed

+258
-57
lines changed

4 files changed

+258
-57
lines changed

commons-core/src/main/scala/com/avsystem/commons/misc/TypeString.scala

+57-6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package com.avsystem.commons
22
package misc
33

4-
import com.avsystem.commons.serialization.{GenCodec, GenKeyCodec, transparent}
4+
import com.avsystem.commons.serialization.{GenCodec, GenKeyCodec}
55

66
/**
77
* Typeclass that contains string representation of a concrete type. This representation should correctly parse
@@ -22,16 +22,67 @@ import com.avsystem.commons.serialization.{GenCodec, GenKeyCodec, transparent}
2222
* }}}
2323
* Then, `listTypeRepr[Int]` will produce a string `"List[Int]"`
2424
*/
25-
@transparent
26-
case class TypeString[T](value: String) extends AnyVal
25+
class TypeString[T](val value: String) extends AnyVal
2726
object TypeString {
28-
def of[T](implicit ts: TypeString[T]): String = ts.value
27+
def apply[T](implicit ts: TypeString[T]): TypeString[T] = ts
28+
def of[T: TypeString]: String = TypeString[T].value
2929

3030
implicit def materialize[T]: TypeString[T] = macro macros.misc.MiscMacros.typeString[T]
3131

3232
implicit val keyCodec: GenKeyCodec[TypeString[_]] =
33-
GenKeyCodec.create[TypeString[Any]](TypeString(_), _.value).asInstanceOf[GenKeyCodec[TypeString[_]]]
33+
GenKeyCodec.create[TypeString[_]](new TypeString(_), _.value)
3434

3535
implicit val codec: GenCodec[TypeString[_]] =
36-
GenCodec.materialize[TypeString[Any]].asInstanceOf[GenCodec[TypeString[_]]]
36+
GenCodec.create[TypeString[_]](i => new TypeString(i.readString()), (o, ts) => o.writeString(ts.value))
37+
}
38+
39+
/**
40+
* Typeclass that contains JVM fully qualified class name corresponding to given type.
41+
* This class name should resolve to runtime class of given type when passed to `java.lang.Class.forName`.
42+
*
43+
* `JavaClassName` can be used instead of `ClassTag` in ScalaJS when ScalaJS linker is configured to drop class names.
44+
* Also, unlike `ClassTag`, `JavaClassName` contains just a string so it can be easily serialized and deserialized.
45+
*/
46+
class JavaClassName[T](val value: String) extends AnyVal
47+
object JavaClassName extends JavaClassNameLowPrio {
48+
def apply[T](implicit ts: JavaClassName[T]): JavaClassName[T] = ts
49+
def of[T: JavaClassName]: String = JavaClassName[T].value
50+
51+
implicit val NothingClassName: JavaClassName[Nothing] = new JavaClassName("scala.runtime.Nothing$")
52+
implicit val NothingArrayClassName: JavaClassName[Array[Nothing]] = new JavaClassName("[Lscala.runtime.Nothing$;")
53+
implicit val UnitClassName: JavaClassName[Unit] = new JavaClassName("void")
54+
implicit val BooleanClassName: JavaClassName[Boolean] = new JavaClassName("boolean")
55+
implicit val ByteClassName: JavaClassName[Byte] = new JavaClassName("byte")
56+
implicit val ShortClassName: JavaClassName[Short] = new JavaClassName("short")
57+
implicit val IntClassName: JavaClassName[Int] = new JavaClassName("int")
58+
implicit val LongClassName: JavaClassName[Long] = new JavaClassName("long")
59+
implicit val FloatClassName: JavaClassName[Float] = new JavaClassName("float")
60+
implicit val DoubleClassName: JavaClassName[Double] = new JavaClassName("double")
61+
implicit val CharClassName: JavaClassName[Char] = new JavaClassName("char")
62+
63+
implicit def arrayClassName[T: JavaClassName]: JavaClassName[Array[T]] = {
64+
val elementName = JavaClassName.of[T] match {
65+
case "void" => "Lscala.runtime.BoxedUnit;"
66+
case "boolean" => "Z"
67+
case "byte" => "B"
68+
case "short" => "S"
69+
case "int" => "I"
70+
case "long" => "J"
71+
case "float" => "F"
72+
case "double" => "D"
73+
case "char" => "C"
74+
case arr if arr.startsWith("[") => arr
75+
case n => s"L$n;"
76+
}
77+
new JavaClassName("[" + elementName)
78+
}
79+
80+
implicit val keyCodec: GenKeyCodec[JavaClassName[_]] =
81+
GenKeyCodec.create[JavaClassName[_]](new JavaClassName(_), _.value)
82+
83+
implicit val codec: GenCodec[JavaClassName[_]] =
84+
GenCodec.create[JavaClassName[_]](i => new JavaClassName(i.readString()), (o, ts) => o.writeString(ts.value))
85+
}
86+
trait JavaClassNameLowPrio { this: JavaClassName.type =>
87+
implicit def materialize[T]: JavaClassName[T] = macro macros.misc.MiscMacros.javaClassName[T]
3788
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package com.avsystem.commons
2+
package macros
3+
4+
import com.avsystem.commons.misc.{JavaClassName, TypeString}
5+
import org.scalactic.source.Position
6+
import org.scalatest.FunSuite
7+
8+
object JavaClassNameTest {
9+
class Inner {
10+
class MoreInner {
11+
class SuperInner
12+
}
13+
}
14+
object Inner {
15+
class EvenInner
16+
object EvenInner
17+
}
18+
}
19+
20+
class JavaClassNameTest extends FunSuite {
21+
def test[T: ClassTag : JavaClassName : TypeString](implicit pos: Position): Unit =
22+
test(TypeString.of[T])(assert(JavaClassName.of[T] == classTag[T].runtimeClass.getName))
23+
24+
test[Any]
25+
test[AnyRef]
26+
test[AnyVal]
27+
test[Unit]
28+
test[Boolean]
29+
test[Char]
30+
test[Byte]
31+
test[Short]
32+
test[Int]
33+
test[Long]
34+
test[Float]
35+
test[Double]
36+
test[String]
37+
test[Nothing]
38+
test[Array[Unit]]
39+
test[Array[Boolean]]
40+
test[Array[Char]]
41+
test[Array[Byte]]
42+
test[Array[Short]]
43+
test[Array[Int]]
44+
test[Array[Long]]
45+
test[Array[Float]]
46+
test[Array[Double]]
47+
test[Array[String]]
48+
test[Array[Nothing]]
49+
test[JavaClassNameTest]
50+
test[JavaClassNameTest.type]
51+
test[JavaClassNameTest.Inner]
52+
test[JavaClassNameTest.Inner#MoreInner]
53+
test[JavaClassNameTest.Inner#MoreInner#SuperInner]
54+
test[JavaClassNameTest.Inner.type]
55+
test[JavaClassNameTest.Inner.EvenInner]
56+
test[JavaClassNameTest.Inner.EvenInner.type]
57+
}

commons-core/src/test/scala/com/avsystem/commons/macros/TypeStringTest.scala

+20-6
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,10 @@ object TypeStringTest {
3131
testTypeString[LocalAlias]("Int")
3232
testTypeString[StaticAlias]("TypeStringTest.StaticAlias")
3333
testTypeString[Integer]("Integer")
34-
testTypeString[String => Int]("(String) => Int")
35-
testTypeString[String => Int => Double]("(String) => (Int) => Double")
36-
testTypeString[(String => Int) => Double]("((String) => Int) => Double")
34+
testTypeString[String => Int]("String => Int")
35+
testTypeString[T => Int](s"(${TypeString.of[T]}) => Int")
36+
testTypeString[String => Int => Double]("String => Int => Double")
37+
testTypeString[(String => Int) => Double]("(String => Int) => Double")
3738
testTypeString[() => Int]("() => Int")
3839
testTypeString[(String, Int)]("(String, Int)")
3940
testTypeString[A]("TypeStringTest.A")
@@ -56,6 +57,19 @@ object TypeStringTest {
5657
testTypeString[fu.bar.q.type forSome {val fu: OFuu}]("fu.bar.q.type forSome {val fu: TypeStringTest.OFuu}")
5758
testTypeString[AnyRef with Serializable]("AnyRef with Serializable")
5859
testTypeString[AnyRef with Serializable {type A <: String}]("AnyRef with Serializable {type A <: String}")
60+
testTypeString[Any {type A = String}]("Any {type A = String}")
61+
testTypeString[Any {type A}]("Any {type A}")
62+
testTypeString[(Any {type A})#A]("(Any {type A})#A")
63+
testTypeString[(Any {type *})# *]("(Any {type *})# *")
64+
testTypeString[Any {type A >: Null <: String}]("Any {type A >: Null <: String}")
65+
testTypeString[Any {type A[+X] = Int}]("Any {type A[+X] = Int}")
66+
testTypeString[Any {type A[X] = List[X]}]("Any {type A[X] = List[X]}")
67+
testTypeString[Any {type A[+X <: Set[X]] = List[X]}]("Any {type A[+X <: Set[X]] = List[X]}")
68+
testTypeString[Any {type A[F[_] <: List[_]] = F[Int]}]("Any {type A[F[_] <: List[_]] = F[Int]}")
69+
testTypeString[Any {type A[M[_, _] <: Map[_, _]] = M[Int, String]}]("Any {type A[M[_, _] <: Map[_, _]] = M[Int, String]}")
70+
testTypeString[Any {type A[F[+X] <: List[X]] = F[Int]}]("Any {type A[F[+X] <: List[X]] = F[Int]}")
71+
testTypeString[Any {type A[F[_ <: String]] = F[String]}]("Any {type A[F[_ <: String]] = F[String]}")
72+
testTypeString[Any {type A[F[X <: List[X]]] = F[Nothing]}]("Any {type A[F[X <: List[X]]] = F[Nothing]}")
5973
}
6074
}
6175

@@ -112,9 +126,9 @@ object UnrelatedTypeString {
112126
testTypeString[Int]("Int")
113127
testTypeString[StaticAlias]("TypeStringTest.StaticAlias")
114128
testTypeString[Integer]("Integer")
115-
testTypeString[String => Int]("(String) => Int")
116-
testTypeString[String => Int => Double]("(String) => (Int) => Double")
117-
testTypeString[(String => Int) => Double]("((String) => Int) => Double")
129+
testTypeString[String => Int]("String => Int")
130+
testTypeString[String => Int => Double]("String => Int => Double")
131+
testTypeString[(String => Int) => Double]("(String => Int) => Double")
118132
testTypeString[() => Int]("() => Int")
119133
testTypeString[(String, Int)]("(String, Int)")
120134
testTypeString[A]("TypeStringTest.A")

0 commit comments

Comments
 (0)