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