Skip to content

Instantly share code, notes, and snippets.

@ghik
Last active November 13, 2019 13:45
Show Gist options
  • Select an option

  • Save ghik/7ca8c6cae3c227bb78f53081003891d5 to your computer and use it in GitHub Desktop.

Select an option

Save ghik/7ca8c6cae3c227bb78f53081003891d5 to your computer and use it in GitHub Desktop.
import java.lang.reflect.{GenericArrayType, Modifier, ParameterizedType, Type, TypeVariable, WildcardType}
import scala.reflect.{ClassTag, classTag}
object StaticForwarderGenerator {
implicit class arrayOps[A](private val array: Array[A]) extends AnyVal {
def mkStringOrEmpty(start: String, sep: String, end: String): String =
if (array.isEmpty) "" else array.mkString(start, sep, end)
}
def generateFor[T: ClassTag]: String = {
val clazz = classTag[T].runtimeClass
val importedClasses = new MLinkedHashSet[Class[_]]
importedClasses += clazz
def classRef(cls: Class[_]): String = {
importedClasses += cls
s"J${cls.getSimpleName}"
}
def tpeRepr(tpe: Type): String = tpe match {
case java.lang.Void.TYPE => "Unit"
case java.lang.Boolean.TYPE => "Boolean"
case java.lang.Character.TYPE => "Char"
case java.lang.Byte.TYPE => "Byte"
case java.lang.Short.TYPE => "Short"
case java.lang.Integer.TYPE => "Int"
case java.lang.Long.TYPE => "Long"
case java.lang.Float.TYPE => "Float"
case java.lang.Double.TYPE => "Double"
case cls: Class[_] if cls == classOf[AnyRef] => "AnyRef"
case cls: Class[_] if cls.isArray => s"Array[${tpeRepr(cls.getComponentType)}]"
case gat: GenericArrayType => s"Array[${tpeRepr(gat.getGenericComponentType)}]"
case cls: Class[_] =>
val wildcards = cls.getTypeParameters.map { tv =>
val bounds = tv.getBounds.filter(_ != classOf[AnyRef]).map(tpeRepr)
.mkStringOrEmpty(" <: ", " with ", "")
s"_$bounds"
}.mkStringOrEmpty("[", ",", "]")
s"${classRef(cls)}$wildcards"
case tv: TypeVariable[_] =>
tv.getName
case wc: WildcardType =>
val lowerBounds = wc.getLowerBounds.map(tpeRepr).mkStringOrEmpty(" >: ", " with ", "")
val upperBounds = wc.getUpperBounds.filter(_ != classOf[AnyRef]).map(tpeRepr)
.mkStringOrEmpty(" <: ", " with ", "")
s"_$lowerBounds$upperBounds"
case pt: ParameterizedType =>
val owner = pt.getOwnerType match {
case null => ""
case ot => s"${tpeRepr(ot)}#"
}
val raw = pt.getRawType match {
case cls: Class[_] => classRef(cls)
case t => tpeRepr(t)
}
s"$owner$raw${pt.getActualTypeArguments.map(tpeRepr).mkString("[", ", ", "]")}"
}
val methods = clazz.getMethods
.filter(m => Modifier.isStatic(m.getModifiers))
.map { m =>
val tparams = m.getTypeParameters.map { tv =>
val bounds = tv.getBounds.filter(_ != classOf[AnyRef]).map(tpeRepr)
.mkStringOrEmpty(" <: ", " with ", "")
s"${tv.getName}$bounds"
}.mkStringOrEmpty("[", ", ", "]")
val params = m.getParameters
.map { p =>
val tpe = if (p.isVarArgs) p.getParameterizedType match {
case cls: Class[_] => s"${tpeRepr(cls.getComponentType)}*"
case gat: GenericArrayType => s"${tpeRepr(gat.getGenericComponentType)}*"
} else tpeRepr(p.getParameterizedType)
s"${p.getName}: $tpe"
}
.mkString("(", ", ", ")")
val targs = m.getTypeParameters.map(_.getName).mkStringOrEmpty("[", ", ", "]")
val args = m.getParameters.map { p =>
if (p.isVarArgs) s"${p.getName}: _*" else p.getName
}.mkString("(", ", ", ")")
s""" def ${m.getName}$tparams$params: ${tpeRepr(m.getGenericReturnType)} =
| J${clazz.getSimpleName}.${m.getName}$targs$args""".stripMargin
}.mkString("\n")
val imports = importedClasses.map(c =>
s"import ${c.getPackage.getName}.{${c.getSimpleName} => J${c.getSimpleName}}")
s"""
|${imports.mkString("\n")}
|
|object ${clazz.getSimpleName} {
|$methods
|}
|""".stripMargin
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment