001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.commons.reflect; 018 019import static org.apache.juneau.commons.utils.AssertionUtils.*; 020import static org.apache.juneau.commons.utils.ThrowableUtils.*; 021import static org.apache.juneau.commons.utils.Utils.*; 022 023import java.lang.reflect.*; 024 025import org.apache.juneau.commons.utils.*; 026 027/** 028 * Lightweight utility class for introspecting information about a Java constructor. 029 * 030 * <p> 031 * This class provides a convenient wrapper around {@link Constructor} that extends the standard Java reflection 032 * API with additional functionality for constructor introspection, annotation handling, and instance creation. 033 * It extends {@link ExecutableInfo} to provide common functionality shared with methods. 034 * 035 * <h5 class='section'>Features:</h5> 036 * <ul class='spaced-list'> 037 * <li>Constructor introspection - access constructor metadata, parameters, exceptions 038 * <li>Annotation support - get annotations declared on the constructor 039 * <li>Instance creation - create new instances with type safety 040 * <li>Accessibility control - make private constructors accessible 041 * <li>Thread-safe - instances are immutable and safe for concurrent access 042 * </ul> 043 * 044 * <h5 class='section'>Use Cases:</h5> 045 * <ul class='spaced-list'> 046 * <li>Introspecting constructor metadata for code generation or analysis 047 * <li>Creating instances of classes dynamically 048 * <li>Finding annotations on constructors 049 * <li>Working with constructor parameters and exceptions 050 * <li>Building frameworks that need to instantiate objects 051 * </ul> 052 * 053 * <h5 class='section'>Usage:</h5> 054 * <p class='bjava'> 055 * <jc>// Get ConstructorInfo from a class</jc> 056 * ClassInfo <jv>ci</jv> = ClassInfo.<jsm>of</jsm>(MyClass.<jk>class</jk>); 057 * ConstructorInfo <jv>ctor</jv> = <jv>ci</jv>.getConstructor(String.<jk>class</jk>); 058 * 059 * <jc>// Get annotations</jc> 060 * List<AnnotationInfo<MyAnnotation>> <jv>annotations</jv> = 061 * <jv>ctor</jv>.getAnnotations(MyAnnotation.<jk>class</jk>).toList(); 062 * 063 * <jc>// Create instance</jc> 064 * <jv>ctor</jv>.accessible(); <jc>// Make accessible if private</jc> 065 * MyClass <jv>obj</jv> = <jv>ctor</jv>.invoke(<js>"arg"</js>); 066 * </p> 067 * 068 * <h5 class='section'>See Also:</h5><ul> 069 * <li class='jc'>{@link ClassInfo} - Class introspection 070 * <li class='jc'>{@link MethodInfo} - Method introspection 071 * <li class='jc'>{@link FieldInfo} - Field introspection 072 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauCommonsReflection">Reflection Package</a> 073 * </ul> 074 */ 075public class ConstructorInfo extends ExecutableInfo implements Comparable<ConstructorInfo>, Annotatable { 076 077 /** 078 * Creates a ConstructorInfo wrapper for the specified constructor. 079 * 080 * <h5 class='section'>Example:</h5> 081 * <p class='bjava'> 082 * ClassInfo <jv>ci</jv> = ClassInfo.<jsm>of</jsm>(MyClass.<jk>class</jk>); 083 * Constructor<?> <jv>c</jv> = MyClass.<jk>class</jk>.getConstructor(String.<jk>class</jk>); 084 * ConstructorInfo <jv>ci2</jv> = ConstructorInfo.<jsm>of</jsm>(<jv>ci</jv>, <jv>c</jv>); 085 * </p> 086 * 087 * @param declaringClass The ClassInfo for the class that declares this constructor. Must not be <jk>null</jk>. 088 * @param inner The constructor being wrapped. Must not be <jk>null</jk>. 089 * @return A new ConstructorInfo object wrapping the constructor. 090 */ 091 public static ConstructorInfo of(ClassInfo declaringClass, Constructor<?> inner) { 092 assertArgNotNull("declaringClass", declaringClass); 093 return declaringClass.getConstructor(inner); 094 } 095 096 /** 097 * Creates a ConstructorInfo wrapper for the specified constructor. 098 * 099 * <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}