001: /*
002: * Javassist, a Java-bytecode translator toolkit.
003: * Copyright (C) 2004 Bill Burke. All Rights Reserved.
004: *
005: * The contents of this file are subject to the Mozilla Public License Version
006: * 1.1 (the "License"); you may not use this file except in compliance with
007: * the License. Alternatively, the contents of this file may be used under
008: * the terms of the GNU Lesser General Public License Version 2.1 or later.
009: *
010: * Software distributed under the License is distributed on an "AS IS" basis,
011: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
012: * for the specific language governing rights and limitations under the
013: * License.
014: */
015:
016: package javassist.bytecode.annotation;
017:
018: import javassist.bytecode.ConstPool;
019: import javassist.bytecode.Descriptor;
020: import javassist.ClassPool;
021: import javassist.CtClass;
022: import javassist.CtMethod;
023:
024: import java.io.IOException;
025: import java.util.HashMap;
026: import java.util.Set;
027: import java.util.Iterator;
028:
029: /**
030: * The <code>annotation</code> structure.
031: *
032: * <p>An instance of this class is returned by
033: * <code>getAnnotations()</code> in <code>AnnotationsAttribute</code>
034: * or in <code>ParameterAnnotationsAttribute</code>.
035: *
036: * @see javassist.bytecode.AnnotationsAttribute#getAnnotations()
037: * @see javassist.bytecode.ParameterAnnotationsAttribute#getAnnotations()
038: * @see MemberValue
039: * @see MemberValueVisitor
040: * @see AnnotationsWriter
041: *
042: * @author <a href="mailto:bill@jboss.org">Bill Burke</a>
043: * @author Shigeru Chiba
044: */
045: public class Annotation {
046: static class Pair {
047: int name;
048: MemberValue value;
049: }
050:
051: ConstPool pool;
052: int typeIndex;
053: HashMap members; // this sould be LinkedHashMap
054:
055: // but it is not supported by JDK 1.3.
056:
057: /**
058: * Constructs an annotation including no members. A member can be
059: * later added to the created annotation by <code>addMemberValue()</code>.
060: *
061: * @param type the index into the constant pool table.
062: * the entry at that index must be the
063: * <code>CONSTANT_Utf8_Info</code> structure
064: * repreenting the name of the annotation interface type.
065: * @param cp the constant pool table.
066: *
067: * @see #addMemberValue(String, MemberValue)
068: */
069: public Annotation(int type, ConstPool cp) {
070: pool = cp;
071: typeIndex = type;
072: members = null;
073: }
074:
075: /**
076: * Constructs an annotation including no members. A member can be
077: * later added to the created annotation by <code>addMemberValue()</code>.
078: *
079: * @param typeName the name of the annotation interface type.
080: * @param cp the constant pool table.
081: *
082: * @see #addMemberValue(String, MemberValue)
083: */
084: public Annotation(String typeName, ConstPool cp) {
085: this (cp.addUtf8Info(Descriptor.of(typeName)), cp);
086: }
087:
088: /**
089: * Constructs an annotation that can be accessed through the interface
090: * represented by <code>clazz</code>. The values of the members are
091: * not specified.
092: *
093: * @param cp the constant pool table.
094: * @param clazz the interface.
095: */
096: public Annotation(ConstPool cp, CtClass clazz)
097: throws javassist.NotFoundException {
098: // todo Enums are not supported right now.
099: this (cp.addUtf8Info(Descriptor.of(clazz.getName())), cp);
100:
101: if (!clazz.isInterface())
102: throw new RuntimeException(
103: "Only interfaces are allowed for Annotation creation.");
104:
105: CtMethod methods[] = clazz.getDeclaredMethods();
106: if (methods.length > 0)
107: members = new HashMap();
108:
109: for (int i = 0; i < methods.length; i++) {
110: CtClass returnType = methods[i].getReturnType();
111: addMemberValue(methods[i].getName(), createMemberValue(cp,
112: returnType));
113: }
114: }
115:
116: /**
117: * Makes an instance of <code>MemberValue</code>.
118: *
119: * @param cp the constant pool table.
120: * @param type the type of the member.
121: */
122: public static MemberValue createMemberValue(ConstPool cp,
123: CtClass type) throws javassist.NotFoundException {
124: if (type == CtClass.booleanType)
125: return new BooleanMemberValue(cp);
126: else if (type == CtClass.byteType)
127: return new ByteMemberValue(cp);
128: else if (type == CtClass.charType)
129: return new CharMemberValue(cp);
130: else if (type == CtClass.shortType)
131: return new ShortMemberValue(cp);
132: else if (type == CtClass.intType)
133: return new IntegerMemberValue(cp);
134: else if (type == CtClass.longType)
135: return new LongMemberValue(cp);
136: else if (type == CtClass.floatType)
137: return new FloatMemberValue(cp);
138: else if (type == CtClass.doubleType)
139: return new DoubleMemberValue(cp);
140: else if (type.getName().equals("java.lang.Class"))
141: return new ClassMemberValue(cp);
142: else if (type.getName().equals("java.lang.String"))
143: return new StringMemberValue(cp);
144: else if (type.isArray()) {
145: CtClass arrayType = type.getComponentType();
146: MemberValue member = createMemberValue(cp, arrayType);
147: return new ArrayMemberValue(member, cp);
148: } else if (type.isInterface()) {
149: Annotation info = new Annotation(cp, type);
150: return new AnnotationMemberValue(info, cp);
151: } else {
152: // treat as enum. I know this is not typed,
153: // but JBoss has an Annotation Compiler for JDK 1.4
154: // and I want it to work with that. - Bill Burke
155: EnumMemberValue emv = new EnumMemberValue(cp);
156: emv.setType(type.getName());
157: return emv;
158: }
159: }
160:
161: /**
162: * Adds a new member.
163: *
164: * @param nameIndex the index into the constant pool table.
165: * The entry at that index must be
166: * a <code>CONSTANT_Utf8_info</code> structure.
167: * structure representing the member name.
168: * @param value the member value.
169: */
170: public void addMemberValue(int nameIndex, MemberValue value) {
171: Pair p = new Pair();
172: p.name = nameIndex;
173: p.value = value;
174: addMemberValue(p);
175: }
176:
177: /**
178: * Adds a new member.
179: *
180: * @param name the member name.
181: * @param value the member value.
182: */
183: public void addMemberValue(String name, MemberValue value) {
184: Pair p = new Pair();
185: p.name = pool.addUtf8Info(name);
186: p.value = value;
187: if (members == null)
188: members = new HashMap();
189:
190: members.put(name, p);
191: }
192:
193: private void addMemberValue(Pair pair) {
194: String name = pool.getUtf8Info(pair.name);
195: if (members == null)
196: members = new HashMap();
197:
198: members.put(name, pair);
199: }
200:
201: /**
202: * Returns a string representation of this object.
203: */
204: public String toString() {
205: StringBuffer buf = new StringBuffer("@");
206: buf.append(getTypeName());
207: if (members != null) {
208: buf.append("(");
209: Iterator mit = members.keySet().iterator();
210: while (mit.hasNext()) {
211: String name = (String) mit.next();
212: buf.append(name).append("=").append(
213: getMemberValue(name));
214: if (mit.hasNext())
215: buf.append(", ");
216: }
217: buf.append(")");
218: }
219:
220: return buf.toString();
221: }
222:
223: /**
224: * Obtains the name of the annotation type.
225: */
226: public String getTypeName() {
227: return Descriptor.toClassName(pool.getUtf8Info(typeIndex));
228: }
229:
230: /**
231: * Obtains all the member names.
232: *
233: * @return null if no members are defined.
234: */
235: public Set getMemberNames() {
236: if (members == null)
237: return null;
238: else
239: return members.keySet();
240: }
241:
242: /**
243: * Obtains the member value with the given name.
244: *
245: * <p>If this annotation does not have a value for the
246: * specified member,
247: * this method returns null. It does not return a
248: * <code>MemberValue</code> with the default value.
249: * The default value can be obtained from the annotation type.
250: *
251: * @return null if the member cannot be found or if the value is
252: * the default value.
253: *
254: * @see javassist.bytecode.AnnotationDefaultAttribute
255: */
256: public MemberValue getMemberValue(String name) {
257: if (members == null)
258: return null;
259: else {
260: Pair p = (Pair) members.get(name);
261: if (p == null)
262: return null;
263: else
264: return p.value;
265: }
266: }
267:
268: /**
269: * Constructs an annotation-type object representing this annotation.
270: * For example, if this annotation represents <code>@Author</code>,
271: * this method returns an <code>Author</code> object.
272: *
273: * @param cl class loader for loading an annotation type.
274: * @param cp class pool for obtaining class files.
275: */
276: public Object toAnnotationType(ClassLoader cl, ClassPool cp)
277: throws ClassNotFoundException {
278: return AnnotationImpl.make(cl, MemberValue.loadClass(cl,
279: getTypeName()), cp, this );
280: }
281:
282: /**
283: * Writes this annotation.
284: *
285: * @param writer the output.
286: */
287: public void write(AnnotationsWriter writer) throws IOException {
288: String typeName = pool.getUtf8Info(typeIndex);
289: if (members == null) {
290: writer.annotation(typeName, 0);
291: return;
292: }
293:
294: writer.annotation(typeName, members.size());
295: Iterator it = members.values().iterator();
296: while (it.hasNext()) {
297: Pair pair = (Pair) it.next();
298: writer.memberValuePair(pair.name);
299: pair.value.write(writer);
300: }
301: }
302: }
|