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

com.android.dx.AnnotationId Maven / Gradle / Ivy

/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * 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 com.android.dx;

import com.android.dx.dex.file.ClassDefItem;
import com.android.dx.rop.annotation.Annotation;
import com.android.dx.rop.annotation.AnnotationVisibility;
import com.android.dx.rop.annotation.Annotations;
import com.android.dx.rop.annotation.NameValuePair;
import com.android.dx.rop.cst.*;

import java.lang.annotation.ElementType;
import java.util.HashMap;

/**
 * Identifies an annotation on a program element, see {@link java.lang.annotation.ElementType}.
 *
 * Currently it is only targeting Class, Method, Field and Parameter because those are supported by
 * {@link com.android.dx.dex.file.AnnotationsDirectoryItem} so far.
 *
 * 

NOTE: * So far it only supports adding method annotation. The annotation of class, field and parameter * will be implemented later. * *

WARNING: * The declared element of an annotation type should either have a default value or be set a value via * {@code AnnotationId.set(Element)}. Otherwise it will incur * {@link java.lang.annotation.IncompleteAnnotationException} when accessing the annotation element * through reflection. The example is as follows: *

 *     {@code @Retention(RetentionPolicy.RUNTIME)}
 *     {@code @Target({ElementType.METHOD})}
 *     {@code @interface MethodAnnotation {
 *                boolean elementBoolean();
 *                // boolean elementBoolean() default false;
 *            }
 *
 *            TypeId GENERATED = TypeId.get("LGenerated;");
 *            MethodId methodId = GENERATED.getMethod(VOID, "call");
 *            Code code = dexMaker.declare(methodId, PUBLIC);
 *            code.returnVoid();
 *
 *            TypeId annotationTypeId = TypeId.get(MethodAnnotation.class);
 *            AnnotationId annotationId = AnnotationId.get(GENERATED,
 *              annotationTypeId, ElementType.METHOD);
 *
 *            AnnotationId.Element element = new AnnotationId.Element("elementBoolean", true);
 *            annotationId.set(element);
 *            annotationId.addToMethod(dexMaker, methodId);
 *     }
 * 
* * @param the type that declares the program element. * @param the annotation type. It should be a known type before compile. */ public final class AnnotationId { private final TypeId declaringType; private final TypeId type; /** The type of program element to be annotated */ private final ElementType annotatedElement; /** The elements this annotation holds */ private final HashMap elements; private AnnotationId(TypeId declaringType, TypeId type, ElementType annotatedElement) { this.declaringType = declaringType; this.type = type; this.annotatedElement = annotatedElement; this.elements = new HashMap<>(); } /** * Construct an instance. It initially contains no elements. * * @param declaringType the type declaring the program element. * @param type the annotation type. * @param annotatedElement the program element type to be annotated. * @return an annotation {@code AnnotationId} instance. */ public static AnnotationId get(TypeId declaringType, TypeId type, ElementType annotatedElement) { if (annotatedElement != ElementType.TYPE && annotatedElement != ElementType.METHOD && annotatedElement != ElementType.FIELD && annotatedElement != ElementType.PARAMETER) { throw new IllegalArgumentException("element type is not supported to annotate yet."); } return new AnnotationId<>(declaringType, type, annotatedElement); } /** * Set an annotation element of this instance. * If there is a preexisting element with the same name, it will be * replaced by this method. * * @param element {@code non-null;} the annotation element to be set. */ public void set(Element element) { if (element == null) { throw new NullPointerException("element == null"); } CstString pairName = new CstString(element.getName()); Constant pairValue = Element.toConstant(element.getValue()); NameValuePair nameValuePair = new NameValuePair(pairName, pairValue); elements.put(element.getName(), nameValuePair); } /** * Add this annotation to a method. * * @param dexMaker DexMaker instance. * @param method Method to be added to. */ public void addToMethod(DexMaker dexMaker, MethodId method) { if (annotatedElement != ElementType.METHOD) { throw new IllegalStateException("This annotation is not for method"); } if (!method.declaringType.equals(declaringType)) { throw new IllegalArgumentException("Method" + method + "'s declaring type is inconsistent with" + this); } ClassDefItem classDefItem = dexMaker.getTypeDeclaration(declaringType).toClassDefItem(); if (classDefItem == null) { throw new NullPointerException("No class defined item is found"); } else { CstMethodRef cstMethodRef = method.constant; if (cstMethodRef == null) { throw new NullPointerException("Method reference is NULL"); } else { // Generate CstType CstType cstType = CstType.intern(type.ropType); // Generate Annotation Annotation annotation = new Annotation(cstType, AnnotationVisibility.RUNTIME); // Add generated annotation Annotations annotations = new Annotations(); for (NameValuePair nvp : elements.values()) { annotation.add(nvp); } annotations.add(annotation); classDefItem.addMethodAnnotations(cstMethodRef, annotations, dexMaker.getDexFile()); } } } /** * A wrapper of NameValuePair represents a (name, value) pair used as the contents * of an annotation. * * An {@code Element} instance is stored in {@code AnnotationId.elements} by calling {@code * AnnotationId.set(Element)}. * *

WARNING:

* the name should be exact same as the annotation element declared in the annotation type * which is referred by field {@code AnnotationId.type},otherwise the annotation will fail * to add and {@code java.lang.reflect.Method.getAnnotations()} will return nothing. * */ public static final class Element { /** {@code non-null;} the name */ private final String name; /** {@code non-null;} the value */ private final Object value; /** * Construct an instance. * * @param name {@code non-null;} the name * @param value {@code non-null;} the value */ public Element(String name, Object value) { if (name == null) { throw new NullPointerException("name == null"); } if (value == null) { throw new NullPointerException("value == null"); } this.name = name; this.value = value; } public String getName() { return name; } public Object getValue() { return value; } /** {@inheritDoc} */ @Override public String toString() { return "[" + name + ", " + value + "]"; } /** {@inheritDoc} */ @Override public int hashCode() { return name.hashCode() * 31 + value.hashCode(); } /** {@inheritDoc} */ @Override public boolean equals(Object other) { if (! (other instanceof Element)) { return false; } Element otherElement = (Element) other; return name.equals(otherElement.name) && value.equals(otherElement.value); } /** * Convert a value of an element to a {@code Constant}. *

Warning: Array or TypeId value is not supported yet. * * @param value an annotation element value. * @return a Constant */ static Constant toConstant(Object value) { Class clazz = value.getClass(); if (clazz.isEnum()) { CstString descriptor = new CstString(TypeId.get(clazz).getName()); CstString name = new CstString(((Enum)value).name()); CstNat cstNat = new CstNat(name, descriptor); return new CstEnumRef(cstNat); } else if (clazz.isArray()) { throw new UnsupportedOperationException("Array is not supported yet"); } else if (value instanceof TypeId) { throw new UnsupportedOperationException("TypeId is not supported yet"); } else { return Constants.getConstant(value); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy