Home | History | Annotate | Download | only in annotation
      1 /*
      2  * Javassist, a Java-bytecode translator toolkit.
      3  * Copyright (C) 2004 Bill Burke. All Rights Reserved.
      4  *
      5  * The contents of this file are subject to the Mozilla Public License Version
      6  * 1.1 (the "License"); you may not use this file except in compliance with
      7  * the License.  Alternatively, the contents of this file may be used under
      8  * the terms of the GNU Lesser General Public License Version 2.1 or later.
      9  *
     10  * Software distributed under the License is distributed on an "AS IS" basis,
     11  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
     12  * for the specific language governing rights and limitations under the
     13  * License.
     14  */
     15 
     16 package javassist.bytecode.annotation;
     17 
     18 import javassist.bytecode.ConstPool;
     19 import javassist.bytecode.Descriptor;
     20 import javassist.ClassPool;
     21 import javassist.CtClass;
     22 import javassist.CtMethod;
     23 import javassist.NotFoundException;
     24 
     25 import java.io.IOException;
     26 import java.util.LinkedHashMap;
     27 import java.util.Set;
     28 import java.util.Iterator;
     29 
     30 /**
     31  * The <code>annotation</code> structure.
     32  *
     33  * <p>An instance of this class is returned by
     34  * <code>getAnnotations()</code> in <code>AnnotationsAttribute</code>
     35  * or in <code>ParameterAnnotationsAttribute</code>.
     36  *
     37  * @see javassist.bytecode.AnnotationsAttribute#getAnnotations()
     38  * @see javassist.bytecode.ParameterAnnotationsAttribute#getAnnotations()
     39  * @see MemberValue
     40  * @see MemberValueVisitor
     41  * @see AnnotationsWriter
     42  *
     43  * @author <a href="mailto:bill (at) jboss.org">Bill Burke</a>
     44  * @author Shigeru Chiba
     45  * @author <a href="mailto:adrian (at) jboss.org">Adrian Brock</a>
     46  */
     47 public class Annotation {
     48     static class Pair {
     49         int name;
     50         MemberValue value;
     51     }
     52 
     53     ConstPool pool;
     54     int typeIndex;
     55     LinkedHashMap members;    // this sould be LinkedHashMap
     56                         // but it is not supported by JDK 1.3.
     57 
     58     /**
     59      * Constructs an annotation including no members.  A member can be
     60      * later added to the created annotation by <code>addMemberValue()</code>.
     61      *
     62      * @param type  the index into the constant pool table.
     63      *              the entry at that index must be the
     64      *              <code>CONSTANT_Utf8_Info</code> structure
     65      *              repreenting the name of the annotation interface type.
     66      * @param cp    the constant pool table.
     67      *
     68      * @see #addMemberValue(String, MemberValue)
     69      */
     70     public Annotation(int type, ConstPool cp) {
     71         pool = cp;
     72         typeIndex = type;
     73         members = null;
     74     }
     75 
     76     /**
     77      * Constructs an annotation including no members.  A member can be
     78      * later added to the created annotation by <code>addMemberValue()</code>.
     79      *
     80      * @param typeName  the name of the annotation interface type.
     81      * @param cp        the constant pool table.
     82      *
     83      * @see #addMemberValue(String, MemberValue)
     84      */
     85     public Annotation(String typeName, ConstPool cp) {
     86         this(cp.addUtf8Info(Descriptor.of(typeName)), cp);
     87     }
     88 
     89     /**
     90      * Constructs an annotation that can be accessed through the interface
     91      * represented by <code>clazz</code>.  The values of the members are
     92      * not specified.
     93      *
     94      * @param cp        the constant pool table.
     95      * @param clazz     the interface.
     96      * @throws NotFoundException when the clazz is not found
     97      */
     98     public Annotation(ConstPool cp, CtClass clazz)
     99         throws NotFoundException
    100     {
    101         // todo Enums are not supported right now.
    102         this(cp.addUtf8Info(Descriptor.of(clazz.getName())), cp);
    103 
    104         if (!clazz.isInterface())
    105             throw new RuntimeException(
    106                 "Only interfaces are allowed for Annotation creation.");
    107 
    108         CtMethod methods[] = clazz.getDeclaredMethods();
    109         if (methods.length > 0) {
    110             members = new LinkedHashMap();
    111         }
    112 
    113         for (int i = 0; i < methods.length; i++) {
    114             CtClass returnType = methods[i].getReturnType();
    115             addMemberValue(methods[i].getName(),
    116                            createMemberValue(cp, returnType));
    117 
    118         }
    119     }
    120 
    121     /**
    122      * Makes an instance of <code>MemberValue</code>.
    123      *
    124      * @param cp            the constant pool table.
    125      * @param type          the type of the member.
    126      * @return the member value
    127      * @throws NotFoundException when the type is not found
    128      */
    129     public static MemberValue createMemberValue(ConstPool cp, CtClass type)
    130         throws NotFoundException
    131     {
    132         if (type == CtClass.booleanType)
    133             return new BooleanMemberValue(cp);
    134         else if (type == CtClass.byteType)
    135             return new ByteMemberValue(cp);
    136         else if (type == CtClass.charType)
    137             return new CharMemberValue(cp);
    138         else if (type == CtClass.shortType)
    139             return new ShortMemberValue(cp);
    140         else if (type == CtClass.intType)
    141             return new IntegerMemberValue(cp);
    142         else if (type == CtClass.longType)
    143             return new LongMemberValue(cp);
    144         else if (type == CtClass.floatType)
    145             return new FloatMemberValue(cp);
    146         else if (type == CtClass.doubleType)
    147             return new DoubleMemberValue(cp);
    148         else if (type.getName().equals("java.lang.Class"))
    149             return new ClassMemberValue(cp);
    150         else if (type.getName().equals("java.lang.String"))
    151             return new StringMemberValue(cp);
    152         else if (type.isArray()) {
    153             CtClass arrayType = type.getComponentType();
    154             MemberValue member = createMemberValue(cp, arrayType);
    155             return new ArrayMemberValue(member, cp);
    156         }
    157         else if (type.isInterface()) {
    158             Annotation info = new Annotation(cp, type);
    159             return new AnnotationMemberValue(info, cp);
    160         }
    161         else {
    162             // treat as enum.  I know this is not typed,
    163             // but JBoss has an Annotation Compiler for JDK 1.4
    164             // and I want it to work with that. - Bill Burke
    165             EnumMemberValue emv = new EnumMemberValue(cp);
    166             emv.setType(type.getName());
    167             return emv;
    168         }
    169     }
    170 
    171     /**
    172      * Adds a new member.
    173      *
    174      * @param nameIndex     the index into the constant pool table.
    175      *                      The entry at that index must be
    176      *                      a <code>CONSTANT_Utf8_info</code> structure.
    177      *                      structure representing the member name.
    178      * @param value         the member value.
    179      */
    180     public void addMemberValue(int nameIndex, MemberValue value) {
    181         Pair p = new Pair();
    182         p.name = nameIndex;
    183         p.value = value;
    184         addMemberValue(p);
    185     }
    186 
    187     /**
    188      * Adds a new member.
    189      *
    190      * @param name      the member name.
    191      * @param value     the member value.
    192      */
    193     public void addMemberValue(String name, MemberValue value) {
    194         Pair p = new Pair();
    195         p.name = pool.addUtf8Info(name);
    196         p.value = value;
    197         if (members == null)
    198             members = new LinkedHashMap();
    199 
    200         members.put(name, p);
    201     }
    202 
    203     private void addMemberValue(Pair pair) {
    204         String name = pool.getUtf8Info(pair.name);
    205         if (members == null)
    206             members = new LinkedHashMap();
    207 
    208         members.put(name, pair);
    209     }
    210 
    211     /**
    212      * Returns a string representation of the annotation.
    213      */
    214     public String toString() {
    215         StringBuffer buf = new StringBuffer("@");
    216         buf.append(getTypeName());
    217         if (members != null) {
    218             buf.append("(");
    219             Iterator mit = members.keySet().iterator();
    220             while (mit.hasNext()) {
    221                 String name = (String)mit.next();
    222                 buf.append(name).append("=").append(getMemberValue(name));
    223                 if (mit.hasNext())
    224                     buf.append(", ");
    225             }
    226             buf.append(")");
    227         }
    228 
    229         return buf.toString();
    230     }
    231 
    232     /**
    233      * Obtains the name of the annotation type.
    234      *
    235      * @return the type name
    236      */
    237     public String getTypeName() {
    238         return Descriptor.toClassName(pool.getUtf8Info(typeIndex));
    239     }
    240 
    241     /**
    242      * Obtains all the member names.
    243      *
    244      * @return null if no members are defined.
    245      */
    246     public Set getMemberNames() {
    247         if (members == null)
    248             return null;
    249         else
    250             return members.keySet();
    251     }
    252 
    253     /**
    254      * Obtains the member value with the given name.
    255      *
    256      * <p>If this annotation does not have a value for the
    257      * specified member,
    258      * this method returns null.  It does not return a
    259      * <code>MemberValue</code> with the default value.
    260      * The default value can be obtained from the annotation type.
    261      *
    262      * @param name the member name
    263      * @return null if the member cannot be found or if the value is
    264      * the default value.
    265      *
    266      * @see javassist.bytecode.AnnotationDefaultAttribute
    267      */
    268     public MemberValue getMemberValue(String name) {
    269         if (members == null)
    270             return null;
    271         else {
    272             Pair p = (Pair)members.get(name);
    273             if (p == null)
    274                 return null;
    275             else
    276                 return p.value;
    277         }
    278     }
    279 
    280     /**
    281      * Constructs an annotation-type object representing this annotation.
    282      * For example, if this annotation represents <code>@Author</code>,
    283      * this method returns an <code>Author</code> object.
    284      *
    285      * @param cl        class loader for loading an annotation type.
    286      * @param cp        class pool for obtaining class files.
    287      * @return the annotation
    288      * @throws ClassNotFoundException   if the class cannot found.
    289      * @throws NoSuchClassError         if the class linkage fails.
    290      */
    291     public Object toAnnotationType(ClassLoader cl, ClassPool cp)
    292         throws ClassNotFoundException, NoSuchClassError
    293     {
    294         return AnnotationImpl.make(cl,
    295                         MemberValue.loadClass(cl, getTypeName()),
    296                         cp, this);
    297     }
    298 
    299     /**
    300      * Writes this annotation.
    301      *
    302      * @param writer            the output.
    303      * @throws IOException for an error during the write
    304      */
    305     public void write(AnnotationsWriter writer) throws IOException {
    306         String typeName = pool.getUtf8Info(typeIndex);
    307         if (members == null) {
    308             writer.annotation(typeName, 0);
    309             return;
    310         }
    311 
    312         writer.annotation(typeName, members.size());
    313         Iterator it = members.values().iterator();
    314         while (it.hasNext()) {
    315             Pair pair = (Pair)it.next();
    316             writer.memberValuePair(pair.name);
    317             pair.value.write(writer);
    318         }
    319     }
    320 
    321     /**
    322      * Returns true if the given object represents the same annotation
    323      * as this object.  The equality test checks the member values.
    324      */
    325     public boolean equals(Object obj) {
    326         if (obj == this)
    327             return true;
    328         if (obj == null || obj instanceof Annotation == false)
    329             return false;
    330 
    331         Annotation other = (Annotation) obj;
    332 
    333         if (getTypeName().equals(other.getTypeName()) == false)
    334             return false;
    335 
    336         LinkedHashMap otherMembers = other.members;
    337         if (members == otherMembers)
    338             return true;
    339         else if (members == null)
    340             return otherMembers == null;
    341         else
    342             if (otherMembers == null)
    343                 return false;
    344             else
    345                 return members.equals(otherMembers);
    346     }
    347 }
    348