/*
* Javassist, a Java-bytecode translator toolkit.
* Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
* the terms of the GNU Lesser General Public License Version 2.1 or later,
* or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*/
package javassist.bytecode;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javassist.bytecode.annotation.Annotation;
import javassist.bytecode.annotation.AnnotationMemberValue;
import javassist.bytecode.annotation.AnnotationsWriter;
import javassist.bytecode.annotation.ArrayMemberValue;
import javassist.bytecode.annotation.BooleanMemberValue;
import javassist.bytecode.annotation.ByteMemberValue;
import javassist.bytecode.annotation.CharMemberValue;
import javassist.bytecode.annotation.ClassMemberValue;
import javassist.bytecode.annotation.DoubleMemberValue;
import javassist.bytecode.annotation.EnumMemberValue;
import javassist.bytecode.annotation.FloatMemberValue;
import javassist.bytecode.annotation.IntegerMemberValue;
import javassist.bytecode.annotation.LongMemberValue;
import javassist.bytecode.annotation.MemberValue;
import javassist.bytecode.annotation.ShortMemberValue;
import javassist.bytecode.annotation.StringMemberValue;
/**
* A class representing
* RuntimeVisibleAnnotations_attribute
and
* RuntimeInvisibleAnnotations_attribute
.
*
*
To obtain an AnnotationAttribute object, invoke
* getAttribute(AnnotationsAttribute.visibleTag)
* in ClassFile
, MethodInfo
,
* or FieldInfo
. The obtained attribute is a
* runtime visible annotations attribute.
* If the parameter is
* AnnotationAttribute.invisibleTag
, then the obtained
* attribute is a runtime invisible one.
*
*
For example, * *
* import javassist.bytecode.annotation.Annotation; * : * CtMethod m = ... ; * MethodInfo minfo = m.getMethodInfo(); * AnnotationsAttribute attr = (AnnotationsAttribute) * minfo.getAttribute(AnnotationsAttribute.invisibleTag); * Annotation an = attr.getAnnotation("Author"); * String s = ((StringMemberValue)an.getMemberValue("name")).getValue(); * System.out.println("@Author(name=" + s + ")"); ** *
This code snippet retrieves an annotation of the type Author
* from the MethodInfo
object specified by minfo
.
* Then, it prints the value of name
in Author
.
*
*
If the annotation type Author
is annotated by a meta annotation:
*
*
* @Retention(RetentionPolicy.RUNTIME) ** *
Then Author
is visible at runtime. Therefore, the third
* statement of the code snippet above must be changed into:
*
*
* AnnotationsAttribute attr = (AnnotationsAttribute) * minfo.getAttribute(AnnotationsAttribute.visibleTag); ** *
The attribute tag must be visibleTag
instead of
* invisibleTag
.
*
*
If the member value of an annotation is not specified, the default value
* is used as that member value. If so, getMemberValue()
in
* Annotation
returns null
* since the default value is not included in the
* AnnotationsAttribute
. It is included in the
* AnnotationDefaultAttribute
of the method declared in the
* annotation type.
*
*
If you want to record a new AnnotationAttribute object, execute the * following snippet: * *
* ClassFile cf = ... ; * ConstPool cp = cf.getConstPool(); * AnnotationsAttribute attr * = new AnnotationsAttribute(cp, AnnotationsAttribute.visibleTag); * Annotation a = new Annotation("Author", cp); * a.addMemberValue("name", new StringMemberValue("Chiba", cp)); * attr.setAnnotation(a); * cf.addAttribute(attr); * cf.setVersionToJava5(); ** *
The last statement is necessary if the class file was produced by
* javac
of JDK 1.4 or earlier. Otherwise, it is not necessary.
*
* @see AnnotationDefaultAttribute
* @see javassist.bytecode.annotation.Annotation
*/
public class AnnotationsAttribute extends AttributeInfo {
/**
* The name of the RuntimeVisibleAnnotations
attribute.
*/
public static final String visibleTag = "RuntimeVisibleAnnotations";
/**
* The name of the RuntimeInvisibleAnnotations
attribute.
*/
public static final String invisibleTag = "RuntimeInvisibleAnnotations";
/**
* Constructs a Runtime(In)VisibleAnnotations_attribute
.
*
* @param cp constant pool
* @param attrname attribute name (visibleTag
or
* invisibleTag
).
* @param info the contents of this attribute. It does not
* include attribute_name_index
or
* attribute_length
.
*/
public AnnotationsAttribute(ConstPool cp, String attrname, byte[] info) {
super(cp, attrname, info);
}
/**
* Constructs an empty
* Runtime(In)VisibleAnnotations_attribute
.
* A new annotation can be later added to the created attribute
* by setAnnotations()
.
*
* @param cp constant pool
* @param attrname attribute name (visibleTag
or
* invisibleTag
).
* @see #setAnnotations(Annotation[])
*/
public AnnotationsAttribute(ConstPool cp, String attrname) {
this(cp, attrname, new byte[] { 0, 0 });
}
/**
* @param n the attribute name.
*/
AnnotationsAttribute(ConstPool cp, int n, DataInputStream in)
throws IOException
{
super(cp, n, in);
}
/**
* Returns num_annotations
.
*/
public int numAnnotations() {
return ByteArray.readU16bit(info, 0);
}
/**
* Copies this attribute and returns a new copy.
*/
@Override
public AttributeInfo copy(ConstPool newCp, MapgetAnnotations()
as to the returned data structure.
*
* @param type the annotation type.
* @return null if the specified annotation type is not included.
* @see #getAnnotations()
*/
public Annotation getAnnotation(String type) {
Annotation[] annotations = getAnnotations();
for (int i = 0; i < annotations.length; i++) {
if (annotations[i].getTypeName().equals(type))
return annotations[i];
}
return null;
}
/**
* Adds an annotation. If there is an annotation with the same type,
* it is removed before the new annotation is added.
*
* @param annotation the added annotation.
*/
public void addAnnotation(Annotation annotation) {
String type = annotation.getTypeName();
Annotation[] annotations = getAnnotations();
for (int i = 0; i < annotations.length; i++) {
if (annotations[i].getTypeName().equals(type)) {
annotations[i] = annotation;
setAnnotations(annotations);
return;
}
}
Annotation[] newlist = new Annotation[annotations.length + 1];
System.arraycopy(annotations, 0, newlist, 0, annotations.length);
newlist[annotations.length] = annotation;
setAnnotations(newlist);
}
/**
* Removes an annotation by type.
* After removing an annotation, if {@link #numAnnotations()} returns 0,
* this annotations attribute has to be removed.
*
* @param type of annotation to remove
* @return whether an annotation with the given type has been removed
* @since 3.21
*/
public boolean removeAnnotation(String type) {
Annotation[] annotations = getAnnotations();
for (int i = 0; i < annotations.length; i++) {
if (annotations[i].getTypeName().equals(type)) {
Annotation[] newlist = new Annotation[annotations.length - 1];
System.arraycopy(annotations, 0, newlist, 0, i);
if (i < annotations.length - 1) {
System.arraycopy(annotations, i + 1, newlist, i,
annotations.length - i - 1);
}
setAnnotations(newlist);
return true;
}
}
return false;
}
/**
* Parses the annotations and returns a data structure representing
* that parsed annotations. Note that changes of the node values of the
* returned tree are not reflected on the annotations represented by
* this object unless the tree is copied back to this object by
* setAnnotations()
.
*
* @see #setAnnotations(Annotation[])
*/
public Annotation[] getAnnotations() {
try {
return new Parser(info, constPool).parseAnnotations();
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* Changes the annotations represented by this object according to
* the given array of Annotation
objects.
*
* @param annotations the data structure representing the
* new annotations.
*/
public void setAnnotations(Annotation[] annotations) {
ByteArrayOutputStream output = new ByteArrayOutputStream();
AnnotationsWriter writer = new AnnotationsWriter(output, constPool);
try {
int n = annotations.length;
writer.numAnnotations(n);
for (int i = 0; i < n; ++i)
annotations[i].write(writer);
writer.close();
}
catch (IOException e) {
throw new RuntimeException(e); // should never reach here.
}
set(output.toByteArray());
}
/**
* Changes the annotations. A call to this method is equivalent to:
* setAnnotations(new Annotation[] { annotation })
*
* @param annotation the data structure representing
* the new annotation.
*/
public void setAnnotation(Annotation annotation) {
setAnnotations(new Annotation[] { annotation });
}
/**
* @param oldname a JVM class name.
* @param newname a JVM class name.
*/
@Override
void renameClass(String oldname, String newname) {
Mapmap
.
*
* @param info the annotations attribute.
* @param cp the constant pool.
* @param map pairs of replaced and substituted class names.
* It can be null.
*/
Renamer(byte[] info, ConstPool cp, Mapmap
when it copies
* an annotation attribute.
*
* @param info the source attribute.
* @param src the constant pool of the source class.
* @param dest the constant pool of the destination class.
* @param map pairs of replaced and substituted class names.
* It can be null.
*/
Copier(byte[] info, ConstPool src, ConstPool dest, Map