001 package edu.rice.cs.cunit.subAnnot; 002 003 import edu.rice.cs.cunit.classFile.ClassFile; 004 import edu.rice.cs.cunit.classFile.ClassFileTools; 005 import edu.rice.cs.cunit.classFile.MethodInfo; 006 import edu.rice.cs.cunit.classFile.attributes.AAnnotationsAttributeInfo; 007 import edu.rice.cs.cunit.classFile.attributes.RuntimeVisibleAnnotationsAttributeInfo; 008 import edu.rice.cs.cunit.classFile.attributes.RuntimeVisibleLocalVariableAnnotationsAttributeInfo; 009 import edu.rice.cs.cunit.classFile.attributes.RuntimeVisibleParameterAnnotationsAttributeInfo; 010 011 import java.io.File; 012 import java.io.IOException; 013 import java.lang.annotation.Annotation; 014 import java.lang.annotation.Inherited; 015 import java.lang.reflect.AnnotatedElement; 016 import java.lang.reflect.Member; 017 import java.lang.reflect.Proxy; 018 import java.util.ArrayList; 019 import java.util.Arrays; 020 import java.util.List; 021 022 /** 023 * Abstract class for annotated elements supporting annotations with subtyping. 024 * 025 * @author Mathias Ricken 026 */ 027 public abstract class AAnnotatedElementEx implements AnnotatedElementEx { 028 /** 029 * Annotations attribute. 030 */ 031 protected RuntimeVisibleAnnotationsAttributeInfo[] _ai; 032 033 /** 034 * True if the class file was not found and only the information given by Java's Class class 035 * is available. 036 */ 037 protected boolean _classFileNotFound = false; 038 039 /** 040 * Class loader to use, or null to use the bootstrap class loader. 041 */ 042 protected ClassLoader _classLoader = null; 043 044 /** 045 * Constructor leaving the _classLoader field empty. 046 */ 047 protected AAnnotatedElementEx() { } 048 049 /** 050 * Constructor setting the _classLoader field. 051 * @param cl class loader 052 */ 053 protected AAnnotatedElementEx(ClassLoader cl) { 054 _classLoader = cl; 055 } 056 057 /** 058 * List of class path entries. 059 */ 060 protected static ArrayList<String> _classPath = new ArrayList<String>(); 061 static { 062 _classPath.addAll(Arrays.asList(System.getProperty("sun.boot.class.path").split(File.pathSeparator))); 063 _classPath.addAll(Arrays.asList(System.getProperty("java.class.path").split(File.pathSeparator))); 064 } 065 066 /** 067 * Returns the class loader used. 068 * @return class loader or null if the bootstrap class loader is used. 069 */ 070 public ClassLoader getClassLoader() { return _classLoader; } 071 072 /** 073 * Returns true if the class file was found and additional information is available beyond what 074 * Java's Class class might return. 075 * @return true if class file was found. 076 */ 077 public boolean isAvailable() { return !_classFileNotFound; } 078 079 /** 080 * Set the state of the _classFileNotFound flag. 081 * @param b new state of the _classFileNotFound flag. 082 */ 083 protected void setClassFileNotFound(boolean b) { 084 // System.err.println("Class file for "+getAnnotatedElement()+" not found!"); 085 _classFileNotFound = b; 086 } 087 088 /** 089 * Returns true if an annotation for the specified type or one of its subtypes is present on this element, 090 * else false. This method is designed primarily for convenient access to marker annotations. 091 * @param c class 092 * @return true if present 093 */ 094 public boolean isAnnotationPresent(ClassEx<? extends Annotation> c) { 095 return (getAnnotations(c).size()>0); 096 } 097 098 /** 099 * Returns true if an annotation for the specified type or one of its subtypes is present on this element, 100 * else false. This method is designed primarily for convenient access to marker annotations. 101 * @param c class 102 * @return true if present 103 */ 104 public boolean isAnnotationPresent(Class<? extends Annotation> c) { 105 return (getAnnotations(c).size()>0); 106 } 107 108 /** 109 * Return an array of the annotations attached to the element that are subclasses of the specified class. 110 * @param c class 111 * @return array of annotations 112 */ 113 public <A extends Annotation> List<A> getAnnotations(ClassEx<A> c) { 114 return getAnnotations(c.java); 115 } 116 117 /** 118 * Return an array of the annotations attached to the element that are subclasses of the specified class. 119 * @param c class 120 * @return array of annotations 121 */ 122 @SuppressWarnings("unchecked") 123 public <A extends Annotation> List<A> getAnnotations(Class<A> c) { 124 ArrayList<A> as = new ArrayList<A>(); 125 for(Annotation a: getAnnotations()) { 126 if (c.isAssignableFrom(a.annotationType())) { as.add((A)a); } 127 } 128 return as; 129 } 130 131 /** 132 * Return an array of all annotations attached to the element. 133 * @return array of annotations 134 */ 135 @SuppressWarnings("unchecked") 136 public Annotation[] getAnnotations() { 137 if (!isAvailable()) { 138 return getAnnotatedElement().getAnnotations(); 139 } 140 if ((_ai==null) || (_ai.length<1)) { return new Annotation[0]; } 141 ArrayList<Annotation> as = new ArrayList<Annotation>(); 142 boolean first = true; 143 for (RuntimeVisibleAnnotationsAttributeInfo ai : _ai) { 144 if (ai!=null) { 145 for (AAnnotationsAttributeInfo.Annotation aia : ai.getAnnotations()) { 146 String name = aia.getType(); 147 try { 148 Class<? extends Annotation> aClass; 149 if (_classLoader!=null) { 150 // System.out.println("ClassLoader!=null: "+_classLoader); 151 aClass = (Class<? extends Annotation>)ClassFileTools.getClassFromType(name, true, _classLoader); 152 } 153 else { 154 // System.out.println("ClassLoader==null"); 155 aClass = (Class<? extends Annotation>)ClassFileTools.getClassFromType(name); 156 } 157 if ((first) || (aClass.isAnnotationPresent(Inherited.class))) { 158 // add annotation if in class itself or if annotation is marked as @Inherited 159 as.add((Annotation) Proxy.newProxyInstance( 160 aClass.getClassLoader(), 161 new Class[]{aClass}, 162 new AnnotationDynamicProxyHandler(aClass, aia))); 163 } 164 } 165 catch (ClassNotFoundException cnfe) { 166 cnfe.printStackTrace(); 167 throw new ClassFormatError("Could not load annotation class " + ClassFileTools.getClassNameFromType(name)); 168 } 169 } 170 } 171 first = false; 172 } 173 return as.toArray(new Annotation[as.size()]); 174 } 175 176 /** 177 * Returns true if an annotation for the specified type or one of its subtypes is present on this element, 178 * else false. This method is designed primarily for convenient access to marker annotations. 179 * Contrary to isAnnotationPresent, this does not consider annotations inherited from superclasses. 180 * @param c class 181 * @return true if present 182 */ 183 public boolean isDeclaredAnnotationPresent(ClassEx<? extends Annotation> c) { 184 return (getDeclaredAnnotations(c).size()>0); 185 } 186 187 /** 188 * Returns true if an annotation for the specified type or one of its subtypes is present on this element, 189 * else false. This method is designed primarily for convenient access to marker annotations. 190 * Contrary to isAnnotationPresent, this does not consider annotations inherited from superclasses. 191 * @param c class 192 * @return true if present 193 */ 194 public boolean isDeclaredAnnotationPresent(Class<? extends Annotation> c) { 195 return (getDeclaredAnnotations(c).size()>0); 196 } 197 198 /** 199 * Return an array of the annotations attached to the element that are subclasses of the specified class. 200 * Contrary to getAnnotations, this does not consider annotations inherited from superclasses. 201 * @param c class 202 * @return array of annotations 203 */ 204 public <A extends Annotation> List<A> getDeclaredAnnotations(ClassEx<A> c) { 205 return getDeclaredAnnotations(c.java); 206 } 207 208 /** 209 * Return an array of the annotations attached to the element that are subclasses of the specified class. 210 * Contrary to isAnnotationPresent, this does not consider annotations inherited from superclasses. 211 * @param c class 212 * @return array of annotations 213 */ 214 @SuppressWarnings("unchecked") 215 public <A extends Annotation> List<A> getDeclaredAnnotations(Class<A> c) { 216 ArrayList<A> as = new ArrayList<A>(); 217 for(Annotation a: getDeclaredAnnotations()) { 218 if (c.isAssignableFrom(a.getClass())) { as.add((A)a); } 219 } 220 return as; 221 } 222 223 /** 224 * Return an array of all annotations attached directly to the element. 225 * Contrary to getAnnotations(), this does not consider annotations inherited from superclasses. 226 * @return array of annotations 227 */ 228 @SuppressWarnings("unchecked") 229 public Annotation[] getDeclaredAnnotations() { 230 if (!isAvailable()) { 231 return getAnnotatedElement().getAnnotations(); 232 } 233 if ((_ai == null) || (_ai.length < 1) || (_ai[0]==null)) { 234 return new Annotation[0]; 235 } 236 ArrayList<Annotation> as = new ArrayList<Annotation>(); 237 for (AAnnotationsAttributeInfo.Annotation aia : _ai[0].getAnnotations()) { 238 String name = aia.getType(); 239 try { 240 Class<? extends Annotation> aClass = (Class<? extends Annotation>)ClassFileTools.getClassFromType(name); 241 as.add((Annotation) Proxy.newProxyInstance( 242 aClass.getClassLoader(), 243 new Class[]{aClass}, 244 new AnnotationDynamicProxyHandler(aClass, aia))); 245 } 246 catch (ClassNotFoundException cnfe) { 247 throw new ClassFormatError("Could not load annotation class " + name); 248 } 249 } 250 return as.toArray(new Annotation[as.size()]); 251 } 252 253 /** 254 * Return the annotated element. 255 * @return annotated element 256 */ 257 protected abstract AnnotatedElement getAnnotatedElement(); 258 259 /** 260 * Find the annotations attribute and assign it to the _ai field. 261 * @param member member whose annotations attribute should be found 262 * @param name member name 263 * @param types parameter types 264 */ 265 protected void findMethodAnnotationsAttributeInfo(Member member, String name, Class<?>[] types) { 266 if (member.getDeclaringClass().isPrimitive()) { _ai = null; return; } 267 ClassFileTools.ClassLocation cl = ClassFileTools.findClassFile(member.getDeclaringClass().getName(), _classPath); 268 if (cl==null) { 269 setClassFileNotFound(true); 270 return; 271 } 272 ClassFile cf = cl.getClassFile(); 273 try { 274 cl.close(); 275 } 276 catch(IOException e) { /* ignore */ } 277 for(MethodInfo mi: cf.getMethods()) { 278 if (mi.getName().toString().equals(name)) { 279 boolean match = true; 280 // descriptor is "(...)...", remove "(" and ")..." 281 String desc = mi.getDescriptor().toString(); 282 List<String> paramTypeList = ClassFileTools.getSignatures(desc.substring(1, desc.lastIndexOf(')'))); 283 if (paramTypeList.size()==types.length) { 284 for(int i=0; i<paramTypeList.size(); ++i) { 285 String pt = paramTypeList.get(i); 286 String cpt = types[i].getName(); 287 if ((pt.length()>0) && (pt.charAt(0)=='[') && 288 (cpt.length()>0) && (cpt.charAt(0)=='[')) { 289 // if both type strings are arrays, cut off matching '[' prefixes 290 do { 291 pt = pt.substring(1); 292 cpt = cpt.substring(1); 293 } while ((pt.length()>0) && (pt.charAt(0)=='[') && 294 (cpt.length()>0) && (cpt.charAt(0)=='[')); 295 // if this is an object array, the class name will be "Lxxx;" 296 // cut off the 'L' and ';' 297 if ((cpt.charAt(0)=='L') && (cpt.charAt(cpt.length()-1)==';')) { 298 cpt = cpt.substring(1,cpt.length()-1); 299 } 300 } 301 if (!ClassFileTools.getTypeString(pt,"").equals(cpt+" ")) { 302 match = false; 303 break; 304 } 305 } 306 if (match) { 307 _ai = new RuntimeVisibleAnnotationsAttributeInfo[1]; 308 _ai[0] = (RuntimeVisibleAnnotationsAttributeInfo) 309 mi.getAttribute(RuntimeVisibleAnnotationsAttributeInfo.getAttributeName()); 310 return; 311 } 312 } 313 } 314 } 315 throw new ClassFormatError("Could not find member "+member+" in class files"); 316 } 317 318 /** 319 * Return the parameter annotations attribute. 320 * @param member member whose parameter annotations attribute should be found 321 * @param name member name 322 * @param types parameter types 323 * @return parameter annotations attribute 324 */ 325 protected RuntimeVisibleParameterAnnotationsAttributeInfo getParameterAnnotationsAttributeInfo(Member member, String name, Class<?>[] types) { 326 if (member.getDeclaringClass().isPrimitive()) { return null; } 327 ClassFileTools.ClassLocation cl = ClassFileTools.findClassFile(member.getDeclaringClass().getName(), _classPath); 328 if (cl==null) { 329 setClassFileNotFound(true); 330 return null; 331 } 332 ClassFile cf = cl.getClassFile(); 333 try { 334 cl.close(); 335 } 336 catch(IOException e) { /* ignore */ } 337 for(MethodInfo mi: cf.getMethods()) { 338 if (mi.getName().toString().equals(name)) { 339 boolean match = true; 340 // descriptor is "(...)...", remove "(" and ")..." 341 String desc = mi.getDescriptor().toString(); 342 List<String> paramTypeList = ClassFileTools.getSignatures(desc.substring(1, desc.lastIndexOf(')'))); 343 if (paramTypeList.size()== types.length) { 344 for(int i=0; i<paramTypeList.size(); ++i) { 345 String pt = paramTypeList.get(i); 346 String cpt = types[i].getName(); 347 if ((pt.length()>0) && (pt.charAt(0)=='[') && 348 (cpt.length()>0) && (cpt.charAt(0)=='[')) { 349 // if both type strings are arrays, cut off matching '[' prefixes 350 do { 351 pt = pt.substring(1); 352 cpt = cpt.substring(1); 353 } while ((pt.length()>0) && (pt.charAt(0)=='[') && 354 (cpt.length()>0) && (cpt.charAt(0)=='[')); 355 // if this is an object array, the class name will be "Lxxx;" 356 // cut off the 'L' and ';' 357 if ((cpt.charAt(0)=='L') && (cpt.charAt(cpt.length()-1)==';')) { 358 cpt = cpt.substring(1,cpt.length()-1); 359 } 360 } 361 if (!ClassFileTools.getTypeString(pt,"").equals(cpt+" ")) { 362 match = false; 363 break; 364 } 365 } 366 if (match) { 367 return (RuntimeVisibleParameterAnnotationsAttributeInfo) 368 mi.getAttribute(RuntimeVisibleParameterAnnotationsAttributeInfo.getAttributeName()); 369 } 370 } 371 } 372 } 373 throw new ClassFormatError("Could not find member "+member+" in class files"); 374 } 375 376 /** 377 * Return the local variable annotations attribute. 378 * @param member member whose parameter annotations attribute should be found 379 * @param name member name 380 * @param types parameter types 381 * @return parameter annotations attribute 382 */ 383 protected RuntimeVisibleLocalVariableAnnotationsAttributeInfo getLocalVarAnnotationsAttributeInfo(Member member, String name, Class<?>[] types) { 384 if (member.getDeclaringClass().isPrimitive()) { return null; } 385 ClassFileTools.ClassLocation cl = ClassFileTools.findClassFile(member.getDeclaringClass().getName(), _classPath); 386 if (cl==null) { 387 setClassFileNotFound(true); 388 return null; 389 } 390 ClassFile cf = cl.getClassFile(); 391 try { 392 cl.close(); 393 } 394 catch(IOException e) { /* ignore */ } 395 for(MethodInfo mi: cf.getMethods()) { 396 if (mi.getName().toString().equals(name)) { 397 boolean match = true; 398 // descriptor is "(...)...", remove "(" and ")..." 399 String desc = mi.getDescriptor().toString(); 400 List<String> paramTypeList = ClassFileTools.getSignatures(desc.substring(1, desc.lastIndexOf(')'))); 401 if (paramTypeList.size()== types.length) { 402 for(int i=0; i<paramTypeList.size(); ++i) { 403 String pt = paramTypeList.get(i); 404 String cpt = types[i].getName(); 405 if ((pt.length()>0) && (pt.charAt(0)=='[') && 406 (cpt.length()>0) && (cpt.charAt(0)=='[')) { 407 // if both type strings are arrays, cut off matching '[' prefixes 408 do { 409 pt = pt.substring(1); 410 cpt = cpt.substring(1); 411 } while ((pt.length()>0) && (pt.charAt(0)=='[') && 412 (cpt.length()>0) && (cpt.charAt(0)=='[')); 413 // if this is an object array, the class name will be "Lxxx;" 414 // cut off the 'L' and ';' 415 if ((cpt.charAt(0)=='L') && (cpt.charAt(cpt.length()-1)==';')) { 416 cpt = cpt.substring(1,cpt.length()-1); 417 } 418 } 419 if (!ClassFileTools.getTypeString(pt,"").equals(cpt+" ")) { 420 match = false; 421 break; 422 } 423 } 424 if (match) { 425 return (RuntimeVisibleLocalVariableAnnotationsAttributeInfo) 426 mi.getAttribute(RuntimeVisibleLocalVariableAnnotationsAttributeInfo.getAttributeName()); 427 } 428 } 429 } 430 } 431 throw new ClassFormatError("Could not find member "+member+" in class files"); 432 } 433 }