001    package edu.rice.cs.cunit.subAnnot;
002    
003    import edu.rice.cs.cunit.classFile.attributes.AAnnotationsAttributeInfo;
004    import edu.rice.cs.cunit.classFile.attributes.AMultipleNamedAnnotationsAttributeInfo;
005    import edu.rice.cs.cunit.classFile.attributes.RuntimeVisibleLocalVariableAnnotationsAttributeInfo;
006    import edu.rice.cs.cunit.classFile.attributes.RuntimeVisibleParameterAnnotationsAttributeInfo;
007    
008    import java.lang.annotation.Annotation;
009    import java.lang.reflect.*;
010    import java.util.ArrayList;
011    import java.util.HashMap;
012    import java.util.List;
013    import java.util.Map;
014    
015    /**
016     * Extended Constructor class to support annotations with subclassing.
017     *
018     * @author Mathias Ricken
019     */
020    public class ConstructorEx<T> extends AAnnotatedElementEx {
021        /**
022         * The java.lang.reflect.Constructor object that represents the constructor.
023         */
024        public final Constructor<T> java;
025    
026        /**
027         * Parameter annotation attribute.
028         */
029        private RuntimeVisibleParameterAnnotationsAttributeInfo _pai;
030    
031        /**
032         * Local variable annotation attribute.
033         */
034        private RuntimeVisibleLocalVariableAnnotationsAttributeInfo _lvai;
035    
036        /**
037         * Create an extended Constructor instance for the specified c.
038         * @param c constructor.
039         */
040        public ConstructorEx(Constructor<T> c) {
041            this(c, null);
042        }
043        /**
044         * Create an extended Constructor instance for the specified c.
045         * @param c constructor.
046         * @param cl class loader
047         */
048        public ConstructorEx(Constructor<T> c, ClassLoader cl) {
049            super(cl);
050            java = c;
051            findMethodAnnotationsAttributeInfo(c, "<init>", c.getParameterTypes());
052            _pai = getParameterAnnotationsAttributeInfo(c, "<init>", c.getParameterTypes());
053            _lvai = getLocalVarAnnotationsAttributeInfo(c, "<init>", c.getParameterTypes());
054        }
055    
056    
057        /**
058         * Return the annotated element.
059         * @return annotated element
060         */
061        protected AnnotatedElement getAnnotatedElement() {
062            return java;
063        }
064    
065        /**
066         * Returns the name of this constructor, as a string.  This is always the same as the simple name of the
067         * constructor's declaring class.
068         * @return name
069         */
070        public String getName() {
071            return java.getName();
072        }
073    
074        /**
075         * Returns the Java language modifiers for the constructor represented by this <code>Constructor</code> object, as
076         * an integer. The <code>Modifier</code> class should be used to decode the modifiers.
077         * @return modifiers
078         * @see java.lang.reflect.Modifier
079         */
080        public int getModifiers() {
081            return java.getModifiers();
082        }
083    
084        /**
085         * Returns an array of <tt>TypeVariable</tt> objects that represent the type variables declared by the generic
086         * declaration represented by this <tt>GenericDeclaration</tt> object, in declaration order.  Returns an array of
087         * length 0 if the underlying generic declaration declares no type variables.
088         *
089         * @return an array of <tt>TypeVariable</tt> objects that represent the type variables declared by this generic
090         *         declaration
091         *
092         * @throws java.lang.reflect.GenericSignatureFormatError
093         *          if the generic signature of this generic declaration does not conform to the format specified in the Java
094         *          Virtual Machine Specification, 3rd edition
095         */
096        public TypeVariable[] getTypeParameters() {
097            return java.getTypeParameters();
098        }
099    
100        /**
101         * Returns an array of <tt>Type</tt> objects that represent the formal parameter types, in declaration order, of the
102         * method represented by this <tt>Constructor</tt> object. Returns an array of length 0 if the underlying method takes
103         * no parameters.
104         * <p/>
105         * <p>If a formal parameter type is a parameterized type, the <tt>Type</tt> object returned for it must accurately
106         * reflect the actual type parameters used in the source code.
107         * <p/>
108         * <p>If a formal parameter type is a type variable or a parameterized type, it is created. Otherwise, it is resolved.
109         *
110         * @return an array of <tt>Type</tt>s that represent the formal parameter types of the underlying method, in
111         *         declaration order
112         *
113         * @throws java.lang.reflect.GenericSignatureFormatError
114         *                                 if the generic method signature does not conform to the format specified in the Java
115         *                                 Virtual Machine Specification, 3rd edition
116         * @throws TypeNotPresentException if any of the parameter types of the underlying method refers to a non-existent type
117         *                                 declaration
118         * @throws java.lang.reflect.MalformedParameterizedTypeException
119         *                                 if any of the underlying method's parameter types refer to a parameterized type that
120         *                                 cannot be instantiated for any reason
121         */
122        public Type[] getGenericParameterTypes() {
123            return java.getGenericParameterTypes();
124        }
125    
126        /**
127         * Returns an array of <code>Class</code> objects that represent the types of exceptions declared to be thrown by
128         * the underlying constructor represented by this <code>Constructor</code> object.  Returns an array of length 0 if
129         * the constructor declares no exceptions in its <code>throws</code> clause.
130         *
131         * @return the exception types declared as being thrown by the constructor this object represents
132         */
133        @SuppressWarnings("unchecked")
134        public ClassEx[] getExceptionTypes() {
135            List<ClassEx> list = new ArrayList<ClassEx>();
136            for(Class c: java.getExceptionTypes()) {
137                list.add(new ClassEx(c, _classLoader));
138            }
139            return list.toArray(new ClassEx[list.size()]);
140        }
141    
142        /**
143         * Returns an array of <tt>Type</tt> objects that represent the exceptions declared to be thrown by this
144         * <tt>Constructor</tt> object. Returns an array of length 0 if the underlying method declares no exceptions in its
145         * <tt>throws</tt> clause.
146         * <p/>
147         * <p>If an exception type is a parameterized type, the <tt>Type</tt> object returned for it must accurately reflect
148         * the actual type parameters used in the source code.
149         * <p/>
150         * <p>If an exception type is a type variable or a parameterized type, it is created. Otherwise, it is resolved.
151         *
152         * @return an array of Types that represent the exception types thrown by the underlying method
153         *
154         * @throws java.lang.reflect.GenericSignatureFormatError
155         *                                 if the generic method signature does not conform to the format specified in the Java
156         *                                 Virtual Machine Specification, 3rd edition
157         * @throws TypeNotPresentException if the underlying method's <tt>throws</tt> clause refers to a non-existent type
158         *                                 declaration
159         * @throws java.lang.reflect.MalformedParameterizedTypeException
160         *                                 if the underlying method's <tt>throws</tt> clause refers to a parameterized type
161         *                                 that cannot be instantiated for any reason
162         */
163        public Type[] getGenericExceptionTypes() {
164            return java.getGenericExceptionTypes();
165        }
166    
167        /**
168         * Compares this <code>Constructor</code> against the specified object. Returns true if the objects are the same.  Two
169         * <code>Constructor</code> objects are the same if they were declared by the same class and have the same formal
170         * parameter types.
171         */
172        public boolean equals(Object obj) {
173            return (obj!=null)&&(obj.getClass().equals(this.getClass()) && (java.equals(obj)));
174        }
175    
176        /**
177         * Returns a hashcode for this <code>Constructor</code>. The hashcode is the same as the hashcode for the underlying
178         * constructor's declaring class name.
179         */
180        public int hashCode() {
181            return java.hashCode();
182        }
183    
184        /**
185         * Returns a string describing this <code>Constructor</code>.  The string is formatted as the constructor access
186         * modifiers, if any, followed by the fully-qualified name of the declaring class, followed by a parenthesized,
187         * comma-separated list of the constructor's formal parameter types.  For example:
188         * <pre>
189         *    public java.util.Hashtable(int,float)
190         * </pre>
191         * <p/>
192         * <p>The only possible modifiers for constructors are the access modifiers <tt>public</tt>, <tt>protected</tt> or
193         * <tt>private</tt>.  Only one of these may appear, or none if the constructor has default (package) access.
194         */
195        public String toString() {
196            return java.toString();
197        }
198    
199        /**
200         * Returns a string describing this <code>Constructor</code>, including type parameters.  The string is formatted as
201         * the constructor access modifiers, if any, followed by an angle-bracketed comma separated list of the
202         * constructor's type parameters, if any, followed by the fully-qualified name of the declaring class, followed by a
203         * parenthesized, comma-separated list of the constructor's generic formal parameter types.  A space is used to
204         * separate access modifiers from one another and from the type parameters or return type.  If there are no type
205         * parameters, the type parameter list is elided; if the type parameter list is present, a space separates the list
206         * from the class name.  If the constructor is declared to throw exceptions, the parameter list is followed by a
207         * space, followed by the word &quot;<tt>throws</tt>&quot; followed by a comma-separated list of the thrown
208         * exception types.
209         * <p/>
210         * <p>The only possible modifiers for constructors are the access modifiers <tt>public</tt>, <tt>protected</tt> or
211         * <tt>private</tt>.  Only one of these may appear, or none if the constructor has default (package) access.
212         *
213         * @return a string describing this <code>Constructor</code>, include type parameters
214         */
215        public String toGenericString() {
216            return java.toGenericString();
217        }
218    
219        /**
220         * Uses the constructor represented by this <code>Constructor</code> object to create and initialize a new instance of
221         * the constructor's declaring class, with the specified initialization parameters. Individual parameters are
222         * automatically unwrapped to match primitive formal parameters, and both primitive and reference parameters are
223         * subject to method invocation conversions as necessary.
224         * <p/>
225         * <p>If the number of formal parameters required by the underlying constructor is 0, the supplied
226         * <code>initargs</code> array may be of length 0 or null.
227         * <p/>
228         * <p>If the required access and argument checks succeed and the instantiation will proceed, the constructor's
229         * declaring class is initialized if it has not already been initialized.
230         * <p/>
231         * <p>If the constructor completes normally, returns the newly created and initialized instance.
232         *
233         * @param initargs array of objects to be passed as arguments to the constructor call; values of primitive types are
234         *                 wrapped in a wrapper object of the appropriate type (e.g. a <tt>float</tt> in a {@link Float
235         *                 Float})
236         *
237         * @return a new object created by calling the constructor this object represents
238         *
239         * @throws IllegalAccessException      if this <code>Constructor</code> object enforces Java language access control
240         *                                     and the underlying constructor is inaccessible.
241         * @throws IllegalArgumentException    if the number of actual and formal parameters differ; if an unwrapping
242         *                                     conversion for primitive arguments fails; or if, after possible unwrapping, a
243         *                                     parameter value cannot be converted to the corresponding formal parameter type
244         *                                     by a method invocation conversion; if this constructor pertains to an enum
245         *                                     type.
246         * @throws InstantiationException      if the class that declares the underlying constructor represents an abstract
247         *                                     class.
248         * @throws java.lang.reflect.InvocationTargetException
249         *                                     if the underlying constructor throws an exception.
250         * @throws ExceptionInInitializerError if the initialization provoked by this method fails.
251         */
252        public T newInstance(Object[] initargs) throws InstantiationException, IllegalAccessException,
253                                                       IllegalArgumentException, InvocationTargetException {
254            return java.newInstance(initargs);
255        }
256    
257        /**
258         * Returns <tt>true</tt> if this constructor was declared to take a variable number of arguments; returns
259         * <tt>false</tt> otherwise.
260         *
261         * @return <tt>true</tt> if an only if this constructor was declared to take a variable number of arguments.
262         */
263        public boolean isVarArgs() {
264            return java.isVarArgs();
265        }
266    
267        /**
268         * Returns <tt>true</tt> if this constructor is a synthetic constructor; returns <tt>false</tt> otherwise.
269         *
270         * @return true if and only if this constructor is a synthetic constructor as defined by the Java Language
271         *         Specification.
272         */
273        public boolean isSynthetic() {
274            return java.isSynthetic();
275        }
276    
277    
278        /**
279         * Returns an array of <code>Class</code> objects that represent the formal parameter types, in declaration order,
280         * of the constructor represented by this <code>Constructor</code> object. Returns an array of length 0 if the
281         * underlying constructor takes no parameters.
282         *
283         * @return the parameter types for the constructor this object represents
284         */
285        @SuppressWarnings("unchecked")
286        public ClassEx[] getParameterTypes() {
287            List<ClassEx> list = new ArrayList<ClassEx>();
288            for(Class c: java.getParameterTypes()) {
289                list.add(new ClassEx(c, _classLoader));
290            }
291            return list.toArray(new ClassEx[list.size()]);
292    
293        }
294    
295        /**
296         * Returns an array of arrays that represent the annotations on the formal parameters, in declaration order, of the
297         * method represented by this <tt>Method</tt> object. (Returns an array of length zero if the underlying method is
298         * parameterless.  If the method has one or more parameters, a nested array of length zero is returned for each
299         * parameter with no annotations.) The annotation objects contained in the returned arrays are serializable.  The
300         * caller of this method is free to modify the returned arrays; it will have no effect on the arrays returned to other
301         * callers.
302         *
303         * @return an array of arrays that represent the annotations on the formal parameters, in declaration order, of the
304         *         method represented by this Method object
305         */
306        public Annotation[][] getParameterAnnotations() {
307            Annotation[][] ann = java.getParameterAnnotations();
308            Annotation[][] out = new Annotation[ann.length][];
309            for(int i=0; i<ann.length; ++i) {
310                out[i] = new Annotation[ann[i].length];
311                if (_pai!=null) {
312                    for(int j=0; j<ann[i].length; ++j) {
313                        out[i][j] = (Annotation)Proxy.newProxyInstance(ann[i][j].annotationType().getClassLoader(),
314                                                                       new Class[]{ann[i][j].annotationType()},
315                                                                       new AnnotationDynamicProxyHandler(ann[i][j].annotationType(),
316                                                                                                         _pai.getEntityAnnotations().get(i)[j]));
317                    }
318                }
319            }
320            return out;
321        }
322    
323        /**
324         * Returns a map from local variable names to arrays of annotations associated with them.
325         * @return map from local variable name to arrays of annotations
326         */
327        @SuppressWarnings("unchecked")
328        public Map<String,Annotation[]> getLocalVariableAnnotations() {
329            Map<String,Annotation[]> out = new HashMap<String,Annotation[]>();
330            if (_lvai!=null) {
331                for(AMultipleNamedAnnotationsAttributeInfo.NamedAnnotationsRecord nar: _lvai.getEntityAnnotations()) {
332                    String name = nar.getName().toString();
333                    Annotation[] arr = new Annotation[nar.getAnnotations().length];
334                    for(int i=0; i<nar.getAnnotations().length; ++i) {
335                        String t = nar.getAnnotations()[i].getType().replace('/','.').replace('$', '.');
336                        // TODO: check for arrays of annotations
337                        if ((t.length()==0) || (t.charAt(0)!='L')) {
338                            throw new ClassFormatError("Unexpected type name for annotation: "+t);
339                        }
340                        t = t.substring(1,t.length()-1);
341                        try {
342                            Class c = Class.forName(t);
343                            arr[i] = (Annotation)Proxy.newProxyInstance(c.getClassLoader(),
344                                                                        new Class[]{c},
345                                                                        new AnnotationDynamicProxyHandler(c,
346                                                                                                          nar.getAnnotations()[i]));
347                        }
348                        catch(ClassNotFoundException e) {
349                            throw new ClassFormatError("Could not find class "+t);
350                        }
351                    }
352                    out.put(name,arr);
353                }
354            }
355            return out;
356        }
357    }