in scio-avro/src/main/scala/com/spotify/scio/avro/types/ConverterProvider.scala [41:125]
private def fromGenericRecordInternal(c: blackbox.Context)(tpe: c.Type): c.Tree = {
import c.universe._
// =======================================================================
// Converter helpers
// =======================================================================
def cast(tree: Tree, tpe: Type): Tree =
tpe match {
case t if t =:= typeOf[Boolean] => q"$tree.asInstanceOf[Boolean]"
case t if t =:= typeOf[Int] => q"$tree.asInstanceOf[Int]"
case t if t =:= typeOf[Long] => q"$tree.asInstanceOf[Long]"
case t if t =:= typeOf[Float] => q"$tree.asInstanceOf[Float]"
case t if t =:= typeOf[Double] => q"$tree.asInstanceOf[Double]"
case t if t =:= typeOf[String] => q"$tree.toString"
case t if t =:= typeOf[ByteString] =>
val bb = q"$tree.asInstanceOf[_root_.java.nio.ByteBuffer]"
q"_root_.com.google.protobuf.ByteString.copyFrom($bb)"
case t if t =:= typeOf[Array[Byte]] =>
val bb = q"$tree.asInstanceOf[_root_.java.nio.ByteBuffer]"
q"_root_.java.util.Arrays.copyOfRange($bb.array(), $bb.position(), $bb.limit())"
case t if t.erasure <:< typeOf[scala.collection.Map[String, _]].erasure =>
map(tree, tpe.typeArgs.tail.head)
case t if t.erasure =:= typeOf[List[_]].erasure =>
list(tree, tpe.typeArgs.head)
case t if isCaseClass(c)(t) =>
val fn = TermName("r" + t.typeSymbol.name)
q"""{
val $fn = $tree.asInstanceOf[${p(c, ApacheAvro)}.generic.GenericRecord]
${constructor(t, fn)}
}
"""
case _ => c.abort(c.enclosingPosition, s"Unsupported type: $tpe")
}
def option(tree: Tree, tpe: Type): Tree =
q"if ($tree == null) None else Some(${cast(tree, tpe)})"
def list(tree: Tree, tpe: Type): Tree = {
val jl = tq"_root_.java.util.List[AnyRef]"
q"asScala($tree.asInstanceOf[$jl]).iterator.map(x => ${cast(q"x", tpe)}).toList"
}
def map(tree: Tree, tpe: Type): Tree = {
val jm = tq"_root_.java.util.Map[AnyRef, AnyRef]"
q"asScala($tree.asInstanceOf[$jm]).iterator.map(kv => (kv._1.toString, ${cast(q"kv._2", tpe)})).toMap"
}
def field(symbol: Symbol, fn: TermName): Tree = {
val name = symbol.name.toString
val tpe = symbol.asMethod.returnType
val tree = q"$fn.get($name)"
if (tpe.erasure =:= typeOf[Option[_]].erasure) {
option(tree, tpe.typeArgs.head)
} else {
cast(tree, tpe)
}
}
def constructor(tpe: Type, fn: TermName): Tree = {
val companion = tpe.typeSymbol.companion
val gets = tpe.erasure match {
case t if isCaseClass(c)(t) => getFields(c)(t).map(s => field(s, fn))
case _ => c.abort(c.enclosingPosition, s"Unsupported type: $tpe")
}
q"$companion(..$gets)"
}
// =======================================================================
// Entry point
// =======================================================================
val tn = TermName("r")
q"""(r: ${p(c, ApacheAvro)}.generic.GenericRecord) => {
import _root_.scala.jdk.javaapi.CollectionConverters._
${constructor(tpe, tn)}
}
"""
}