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