/* __ *\ ** ________ ___ / / ___ Scala API ** ** / __/ __// _ | / / / _ | (c) 2002-2009, LAMP/EPFL ** ** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** ** /____/\___/_/ |_/____/_/ | | ** ** |/ ** \* */ // $Id: $ package scala.reflect import scala.annotation.experimental import scala.util.control.Exception.catching import java.lang.{ Class => JClass } import java.lang.reflect.{ Method => JMethod } import scala.{ Symbol => ScalaSymbol } /** <p> * A more convenient syntax for reflective invocation.<br/> * Example usage: * </p><pre> * <b>class</b> Obj { <b>private def</b> foo(x: Int, y: String): Long = x + y.length }</pre> * <p> * You can call it reflectively one of two ways: * </p><pre> * <b>import</b> scala.reflect.Invocation._ * (<b>new</b> Obj) o 'foo(5, "abc") // the 'o' method returns Any * <b>val</b> x: Long = (<b>new</b> Obj) oo 'foo(5, "abc") // the 'oo' method casts to expected type.</pre> * <p> * If you call the <code>oo</code> method and do not give the type inferencer * enough help, it will most likely infer <code>Nothing</code>, which will * result in a <code>ClassCastException</code>. * </p> * * @author Paul Phillips */ @experimental object Invocation { /** <p> * In order to encapsulate anything to do with reflection, we must * overcome an issue with the boxing of primitives. If we declare a * method which takes arguments of type <code>Any</code>, by the time the * method parameters can be examined, the primitives have already been boxed. * The reflective call will then fail because <code>classOf[java.lang.Integer]</code> * is not the same thing as <code>classOf[scala.Int].</code> * </p> * <p> * Any useful workaround will require examining the arguments before * the method is called. The approach here is to define two implicits, * one for <code>AnyRef</code>'s and one for <code>AnyVal</code>'s, and * box them in a container which preserves their original class identity. * </p> */ trait PrimitivePreserver[T] { val value: T val clazz: JClass[_] } case class PreservedAnyVal[T <: AnyVal](value: T) extends PrimitivePreserver[T] { val clazz = getAnyValClass(value) } case class PreservedAnyRef[T <: AnyRef](value: T) extends PrimitivePreserver[T] { val clazz = value.getClass } implicit def makePreservedAnyRef[T <: AnyRef](x: T) = PreservedAnyRef(x) implicit def makePreservedAnyVal[T <: AnyVal](x: T) = PreservedAnyVal(x) /** We also require an implicit on scala.Symbol so they appear to contain * an apply method, which packages the method arguments. The type parameter * is the method's expected result type. */ class SymbolWithArguments(val sym: ScalaSymbol, val args: PrimitivePreserver[_]*) { def getArgs = args map (_.value.asInstanceOf[AnyRef]) def getArgTypes = args.toList map (_.clazz) def argsMatch(m: JMethod) = List.map2(m.getParameterTypes.toList, getArgTypes)(_ isAssignableFrom _) forall (_ == true) // only called if getMethod() fails - searches private methods too. def getDeclaredMethodsOn(x: AnyRef) = (x.getClass.getDeclaredMethods filter (_.getName == sym.name) find argsMatch) match { case Some(m) => m setAccessible true ; m case None => throw new NoSuchMethodException(sym.name) } def getMethodOn(x: AnyRef) = catching(classOf[NoSuchMethodException]) . opt (x.getClass.getMethod(sym.name, getArgTypes: _*)) . getOrElse (getDeclaredMethodsOn(x)) } class RichSymbol(sym: ScalaSymbol) { def apply(args: PrimitivePreserver[_]*): SymbolWithArguments = new SymbolWithArguments(sym, args: _*) } implicit def makeRichSymbol(sym: ScalaSymbol): RichSymbol = new RichSymbol(sym) /** An implicit on AnyRef provides it with the 'o' method, which is supposed * to look like a giant '.' and present the feel of method invocation. */ class ReflectionOperators[T <: AnyRef](self: T) { val clazz = self.getClass.asInstanceOf[JClass[T]] /** Issue call without touching result - returns Any. */ def o(sym: ScalaSymbol): Any = oo(new SymbolWithArguments(sym)) def o(symApp: SymbolWithArguments): Any = oo(symApp) /** Issue call expecting return type R - casts result to R. */ def oo[R](sym: ScalaSymbol): R = oo[R](new SymbolWithArguments(sym)) def oo[R](symApp: SymbolWithArguments): R = { def method = symApp getMethodOn self method.invoke(self, symApp.getArgs: _*).asInstanceOf[R] } } implicit def makeReflectionOperators[T <: AnyRef](x: T): ReflectionOperators[T] = new ReflectionOperators(x) /** Obtain the class object for an <code>AnyVal</code>. */ def getAnyValClass(x: AnyVal): JClass[_] = x match { case _: Byte => classOf[Byte] case _: Short => classOf[Short] case _: Int => classOf[Int] case _: Long => classOf[Long] case _: Float => classOf[Float] case _: Double => classOf[Double] case _: Char => classOf[Char] case _: Boolean => classOf[Boolean] case _: Unit => classOf[Unit] } }