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

org.opalj.av.checking.AnnotationPredicate.scala Maven / Gradle / Ivy

The newest version!
/* BSD 2-Clause License - see OPAL/LICENSE for details. */
package org.opalj
package av
package checking

import org.opalj.br._
import org.opalj.br.ConcreteSourceElement

/**
 * Matches an annotation of a class, field, method or method parameter.
 *
 * @author Marco Torsello
 */
trait AnnotationPredicate extends SourceElementPredicate[ConcreteSourceElement] {

    def apply(other: Annotation): Boolean

    def toDescription(): String

}

/**
 * An annotation matcher that always returns `true`; it matches any annotation.
 *
 * @author Marco Torsello
 */
case object AnyAnnotation extends AnnotationPredicate {

    override def apply(other: Annotation): Boolean = true

    override def apply(other: ConcreteSourceElement): Boolean = true

    override def toDescription(): String = "/*any annotation*/";

}

/**
 * Matches an annotation of a class, field, method or method parameter that has the
 * specified type. The annotation is matched independent of the annotation's values.
 *
 * ==Example==
 * {{{
 * scala> import org.opalj.br._
 * scala> val foo = ObjectType("java/lang/Foo")
 * scala> val aw = org.opalj.av.checking.AnnotatedWith(foo)
 * aw: org.opalj.av.checking.AnnotatedWith = @java.lang.Foo
 * scala> aw(Annotation(foo,IndexedSeq(ElementValuePair("clazz",StringValue(" ")))))
 * res: Boolean = true
 * }}}
 * @author Marco Torsello
 */
case class HasAnnotation(annotationType: FieldType) extends AnnotationPredicate {

    /**
     * Checks if the type of the given annotation is the same as the type of this
     * predicate.
     */
    def apply(other: Annotation): Boolean = other.annotationType eq this.annotationType

    override def apply(sourceElement: ConcreteSourceElement): Boolean = {
        sourceElement.annotations.exists(this(_))
    }

    override def toDescription(): String = "@"+annotationType.toJava

}
/**
 * Factory methods to create [[AnnotatedWith]] predicates.
 */
object HasAnnotation {

    def apply(annotationType: String): HasAnnotation = {
        new HasAnnotation(ObjectType(annotationType.replace('.', '/')))
    }
}

/**
 * Tests if an annotation of a class, field, method or method parameter is as specified.
 * The test takes the element values into consideration; if you don't want to take
 * them into consideration use a [[HasAnnotation]] predicate.
 *
 * {{{
 * scala> import org.opalj.br._
 * scala> import org.opalj.av.checking._
 * scala> val foo = ObjectType("java/lang/Foo")
 *
 * scala> val am = AnnotationPredicate("java.lang.Foo",Map("clazz" -> StringValue("")))
 * am: org.opalj.av.checking.AnnotationPredicate = @java.lang.Foo(clazz="")
 *
 * scala> am(Annotation(foo,IndexedSeq(ElementValuePair("clazz",StringValue("")))))
 * res: Boolean = true
 *
 * scala> am(Annotation(foo,IndexedSeq(ElementValuePair("clazz",StringValue("-+-")))))
 * res: Boolean = false
 *
 * scala> val am = DefaultAnnotationPredicate("java.lang.Foo",IndexedSeq.empty)
 * am: org.opalj.av.checking.DefaultAnnotationPredicate = @java.lang.Foo()
 *
 * scala> am(Annotation(ObjectType("java/lang/Foo"),IndexedSeq(ElementValuePair("clazz",StringValue(" ")))))
 * res: Boolean = false
 * }}}
 *
 * @author Marco Torsello
 */
case class AnnotatedWith(
        annotationType:    FieldType,
        elementValuePairs: Seq[ElementValuePair]
)
    extends AnnotationPredicate {

    /**
     * Checks if the given annotation is as specified by this predicate.
     * Returns `true` if the given annotation has the type `annotationType`and
     * if all element value pairs are matched independent of their order.
     *
     * ==Example Scenarios==
     *  - If the predicate's annotation type is `A` and the other annotation's type is `B`
     *    then the elements will not match.
     *
     *  - If the predicate defines specific [[org.opalj.br.ElementValuePair]]s such as
     *      `ArrayBuffer(ElementValuePair("target", ClassValue("org.opalj.av.checking.AnnotationPredicate")),
     *              ElementValuePair("name", StringValue("Annotation_Predicate"))`
     *      it will then match annotations where the [[org.opalj.br.ElementValuePair]]s have a different order:
     *      `ArrayBuffer(ElementValuePair("name", StringValue("Annotation_Predicate"),
     *              ElementValuePair("target", ClassValue("org.opalj.av.checking.AnnotationPredicate")))`
     *      But it will not match if one or both of the two [[org.opalj.br.ElementValuePair]]s are missing or there is
     *      another [[org.opalj.br.ElementValuePair]] not defined by this predicate.
     */
    override def apply(that: Annotation): Boolean = {
        (that.annotationType eq this.annotationType) && {
            val thisEVPs = this.elementValuePairs
            val thatEVPs = that.elementValuePairs

            thatEVPs.size == thisEVPs.size &&
                thisEVPs.forall(thisEVP => thatEVPs.exists(_ == thisEVP))
        }
    }

    override def apply(sourceElement: ConcreteSourceElement): Boolean = {
        sourceElement.annotations.exists(this(_))
    }

    override def toDescription(): String = {
        elementValuePairs.
            map(_.toJava).
            mkString("@"+annotationType.toJava+"(", ",", ")")
    }

}

/**
 * Defines several additional factory methods to facilitate the creation of
 * [[AnnotationPredicate]]s.
 *
 * @author Marco Torsello
 */
object AnnotatedWith {

    type SomeClass = Class[_]

    /**
     * @param annotationType The type of the annotation. The given value must not be
     *      `java.lang.Void.TYPE`.
     */
    def apply(
        annotationType:    SomeClass,
        elementValuePairs: ElementValuePairs
    ): AnnotatedWith = {
        new AnnotatedWith(Type(annotationType).asFieldType, elementValuePairs)
    }

    def apply(
        annotationTypeName: BinaryString,
        elementValuePairs:  ElementValuePairs
    ): AnnotatedWith = {

        val annotationType = ObjectType(annotationTypeName.asString)
        new AnnotatedWith(annotationType, elementValuePairs)
    }

    def apply(
        annotationTypeName: BinaryString,
        elementValuePairs:  Map[String, ElementValue]
    ): AnnotatedWith = {
        new AnnotatedWith(
            ObjectType(annotationTypeName.asString),
            elementValuePairs.map(kv => ElementValuePair(kv._1, kv._2)).toSeq
        )
    }
    def apply(
        annotationTypeName: BinaryString,
        elementValuePairs:  (String, ElementValue)*
    ): AnnotatedWith = {
        new AnnotatedWith(
            ObjectType(annotationTypeName.asString),
            elementValuePairs.map(kv => ElementValuePair(kv._1, kv._2)).toSeq
        )
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy