001    package edu.rice.cs.cunit.subAnnot;
002    
003    import edu.rice.cs.cunit.classFile.attributes.AMultipleNamedAnnotationsAttributeInfo;
004    import edu.rice.cs.cunit.classFile.attributes.RuntimeVisibleLocalVariableAnnotationsAttributeInfo;
005    import edu.rice.cs.cunit.classFile.attributes.RuntimeVisibleParameterAnnotationsAttributeInfo;
006    
007    import java.lang.annotation.Annotation;
008    import java.lang.reflect.*;
009    import java.util.ArrayList;
010    import java.util.HashMap;
011    import java.util.List;
012    import java.util.Map;
013    
014    /**
015     * Extended Method class to support annotations with subclassing.
016     *
017     * @author Mathias Ricken
018     */
019    public class MethodEx extends AAnnotatedElementEx {
020        /**
021         * The java.lang.reflect.Method object that represents the method.
022         */
023        public final Method java;
024    
025        /**
026         * Parameter annotation attribute.
027         */
028        private RuntimeVisibleParameterAnnotationsAttributeInfo _pai;
029    
030        /**
031         * Local variable annotation attribute.
032         */
033        private RuntimeVisibleLocalVariableAnnotationsAttributeInfo _lvai;
034    
035        /**
036         * Create an extended Method instance for the specified method.
037         * @param m method
038         */
039        public MethodEx(Method m) {
040            this(m, null);
041        }
042    
043        /**
044         * Create an extended Method instance for the specified method.
045         * @param m method
046         * @param cl class loader
047         */
048        public MethodEx(Method m, ClassLoader cl) {
049            super(cl);
050            java = m;
051            findMethodAnnotationsAttributeInfo(m, m.getName(), m.getParameterTypes());
052            _pai = getParameterAnnotationsAttributeInfo(m, m.getName(), m.getParameterTypes());
053            _lvai = getLocalVarAnnotationsAttributeInfo(m, m.getName(), m.getParameterTypes());
054        }
055    
056        /**
057         * Return the annotated element.
058         * @return annotated element
059         */
060        protected AnnotatedElement getAnnotatedElement() {
061            return java;
062        }
063    
064        /**
065         * Returns the name of the method represented by this <code>Method</code> object, as a <code>String</code>.
066         * @return name
067         */
068        public String getName() {
069            return java.getName();
070        }
071    
072        /**
073         * Returns the Java language modifiers for the method represented by this <code>Method</code> object, as an integer.
074         * The <code>Modifier</code> class should be used to decode the modifiers.
075         * @return modifiers
076         * @see java.lang.reflect.Modifier
077         */
078        public int getModifiers() {
079            return java.getModifiers();
080        }
081    
082        /**
083         * Returns an array of <tt>TypeVariable</tt> objects that represent the type variables declared by the generic
084         * declaration represented by this <tt>GenericDeclaration</tt> object, in declaration order.  Returns an array of
085         * length 0 if the underlying generic declaration declares no type variables.
086         *
087         * @return an array of <tt>TypeVariable</tt> objects that represent the type variables declared by this generic
088         *         declaration
089         *
090         * @throws java.lang.reflect.GenericSignatureFormatError
091         *          if the generic signature of this generic declaration does not conform to the format specified in the
092         *          Java Virtual Machine Specification, 3rd edition
093         * @since 1.5
094         */
095        public TypeVariable<Method>[] getTypeParameters() {
096            return java.getTypeParameters();
097        }
098    
099        /**
100         * Returns a <code>Class</code> object that represents the formal return type of the method represented by this
101         * <code>Method</code> object.
102         *
103         * @return the return type for the method this object represents
104         */
105        @SuppressWarnings("unchecked")
106        public ClassEx<?> getReturnType() {
107            Class<?> c = java.getReturnType();
108            return new ClassEx(c, c.getClassLoader());
109        }
110    
111        /**
112         * Returns a <tt>Type</tt> object that represents the formal return type of the method represented by this
113         * <tt>Method</tt> object.
114         * <p/>
115         * <p>If the return type is a parameterized type, the <tt>Type</tt> object returned must accurately reflect the
116         * actual type parameters used in the source code.
117         * <p/>
118         * <p>If the return type is a type variable or a parameterized type, it is created. Otherwise, it is resolved.
119         *
120         * @return a <tt>Type</tt> object that represents the formal return type of the underlying  method
121         *
122         * @throws java.lang.reflect.GenericSignatureFormatError
123         *                                 if the generic method signature does not conform to the format specified in the
124         *                                 Java Virtual Machine Specification, 3rd edition
125         * @throws TypeNotPresentException if the underlying method's return type refers to a non-existent type declaration
126         * @throws java.lang.reflect.MalformedParameterizedTypeException
127         *                                 if the underlying method's return typed refers to a parameterized type that
128         *                                 cannot be instantiated for any reason
129         * @since 1.5
130         */
131        public Type getGenericReturnType() {
132            return java.getGenericReturnType();
133        }
134    
135        /**
136         * Returns an array of <code>Class</code> objects that represent the formal parameter types, in declaration order,
137         * of the method represented by this <code>Method</code> object.  Returns an array of length 0 if the underlying
138         * method takes no parameters.
139         *
140         * @return the parameter types for the method this object represents
141         */
142        @SuppressWarnings("unchecked")
143        public ClassEx[] getParameterTypes() {
144            List<ClassEx> list = new ArrayList<ClassEx>();
145            for(Class c: java.getParameterTypes()) {
146                list.add(new ClassEx(c, c.getClassLoader()));
147            }
148            return list.toArray(new ClassEx[list.size()]);
149    
150        }
151    
152        /**
153         * Returns an array of <tt>Type</tt> objects that represent the formal parameter types, in declaration order, of the
154         * method represented by this <tt>Method</tt> object. Returns an array of length 0 if the underlying method takes no
155         * parameters.
156         * <p/>
157         * <p>If a formal parameter type is a parameterized type, the <tt>Type</tt> object returned for it must accurately
158         * reflect the actual type parameters used in the source code.
159         * <p/>
160         * <p>If a formal parameter type is a type variable or a parameterized type, it is created. Otherwise, it is
161         * resolved.
162         *
163         * @return an array of Types that represent the formal parameter types of the underlying method, in declaration
164         *         order
165         *
166         * @throws java.lang.reflect.GenericSignatureFormatError
167         *                                 if the generic method signature does not conform to the format specified in the
168         *                                 Java Virtual Machine Specification, 3rd edition
169         * @throws TypeNotPresentException if any of the parameter types of the underlying method refers to a non-existent
170         *                                 type declaration
171         * @throws java.lang.reflect.MalformedParameterizedTypeException
172         *                                 if any of the underlying method's parameter types refer to a parameterized type
173         *                                 that cannot be instantiated for any reason
174         * @since 1.5
175         */
176        public Type[] getGenericParameterTypes() {
177            return java.getGenericParameterTypes();
178        }
179    
180        /**
181         * Returns an array of <code>Class</code> objects that represent the types of the exceptions declared to be thrown
182         * by the underlying method represented by this <code>Method</code> object.  Returns an array of length 0 if the
183         * method declares no exceptions in its <code>throws</code> clause.
184         *
185         * @return the exception types declared as being thrown by the method this object represents
186         */
187        @SuppressWarnings("unchecked")
188        public ClassEx[] getExceptionTypes() {
189            List<ClassEx> list = new ArrayList<ClassEx>();
190            for(Class c: java.getExceptionTypes()) {
191                list.add(new ClassEx(c, c.getClassLoader()));
192            }
193            return list.toArray(new ClassEx[list.size()]);
194        }
195    
196        /**
197         * Returns an array of <tt>Type</tt> objects that represent the exceptions declared to be thrown by this
198         * <tt>Method</tt> object. Returns an array of length 0 if the underlying method declares no exceptions in its
199         * <tt>throws</tt> clause.
200         * <p/>
201         * <p>If an exception type is a parameterized type, the <tt>Type</tt> object returned for it must accurately
202         * reflect the actual type parameters used in the source code.
203         * <p/>
204         * <p>If an exception type is a type variable or a parameterized type, it is created. Otherwise, it is resolved.
205         *
206         * @return an array of Types that represent the exception types thrown by the underlying method
207         *
208         * @throws java.lang.reflect.GenericSignatureFormatError
209         *                                 if the generic method signature does not conform to the format specified in the
210         *                                 Java Virtual Machine Specification, 3rd edition
211         * @throws TypeNotPresentException if the underlying method's <tt>throws</tt> clause refers to a non-existent type
212         *                                 declaration
213         * @throws java.lang.reflect.MalformedParameterizedTypeException
214         *                                 if the underlying method's <tt>throws</tt> clause refers to a parameterized
215         *                                 type that cannot be instantiated for any reason
216         * @since 1.5
217         */
218        public Type[] getGenericExceptionTypes() {
219            return java.getGenericExceptionTypes();
220        }
221    
222        /**
223         * Compares this <code>Method</code> against the specified object.  Returns true if the objects are the same.  Two
224         * <code>Methods</code> are the same if they were declared by the same class and have the same name and formal
225         * parameter types and return type.
226         */
227        public boolean equals(Object obj) {
228            return (obj!=null)&&(obj.getClass().equals(this.getClass()) && (java.equals(obj)));
229        }
230    
231        /**
232         * Returns a hashcode for this <code>Method</code>.  The hashcode is computed as the exclusive-or of the hashcodes
233         * for the underlying method's declaring class name and the method's name.
234         */
235        public int hashCode() {
236            return java.hashCode();
237        }
238    
239        /**
240         * Returns a string describing this <code>Method</code>.  The string is formatted as the method access modifiers, if
241         * any, followed by the method return type, followed by a space, followed by the class declaring the method,
242         * followed by a period, followed by the method name, followed by a parenthesized, comma-separated list of the
243         * method's formal parameter types. If the method throws checked exceptions, the parameter list is followed by a
244         * space, followed by the word throws followed by a comma-separated list of the thrown exception types. For
245         * example:
246         * <pre>
247         *    public boolean java.lang.Object.equals(java.lang.Object)
248         * </pre>
249         * <p/>
250         * <p>The access modifiers are placed in canonical order as specified by "The Java Language Specification".  This is
251         * <tt>public</tt>, <tt>protected</tt> or <tt>private</tt> first, and then other modifiers in the following order:
252         * <tt>abstract</tt>, <tt>static</tt>, <tt>final</tt>, <tt>synchronized</tt> <tt>native</tt>.
253         */
254        public String toString() {
255            return java.toString();
256        }
257    
258        /**
259         * Returns a string describing this <code>Method</code>, including type parameters.  The string is formatted as the
260         * method access modifiers, if any, followed by an angle-bracketed comma-separated list of the method's type
261         * parameters, if any, followed by the method's generic return type, followed by a space, followed by the class
262         * declaring the method, followed by a period, followed by the method name, followed by a parenthesized,
263         * comma-separated list of the method's generic formal parameter types. A space is used to separate access modifiers
264         * from one another and from the type parameters or return type.  If there are no type parameters, the type
265         * parameter list is elided; if the type parameter list is present, a space separates the list from the class name.
266         * If the method is declared to throw exceptions, the parameter list is followed by a space, followed by the word
267         * throws followed by a comma-separated list of the generic thrown exception types. If there are no type parameters,
268         * the type parameter list is elided.
269         * <p/>
270         * <p>The access modifiers are placed in canonical order as specified by "The Java Language Specification".  This is
271         * <tt>public</tt>, <tt>protected</tt> or <tt>private</tt> first, and then other modifiers in the following order:
272         * <tt>abstract</tt>, <tt>static</tt>, <tt>final</tt>, <tt>synchronized</tt> <tt>native</tt>.
273         *
274         * @return a string describing this <code>Method</code>, include type parameters
275         *
276         * @since 1.5
277         */
278        public String toGenericString() {
279            return java.toGenericString();
280        }
281    
282        /**
283         * Invokes the underlying method represented by this <code>Method</code> object, on the specified object with the
284         * specified parameters. Individual parameters are automatically unwrapped to match primitive formal parameters, and
285         * both primitive and reference parameters are subject to method invocation conversions as necessary.
286         * <p/>
287         * <p>If the underlying method is static, then the specified <code>obj</code> argument is ignored. It may be null.
288         * <p/>
289         * <p>If the number of formal parameters required by the underlying method is 0, the supplied <code>args</code>
290         * array may be of length 0 or null.
291         * <p/>
292         * <p>If the underlying method is an instance method, it is invoked using dynamic method lookup as documented in The
293         * Java Language Specification, Second Edition, section 15.12.4.4; in particular, overriding based on the runtime
294         * type of the target object will occur.
295         * <p/>
296         * <p>If the underlying method is static, the class that declared the method is initialized if it has not already
297         * been initialized.
298         * <p/>
299         * <p>If the method completes normally, the value it returns is returned to the caller of invoke; if the value has a
300         * primitive type, it is first appropriately wrapped in an object. However, if the value has the type of an array of
301         * a primitive type, the elements of the array are <i>not</i> wrapped in objects; in other words, an array of
302         * primitive type is returned.  If the underlying method return type is void, the invocation returns null.
303         *
304         * @param obj  the object the underlying method is invoked from
305         * @param args the arguments used for the method call
306         *
307         * @return the result of dispatching the method represented by this object on <code>obj</code> with parameters
308         *         <code>args</code>
309         *
310         * @throws IllegalAccessException      if this <code>Method</code> object enforces Java language access control and
311         *                                     the underlying method is inaccessible.
312         * @throws IllegalArgumentException    if the method is an instance method and the specified object argument is not
313         *                                     an instance of the class or interface declaring the underlying method (or of
314         *                                     a subclass or implementor thereof); if the number of actual and formal
315         *                                     parameters differ; if an unwrapping conversion for primitive arguments fails;
316         *                                     or if, after possible unwrapping, a parameter value cannot be converted to
317         *                                     the corresponding formal parameter type by a method invocation conversion.
318         * @throws java.lang.reflect.InvocationTargetException
319         *                                     if the underlying method throws an exception.
320         * @throws NullPointerException        if the specified object is null and the method is an instance method.
321         * @throws ExceptionInInitializerError if the initialization provoked by this method fails.
322         */
323        public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException,
324                                                                InvocationTargetException {
325            return java.invoke(obj, args);
326        }
327    
328        /**
329         * Returns <tt>true</tt> if this method is a bridge method; returns <tt>false</tt> otherwise.
330         *
331         * @return true if and only if this method is a bridge method as defined by the Java Language Specification.
332         *
333         * @since 1.5
334         */
335        public boolean isBridge() {
336            return java.isBridge();
337        }
338    
339        /**
340         * Returns <tt>true</tt> if this method was declared to take a variable number of arguments; returns <tt>false</tt>
341         * otherwise.
342         *
343         * @return <tt>true</tt> if an only if this method was declared to take a variable number of arguments.
344         *
345         * @since 1.5
346         */
347        public boolean isVarArgs() {
348            return java.isVarArgs();
349        }
350    
351        /**
352         * Returns <tt>true</tt> if this method is a synthetic method; returns <tt>false</tt> otherwise.
353         *
354         * @return true if and only if this method is a synthetic method as defined by the Java Language Specification.
355         *
356         * @since 1.5
357         */
358        public boolean isSynthetic() {
359            return java.isSynthetic();
360        }
361    
362        /**
363         * Returns the default value for the annotation member represented by this <tt>Method</tt> instance.  If the member
364         * is of a primitive type, an instance of the corresponding wrapper type is returned. Returns null if no default is
365         * associated with the member, or if the method instance does not represent a declared member of an annotation
366         * type.
367         *
368         * @return the default value for the annotation member represented by this <tt>Method</tt> instance.
369         *
370         * @throws TypeNotPresentException if the annotation is of type {@link Class} and no definition can be found for the
371         *                                 default class value.
372         * @since 1.5
373         */
374        public Object getDefaultValue() {
375            return java.getDefaultValue();
376        }
377        
378        /**
379         * Returns an array of arrays that represent the annotations on the formal parameters, in declaration order, of the
380         * method represented by this <tt>Method</tt> object. (Returns an array of length zero if the underlying method is
381         * parameterless.  If the method has one or more parameters, a nested array of length zero is returned for each
382         * parameter with no annotations.) The annotation objects contained in the returned arrays are serializable.  The
383         * caller of this method is free to modify the returned arrays; it will have no effect on the arrays returned to
384         * other callers.
385         *
386         * @return an array of arrays that represent the annotations on the formal parameters, in declaration order, of the
387         *         method represented by this Method object
388         *
389         * @since 1.5
390         */
391        public Annotation[][] getParameterAnnotations() {
392            Annotation[][] ann = java.getParameterAnnotations();
393            Annotation[][] out = new Annotation[ann.length][];
394            for(int i=0; i<ann.length; ++i) {
395                out[i] = new Annotation[ann[i].length];
396                if (_pai!=null) {
397                    for(int j=0; j<ann[i].length; ++j) {
398                        out[i][j] = (Annotation)Proxy.newProxyInstance(ann[i][j].annotationType().getClassLoader(),
399                                                                       new Class[]{ann[i][j].annotationType()},
400                                                                       new AnnotationDynamicProxyHandler(ann[i][j].annotationType(),
401                                                                                                         _pai.getEntityAnnotations().get(i)[j]));
402                    }
403                }
404            }
405            return out;
406        }
407    
408        /**
409         * Returns a map from local variable names to arrays of annotations associated with them.
410         * @return map from local variable name to arrays of annotations
411         */
412        @SuppressWarnings("unchecked")
413        public Map<String,Annotation[]> getLocalVariableAnnotations() {
414            Map<String,Annotation[]> out = new HashMap<String,Annotation[]>();
415            if (_lvai!=null) {
416                for(AMultipleNamedAnnotationsAttributeInfo.NamedAnnotationsRecord nar: _lvai.getEntityAnnotations()) {
417                    String name = nar.getName().toString();
418                    Annotation[] arr = new Annotation[nar.getAnnotations().length];
419                    for(int i=0; i<nar.getAnnotations().length; ++i) {
420                        String t = nar.getAnnotations()[i].getType().replace('/','.').replace('$', '.');
421                        // TODO: check for arrays of annotations
422                        if ((t.length()==0) || (t.charAt(0)!='L')) {
423                            throw new ClassFormatError("Unexpected type name for annotation: "+t);
424                        }
425                        t = t.substring(1,t.length()-1);
426                        try {
427                            Class c = Class.forName(t);
428                            arr[i] = (Annotation)Proxy.newProxyInstance(c.getClassLoader(),
429                                                                        new Class[]{c},
430                                                                        new AnnotationDynamicProxyHandler(c,
431                                                                                                          nar.getAnnotations()[i]));
432                        }
433                        catch(ClassNotFoundException e) {
434                            throw new ClassFormatError("Could not find class "+t);
435                        }
436                    }
437                    out.put(name,arr);
438                }
439            }
440            return out;
441        }
442    }