2012-07-13 15 views
9

でケースクラスのフィールドに定義された注釈にアクセスするためにどのように私は次のJavaアノテーションは は、実行時

@Target({ElementType.METHOD, ElementType.PARAMETER, ElementType.CONSTRUCTOR, ElementType.FIELD}) 
    @Retention(RetentionPolicy.RUNTIME) 
    public @interface MyAnnotation { 
    String value() default ""; 
    } 

を定義したと私は、次のScalaのケースクラスを定義しました:

@Prefer("xyz") 
case class TestAnno(arg1 : String, @Prefer("abc") agr2 : String) 

私は反射を使用してケースクラスレベルで定義されたアノテーションを表示できますが、ケースクラスTestAnnoのメンバーarg2のアノテーションにはアクセスできません。コードのコンパイルをしています。私は、変数宣言もスカラアクセサもどちらも注釈を持っていないようです。コンストラクタ定義とcopyメソッドのみが、caseクラス宣言で定義されたパラメータの注釈を保持しているように見えます。

ケースクラスで宣言されたフィールドの注釈を強制的に生成する別の方法がありますか?コンストラクタを読み込み、ASM ByteCode LibraryまたはParaNamerなどのライブラリを使用して、注釈を持つパラメータを見つける必要がありますか?主にScalaのケースクラスで使用できるソリューションが必要です。

答えて

2

私はいくつかの検索を行い、2つのソリューションを提供しました。コメント、Suggesstions、改善を歓迎します。私はこの答えをwikiとしてマークしました。最初のものはScalaBeansParaNamerに基づいています。

def valNamesWithAnnotations[T <: AnyRef](obj : T)(implicit m : Manifest[T]) : List[(String, List[java.lang.annotation.Annotation])] = { 
    val descriptor = descriptorOf(obj.getClass) 

    val c = descriptor.beanType.erasure 

    val constructor: Option[Constructor[_]] = { 
     if (c.getConstructors().isEmpty) None 
     else Some(c.getConstructors()(0).asInstanceOf[Constructor[_]]) 
    } 

    val paranamer = new BytecodeReadingParanamer 
    val ctorParameterNames = constructor.map(paranamer.lookupParameterNames(_)).getOrElse(scala.Array[String]()).toList 
    val ctorParamAnnos = constructor.getOrElse(sys.error("Cannot find constructor entry for class " + c.getName)).getParameterAnnotations 

    val builder = List.newBuilder[(String, List[java.lang.annotation.Annotation])] 
    val paramIter = ctorParameterNames.iterator 
    val annoIter = ctorParamAnnos.iterator 

    while(paramIter.hasNext && annoIter.hasNext) { 
     builder += ((paramIter.next, annoIter.next.toList)) 
    } 

    builder.result 
    } 

第二のアプローチはScalaSignaturesを使用し、このSO answerに基づいています。

def valNamesWithAnnotations[C: ClassManifest] : List[(String, List[java.lang.annotation.Annotation])] = { 
    val cls = classManifest[C].erasure 
    val ctors = cls.getConstructors 

    assert(ctors.size == 1, "Class " + cls.getName + " should have only one constructor") 
    val sig = ScalaSigParser.parse(cls).getOrElse(sys.error("No ScalaSig for class " + cls.getName + ", make sure it is a top-level case class")) 

    val classSymbol = sig.parseEntry(0).asInstanceOf[ClassSymbol] 
    assert(classSymbol.isCase, "Class " + cls.getName + " is not a case class") 

    val tableSize = sig.table.size 
    val ctorIndex = (1 until tableSize).find { i => 
     sig.parseEntry(i) match { 
     case m @ MethodSymbol(SymbolInfo("<init>", owner, _, _, _, _), _) => owner match { 
      case sym: SymbolInfoSymbol if sym.index == 0 => true 
      case _ => false 
     } 
     case _ => false 
     } 
    }.getOrElse(sys.error("Cannot find constructor entry in ScalaSig for class " + cls.getName)) 

    val paramsListBuilder = List.newBuilder[String] 
    for (i <- (ctorIndex + 1) until tableSize) { 
     sig.parseEntry(i) match { 
     case MethodSymbol(SymbolInfo(name, owner, _, _, _, _), _) => owner match { 
      case sym: SymbolInfoSymbol if sym.index == ctorIndex => paramsListBuilder += name 
      case _ => 
     } 
     case _ => 
     } 
    } 

    val paramAnnoArr = ctors(0).getParameterAnnotations 
    val builder = List.newBuilder[(String, List[java.lang.annotation.Annotation])] 

    val paramIter = paramsListBuilder.result.iterator 
    val annoIter = paramAnnoArr.iterator 

    while(paramIter.hasNext && annoIter.hasNext) { 
     builder += ((paramIter.next, annoIter.next.toList)) 
    } 

    builder.result 
    } 
9

あなただけの、次の操作を行う必要があります

case class TestAnno(arg1 : String, @(Prefer @field)("abc") agr2 : String) 

詳細情報ここhttp://www.scala-lang.org/api/current/#scala.annotation.meta.package

+0

これらのパラメータへのアクセス方法に応じて、代わりに '@setter'、' @getter'、または '@param'を' @field'と一緒に追加する必要があります。 (あなたはそれらのすべてを試して、1つずつ削除することができます) – al3xar

5

クエンティンさんソリューションは機能しましたが、IMHOはあまりにも多くのユーザー。

コンストラクタ引数のアノテーションは、標準リフレクションAPIで読み取ることができます。私はこれをマクロの実装に必要としました。

scala> :paste 
// Entering paste mode (ctrl-D to finish) 

import scala.annotation.StaticAnnotation 
final class min(i: Long) extends StaticAnnotation 

case class Foo(@min(1) c: String) 
import scala.reflect.runtime.universe._ 
symbolOf[Foo].asClass.primaryConstructor.typeSignature.paramLists.head.head.annotations 

// Exiting paste mode, now interpreting. 

import scala.annotation.StaticAnnotation 
defined class min 
defined class Foo 
import scala.reflect.runtime.universe._ 
res0: List[reflect.runtime.universe.Annotation] = List(min(1L)) 
+0

注釈情報にはどのようにアクセスできますか? – bashan