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<AnnotationInfo<MyAnnotation>> <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<?> <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<?> <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<MyClass> <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 }