All Downloads are FREE. Search and download functionalities are using the official Maven repository.

format.specific.converters.ScalaConverter.scala Maven / Gradle / Ivy

There is a newer version: 1.0.0-RC23
Show newest version
package avrohugger
package format
package specific
package converters

import treehugger.forest._
import definitions._
import treehuggerDSL._

import org.apache.avro.Schema

import scala.language.postfixOps
import scala.collection.JavaConversions._


object ScalaConverter {

  def checkCustomArrayType(
    maybeCustomArray: Option[Class[_]],
    elementType: Type,
    seqArgs: Typed,
    defaultConversion: Tree) = {
    val classTagIdent = REF(s"scala.reflect.ClassTag(classOf[$elementType])")
    val arrayConversion = ARRAY(seqArgs).APPLY(classTagIdent).AS(TYPE_ARRAY(elementType))
    maybeCustomArray match {
      case Some(c) if c == classOf[Array[_]] => arrayConversion
      case Some(c) if c == classOf[List[_]]  => LIST(seqArgs)
      case Some(c) if c == classOf[Seq[_]]   => SEQ(seqArgs)
      case _                                 => defaultConversion
    }
  }
  
  // takes as args a REF wrapped according to field Type
  def convertFromJava(
    classStore: ClassStore,
    namespace: Option[String],
    schema: Schema,
    tree: Tree, 
    typeMatcher: TypeMatcher): Tree = {

    schema.getType match {
      case Schema.Type.ARRAY => {
        val elementSchema = schema.getElementType
        val elementType = typeMatcher.toScalaType(classStore, namespace, elementSchema)
        val JavaList = RootClass.newClass("java.util.List[_]")
        val applyParam = REF("array") DOT("iterator")
        val elementConversion = convertFromJava(classStore, namespace, elementSchema, REF("x"), typeMatcher)
        val seqArgs = {
          SEQARG(
            REF("scala.collection.JavaConversions.asScalaIterator").APPLY(applyParam).DOT("toSeq")
              .MAP(LAMBDA(PARAM("x")) ==> BLOCK(elementConversion))
          )
        }
        val maybeCustomArrayType = typeMatcher.typeMap.get("array")
        val resultExpr = BLOCK(
          checkCustomArrayType(maybeCustomArrayType, elementType, seqArgs, LIST(seqArgs))
        )
        val arrayConversion = CASE(ID("array") withType(JavaList)) ==> resultExpr
        val errorMessage = INTERP("s", LIT(s"expected array with type $JavaList, found "), LIT("array"))
        val errorExpr = NEW("org.apache.avro.AvroRuntimeException", errorMessage)
        val conversionCases = List(arrayConversion)
        val arrayMatchError = CASE(WILDCARD) ==> errorExpr
        tree MATCH(conversionCases:_*)
      }
      case Schema.Type.STRING => tree TOSTRING
      case Schema.Type.MAP => {
        val JavaMap = RootClass.newClass("java.util.Map[_,_]")
        val resultExpr = {
          BLOCK(
            REF("scala.collection.JavaConversions.mapAsScalaMap")
            .APPLY(REF("map"))
            .DOT("toMap")
            .MAP(LAMBDA(PARAM("kvp")) ==> BLOCK(
              VAL("key") := REF("kvp._1").DOT("toString"),
              VAL("value") := REF("kvp._2"),
              PAREN(REF("key"), convertFromJava(classStore, namespace, schema.getValueType, REF("value"), typeMatcher)))
            )
          )
        }
        val mapConversion = CASE(ID("map") withType(JavaMap)) ==> resultExpr
        tree MATCH(mapConversion)
      }
      case Schema.Type.FIXED    => sys.error("the FIXED datatype is not yet supported")
      case Schema.Type.BYTES => {
        val JavaBuffer = RootClass.newClass("java.nio.ByteBuffer")
        val resultExpr = Block(
          REF("buffer") DOT "array" APPLY()
        )
        val bufferConversion = CASE(ID("buffer") withType(JavaBuffer)) ==> resultExpr
        tree MATCH bufferConversion
      }
      case Schema.Type.UNION  => {
        // check if it's the kind of union that we support (i.e. nullable fields)
        if (schema.getTypes.length != 2 ||
           !schema.getTypes.map(x => x.getType).contains(Schema.Type.NULL) ||
            schema.getTypes.filterNot(x => x.getType == Schema.Type.NULL).length != 1) {
              sys.error("Unions beyond nullable fields are not supported")
        }
        // the union represents a nullable field, the kind of union supported in avrohugger
        else {
          val typeParamSchema = schema.getTypes.find(x => x.getType != Schema.Type.NULL).get
          val nullConversion = CASE(NULL) ==> NONE
          val someExpr = SOME(convertFromJava(classStore, namespace, typeParamSchema, tree, typeMatcher))
          val someConversion = CASE(WILDCARD) ==> someExpr
          val conversionCases = List(nullConversion, someConversion)
          tree MATCH(conversionCases:_*)
        }
      }
      case _ => tree
    }
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy