• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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@jboss.org">Bill Burke</a>
44  * @author Shigeru Chiba
45  * @author <a href="mailto:adrian@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      */
Annotation(int type, ConstPool cp)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      */
Annotation(String typeName, ConstPool cp)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      */
Annotation(ConstPool cp, CtClass clazz)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      */
createMemberValue(ConstPool cp, CtClass type)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      */
addMemberValue(int nameIndex, MemberValue value)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      */
addMemberValue(String name, MemberValue value)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 
addMemberValue(Pair pair)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      */
toString()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      */
getTypeName()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      */
getMemberNames()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      */
getMemberValue(String name)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      */
toAnnotationType(ClassLoader cl, ClassPool cp)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      */
write(AnnotationsWriter writer)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      */
equals(Object obj)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