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