View Javadoc
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  package org.apache.juneau.commons.reflect;
18  
19  import static org.apache.juneau.commons.utils.AssertionUtils.*;
20  import static org.apache.juneau.commons.utils.ThrowableUtils.*;
21  import static org.apache.juneau.commons.utils.Utils.*;
22  
23  import java.lang.reflect.*;
24  
25  import org.apache.juneau.commons.utils.*;
26  
27  /**
28   * Lightweight utility class for introspecting information about a Java constructor.
29   *
30   * <p>
31   * This class provides a convenient wrapper around {@link Constructor} that extends the standard Java reflection
32   * API with additional functionality for constructor introspection, annotation handling, and instance creation.
33   * It extends {@link ExecutableInfo} to provide common functionality shared with methods.
34   *
35   * <h5 class='section'>Features:</h5>
36   * <ul class='spaced-list'>
37   * 	<li>Constructor introspection - access constructor metadata, parameters, exceptions
38   * 	<li>Annotation support - get annotations declared on the constructor
39   * 	<li>Instance creation - create new instances with type safety
40   * 	<li>Accessibility control - make private constructors accessible
41   * 	<li>Thread-safe - instances are immutable and safe for concurrent access
42   * </ul>
43   *
44   * <h5 class='section'>Use Cases:</h5>
45   * <ul class='spaced-list'>
46   * 	<li>Introspecting constructor metadata for code generation or analysis
47   * 	<li>Creating instances of classes dynamically
48   * 	<li>Finding annotations on constructors
49   * 	<li>Working with constructor parameters and exceptions
50   * 	<li>Building frameworks that need to instantiate objects
51   * </ul>
52   *
53   * <h5 class='section'>Usage:</h5>
54   * <p class='bjava'>
55   * 	<jc>// Get ConstructorInfo from a class</jc>
56   * 	ClassInfo <jv>ci</jv> = ClassInfo.<jsm>of</jsm>(MyClass.<jk>class</jk>);
57   * 	ConstructorInfo <jv>ctor</jv> = <jv>ci</jv>.getConstructor(String.<jk>class</jk>);
58   *
59   * 	<jc>// Get annotations</jc>
60   * 	List&lt;AnnotationInfo&lt;MyAnnotation&gt;&gt; <jv>annotations</jv> =
61   * 		<jv>ctor</jv>.getAnnotations(MyAnnotation.<jk>class</jk>).toList();
62   *
63   * 	<jc>// Create instance</jc>
64   * 	<jv>ctor</jv>.accessible();  <jc>// Make accessible if private</jc>
65   * 	MyClass <jv>obj</jv> = <jv>ctor</jv>.invoke(<js>"arg"</js>);
66   * </p>
67   *
68   * <h5 class='section'>See Also:</h5><ul>
69   * 	<li class='jc'>{@link ClassInfo} - Class introspection
70   * 	<li class='jc'>{@link MethodInfo} - Method introspection
71   * 	<li class='jc'>{@link FieldInfo} - Field introspection
72   * 	<li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauCommonsReflection">Reflection Package</a>
73   * </ul>
74   */
75  public class ConstructorInfo extends ExecutableInfo implements Comparable<ConstructorInfo>, Annotatable {
76  
77  	/**
78  	 * Creates a ConstructorInfo wrapper for the specified constructor.
79  	 *
80  	 * <h5 class='section'>Example:</h5>
81  	 * <p class='bjava'>
82  	 * 	ClassInfo <jv>ci</jv> = ClassInfo.<jsm>of</jsm>(MyClass.<jk>class</jk>);
83  	 * 	Constructor&lt;?&gt; <jv>c</jv> = MyClass.<jk>class</jk>.getConstructor(String.<jk>class</jk>);
84  	 * 	ConstructorInfo <jv>ci2</jv> = ConstructorInfo.<jsm>of</jsm>(<jv>ci</jv>, <jv>c</jv>);
85  	 * </p>
86  	 *
87  	 * @param declaringClass The ClassInfo for the class that declares this constructor. Must not be <jk>null</jk>.
88  	 * @param inner The constructor being wrapped. Must not be <jk>null</jk>.
89  	 * @return A new ConstructorInfo object wrapping the constructor.
90  	 */
91  	public static ConstructorInfo of(ClassInfo declaringClass, Constructor<?> inner) {
92  		assertArgNotNull("declaringClass", declaringClass);
93  		return declaringClass.getConstructor(inner);
94  	}
95  
96  	/**
97  	 * Creates a ConstructorInfo wrapper for the specified constructor.
98  	 *
99  	 * <p>
100 	 * This convenience method automatically determines the declaring class from the constructor.
101 	 *
102 	 * <h5 class='section'>Example:</h5>
103 	 * <p class='bjava'>
104 	 * 	Constructor&lt;?&gt; <jv>c</jv> = MyClass.<jk>class</jk>.getConstructor(String.<jk>class</jk>);
105 	 * 	ConstructorInfo <jv>ci</jv> = ConstructorInfo.<jsm>of</jsm>(<jv>c</jv>);
106 	 * </p>
107 	 *
108 	 * @param inner The constructor being wrapped. Must not be <jk>null</jk>.
109 	 * @return A new ConstructorInfo object wrapping the constructor.
110 	 */
111 	public static ConstructorInfo of(Constructor<?> inner) {
112 		assertArgNotNull("inner", inner);
113 		return ClassInfo.of(inner.getDeclaringClass()).getConstructor(inner);
114 	}
115 
116 	private final Constructor<?> inner;
117 
118 	/**
119 	 * Constructor.
120 	 *
121 	 * <p>
122 	 * Creates a new ConstructorInfo wrapper for the specified constructor. This constructor is protected
123 	 * and should not be called directly. Use the static factory methods {@link #of(Constructor)} or
124 	 * obtain ConstructorInfo instances from {@link ClassInfo#getConstructor(Constructor)}.
125 	 *
126 	 * @param declaringClass The ClassInfo for the class that declares this constructor.
127 	 * @param inner The constructor being wrapped.
128 	 */
129 	protected ConstructorInfo(ClassInfo declaringClass, Constructor<?> inner) {
130 		super(declaringClass, inner);
131 		this.inner = inner;
132 	}
133 
134 	@Override /* Overridden from ExecutableInfo */
135 	public ConstructorInfo accessible() {
136 		super.accessible();
137 		return this;
138 	}
139 
140 	@Override
141 	public int compareTo(ConstructorInfo o) {
142 		int i = cmp(getSimpleName(), o.getSimpleName());
143 		if (i == 0) {
144 			i = getParameterCount() - o.getParameterCount();
145 			if (i == 0) {
146 				var params = getParameters();
147 				var oParams = o.getParameters();
148 				for (var j = 0; j < params.size() && i == 0; j++) {
149 					i = cmp(params.get(j).getParameterType().getName(), oParams.get(j).getParameterType().getName());
150 				}
151 			}
152 		}
153 		return i;
154 	}
155 
156 	@Override /* Annotatable */
157 	public AnnotatableType getAnnotatableType() { return AnnotatableType.CONSTRUCTOR_TYPE; }
158 
159 	@Override /* Annotatable */
160 	public String getLabel() { return getDeclaringClass().getNameSimple() + "." + getShortName(); }
161 
162 	/**
163 	 * Returns the wrapped constructor.
164 	 *
165 	 * <h5 class='section'>Example:</h5>
166 	 * <p class='bjava'>
167 	 * 	ConstructorInfo <jv>ci</jv> = ...;
168 	 * 	Constructor&lt;MyClass&gt; <jv>ctor</jv> = <jv>ci</jv>.inner();
169 	 * </p>
170 	 *
171 	 * @param <T> The class type of the constructor.
172 	 * @return The wrapped constructor.
173 	 */
174 	@SuppressWarnings("unchecked")
175 	public <T> Constructor<T> inner() {
176 		return (Constructor<T>)inner;
177 	}
178 
179 	/**
180 	 * Compares this ConstructorInfo with the specified object for equality.
181 	 *
182 	 * <p>
183 	 * Two ConstructorInfo objects are considered equal if they wrap the same underlying {@link Constructor} object.
184 	 * This delegates to the underlying {@link Constructor#equals(Object)} method.
185 	 *
186 	 * <p>
187  * This method makes ConstructorInfo suitable for use as keys in hash-based collections such as {@link java.util.HashMap}
188  * and {@link java.util.HashSet}.
189 	 *
190 	 * @param obj The object to compare with.
191 	 * @return <jk>true</jk> if the objects are equal, <jk>false</jk> otherwise.
192 	 */
193 	@Override
194 	public boolean equals(Object obj) {
195 		return obj instanceof ConstructorInfo other && eq(this, other, (x, y) -> eq(x.inner, y.inner));
196 	}
197 
198 	/**
199 	 * Returns a hash code value for this ConstructorInfo.
200 	 *
201 	 * <p>
202 	 * This delegates to the underlying {@link Constructor#hashCode()} method.
203 	 *
204 	 * <p>
205  * This method makes ConstructorInfo suitable for use as keys in hash-based collections such as {@link java.util.HashMap}
206  * and {@link java.util.HashSet}.
207 	 *
208 	 * @return A hash code value for this ConstructorInfo.
209 	 */
210 	@Override
211 	public int hashCode() {
212 		return inner.hashCode();
213 	}
214 
215 	/**
216 	 * Shortcut for calling the new-instance method on the underlying constructor.
217 	 *
218 	 * @param <T> The constructor class type.
219 	 * @param args the arguments used for the method call.
220 	 * @return The object returned from the constructor.
221 	 * @throws ExecutableException Exception occurred on invoked constructor/method/field.
222 	 */
223 	@SuppressWarnings("unchecked")
224 	public <T> T newInstance(Object...args) throws ExecutableException {
225 		return safe(() -> {
226 			try {
227 				return (T)inner.newInstance(args);
228 			} catch (InvocationTargetException e) {
229 				throw exex(e.getTargetException());
230 			}
231 		}, e -> exex(e));  // HTT
232 	}
233 
234 	/**
235 	 * Shortcut for calling the new-instance method on the underlying constructor using lenient argument matching.
236 	 *
237 	 * <p>
238 	 * Lenient matching allows arguments to be matched to parameters based on parameter types.
239 	 * <br>Arguments can be in any order.
240 	 * <br>Extra arguments are ignored.
241 	 * <br>Missing arguments are set to <jk>null</jk>.
242 	 *
243 	 * @param <T> The constructor class type.
244 	 * @param args The arguments used for the constructor call.
245 	 * @return The object returned from the constructor.
246 	 * @throws ExecutableException Exception occurred on invoked constructor/method/field.
247 	 */
248 	public <T> T newInstanceLenient(Object...args) throws ExecutableException {
249 		return newInstance(ClassUtils.getMatchingArgs(inner.getParameterTypes(), args));
250 	}
251 	//-----------------------------------------------------------------------------------------------------------------
252 	// Annotatable interface methods
253 	//-----------------------------------------------------------------------------------------------------------------
254 }