Constructor.java revision 98a7a76fe5c0dd5ff949b38da809368681169205
1/*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements.  See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17/*
18 * Copyright (C) 2008 The Android Open Source Project
19 *
20 * Licensed under the Apache License, Version 2.0 (the "License");
21 * you may not use this file except in compliance with the License.
22 * You may obtain a copy of the License at
23 *
24 *      http://www.apache.org/licenses/LICENSE-2.0
25 *
26 * Unless required by applicable law or agreed to in writing, software
27 * distributed under the License is distributed on an "AS IS" BASIS,
28 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
29 * See the License for the specific language governing permissions and
30 * limitations under the License.
31 */
32
33package java.lang.reflect;
34
35import dalvik.system.VMStack;
36
37import java.lang.annotation.Annotation;
38
39import org.apache.harmony.kernel.vm.StringUtils;
40import org.apache.harmony.luni.lang.reflect.GenericSignatureParser;
41import org.apache.harmony.luni.lang.reflect.ListOfTypes;
42import org.apache.harmony.luni.lang.reflect.Types;
43
44/**
45 * This class represents a constructor. Information about the constructor can be
46 * accessed, and the constructor can be invoked dynamically.
47 *
48 * @param <T> the class that declares this constructor
49 */
50public final class Constructor<T> extends AccessibleObject implements GenericDeclaration,
51        Member {
52
53    Class<T> declaringClass;
54
55    Class<?>[] parameterTypes;
56
57    Class<?>[] exceptionTypes;
58
59    ListOfTypes genericExceptionTypes;
60    ListOfTypes genericParameterTypes;
61    TypeVariable<Constructor<T>>[] formalTypeParameters;
62    private volatile boolean genericTypesAreInitialized = false;
63
64    private synchronized void initGenericTypes() {
65        if (!genericTypesAreInitialized) {
66            String signatureAttribute = getSignatureAttribute();
67            GenericSignatureParser parser = new GenericSignatureParser(
68                    VMStack.getCallingClassLoader2());
69            parser.parseForConstructor(this, signatureAttribute, exceptionTypes);
70            formalTypeParameters = parser.formalTypeParameters;
71            genericParameterTypes = parser.parameterTypes;
72            genericExceptionTypes = parser.exceptionTypes;
73            genericTypesAreInitialized = true;
74        }
75    }
76
77    int slot;
78
79    /**
80     * Prevent this class from being instantiated.
81     */
82    private Constructor(){
83        //do nothing
84    }
85
86    /**
87     * Creates an instance of the class. Only called from native code, thus
88     * private.
89     *
90     * @param declaringClass
91     *            the class this constructor object belongs to
92     * @param ptypes
93     *            the parameter types of the constructor
94     * @param extypes
95     *            the exception types of the constructor
96     * @param slot
97     *            the slot of the constructor inside the VM class structure
98     */
99    private Constructor (Class<T> declaringClass, Class<?>[] ptypes, Class<?>[] extypes, int slot){
100        this.declaringClass = declaringClass;
101        this.parameterTypes = ptypes;
102        this.exceptionTypes = extypes;          // may be null
103        this.slot = slot;
104    }
105
106    @Override /*package*/ String getSignatureAttribute() {
107        Object[] annotation = getSignatureAnnotation(declaringClass, slot);
108
109        if (annotation == null) {
110            return null;
111        }
112
113        return StringUtils.combineStrings(annotation);
114    }
115
116    /**
117     * Get the Signature annotation for this constructor.  Returns null if not
118     * found.
119     */
120    native private Object[] getSignatureAnnotation(Class declaringClass,
121            int slot);
122
123    public TypeVariable<Constructor<T>>[] getTypeParameters() {
124        initGenericTypes();
125        return formalTypeParameters.clone();
126    }
127
128    /**
129     * Returns the string representation of the constructor's declaration,
130     * including the type parameters.
131     *
132     * @return the string representation of the constructor's declaration
133     */
134    public String toGenericString() {
135        StringBuilder sb = new StringBuilder(80);
136        initGenericTypes();
137        // append modifiers if any
138        int modifier = getModifiers();
139        if (modifier != 0) {
140            sb.append(Modifier.toString(modifier & ~Modifier.VARARGS)).append(' ');
141        }
142        // append type parameters
143        if (formalTypeParameters != null && formalTypeParameters.length > 0) {
144            sb.append('<');
145            for (int i = 0; i < formalTypeParameters.length; i++) {
146                appendGenericType(sb, formalTypeParameters[i]);
147                if (i < formalTypeParameters.length - 1) {
148                    sb.append(", ");
149                }
150            }
151            sb.append("> ");
152        }
153        // append constructor name
154        appendArrayType(sb, getDeclaringClass());
155        // append parameters
156        sb.append('(');
157        appendArrayGenericType(sb,
158                Types.getClonedTypeArray(genericParameterTypes));
159        sb.append(')');
160        // append exceptions if any
161        Type[] genericExceptionTypeArray =
162                Types.getClonedTypeArray(genericExceptionTypes);
163        if (genericExceptionTypeArray.length > 0) {
164            sb.append(" throws ");
165            appendArrayGenericType(sb, genericExceptionTypeArray);
166        }
167        return sb.toString();
168    }
169
170    /**
171     * Returns the generic parameter types as an array of {@code Type}
172     * instances, in declaration order. If this constructor has no generic
173     * parameters, an empty array is returned.
174     *
175     * @return the parameter types
176     *
177     * @throws GenericSignatureFormatError
178     *             if the generic constructor signature is invalid
179     * @throws TypeNotPresentException
180     *             if any parameter type points to a missing type
181     * @throws MalformedParameterizedTypeException
182     *             if any parameter type points to a type that cannot be
183     *             instantiated for some reason
184     */
185    public Type[] getGenericParameterTypes() {
186        initGenericTypes();
187        return Types.getClonedTypeArray(genericParameterTypes);
188    }
189
190    /**
191     * Returns the exception types as an array of {@code Type} instances. If
192     * this constructor has no declared exceptions, an empty array will be
193     * returned.
194     *
195     * @return an array of generic exception types
196     *
197     * @throws GenericSignatureFormatError
198     *             if the generic constructor signature is invalid
199     * @throws TypeNotPresentException
200     *             if any exception type points to a missing type
201     * @throws MalformedParameterizedTypeException
202     *             if any exception type points to a type that cannot be
203     *             instantiated for some reason
204     */
205    public Type[] getGenericExceptionTypes() {
206        initGenericTypes();
207        return Types.getClonedTypeArray(genericExceptionTypes);
208    }
209
210    @Override
211    public Annotation[] getDeclaredAnnotations() {
212        return getDeclaredAnnotations(declaringClass, slot);
213    }
214    native private Annotation[] getDeclaredAnnotations(Class declaringClass,
215        int slot);
216
217    /**
218     * Returns an array of arrays that represent the annotations of the formal
219     * parameters of this constructor. If there are no parameters on this
220     * constructor, then an empty array is returned. If there are no annotations
221     * set, then an array of empty arrays is returned.
222     *
223     * @return an array of arrays of {@code Annotation} instances
224     */
225    public Annotation[][] getParameterAnnotations() {
226        Annotation[][] parameterAnnotations
227                = getParameterAnnotations(declaringClass, slot);
228        if (parameterAnnotations.length == 0) {
229            return Method.noAnnotations(parameterTypes.length);
230        }
231        return parameterAnnotations;
232    }
233    native private Annotation[][] getParameterAnnotations(Class declaringClass,
234        int slot);
235
236    /**
237     * Indicates whether or not this constructor takes a variable number of
238     * arguments.
239     *
240     * @return {@code true} if a vararg is declare, otherwise
241     *         {@code false}
242     */
243    public boolean isVarArgs() {
244        int mods = getConstructorModifiers(declaringClass, slot);
245        return (mods & Modifier.VARARGS) != 0;
246    }
247
248    /**
249     * Indicates whether or not this constructor is synthetic (artificially
250     * introduced by the compiler).
251     *
252     * @return {@code true} if this constructor is synthetic, {@code false}
253     *         otherwise
254     */
255    public boolean isSynthetic() {
256        int mods = getConstructorModifiers(declaringClass, slot);
257        return (mods & Modifier.SYNTHETIC) != 0;
258    }
259
260    /**
261     * Indicates whether or not the specified {@code object} is equal to this
262     * constructor. To be equal, the specified object must be an instance
263     * of {@code Constructor} with the same declaring class and parameter types
264     * as this constructor.
265     *
266     * @param object
267     *            the object to compare
268     *
269     * @return {@code true} if the specified object is equal to this
270     *         constructor, {@code false} otherwise
271     *
272     * @see #hashCode
273     */
274    @Override
275    public boolean equals(Object object) {
276        return object instanceof Constructor && toString().equals(object.toString());
277    }
278
279    /**
280     * Returns the class that declares this constructor.
281     *
282     * @return the declaring class
283     */
284    public Class<T> getDeclaringClass() {
285        return declaringClass;
286    }
287
288    /**
289     * Returns the exception types as an array of {@code Class} instances. If
290     * this constructor has no declared exceptions, an empty array will be
291     * returned.
292     *
293     * @return the declared exception classes
294     */
295    public Class<?>[] getExceptionTypes() {
296        if (exceptionTypes == null)
297            return new Class[0];
298        return exceptionTypes.clone();
299    }
300
301    /**
302     * Returns the modifiers for this constructor. The {@link Modifier} class
303     * should be used to decode the result.
304     *
305     * @return the modifiers for this constructor
306     *
307     * @see Modifier
308     */
309    public int getModifiers() {
310        return getConstructorModifiers(declaringClass, slot);
311    }
312
313    private native int getConstructorModifiers(Class<T> declaringClass, int slot);
314
315    /**
316     * Returns the name of this constructor.
317     *
318     * @return the name of this constructor
319     */
320    public String getName() {
321        return declaringClass.getName();
322    }
323
324    /**
325     * Returns an array of the {@code Class} objects associated with the
326     * parameter types of this constructor. If the constructor was declared with
327     * no parameters, an empty array will be returned.
328     *
329     * @return the parameter types
330     */
331    public Class<?>[] getParameterTypes() {
332        return parameterTypes.clone();
333    }
334
335    /**
336     * Returns the constructor's signature in non-printable form. This is called
337     * (only) from IO native code and needed for deriving the serialVersionUID
338     * of the class
339     *
340     * @return the constructor's signature
341     */
342    @SuppressWarnings("unused")
343    private String getSignature() {
344        StringBuilder result = new StringBuilder();
345
346        result.append('(');
347        for(int i = 0; i < parameterTypes.length; i++) {
348            result.append(getSignature(parameterTypes[i]));
349        }
350        result.append(")V");
351
352        return result.toString();
353    }
354
355    /**
356     * Returns an integer hash code for this constructor. Constructors which are
357     * equal return the same value for this method. The hash code for a
358     * Constructor is the hash code of the name of the declaring class.
359     *
360     * @return the hash code
361     *
362     * @see #equals
363     */
364    @Override
365    public int hashCode() {
366        return declaringClass.getName().hashCode();
367    }
368
369    /**
370     * Returns a new instance of the declaring class, initialized by dynamically
371     * invoking the constructor represented by this {@code Constructor} object.
372     * This reproduces the effect of {@code new declaringClass(arg1, arg2, ... ,
373     * argN)} This method performs the following:
374     * <ul>
375     * <li>A new instance of the declaring class is created. If the declaring
376     * class cannot be instantiated (i.e. abstract class, an interface, an array
377     * type, or a primitive type) then an InstantiationException is thrown.</li>
378     * <li>If this Constructor object is enforcing access control (see
379     * {@link AccessibleObject}) and this constructor is not accessible from the
380     * current context, an IllegalAccessException is thrown.</li>
381     * <li>If the number of arguments passed and the number of parameters do not
382     * match, an IllegalArgumentException is thrown.</li>
383     * <li>For each argument passed:
384     * <ul>
385     * <li>If the corresponding parameter type is a primitive type, the argument
386     * is unwrapped. If the unwrapping fails, an IllegalArgumentException is
387     * thrown.</li>
388     * <li>If the resulting argument cannot be converted to the parameter type
389     * via a widening conversion, an IllegalArgumentException is thrown.</li>
390     * </ul>
391     * <li>The constructor represented by this {@code Constructor} object is
392     * then invoked. If an exception is thrown during the invocation, it is
393     * caught and wrapped in an InvocationTargetException. This exception is
394     * then thrown. If the invocation completes normally, the newly initialized
395     * object is returned.
396     * </ul>
397     *
398     * @param args
399     *            the arguments to the constructor
400     *
401     * @return the new, initialized, object
402     *
403     * @exception InstantiationException
404     *                if the class cannot be instantiated
405     * @exception IllegalAccessException
406     *                if this constructor is not accessible
407     * @exception IllegalArgumentException
408     *                if an incorrect number of arguments are passed, or an
409     *                argument could not be converted by a widening conversion
410     * @exception InvocationTargetException
411     *                if an exception was thrown by the invoked constructor
412     *
413     * @see AccessibleObject
414     */
415    public T newInstance(Object... args) throws InstantiationException, IllegalAccessException,
416            IllegalArgumentException, InvocationTargetException {
417        return constructNative (args, declaringClass, parameterTypes, slot, flag);
418    }
419
420    private native T constructNative(Object[] args, Class<T> declaringClass,
421            Class<?>[] parameterTypes, int slot,
422            boolean noAccessCheck) throws InstantiationException, IllegalAccessException,
423            InvocationTargetException;
424
425    /**
426     * Returns a string containing a concise, human-readable description of this
427     * constructor. The format of the string is:
428     *
429     * <ol>
430     *   <li>modifiers (if any)
431     *   <li>declaring class name
432     *   <li>'('
433     *   <li>parameter types, separated by ',' (if any)
434     *   <li>')'
435     *   <li>'throws' plus exception types, separated by ',' (if any)
436     * </ol>
437     *
438     * For example:
439     * {@code public String(byte[],String) throws UnsupportedEncodingException}
440     *
441     * @return a printable representation for this constructor
442     */
443    @Override
444    public String toString() {
445        StringBuilder result = new StringBuilder(Modifier.toString(getModifiers()));
446
447        if (result.length() != 0)
448            result.append(' ');
449        result.append(declaringClass.getName());
450        result.append("(");
451        result.append(toString(parameterTypes));
452        result.append(")");
453        if (exceptionTypes != null && exceptionTypes.length != 0) {
454            result.append(" throws ");
455            result.append(toString(exceptionTypes));
456        }
457
458        return result.toString();
459    }
460}
461