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

ters.case-class-generator_2.10.0.6.1.source-code.DynamicCaseClass.scala Maven / Gradle / Ivy

The newest version!
package com.julianpeeters.caseclass.generator

case class DynamicCaseClass(classData: ClassData) {

  // Prepare the input as to improve readability of the code
  val name = classData.className.name
  val namespace: Option[String] = {
    if (classData.classNamespace.namespace.isDefined) Some(classData.classNamespace.namespace.get.replaceAllLiterally("/", "."))
    else None
  }
  val fullName = {
    if (namespace.isDefined) (namespace.get + "." + name)
    else name
  }

  // Using data from the input, prepare the arguments for reflective instantiation of the yet-to-be-gen'd class
  def matchTypeData(namespace: Option[String], tf: FieldData) = {
    FieldMatcher.getTypeData(namespace, tf.fieldType).asParam
  }
  val instantiationParams: List[Object] = classData.classFields.fields.map(fd => matchTypeData(namespace, fd))
  val methodParams: List[Class[_]] = FieldMatcher.getReturnType(classData.classFields.fields).asInstanceOf[List[Class[_]]]

  // Get an ASM classwriter, passing it the data needed to dynamically generate a class, "dump" the bytecode
  val bytecode = BytecodeGenerator.dump(classData) //defines the class
  //val loader = new DynamicClassLoader
  val runtimeClass: java.lang.Class[_] = DynamicClassLoader.loadClass(fullName, bytecode.classBytes.bytes) 
  val runtimeCompanion: java.lang.Class[_] = DynamicClassLoader.loadClass(fullName + "$", bytecode.moduleBytes.bytes)

  // Now that we've loaded our classes, use reflection to get an instance of the companion class
  val method_apply = runtimeCompanion.getMethod("apply", methodParams: _*) // populate the instance values
  val companionCtor = runtimeCompanion.getConstructor()

  val runtimeCompanionObject = companionCtor.newInstance() // get an instance of the companion

  // Offer a method to return new instances of the runtime case class
  def newInstance(arguments: Any*) = {
    val objs = arguments.map(x=>x.asInstanceOf[Object])
    method_apply.invoke(runtimeCompanionObject, objs: _*)
  }
  
  // A "zero-arg contstructor" for new instances with default arguments
  val runtimeInstance: java.lang.Object = method_apply.invoke(runtimeCompanionObject, instantiationParams: _*)

  // Get a  type tag for the new class (adapted from http://stackoverflow.com/questions/22970209/get-typetaga-from-classa/22972751#22972751
  import scala.reflect.runtime.universe._
  import scala.reflect.api._

  val mirror = runtimeMirror(runtimeClass.getClassLoader) // obtain runtime mirror
  val sym = mirror.staticClass(runtimeClass.getName) // obtain class symbol
  val tpe = sym.selfType // obtain type object 

  // Create a type tag which contains a type object
  val tt = TypeTag(mirror, new TypeCreator {
    def apply[U <: Universe with Singleton](m: scala.reflect.api.Mirror[U]) = {
      if (m eq mirror)  tpe.asInstanceOf[U#Type] 
      else throw new IllegalArgumentException(s"Type tag defined in $mirror cannot be migrated to other mirrors.")
    }
  })

  // Use a dummy class to hold the type tag/manifest for the type member T - Thanks Eugene Burmako (http://stackoverflow.com/questions/25601058/can-i-use-a-class-defined-in-a-toolbox-as-a-type-parameter-of-typeof)
  class TypeCheckDummy {
    type T = AnyRef 
    implicit val tag: TypeTag[T] = tt.asInstanceOf[TypeTag[T]]
    implicit val manifest: Manifest[T] = Manifest.classType(runtimeClass)
  }

  // A type alias of the dummy class' type member T with TypeTag and Manifest, can be used as a type parameter
  val implicits = new TypeCheckDummy;
  import implicits._
  type TYPE = T  

  // Finally, after a DynamicCaseClass is made, add it to the list of generated Classes 
  ClassStore.accept(this)

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy