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

breeze.config.ReflectionUtils.scala Maven / Gradle / Ivy

The newest version!
package breeze.config

import java.{lang=>jl}
import jl.reflect._

/**
 * Various utilities for determining properties of an object given Manifests and Class information
 * @author dlwh
 */
private[config] object ReflectionUtils {
  def manifestFromClass(c: Class[_]):Manifest[_] = {
    import java.lang._
    if(c == Float.TYPE) Manifest.Float
    else if(c == Long.TYPE) Manifest.Long
    else if(c == Double.TYPE) Manifest.Double
    else if(c == Integer.TYPE) Manifest.Int
    else if(c == Byte.TYPE) Manifest.Byte
    else if(c == Short.TYPE) Manifest.Short
    else if(c == Character.TYPE) Manifest.Char
    else if(c == Boolean.TYPE) Manifest.Boolean
    else if(c == Void.TYPE) Manifest.Unit
    else Manifest.classType(c)
  }

  /**
   * For a seq of parameters in a class's primary constructor, acquires the default parameters
   * for that class. The returned functions will throw exceptions if there
   * isn't a default for that parameter, or if there's an error processing it.
   *
   * Super hacky.
   */
  def lookupDefaultValues(clazz: Class[_], paramNames: Seq[String]): Seq[() => AnyRef] = {
    try {
      val companion = Class.forName(clazz.getName() + "$").getField("MODULE$").get(null)
      // defaults have the form  init$default$X, for X = 1...
      paramNames.zipWithIndex.map{
        case (name, idx) => () =>
          try {
            val method = companion.getClass.getMethod("init$default$" + (idx + 1))
            method.invoke(companion)
          } catch {
            case e: java.lang.NoSuchMethodException =>
              try {
                val method = companion.getClass.getMethod("$lessinit$greater$default$" + (idx + 1))
                method.invoke(companion)
              } catch {
                case e: java.lang.NoSuchMethodException =>
                throw new NoParameterException("Could not find a matching property!", name)
                case e: Exception =>
                  throw new RuntimeException("Problem processing default argument for " + name, e)
              }
            case e: Exception =>
              throw new RuntimeException("Problem processing default argument for " + name, e)
          }

      }
    } catch {
      case e: Exception =>
        paramNames.map{
          paramName => (() => throw new NoParameterException("Could not find a matching property!", paramName))
        }
    }
  }

  /**
   * This is a relatively unsophisticated attempt to infer the types of type parameters for a dynamic
   * class given the static class.
   */
  def solveTypes(knownTypes: Map[String, OptManifest[_]], staticClass: Class[_], dynamicClass: Class[_]): Map[String, OptManifest[_]] = {
    if (!staticClass.isAssignableFrom(dynamicClass)) throw new RuntimeException(staticClass  + " is not assignable from " + dynamicClass + "!")
    // iterate up the inheritance chain
    def superTypes = if (dynamicClass.isInterface) (
      dynamicClass.getInterfaces.iterator
      )
    else /* class*/ (
      Iterator.iterate(dynamicClass.asInstanceOf[Class[AnyRef]])(_.getSuperclass.asInstanceOf[Class[AnyRef]])
        .takeWhile(clss => clss != null && staticClass.isAssignableFrom(clss))
      )
    val highestType = superTypes.reduceLeft((a, b) => b)
    val dynamicToStaticMapping: Map[String, OptManifest[_]] = superTypes.sliding(2, 1).foldRight(knownTypes) {
      (classPair, knownTypes) =>
        if (classPair.length < 2) knownTypes
        else {
          val generic = classPair(0).getGenericSuperclass
          extendMapping(classPair(0), classPair(1), generic, knownTypes)
        }
    }
    if (!staticClass.isInterface) {
      dynamicToStaticMapping
    } else {
      // solve for the interface too:
      val matchedIFace = highestType.getGenericInterfaces.find{
        case x: ParameterizedType =>
          x.getRawType == staticClass
        case x: Class[_] => x == staticClass
        case _ => false
      } getOrElse (highestType)
      extendMapping(highestType, staticClass, matchedIFace, dynamicToStaticMapping)
    }
  }

  /**
   * For the above method, tries to map the figure out the immediate subtype's parameters given the parent's
   */
  private def extendMapping(subType: Class[_], superType: Class[_], genericSuper: Type, knownTypes: Map[String, OptManifest[_]]) = {
    val mapping: Map[String, String] = genericSuper match {
      case x: ParameterizedType =>
        matchImmediateSubclassToSuperClass(subType, x, superType)
      case _ => Map.empty
    }
    mapping.mapValues(knownTypes).toMap withDefaultValue NoManifest

  }

  /*
   * returns a map from subclass's type parameters to the super type's.
   * Far from perfect, but works for simple cases, where there's an obvious match (no embedding in other types, and the like)
   * TODO: more sophisticated mapping, e.g. if we have Foo[A] extends Bar[List[A]], and class Bar[X], with X=List[Int]. Need to
   * infer A = Int
   */
  private def matchImmediateSubclassToSuperClass(sub: Class[_], genSup: ParameterizedType, sup: Class[_]): Map[String, String] = {
    val superTypeParams = sup.getTypeParameters.map(_.toString)
    genSup.getActualTypeArguments.map(_.toString) zip superTypeParams toMap
  }


  /**
   *
   *Tries to create the appropriate manifest for a jl.reflect.Type that has
   * using typeMap as a way of handling type parameters.
   */
  def mkManifest(typeMap: Map[String, OptManifest[_]], tpe: Type): Manifest[Object] = tpe match {
    case jl.Integer.TYPE => Manifest.Int.asInstanceOf[Manifest[Object]]
    case jl.Double.TYPE => Manifest.Double.asInstanceOf[Manifest[Object]]
    case jl.Float.TYPE => Manifest.Float.asInstanceOf[Manifest[Object]]
    case jl.Boolean.TYPE => Manifest.Boolean.asInstanceOf[Manifest[Object]]
    case tpe: Class[_] with ParameterizedType => new Manifest[Object] {
      override def erasure: Class[_] = tpe
      def runtimeClass = tpe

      override def typeArguments = tpe.getActualTypeArguments.map(mkManifest(typeMap, _)).toList

      override def toString = erasure.getName + argString
    }
    case tpe: ParameterizedType =>
      val innerMan = mkManifest(Map.empty, tpe.getRawType)
      new Manifest[Object] {
        override def erasure = innerMan.erasure

        def runtimeClass = innerMan.erasure

        override def typeArguments = tpe.getActualTypeArguments.map(mkManifest(typeMap, _)).toList

        override def toString = erasure.getName + argString
      }
    case tpe: Class[_] => new Manifest[Object] {
      def runtimeClass = tpe


      override def erasure: Class[_] = tpe

      override def toString = erasure.getName + argString
    }
    case tpe: TypeVariable[_] => typeMap(tpe.toString) match {
      case x: Manifest[_] => x.asInstanceOf[Manifest[Object]]
      case _ => throw new ConfigurationException("Don't know how to deal with " + tpe + " yet! Add an ArgumentParser.")
    }
    case tpe: GenericArrayType => mkManifest(typeMap, tpe.getGenericComponentType).arrayManifest.asInstanceOf[Manifest[Object]]
    case _ => throw new ConfigurationException("Don't know how to deal with " + tpe + " yet! Add an ArgumentParser." + tpe.getClass.getName)
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy