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