com.feilong.lib.javassist.bytecode.annotation.Annotation Maven / Gradle / Ivy
Show all versions of feilong Show documentation
/*
* Javassist, a Java-bytecode translator toolkit.
* Copyright (C) 2004 Bill Burke. 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 com.feilong.lib.javassist.bytecode.annotation;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import com.feilong.lib.javassist.ClassPool;
import com.feilong.lib.javassist.CtClass;
import com.feilong.lib.javassist.CtMethod;
import com.feilong.lib.javassist.NotFoundException;
import com.feilong.lib.javassist.bytecode.ConstPool;
import com.feilong.lib.javassist.bytecode.Descriptor;
/**
* The annotation
structure.
*
*
* An instance of this class is returned by
* getAnnotations()
in AnnotationsAttribute
* or in ParameterAnnotationsAttribute
.
*
* @see com.feilong.lib.javassist.bytecode.AnnotationsAttribute#getAnnotations()
* @see com.feilong.lib.javassist.bytecode.ParameterAnnotationsAttribute#getAnnotations()
* @see MemberValue
* @see MemberValueVisitor
* @see AnnotationsWriter
*
* @author Bill Burke
* @author Shigeru Chiba
* @author Adrian Brock
*/
public class Annotation{
static class Pair{
int name;
MemberValue value;
}
ConstPool pool;
int typeIndex;
Map members; // this sould be LinkedHashMap
// but it is not supported by JDK 1.3.
/**
* Constructs an annotation including no members. A member can be
* later added to the created annotation by addMemberValue()
.
*
* @param type
* the index into the constant pool table.
* the entry at that index must be the
* CONSTANT_Utf8_Info
structure
* repreenting the name of the annotation interface type.
* @param cp
* the constant pool table.
*
* @see #addMemberValue(String, MemberValue)
*/
public Annotation(int type, ConstPool cp){
pool = cp;
typeIndex = type;
members = null;
}
/**
* Constructs an annotation including no members. A member can be
* later added to the created annotation by addMemberValue()
.
*
* @param typeName
* the fully-qualified name of the annotation interface type.
* @param cp
* the constant pool table.
*
* @see #addMemberValue(String, MemberValue)
*/
public Annotation(String typeName, ConstPool cp){
this(cp.addUtf8Info(Descriptor.of(typeName)), cp);
}
/**
* Constructs an annotation that can be accessed through the interface
* represented by clazz
. The values of the members are
* not specified.
*
* @param cp
* the constant pool table.
* @param clazz
* the interface.
* @throws NotFoundException
* when the clazz is not found
*/
public Annotation(ConstPool cp, CtClass clazz) throws NotFoundException{
// todo Enums are not supported right now.
this(cp.addUtf8Info(Descriptor.of(clazz.getName())), cp);
if (!clazz.isInterface()){
throw new RuntimeException("Only interfaces are allowed for Annotation creation.");
}
CtMethod[] methods = clazz.getDeclaredMethods();
if (methods.length > 0){
members = new LinkedHashMap<>();
}
for (CtMethod m : methods){
addMemberValue(m.getName(), createMemberValue(cp, m.getReturnType()));
}
}
/**
* Makes an instance of MemberValue
.
*
* @param cp
* the constant pool table.
* @param type
* the type of the member.
* @return the member value
* @throws NotFoundException
* when the type is not found
*/
public static MemberValue createMemberValue(ConstPool cp,CtClass type) throws NotFoundException{
if (type == CtClass.booleanType){
return new BooleanMemberValue(cp);
}else if (type == CtClass.byteType){
return new ByteMemberValue(cp);
}else if (type == CtClass.charType){
return new CharMemberValue(cp);
}else if (type == CtClass.shortType){
return new ShortMemberValue(cp);
}else if (type == CtClass.intType){
return new IntegerMemberValue(cp);
}else if (type == CtClass.longType){
return new LongMemberValue(cp);
}else if (type == CtClass.floatType){
return new FloatMemberValue(cp);
}else if (type == CtClass.doubleType){
return new DoubleMemberValue(cp);
}else if (type.getName().equals("java.lang.Class")){
return new ClassMemberValue(cp);
}else if (type.getName().equals("java.lang.String")){
return new StringMemberValue(cp);
}else if (type.isArray()){
CtClass arrayType = type.getComponentType();
MemberValue member = createMemberValue(cp, arrayType);
return new ArrayMemberValue(member, cp);
}else if (type.isInterface()){
Annotation info = new Annotation(cp, type);
return new AnnotationMemberValue(info, cp);
}else{
// treat as enum. I know this is not typed,
// but JBoss has an Annotation Compiler for JDK 1.4
// and I want it to work with that. - Bill Burke
EnumMemberValue emv = new EnumMemberValue(cp);
emv.setType(type.getName());
return emv;
}
}
/**
* Adds a new member.
*
* @param nameIndex
* the index into the constant pool table.
* The entry at that index must be
* a CONSTANT_Utf8_info
structure.
* structure representing the member name.
* @param value
* the member value.
*/
public void addMemberValue(int nameIndex,MemberValue value){
Pair p = new Pair();
p.name = nameIndex;
p.value = value;
addMemberValue(p);
}
/**
* Adds a new member.
*
* @param name
* the member name.
* @param value
* the member value.
*/
public void addMemberValue(String name,MemberValue value){
Pair p = new Pair();
p.name = pool.addUtf8Info(name);
p.value = value;
if (members == null){
members = new LinkedHashMap<>();
}
members.put(name, p);
}
private void addMemberValue(Pair pair){
String name = pool.getUtf8Info(pair.name);
if (members == null){
members = new LinkedHashMap<>();
}
members.put(name, pair);
}
/**
* Returns a string representation of the annotation.
*/
@Override
public String toString(){
StringBuffer buf = new StringBuffer("@");
buf.append(getTypeName());
if (members != null){
buf.append("(");
for (String name : members.keySet()){
buf.append(name).append("=").append(getMemberValue(name)).append(", ");
}
buf.setLength(buf.length() - 2);
buf.append(")");
}
return buf.toString();
}
/**
* Obtains the name of the annotation type.
*
* @return the type name
*/
public String getTypeName(){
return Descriptor.toClassName(pool.getUtf8Info(typeIndex));
}
/**
* Obtains all the member names.
*
* @return null if no members are defined.
*/
public Set getMemberNames(){
if (members == null){
return null;
}
return members.keySet();
}
/**
* Obtains the member value with the given name.
*
*
* If this annotation does not have a value for the
* specified member,
* this method returns null. It does not return a
* MemberValue
with the default value.
* The default value can be obtained from the annotation type.
*
* @param name
* the member name
* @return null if the member cannot be found or if the value is
* the default value.
*
* @see com.feilong.lib.javassist.bytecode.AnnotationDefaultAttribute
*/
public MemberValue getMemberValue(String name){
if (members == null || members.get(name) == null){
return null;
}
return members.get(name).value;
}
/**
* Constructs an annotation-type object representing this annotation.
* For example, if this annotation represents @Author
,
* this method returns an Author
object.
*
* @param cl
* class loader for loading an annotation type.
* @param cp
* class pool for obtaining class files.
* @return the annotation
* @throws ClassNotFoundException
* if the class cannot found.
* @throws NoSuchClassError
* if the class linkage fails.
*/
public Object toAnnotationType(ClassLoader cl,ClassPool cp) throws ClassNotFoundException,NoSuchClassError{
Class clazz = MemberValue.loadClass(cl, getTypeName());
try{
return AnnotationImpl.make(cl, clazz, cp, this);
}catch (IllegalArgumentException e){
/*
* AnnotationImpl.make() may throw this exception
* when it fails to make a proxy object for some
* reason.
*/
throw new ClassNotFoundException(clazz.getName(), e);
}catch (IllegalAccessError e2){
// also IllegalAccessError
throw new ClassNotFoundException(clazz.getName(), e2);
}
}
/**
* Writes this annotation.
*
* @param writer
* the output.
* @throws IOException
* for an error during the write
*/
public void write(AnnotationsWriter writer) throws IOException{
String typeName = pool.getUtf8Info(typeIndex);
if (members == null){
writer.annotation(typeName, 0);
return;
}
writer.annotation(typeName, members.size());
for (Pair pair : members.values()){
writer.memberValuePair(pair.name);
pair.value.write(writer);
}
}
@Override
public int hashCode(){
return getTypeName().hashCode() + (members == null ? 0 : members.hashCode());
}
/**
* Returns true if the given object represents the same annotation
* as this object. The equality test checks the member values.
*/
@Override
public boolean equals(Object obj){
if (obj == this){
return true;
}
if (obj == null || obj instanceof Annotation == false){
return false;
}
Annotation other = (Annotation) obj;
if (getTypeName().equals(other.getTypeName()) == false){
return false;
}
Map otherMembers = other.members;
if (members == otherMembers){
return true;
}else if (members == null){
return otherMembers == null;
}else if (otherMembers == null){
return false;
}else{
return members.equals(otherMembers);
}
}
}