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

xerial.lens.ObjectParameter.scala Maven / Gradle / Ivy

/*
 * Copyright 2012 Taro L. Saito
 *
 * 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 xerial.lens

import java.{lang => jl}
import xerial.core.log.Logger
import reflect.ClassTag
import scala.language.existentials
//--------------------------------------
//
// ObjectParameter.scala
// Since: 2012/08/02 19:49
//
//--------------------------------------

/**
 * A base class of field parameters and method parameters
 * @param name
 * @param valueType
 */
sealed abstract class Parameter(val name: String, val valueType: ObjectType) extends Serializable {
  val rawType = valueType.rawType

  override def toString = "%s:%s".format(name, valueType)

  def findAnnotationOf[T <: jl.annotation.Annotation](implicit c: ClassTag[T]): Option[T]

  protected def findAnnotationOf[T <: jl.annotation.Annotation](annot: Array[jl.annotation.Annotation])(implicit c: ClassTag[T]): Option[T] = {
    annot.collectFirst {
      case x if (c.runtimeClass isAssignableFrom x.annotationType) => x
    }.asInstanceOf[Option[T]]
  }

  def get(obj: Any): Any
}



/**
 * Represents a constructor parameter
 * @param owner
 * @param fieldOwner
 * @param index
 * @param name
 * @param valueType
 */
case class ConstructorParameter(owner: Class[_], fieldOwner: Option[Class[_]], index: Int, override val name: String, override val valueType: ObjectType) extends Parameter(name, valueType) with Logger {
  lazy val field : jl.reflect.Field =
    if(fieldOwner.isDefined)
      fieldOwner.get.getDeclaredField(name)
    else
      sys.error("no field owner is defined in %s".format(this))

  def findAnnotationOf[T <: jl.annotation.Annotation](implicit c: ClassTag[T]) = {
    val cc = owner.getConstructors()(0)
    val annot: Array[jl.annotation.Annotation] = cc.getParameterAnnotations()(index)
    findAnnotationOf[T](annot)
  }

  /**
   * Get the default value of this parameter or
   * @return
   */
  def getDefaultValue : Option[Any] = {
    trace(s"get the default value of ${this}")

    TypeUtil.companionObject(owner).flatMap { companion =>
      def findMethod(name:String) = {
        try {
          Some(TypeUtil.cls(companion).getDeclaredMethod(name))
        }
        catch {
          case e: NoSuchMethodException => None
        }
      }
      // Find Scala methods for retrieving default values. Since Scala 2.10 appply or $lessinit$greater$ can be the prefix
      val m = findMethod("apply$default$%d".format(index + 1)) orElse(findMethod("$lessinit$greater$default$%d".format(index + 1)))
      try
        m.map(_.invoke(companion))
      catch {
        case e : Throwable =>
          None
      }
    }
  }

  def get(obj: Any) = {
    try {
      Reflect.readField(obj, field)
    }
    catch {
      case e:IllegalAccessException =>
        error(s"read field: class ${obj.getClass}, field:${field.getName}")
        error(e)
        throw e
    }
  }

}

/**
 * Field defined in a class
 * @param owner
 * @param ref
 * @param name
 * @param valueType
 */
case class FieldParameter(owner: Class[_], ref: Class[_], override val name: String, override val valueType: ObjectType)
  extends Parameter(name, valueType) with Logger {
  lazy val field = {
    try
      owner.getDeclaredField(name)
    catch {
      case _ : Throwable =>
        warn(s"no such field $name in ${owner.getSimpleName} (ref:${ref.getSimpleName})")
        null
    }
  }

  def findAnnotationOf[T <: jl.annotation.Annotation](implicit c: ClassTag[T]) = {
    field match {
      case null => None
      case field =>
        field.getAnnotation[T](c.runtimeClass.asInstanceOf[Class[T]]) match {
          case null => None
          case a => Some(a.asInstanceOf[T])
        }
    }
  }

  def get(obj: Any) = {
    try
      Reflect.readField(obj, field)
    catch {
      case e : IllegalAccessException =>
        error(f"get obj: ${obj.getClass}, field:${field.getName}")
        throw e
    }
  }
}

/**
 * A method argument
 * @param owner
 * @param index
 * @param name
 * @param valueType
 */
case class MethodParameter(owner: jl.reflect.Method, index: Int, override val name: String, override val valueType: ObjectType)
  extends Parameter(name, valueType) {
  def findAnnotationOf[T <: jl.annotation.Annotation](implicit c: ClassTag[T]): Option[T] = {
    val annot: Array[jl.annotation.Annotation] = owner.getParameterAnnotations()(index)
    findAnnotationOf[T](annot)
  }

  def get(obj: Any) = {
    sys.error("get for method parameter is not supported")
  }
}

/**
 * A method defined in a scala class
 * @param owner
 * @param jMethod
 * @param name
 * @param params
 * @param returnType
 */
case class ScMethod(owner: Class[_], jMethod: jl.reflect.Method, name: String, params: Array[MethodParameter], returnType: ObjectType)
  extends ObjectMethod {
  override def toString = "Method(%s#%s, [%s], %s)".format(owner.getSimpleName, name, params.mkString(", "), returnType)

  def findAnnotationOf[T <: jl.annotation.Annotation](implicit c: ClassTag[T]): Option[T] = {
    jMethod.getAnnotation(c.runtimeClass.asInstanceOf[Class[T]]) match {
      case null => None
      case a => Some(a.asInstanceOf[T])
    }
  }
  def findAnnotationOf[T <: jl.annotation.Annotation](paramIndex: Int)(implicit c: ClassTag[T]): Option[T] = {
    params(paramIndex).findAnnotationOf[T]
  }

  override def hashCode = {
    owner.hashCode() + name.hashCode()
  }

  def invoke(obj:AnyRef, params:AnyRef*) : Any = {
    jMethod.invoke(obj, params:_*)
  }
}

case class CompanionMethod(owner:Class[_], jMethod:jl.reflect.Method, name:String, params: Array[MethodParameter], returnType: ObjectType)
 extends ObjectMethod with Logger
{
  def findAnnotationOf[T <: jl.annotation.Annotation](implicit c: ClassTag[T]): Option[T] = {
    jMethod.getAnnotation(c.runtimeClass.asInstanceOf[Class[T]]) match {
      case null => None
      case a => Some(a.asInstanceOf[T])
    }
  }
  def findAnnotationOf[T <: jl.annotation.Annotation](paramIndex: Int)(implicit c: ClassTag[T]): Option[T] = {
    params(paramIndex).findAnnotationOf[T]
  }

  override def hashCode = {
    owner.hashCode() + name.hashCode()
  }

  def invoke(obj:AnyRef, params:AnyRef*) : Any = {
    debug(s"invoking jMethod:$jMethod, owner:$owner")
    TypeUtil.companionObject(owner).map{ co =>
      debug(s"found a companion object of $owner")
      jMethod.invoke(co, params:_*)
    }.orNull
  }

}

/**
 * Constructor of the class
 * @param cl
 * @param params
 */
case class Constructor(cl: Class[_], params: Array[ConstructorParameter]) extends Type {
  val name = cl.getSimpleName
  override def toString = "Constructor(%s, [%s])".format(cl.getSimpleName, params.mkString(", "))

  def findParameter(name:String) = params.find(_.name == name)

  def newInstance(args: Array[AnyRef]): Any = {
    val cc = cl.getConstructors()(0)
    if (args.isEmpty)
      cc.newInstance()
    else
      cc.newInstance(args: _*)
  }
}






© 2015 - 2024 Weber Informatics LLC | Privacy Policy