alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Lift Framework example source code file (Meta.scala)

This example Lift Framework source code file (Meta.scala) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - Lift Framework tags/keywords

class, class, jconstructor, kind, list, list, mapping, mapping, none, parameterizedtype, reflection, string, type, type, typeinfo, util

The Lift Framework Meta.scala source code

/*
 * Copyright 2009-2010 WorldWide Conferencing, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.liftweb
package json

import java.lang.reflect.{Constructor => JConstructor, Field, Type, ParameterizedType, GenericArrayType}
import java.util.Date

case class TypeInfo(clazz: Class[_], parameterizedType: Option[ParameterizedType])

trait ParameterNameReader {
  def lookupParameterNames(constructor: JConstructor[_]): Traversable[String]
}

private[json] object Meta {
  import com.thoughtworks.paranamer._

  /** Intermediate metadata format for case classes.
   *  This ADT is constructed (and then memoized) from given case class using reflection.
   *
   *  Example mapping.
   *
   *  package xx
   *  case class Person(name: String, address: Address, children: List[Child])
   *  case class Address(street: String, city: String)
   *  case class Child(name: String, age: BigInt)
   *
   *  will produce following Mapping:
   *
   *  Constructor("xx.Person", List(
   *    Arg("name", Value(classOf[String])),
   *    Arg("address", Constructor("xx.Address", List(Value("street"), Value("city")))),
   *    Arg("children", Col(classOf[List[_]], Constructor("xx.Child", List(Value("name"), Value("age")))))))
   */
  sealed abstract class Mapping
  case class Arg(path: String, mapping: Mapping, optional: Boolean) extends Mapping
  case class Value(targetType: Class[_]) extends Mapping
  case class Cycle(targetType: Type) extends Mapping
  case class Dict(mapping: Mapping) extends Mapping
  case class Col(targetType: TypeInfo, mapping: Mapping) extends Mapping
  case class Constructor(targetType: TypeInfo, choices: List[DeclaredConstructor]) extends Mapping {
    def bestMatching(argNames: List[String]): Option[DeclaredConstructor] = {
      val names = Set(argNames: _*)
      def countOptionals(args: List[Arg]) =
        args.foldLeft(0)((n, x) => if (x.optional) n+1 else n)
      def score(args: List[Arg]) =
        args.foldLeft(0)((s, arg) => if (names.contains(arg.path)) s+1 else -100)

      if (choices.isEmpty) None
      else {
        val best = choices.tail.foldLeft((choices.head, score(choices.head.args))) { (best, c) =>
          val newScore = score(c.args)
          if (newScore == best._2) {
            if (countOptionals(c.args) < countOptionals(best._1.args))
              (c, newScore) else best
          } else if (newScore > best._2) (c, newScore) else best
        }
        Some(best._1)
      }
    }
  }

  case class DeclaredConstructor(constructor: JConstructor[_], args: List[Arg])

  // Current constructor parsing context. (containingClass + allArgs could be replaced with Constructor)
  case class Context(argName: String, containingClass: Class[_], allArgs: List[(String, Type)])

  private val mappings = new Memo[Type, Mapping]
  private val unmangledNames = new Memo[String, String]
  private val paranamer = new CachingParanamer(new BytecodeReadingParanamer)

  object ParanamerReader extends ParameterNameReader {
    def lookupParameterNames(constructor: JConstructor[_]): Traversable[String] =
      paranamer.lookupParameterNames(constructor)
  }

  private[json] def mappingOf(clazz: Type, typeArgs: Seq[Class[_]] = Seq())
                             (implicit formats: Formats): Mapping = {
    import Reflection._

    def constructors(t: Type, visited: Set[Type], context: Option[Context]) = {
      Reflection.constructors(t, formats.parameterNameReader, context).map { case (c, args) =>
        DeclaredConstructor(c, args.map { case (name, t) =>
          toArg(unmangleName(name), t, visited, Context(name, c.getDeclaringClass, args)) })
      }
    }

    def toArg(name: String, genericType: Type, visited: Set[Type], context: Context): Arg = {
      def mkContainer(t: Type, k: Kind, valueTypeIndex: Int, factory: Mapping => Mapping) =
        if (typeConstructor_?(t)) {
          val typeArgs = typeConstructors(t, k)(valueTypeIndex)
          factory(fieldMapping(typeArgs)._1)
        } else factory(fieldMapping(typeParameters(t, k, context)(valueTypeIndex))._1)

      def parameterizedTypeOpt(t: Type) = t match {
        case x: ParameterizedType => 
          val typeArgs = x.getActualTypeArguments.toList.zipWithIndex
            .map { case (t, idx) =>
              if (t == classOf[java.lang.Object]) 
                ScalaSigReader.readConstructor(context.argName, context.containingClass, idx, context.allArgs.map(_._1))
              else t
            }
          Some(mkParameterizedType(x.getRawType, typeArgs))
        case _ => None
      }

      def mkConstructor(t: Type) = 
        if (visited.contains(t)) (Cycle(t), false)
        else (Constructor(TypeInfo(rawClassOf(t), parameterizedTypeOpt(t)), constructors(t, visited + t, Some(context))), false)

      def fieldMapping(t: Type): (Mapping, Boolean) = t match {
        case pType: ParameterizedType => 
          val raw = rawClassOf(pType)
          val info = TypeInfo(raw, Some(pType))
          if (classOf[Set[_]].isAssignableFrom(raw))
            (mkContainer(t, `* -> *`, 0, Col.apply(info, _)), false)
          else if (raw.isArray)
            (mkContainer(t, `* -> *`, 0, Col.apply(info, _)), false)
          else if (classOf[Option[_]].isAssignableFrom(raw))
            (mkContainer(t, `* -> *`, 0, identity _), true)
          else if (classOf[Map[_, _]].isAssignableFrom(raw))
            (mkContainer(t, `(*,*) -> *`, 1, Dict.apply _), false)
          else if (classOf[Seq[_]].isAssignableFrom(raw))
            (mkContainer(t, `* -> *`, 0, Col.apply(info, _)), false)
          else 
            mkConstructor(t)
        case aType: GenericArrayType =>
          // Couldn't find better way to reconstruct proper array type:
          val raw = java.lang.reflect.Array.newInstance(rawClassOf(aType.getGenericComponentType), 0: Int).getClass
          (Col(TypeInfo(raw, None), fieldMapping(aType.getGenericComponentType)._1), false)
        case raw: Class[_] =>
          if (primitive_?(raw)) (Value(raw), false)
          else if (raw.isArray)
            (mkContainer(t, `* -> *`, 0, Col.apply(TypeInfo(raw, None), _)), false)
          else 
            mkConstructor(t)
        case x => (Constructor(TypeInfo(classOf[AnyRef], None), Nil), false)
      }

      val (mapping, optional) = fieldMapping(genericType)
      Arg(name, mapping, optional)
    }

    if (primitive_?(clazz)) Value(rawClassOf(clazz))
    else {
      mappings.memoize(clazz, t => {
        val c = rawClassOf(t)
        val (pt, typeInfo) = 
          if (typeArgs.isEmpty) (t, TypeInfo(c, None))
          else {
            val t = mkParameterizedType(c, typeArgs)
            (t, TypeInfo(c, Some(t)))
          }
        Constructor(typeInfo, constructors(pt, Set(), None))         
      })
    }
  }

  private[json] def rawClassOf(t: Type): Class[_] = t match {
    case c: Class[_] => c
    case p: ParameterizedType => rawClassOf(p.getRawType)
    case x => fail("Raw type of " + x + " not known")
  }

  private[json] def mkParameterizedType(owner: Type, typeArgs: Seq[Type]) = 
    new ParameterizedType {
      def getActualTypeArguments = typeArgs.toArray
      def getOwnerType = owner
      def getRawType = owner
      override def toString = getOwnerType + "[" + getActualTypeArguments.mkString(",") + "]"
    }

  private[json] def unmangleName(name: String) =
    unmangledNames.memoize(name, scala.reflect.NameTransformer.decode)

  private[json] def fail(msg: String, cause: Exception = null) = throw new MappingException(msg, cause)

  private class Memo[A, R] {
    private var cache = Map[A, R]()

    def memoize(x: A, f: A => R): R = synchronized {
      if (cache contains x) cache(x) else {
        val ret = f(x)
        cache += (x -> ret)
        ret
      }
    }
  }

  object Reflection {
    import java.lang.reflect._
    import scala.collection.JavaConversions._

    sealed abstract class Kind
    case object `* -> *` extends Kind
    case object `(*,*) -> *` extends Kind

    val primitives = Map[Class[_], Unit]() ++ (List[Class[_]](
      classOf[String], classOf[Int], classOf[Long], classOf[Double],
      classOf[Float], classOf[Byte], classOf[BigInt], classOf[Boolean],
      classOf[Short], classOf[java.lang.Integer], classOf[java.lang.Long],
      classOf[java.lang.Double], classOf[java.lang.Float],
      classOf[java.lang.Byte], classOf[java.lang.Boolean], classOf[Number],
      classOf[java.lang.Short], classOf[Date], classOf[Symbol], classOf[JValue],
      classOf[JObject], classOf[JArray]).map((_, ())))

    def constructors(t: Type, names: ParameterNameReader, context: Option[Context]): List[(JConstructor[_], List[(String, Type)])] =
      rawClassOf(t).getDeclaredConstructors.map(c => (c, constructorArgs(t, c, names, context))).toList

    def constructorArgs(t: Type, constructor: JConstructor[_], 
                        nameReader: ParameterNameReader, context: Option[Context]): List[(String, Type)] = {
      def argsInfo(c: JConstructor[_], typeArgs: Map[TypeVariable[_], Type]) = {
        val Name = """^((?:[^$]|[$][^0-9]+)+)([$][0-9]+)?$"""r
        def clean(name: String) = name match {
          case Name(text, junk) => text
        }
        try {
          val names = nameReader.lookupParameterNames(c).map(clean)
          val types = c.getGenericParameterTypes.toList.zipWithIndex map {
            case (v: TypeVariable[_], idx) => 
              val arg = typeArgs.getOrElse(v, v)
              if (arg == classOf[java.lang.Object]) 
                context.map(ctx => ScalaSigReader.readConstructor(ctx.argName, ctx.containingClass, idx, ctx.allArgs.map(_._1))).getOrElse(arg)
              else arg
            case (x, _) => x
          }
          names.toList.zip(types)
        } catch {
          case e: ParameterNamesNotFoundException => Nil
        }
      }

      t match {
        case c: Class[_] => argsInfo(constructor, Map())
        case p: ParameterizedType =>
          val vars = 
            Map() ++ rawClassOf(p).getTypeParameters.toList.map(_.asInstanceOf[TypeVariable[_]]).zip(p.getActualTypeArguments.toList) // FIXME this cast should not be needed
          argsInfo(constructor, vars)
        case x => fail("Do not know how query constructor info for " + x)
      }
    }

    def primaryConstructorArgs(c: Class[_])(implicit formats: Formats) = {
      val ord = Ordering[Int].on[JConstructor[_]](_.getParameterTypes.size)
      val primary = c.getDeclaredConstructors.max(ord)
      constructorArgs(c, primary, formats.parameterNameReader, None)
    }

    def typeParameters(t: Type, k: Kind, context: Context): List[Class[_]] = {
      def term(i: Int) = t match {
        case ptype: ParameterizedType => ptype.getActualTypeArguments()(i) match {
          case c: Class[_] => 
            if (c == classOf[java.lang.Object]) 
              ScalaSigReader.readConstructor(context.argName, context.containingClass, i, context.allArgs.map(_._1))
            else c
          case p: ParameterizedType => p.getRawType.asInstanceOf[Class[_]]
          case x => fail("do not know how to get type parameter from " + x)
        }
        case clazz: Class[_] if (clazz.isArray) => i match {
          case 0 => clazz.getComponentType.asInstanceOf[Class[_]]
          case _ => fail("Arrays only have one type parameter")
        }
        case clazz: GenericArrayType => i match {
          case 0 => clazz.getGenericComponentType.asInstanceOf[Class[_]]
          case _ => fail("Arrays only have one type parameter")
        }
        case _ => fail("Unsupported Type: " + t + " (" + t.getClass + ")")
      }

      k match {
        case `* -> *`     => List(term(0))
        case `(*,*) -> *` => List(term(0), term(1))
      }
    }

    def typeConstructors(t: Type, k: Kind): List[Type] = {
      def types(i: Int): Type = {
        val ptype = t.asInstanceOf[ParameterizedType]
        ptype.getActualTypeArguments()(i) match {
          case p: ParameterizedType => p
          case c: Class[_] => c
        }
      }

      k match {
        case `* -> *`     => List(types(0))
        case `(*,*) -> *` => List(types(0), types(1))
      }
    }

    def primitive_?(t: Type) = t match {
      case clazz: Class[_] => primitives contains clazz
      case _ => false
    }

    def static_?(f: Field) = Modifier.isStatic(f.getModifiers)
    def typeConstructor_?(t: Type) = t match {
      case p: ParameterizedType =>
        p.getActualTypeArguments.exists(_.isInstanceOf[ParameterizedType])
      case _ => false
    }

    def array_?(x: Any) = x != null && classOf[scala.Array[_]].isAssignableFrom(x.asInstanceOf[AnyRef].getClass)

    def fields(clazz: Class[_]): List[(String, TypeInfo)] = {
      val fs = clazz.getDeclaredFields.toList
        .filterNot(f => Modifier.isStatic(f.getModifiers) || Modifier.isTransient(f.getModifiers))
        .map(f => (f.getName, TypeInfo(f.getType, f.getGenericType match {
          case p: ParameterizedType => Some(p)
          case _ => None
        })))
      fs ::: (if (clazz.getSuperclass == null) Nil else fields(clazz.getSuperclass))
    }

    def setField(a: AnyRef, name: String, value: Any) = {      
      val f = findField(a.getClass, name)
      f.setAccessible(true)
      f.set(a, value)
    }

    def getField(a: AnyRef, name: String) = {
      val f = findField(a.getClass, name)
      f.setAccessible(true)
      f.get(a)
    }

    def findField(clazz: Class[_], name: String): Field = try {
      clazz.getDeclaredField(name)
    } catch {
      case e: NoSuchFieldException => 
        if (clazz.getSuperclass == null) throw e 
        else findField(clazz.getSuperclass, name)
    }

    def hasDeclaredField(clazz: Class[_], name: String): Boolean = try {
      clazz.getDeclaredField(name)
      true
    } catch {
      case e: NoSuchFieldException => false
    }

    def mkJavaArray(x: Any, componentType: Class[_]) = {
      val arr = x.asInstanceOf[scala.Array[_]]
      val a = java.lang.reflect.Array.newInstance(componentType, arr.size)
      var i = 0
      while (i < arr.size) {
        java.lang.reflect.Array.set(a, i, arr(i))
        i += 1
      }
      a
    }

    def primitive2jvalue(a: Any)(implicit formats: Formats) = a match {
      case x: String => JString(x)
      case x: Int => JInt(x)
      case x: Long => JInt(x)
      case x: Double => JDouble(x)
      case x: Float => JDouble(x)
      case x: Byte => JInt(BigInt(x))
      case x: BigInt => JInt(x)
      case x: Boolean => JBool(x)
      case x: Short => JInt(BigInt(x))
      case x: java.lang.Integer => JInt(BigInt(x.asInstanceOf[Int]))
      case x: java.lang.Long => JInt(BigInt(x.asInstanceOf[Long]))
      case x: java.lang.Double => JDouble(x.asInstanceOf[Double])
      case x: java.lang.Float => JDouble(x.asInstanceOf[Float])
      case x: java.lang.Byte => JInt(BigInt(x.asInstanceOf[Byte]))
      case x: java.lang.Boolean => JBool(x.asInstanceOf[Boolean])
      case x: java.lang.Short => JInt(BigInt(x.asInstanceOf[Short]))
      case x: Date => JString(formats.dateFormat.format(x))
      case x: Symbol => JString(x.name)
      case _ => error("not a primitive " + a.asInstanceOf[AnyRef].getClass)
    }
  }
}

case class MappingException(msg: String, cause: Exception) extends Exception(msg, cause) {
  def this(msg: String) = this(msg, null)
}

Other Lift Framework examples (source code examples)

Here is a short list of links related to this Lift Framework Meta.scala source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.