Skip to content

Instantly share code, notes, and snippets.

@emesday
Created February 1, 2017 01:22
Show Gist options
  • Save emesday/a3c747e1315429c48ba6c5ef25b5fd97 to your computer and use it in GitHub Desktop.
Save emesday/a3c747e1315429c48ba6c5ef25b5fd97 to your computer and use it in GitHub Desktop.
protected def typeToTypeTag[T](tpe: Type): TypeTag[T] = {
TypeTag(currentMirror, new TypeCreator {
override def apply[U <: Universe with Singleton](m: api.Mirror[U]): U#Type = {
tpe.asInstanceOf[U#Type]
}
})
}
protected def typeToClassTag[T](tpe: Type): ClassTag[T] = typeTagToClassTag(typeToTypeTag(tpe))
protected def typeTagToClassTag[T](typeTag: TypeTag[T]): ClassTag[T] = ClassTag[T](typeTag.mirror.runtimeClass(typeTag.tpe))
protected def toClass[T](tpe: Type): Class[T] = typeToClassTag(tpe).runtimeClass.asInstanceOf[Class[T]]
protected def toClass[T](tt: TypeTag[T]): Class[T] = typeTagToClassTag(tt).runtimeClass.asInstanceOf[Class[T]]
protected def toJavaListClass[T](clazz: Class[T]): Class[util.List[T]] = {
classOf[java.util.List[T]]
}
protected def paramType[T]()(implicit tt: TypeTag[T]): Type = {
val a = typeOf[T] match { case TypeRef(_, _, args) => args }
a.head
}
protected def getProperty[T](key: String, tpe: Class[T])(implicit tt: TypeTag[T]): Option[_] = {
val clazz = if (tpe.isAssignableFrom(classOf[Option[Any]])) {
toClass(paramType()(tt))
} else if (tpe.isAssignableFrom(classOf[Seq[_]])) {
toJavaListClass(toClass(paramType()(tt))): Class[_]
} else {
tpe
}
var v = Try(underlying.head.getProperty(key, clazz)).toOption
var i = 1
while (v.isEmpty && i < underlying.length) {
v = Try(underlying(i).getProperty(key, clazz)).toOption
i += 1
}
v
}
protected def newCaseClass[A](prefix: String)(implicit t: ClassTag[A]): A = {
val module = currentMirror.classSymbol(t.runtimeClass).companion.asModule
val instanceMirror = currentMirror.reflect(currentMirror.reflectModule(module).instance)
val typeSignature = instanceMirror.symbol.typeSignature
val method = typeSignature.member(TermName("apply")).asMethod
val args = method.paramLists.flatten.zipWithIndex.map { case (sym, i) =>
val key = s"$prefix.${sym.name.toString.replace("$u002E", ".")}"
logger.debug(s"key: $key, sym: ${sym.typeSignature}")
sym.typeSignature match {
case t if t <:< typeOf[Product] && !(t <:< typeOf[Option[_]]) =>
// case class
newCaseClass(key)(typeToClassTag(t))
case t if t <:< typeOf[Option[Product]]=>
scala.util.Try(newCaseClass(key)(typeToClassTag(paramType()(typeToTypeTag(t)))): Any).toOption
case t if t <:< typeOf[Seq[Product]] =>
// Seq[case class]
throw new NotImplementedError
case t if t <:< typeOf[Option[Seq[Product]]] =>
// Option[Seq[case class]]
throw new NotImplementedError
case t =>
getProperty(key, toClass(sym.typeSignature))(typeToTypeTag(sym.typeSignature)) match {
case Some(v) =>
val c = v match {
case v: java.util.Collection[_] if t <:< typeOf[Option[_]] => Some(v.toArray.toSeq)
case v: java.util.Collection[_] => v.toArray.toSeq
case v if t <:< typeOf[Option[_]] => Some(v)
case v => v
}
logger.debug(s"set value from config: $c")
c
case None =>
val defaultArgument = typeSignature.member(TermName(s"apply$$default$$${i + 1}"))
if (defaultArgument != NoSymbol) {
// set to defaultArgument
val v = instanceMirror.reflectMethod(defaultArgument.asMethod)()
logger.debug(s"set value from default value: $v")
v
} else {
// no defaultArgument
sym.typeSignature match {
case t if t <:< typeOf[Option[_]] =>
logger.debug("set None")
None
case _ => throw new NoSuchElementException(s"Not Found: $key")
}
}
}
}
}
instanceMirror.reflectMethod(method)(args: _*).asInstanceOf[A]
}
def bind[T: ClassTag](prefix: String): T = {
newCaseClass[T](prefix.replace("-", "."))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment