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 }