/*
* Javassist, a Java-bytecode translator toolkit.
* Copyright (C) 1999-2004 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.
*
* 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.util.Map;
import java.io.IOException;
import java.io.DataInputStream;
import java.io.ByteArrayOutputStream;
import javassist.bytecode.annotation.*;
/**
* A class representing
* RuntimeVisibleAnnotations_attribute
and
* RuntimeInvisibleAnnotations_attribute
.
*
*
To obtain an AnnotationAttribute object, invoke
* getAttribute(AnnotationsAttribute.invisibleTag)
* in ClassFile
, MethodInfo
,
* or FieldInfo
. The obtained attribute is a
* runtime invisible annotations attribute.
* If the parameter is
* AnnotationAttribute.visibleTag
, then the obtained
* attribute is a runtime visible one.
*
*
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.invisibleTag); * Annotation a = new Annotation("Author", cp); * a.addMemberValue("name", new StringMemberValue("Chiba", cp)); * attr.setAnnotation(a); * cf.addAttribute(attr); **/ 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)VisisbleAnnotations_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)VisisbleAnnotations_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(Annotations[])
*/
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.
*/
public AttributeInfo copy(ConstPool newCp, Map classnames) {
Copier copier = new Copier(info, constPool, newCp, classnames);
try {
copier.annotationArray();
return new AnnotationsAttribute(newCp, getName(), copier.close());
}
catch (Exception e) {
throw new RuntimeException(e.toString());
}
}
/**
* 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()
*/
public Annotation[] getAnnotations() {
try {
return new Parser(info, constPool).parseAnnotations();
}
catch (Exception e) {
throw new RuntimeException(e.toString());
}
}
/**
* 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 }); } /** * Returns a string representation of this object. */ public String toString() { Annotation[] a = getAnnotations(); StringBuffer sbuf = new StringBuffer(); int i = 0; while (i < a.length) { sbuf.append(a[i++].toString()); if (i != a.length) sbuf.append(", "); } return sbuf.toString(); } static class Walker { byte[] info; Walker(byte[] attrInfo) { info = attrInfo; } final void parameters() throws Exception { int numParam = info[0] & 0xff; parameters(numParam, 1); } void parameters(int numParam, int pos) throws Exception { for (int i = 0; i < numParam; ++i) pos = annotationArray(pos); } final void annotationArray() throws Exception { annotationArray(0); } final int annotationArray(int pos) throws Exception { int num = ByteArray.readU16bit(info, pos); return annotationArray(pos + 2, num); } int annotationArray(int pos, int num) throws Exception { for (int i = 0; i < num; ++i) pos = annotation(pos); return pos; } final int annotation(int pos) throws Exception { int type = ByteArray.readU16bit(info, pos); int numPairs = ByteArray.readU16bit(info, pos + 2); return annotation(pos + 4, type, numPairs); } int annotation(int pos, int type, int numPairs) throws Exception { for (int j = 0; j < numPairs; ++j) pos = memberValuePair(pos); return pos; } final int memberValuePair(int pos) throws Exception { int nameIndex = ByteArray.readU16bit(info, pos); return memberValuePair(pos + 2, nameIndex); } int memberValuePair(int pos, int nameIndex) throws Exception { return memberValue(pos); } final int memberValue(int pos) throws Exception { int tag = info[pos] & 0xff; if (tag == 'e') { int typeNameIndex = ByteArray.readU16bit(info, pos + 1); int constNameIndex = ByteArray.readU16bit(info, pos + 3); enumMemberValue(typeNameIndex, constNameIndex); return pos + 5; } else if (tag == 'c') { int index = ByteArray.readU16bit(info, pos + 1); classMemberValue(index); return pos + 3; } else if (tag == '@') return annotationMemberValue(pos + 1); else if (tag == '[') { int num = ByteArray.readU16bit(info, pos + 1); return arrayMemberValue(pos + 3, num); } else { // primitive types or String. int index = ByteArray.readU16bit(info, pos + 1); constValueMember(tag, index); return pos + 3; } } void constValueMember(int tag, int index) throws Exception {} void enumMemberValue(int typeNameIndex, int constNameIndex) throws Exception {} void classMemberValue(int index) throws Exception {} int annotationMemberValue(int pos) throws Exception { return annotation(pos); } int arrayMemberValue(int pos, int num) throws Exception { for (int i = 0; i < num; ++i) { pos = memberValue(pos); } return pos; } } static class Copier extends Walker { ByteArrayOutputStream output; AnnotationsWriter writer; ConstPool srcPool, destPool; Map classnames; /** * Constructs a copier. This copier renames some class names * into the new names specified by
map
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 map) {
super(info);
output = new ByteArrayOutputStream();
writer = new AnnotationsWriter(output, dest);
srcPool = src;
destPool = dest;
classnames = map;
}
byte[] close() throws IOException {
writer.close();
return output.toByteArray();
}
void parameters(int numParam, int pos) throws Exception {
writer.numParameters(numParam);
super.parameters(numParam, pos);
}
int annotationArray(int pos, int num) throws Exception {
writer.numAnnotations(num);
return super.annotationArray(pos, num);
}
int annotation(int pos, int type, int numPairs) throws Exception {
writer.annotation(copy(type), numPairs);
return super.annotation(pos, type, numPairs);
}
int memberValuePair(int pos, int nameIndex) throws Exception {
writer.memberValuePair(copy(nameIndex));
return super.memberValuePair(pos, nameIndex);
}
void constValueMember(int tag, int index) throws Exception {
writer.constValueIndex(tag, copy(index));
super.constValueMember(tag, index);
}
void enumMemberValue(int typeNameIndex, int constNameIndex)
throws Exception
{
writer.enumConstValue(copy(typeNameIndex), copy(constNameIndex));
super.enumMemberValue(typeNameIndex, constNameIndex);
}
void classMemberValue(int index) throws Exception {
writer.classInfoIndex(copy(index));
super.classMemberValue(index);
}
int annotationMemberValue(int pos) throws Exception {
writer.annotationValue();
return super.annotationMemberValue(pos);
}
int arrayMemberValue(int pos, int num) throws Exception {
writer.arrayValue(num);
return super.arrayMemberValue(pos, num);
}
/**
* Copies a constant pool entry into the destination constant pool
* and returns the index of the copied entry.
*
* @param srcIndex the index of the copied entry into the source
* constant pool.
* @return the index of the copied item into the destination
* constant pool.
*/
int copy(int srcIndex) {
return srcPool.copy(srcIndex, destPool, classnames);
}
}
static class Parser extends Walker {
ConstPool pool;
Annotation[][] allParams; // all parameters
Annotation[] allAnno; // all annotations
Annotation currentAnno; // current annotation
MemberValue memberValue;
/**
* Constructs a parser. This parser constructs a parse tree of
* the annotations.
*
* @param info the attribute.
* @param src the constant pool.
*/
Parser(byte[] info, ConstPool cp) {
super(info);
pool = cp;
}
Annotation[][] parseParameters() throws Exception {
parameters();
return allParams;
}
Annotation[] parseAnnotations() throws Exception {
annotationArray();
return allAnno;
}
void parameters(int numParam, int pos) throws Exception {
Annotation[][] params = new Annotation[numParam][];
for (int i = 0; i < numParam; ++i) {
pos = annotationArray(pos);
params[i] = allAnno;
}
allParams = params;
}
int annotationArray(int pos, int num) throws Exception {
Annotation[] array = new Annotation[num];
for (int i = 0; i < num; ++i) {
pos = annotation(pos);
array[i] = currentAnno;
}
allAnno = array;
return pos;
}
int annotation(int pos, int type, int numPairs) throws Exception {
currentAnno = new Annotation(type, pool);
return super.annotation(pos, type, numPairs);
}
int memberValuePair(int pos, int nameIndex) throws Exception {
pos = super.memberValuePair(pos, nameIndex);
currentAnno.addMemberValue(nameIndex, memberValue);
return pos;
}
void constValueMember(int tag, int index) throws Exception {
MemberValue m;
ConstPool cp = pool;
switch (tag) {
case 'B' :
m = new ByteMemberValue(index, cp);
break;
case 'C' :
m = new CharMemberValue(index, cp);
break;
case 'D' :
m = new DoubleMemberValue(index, cp);
break;
case 'F' :
m = new FloatMemberValue(index, cp);
break;
case 'I' :
m = new IntegerMemberValue(index, cp);
break;
case 'J' :
m = new LongMemberValue(index, cp);
break;
case 'S' :
m = new ShortMemberValue(index, cp);
break;
case 'Z' :
m = new BooleanMemberValue(index, cp);
break;
case 's' :
m = new StringMemberValue(index, cp);
break;
default :
throw new RuntimeException("unknown tag:" + tag);
}
memberValue = m;
super.constValueMember(tag, index);
}
void enumMemberValue(int typeNameIndex, int constNameIndex)
throws Exception
{
memberValue = new EnumMemberValue(typeNameIndex,
constNameIndex, pool);
super.enumMemberValue(typeNameIndex, constNameIndex);
}
void classMemberValue(int index) throws Exception {
memberValue = new ClassMemberValue(index, pool);
super.classMemberValue(index);
}
int annotationMemberValue(int pos) throws Exception {
Annotation anno = currentAnno;
pos = super.annotationMemberValue(pos);
memberValue = new AnnotationMemberValue(currentAnno, pool);
currentAnno = anno;
return pos;
}
int arrayMemberValue(int pos, int num) throws Exception {
ArrayMemberValue amv = new ArrayMemberValue(pool);
MemberValue[] elements = new MemberValue[num];
for (int i = 0; i < num; ++i) {
pos = memberValue(pos);
elements[i] = memberValue;
}
amv.setValue(elements);
memberValue = amv;
return pos;
}
}
}