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.CollectionUtils.*;
021import static org.apache.juneau.commons.utils.Utils.*;
022
023import java.lang.annotation.*;
024import java.lang.reflect.*;
025import java.util.*;
026import java.util.function.*;
027import java.util.stream.*;
028
029import org.apache.juneau.commons.function.*;
030import org.apache.juneau.commons.utils.*;
031
032/**
033 * Lightweight utility class for introspecting information about a method or constructor parameter.
034 *
035 * <p>
036 * This class provides a convenient wrapper around {@link Parameter} that extends the standard Java reflection
037 * API with additional functionality for parameter introspection, annotation handling, and name resolution.
038 * It supports resolving parameter names from bytecode (when compiled with <c>-parameters</c>) or from
039 * {@link org.apache.juneau.annotation.Name @Name} annotations.
040 *
041 * <h5 class='section'>Features:</h5>
042 * <ul class='spaced-list'>
043 *    <li>Parameter introspection - access parameter metadata, type, annotations
044 *    <li>Name resolution - resolve parameter names from bytecode or annotations
045 *    <li>Qualifier support - extract qualifier names from annotations
046 *    <li>Hierarchy traversal - find matching parameters in parent methods/constructors
047 *    <li>Annotation support - get annotations declared on the parameter
048 * </ul>
049 *
050 * <h5 class='section'>Use Cases:</h5>
051 * <ul class='spaced-list'>
052 *    <li>Introspecting parameter metadata for code generation or analysis
053 *    <li>Resolving parameter names for dependency injection frameworks
054 *    <li>Finding annotations on parameters
055 *    <li>Working with parameter types and qualifiers
056 *    <li>Building frameworks that need to analyze method/constructor signatures
057 * </ul>
058 *
059 * <h5 class='section'>Usage:</h5>
060 * <p class='bjava'>
061 *    <jc>// Get ParameterInfo from a method</jc>
062 *    MethodInfo <jv>mi</jv> = ...;
063 *    ParameterInfo <jv>param</jv> = <jv>mi</jv>.getParameters().get(0);
064 *
065    *    <jc>// Get parameter type</jc>
066    *    ClassInfo <jv>type</jv> = <jv>param</jv>.getParameterType();
067    *
068    *    <jc>// Get resolved name (from bytecode or @Name annotation)</jc>
069    *    String <jv>name</jv> = <jv>param</jv>.getResolvedName();
070    *
071    *    <jc>// Get annotations</jc>
072    *    List&lt;AnnotationInfo&lt;MyAnnotation&gt;&gt; <jv>annotations</jv> =
073    *       <jv>param</jv>.getAnnotations(MyAnnotation.<jk>class</jk>).toList();
074    * </p>
075    *
076    * <h5 class='section'>Parameter Name Resolution:</h5>
077    * <p>
078    * Parameter names are resolved in the following order:
079    * <ol class='spaced-list'>
080    *    <li>{@link org.apache.juneau.annotation.Name @Name} annotation value (if present)
081    *    <li>Bytecode parameter names (if compiled with <c>-parameters</c> flag)
082    *    <li><c>arg0</c>, <c>arg1</c>, etc. (fallback if names unavailable)
083    * </ol>
084    *
085    * <h5 class='section'>See Also:</h5><ul>
086    *    <li class='jc'>{@link MethodInfo} - Method introspection
087    *    <li class='jc'>{@link ConstructorInfo} - Constructor introspection
088    *    <li class='jc'>{@link ExecutableInfo} - Common executable functionality
089    *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauCommonsReflection">Reflection Package</a>
090    * </ul>
091    */
092public class ParameterInfo extends ElementInfo implements Annotatable {
093
094   /**
095    * Resettable supplier for the system property to disable bytecode parameter name detection.
096    *
097    * <p>
098    * When the value is <jk>true</jk>, parameter names will only come from {@link org.apache.juneau.annotation.Name @Name}
099    * annotations and not from bytecode parameter names (even if compiled with <c>-parameters</c> flag).
100    *
101    * <p>
102    * This can be set via system property: <c>juneau.disableParamNameDetection=true</c>
103    *
104    * <p>
105    * The supplier can be reset for testing purposes using {@link #resetDisableParamNameDetection()}.
106    */
107   static final ResettableSupplier<Boolean> DISABLE_PARAM_NAME_DETECTION = memr(() -> Boolean.getBoolean("juneau.disableParamNameDetection"));
108
109   /**
110    * Creates a ParameterInfo wrapper for the specified parameter.
111    *
112    * <p>
113    * This convenience method automatically determines the declaring executable from the parameter
114    * and finds the matching ParameterInfo.
115    *
116    * <h5 class='section'>Example:</h5>
117    * <p class='bjava'>
118    *    Parameter <jv>p</jv> = ...;
119    *    ParameterInfo <jv>pi</jv> = ParameterInfo.<jsm>of</jsm>(<jv>p</jv>);
120    * </p>
121    *
122    * @param inner The parameter being wrapped. Must not be <jk>null</jk>.
123    * @return A ParameterInfo object wrapping the parameter.
124    * @throws IllegalArgumentException If the parameter is <jk>null</jk> or cannot be found in its declaring executable.
125    */
126   public static ParameterInfo of(Parameter inner) {
127      assertArgNotNull("inner", inner);
128      var exec = inner.getDeclaringExecutable();
129      ExecutableInfo execInfo;
130      if (exec instanceof Constructor<?> c)
131         execInfo = ConstructorInfo.of(c);
132      else
133         execInfo = MethodInfo.of((Method)exec);
134      return execInfo.getParameters().stream()
135         .filter(x -> eq(x.inner(), inner))
136         .findFirst()
137         .orElse(null);
138   }
139
140   static void reset() {
141      DISABLE_PARAM_NAME_DETECTION.reset();
142   }
143
144   private final ExecutableInfo executable;
145   private final Parameter inner;
146
147   private final int index;
148   private final ClassInfo type;
149   private final Supplier<List<AnnotationInfo<Annotation>>> annotations;  // All annotations declared directly on this parameter.
150   private final Supplier<List<ParameterInfo>> matchingParameters;  // Matching parameters in parent methods.
151
152   private final ResettableSupplier<String> resolvedName = memr(this::findNameInternal);  // Resolved name from @Name annotation or bytecode.
153
154   private final ResettableSupplier<String> resolvedQualifier = memr(this::findQualifierInternal);  // Resolved qualifier from @Named annotation.
155
156   /**
157    * Constructor.
158    *
159    * <p>
160    * Creates a new ParameterInfo wrapper for the specified parameter. This constructor is protected
161    * and should not be called directly. ParameterInfo instances are typically obtained from
162    * {@link ExecutableInfo#getParameters()} or {@link #of(Parameter)}.
163    *
164    * @param executable The ExecutableInfo (MethodInfo or ConstructorInfo) that contains this parameter.
165    * @param inner The parameter being wrapped.
166    * @param index The zero-based index of this parameter in the method/constructor signature.
167    * @param type The ClassInfo representing the parameter type.
168    */
169   // TODO - Investigate if we can construct ClassInfo directly from parameter.
170   protected ParameterInfo(ExecutableInfo executable, Parameter inner, int index, ClassInfo type) {
171      super(inner.getModifiers());
172      this.executable = executable;
173      this.inner = inner;
174      this.index = index;
175      this.type = type;
176      this.annotations = mem(() -> stream(inner.getAnnotations()).flatMap(a -> AnnotationUtils.streamRepeated(a)).map(a -> ai(this, a)).toList());
177      this.matchingParameters = mem(this::findMatchingParameters);
178   }
179
180   /**
181    * Returns <jk>true</jk> if this parameter can accept the specified value.
182    *
183    * @param value The value to check.
184    * @return <jk>true</jk> if this parameter can accept the specified value.
185    */
186   public boolean canAccept(Object value) {
187      return getParameterType().canAcceptArg(value);
188   }
189
190   @Override /* Annotatable */
191   public AnnotatableType getAnnotatableType() { return AnnotatableType.PARAMETER_TYPE; }
192
193   /**
194    * Returns an {@link AnnotatedType} object that represents the use of a type to specify the type of this parameter.
195    *
196    * <p>
197    * Same as calling {@link Parameter#getAnnotatedType()}.
198    *
199    * <h5 class='section'>Example:</h5>
200    * <p class='bjava'>
201    *    <jc>// Get annotated type: void method(@NotNull String value)</jc>
202    *    ParameterInfo <jv>pi</jv> = ...;
203    *    AnnotatedType <jv>aType</jv> = <jv>pi</jv>.getAnnotatedType();
204    *    <jc>// Check for @NotNull on the type</jc>
205    * </p>
206    *
207    * @return An {@link AnnotatedType} object representing the type of this parameter.
208    * @see Parameter#getAnnotatedType()
209    */
210   public AnnotatedType getAnnotatedType() { return inner.getAnnotatedType(); }
211
212   /**
213    * Returns all annotations declared on this parameter.
214    *
215    * <p>
216    * Returns annotations directly declared on this parameter, wrapped as {@link AnnotationInfo} objects.
217    *
218    * <p>
219    * <b>Note on Repeatable Annotations:</b>
220    * Repeatable annotations (those marked with {@link java.lang.annotation.Repeatable @Repeatable}) are automatically
221    * expanded into their individual annotation instances. For example, if a parameter has multiple {@code @Bean} annotations,
222    * this method returns each {@code @Bean} annotation separately, rather than the container annotation.
223    *
224    * @return
225    *    An unmodifiable list of annotations on this parameter, never <jk>null</jk>.
226    *    <br>Repeatable annotations are expanded into individual instances.
227    */
228   public List<AnnotationInfo<Annotation>> getAnnotations() { return annotations.get(); }
229
230   /**
231    * Returns a stream of annotation infos of the specified type declared on this parameter.
232    *
233    * @param <A> The annotation type.
234    * @param type The annotation type.
235    * @return A stream of annotation infos, never <jk>null</jk>.
236    */
237   @SuppressWarnings("unchecked")
238   public <A extends Annotation> Stream<AnnotationInfo<A>> getAnnotations(Class<A> type) {
239      return getAnnotations().stream().filter(x -> x.isType(type)).map(x -> (AnnotationInfo<A>)x);
240   }
241
242   /**
243    * Returns the constructor that this parameter belongs to.
244    *
245    * @return The constructor that this parameter belongs to, or <jk>null</jk> if it belongs to a method.
246    */
247   public ConstructorInfo getConstructor() { return executable.isConstructor() ? (ConstructorInfo)executable : null; }
248
249   /**
250    * Returns the {@link ExecutableInfo} which declares this parameter.
251    *
252    * <p>
253    * Same as calling {@link Parameter#getDeclaringExecutable()} but returns {@link ExecutableInfo} instead.
254    *
255    * <h5 class='section'>Example:</h5>
256    * <p class='bjava'>
257    *    <jc>// Get the method or constructor that declares this parameter</jc>
258    *    ParameterInfo <jv>pi</jv> = ...;
259    *    ExecutableInfo <jv>executable</jv> = <jv>pi</jv>.getDeclaringExecutable();
260    *    <jk>if</jk> (<jv>executable</jv>.isConstructor()) {
261    *       ConstructorInfo <jv>ci</jv> = (ConstructorInfo)<jv>executable</jv>;
262    *    }
263    * </p>
264    *
265    * @return The {@link ExecutableInfo} declaring this parameter.
266    * @see Parameter#getDeclaringExecutable()
267    */
268   public ExecutableInfo getDeclaringExecutable() { return executable; }
269
270   /**
271    * Returns the index position of this parameter.
272    *
273    * @return The index position of this parameter.
274    */
275   public int getIndex() { return index; }
276
277   @Override /* Annotatable */
278   public String getLabel() {
279      var exec = getDeclaringExecutable();
280      var label = exec.getDeclaringClass().getNameSimple() + "." + exec.getShortName();
281      return label + "[" + index + "]";
282   }
283
284   /**
285    * Returns this parameter and all matching parameters in parent classes.
286    *
287    * <p>
288    * For constructors, searches parent class constructors for parameters with matching name and type,
289    * regardless of parameter count or position. This allows finding annotated parameters that may be
290    * inherited by child classes even when constructor signatures differ.
291    *
292    * <p>
293    * For methods, searches matching methods (same signature) in parent classes/interfaces
294    * for parameters at the same index with matching name and type.
295    *
296    * <h5 class='section'>Examples:</h5>
297    * <p class='bjava'>
298    *    <jc>// Constructor with different parameter counts:</jc>
299    *    <jk>class</jk> A {
300    *       A(String <jv>foo</jv>, <jk>int</jk> <jv>bar</jv>) {}
301    *    }
302    *    <jk>class</jk> B <jk>extends</jk> A {
303    *       B(String <jv>foo</jv>) {}
304    *    }
305    *    <jc>// For B's foo parameter, returns: [B.foo, A.foo]</jc>
306    *    ParameterInfo <jv>pi</jv> = ...;
307    *    List&lt;ParameterInfo&gt; <jv>matching</jv> = <jv>pi</jv>.getMatchingParameters();
308    * </p>
309    *
310    * @return A list of matching parameters including this one, in child-to-parent order.
311    */
312   public List<ParameterInfo> getMatchingParameters() { return matchingParameters.get(); }
313
314   /**
315    * Returns the method that this parameter belongs to.
316    *
317    * @return The method that this parameter belongs to, or <jk>null</jk> if it belongs to a constructor.
318    */
319   public MethodInfo getMethod() { return executable.isConstructor() ? null : (MethodInfo)executable; }
320
321   /**
322    * Returns the Java language modifiers for the parameter represented by this object, as an integer.
323    *
324    * <p>
325    * The {@link java.lang.reflect.Modifier} class should be used to decode the modifiers.
326    *
327    * <p>
328    * Same as calling {@link Parameter#getModifiers()}.
329    *
330    * <h5 class='section'>Example:</h5>
331    * <p class='bjava'>
332    *    <jc>// Check if parameter is final</jc>
333    *    ParameterInfo <jv>pi</jv> = ...;
334    *    <jk>int</jk> <jv>modifiers</jv> = <jv>pi</jv>.getModifiers();
335    *    <jk>boolean</jk> <jv>isFinal</jv> = Modifier.<jsm>isFinal</jsm>(<jv>modifiers</jv>);
336    * </p>
337    *
338    * @return The Java language modifiers for this parameter.
339    * @see Parameter#getModifiers()
340    * @see java.lang.reflect.Modifier
341    */
342   @Override
343   public int getModifiers() { return inner.getModifiers(); }
344
345   /**
346    * Returns the name of the parameter.
347    *
348    * <p>
349    * Searches for the name in the following order:
350    * <ol>
351    *    <li>@Name annotation value (takes precedence over bytecode parameter names)
352    *    <li>Bytecode parameter name (if compiled with -parameters flag)
353    *    <li>Matching parameters in parent classes/interfaces (for methods)
354    *    <li>Synthetic name like "arg0", "arg1", etc. (fallback)
355    * </ol>
356    *
357    * <p>
358    * This method works with any annotation named "Name" (from any package) that has a <c>String value()</c> method.
359    *
360    * @return The name of the parameter, never <jk>null</jk>.
361    * @see Parameter#getName()
362    */
363   public String getName() {
364      var name = getResolvedName();
365      return name != null ? name : inner.getName();
366   }
367
368   /**
369    * Returns a {@link Type} object that identifies the parameterized type for this parameter.
370    *
371    * <p>
372    * Same as calling {@link Parameter#getParameterizedType()}.
373    *
374    * <h5 class='section'>Example:</h5>
375    * <p class='bjava'>
376    *    <jc>// Get generic type information for parameter: List&lt;String&gt; values</jc>
377    *    ParameterInfo <jv>pi</jv> = ...;
378    *    Type <jv>type</jv> = <jv>pi</jv>.getParameterizedType();
379    *    <jk>if</jk> (<jv>type</jv> <jk>instanceof</jk> ParameterizedType) {
380    *       ParameterizedType <jv>pType</jv> = (ParameterizedType)<jv>type</jv>;
381    *       <jc>// pType.getActualTypeArguments()[0] is String.class</jc>
382    *    }
383    * </p>
384    *
385    * @return A {@link Type} object identifying the parameterized type.
386    * @see Parameter#getParameterizedType()
387    */
388   public Type getParameterizedType() { return inner.getParameterizedType(); }
389
390   /**
391    * Returns the class type of this parameter.
392    *
393    * @return The class type of this parameter.
394    */
395   public ClassInfo getParameterType() { return type; }
396
397   /**
398    * Finds the name of this parameter for bean property mapping.
399    *
400    * <p>
401    * Searches for the parameter name in the following order:
402    * <ol>
403    *    <li>{@link org.apache.juneau.annotation.Name @Name} annotation value
404    *    <li>Bytecode parameter name (if available and not disabled via system property)
405    *    <li>Matching parameters in parent classes/interfaces
406    * </ol>
407    *
408    * <p>
409    * This method is used for mapping constructor parameters to bean properties.
410    *
411    * <p>
412    * <b>Note:</b> This is different from {@link #getResolvedQualifier()} which looks for {@link org.apache.juneau.annotation.Named @Named}
413    * annotations for bean injection purposes.
414    *
415    * @return The parameter name if found, or <jk>null</jk> if not available.
416    * @see #getResolvedQualifier()
417    * @see #getName()
418    */
419   public String getResolvedName() { return resolvedName.get(); }
420
421   /**
422    * Finds the bean injection qualifier for this parameter.
423    *
424    * <p>
425    * Searches for the {@link org.apache.juneau.annotation.Named @Named} annotation value to determine
426    * which named bean should be injected.
427    *
428    * <p>
429    * This method is used by the {@link org.apache.juneau.cp.BeanStore} for bean injection.
430    *
431    * <p>
432    * <b>Note:</b> This is different from {@link #getResolvedName()} which looks for {@link org.apache.juneau.annotation.Name @Name}
433    * annotations for bean property mapping.
434    *
435    * @return The bean qualifier name if {@code @Named} annotation is found, or <jk>null</jk> if not annotated.
436    * @see #getResolvedName()
437    */
438   public String getResolvedQualifier() { return resolvedQualifier.get(); }
439
440   /**
441    * Returns <jk>true</jk> if the parameter has a name.
442    *
443    * <p>
444    * This returns <jk>true</jk> if the parameter has an annotation with the simple name "Name",
445    * or if the parameter's name is present in the class file.
446    *
447    * @return <jk>true</jk> if the parameter has a name.
448    */
449   public boolean hasName() {
450      return getResolvedName() != null;
451   }
452
453   /**
454    * Returns the wrapped {@link Parameter} object.
455    *
456    * @return The wrapped {@link Parameter} object.
457    */
458   public Parameter inner() {
459      return inner;
460   }
461
462   /**
463    * Compares this ParameterInfo with the specified object for equality.
464    *
465    * <p>
466    * Two ParameterInfo objects are considered equal if they wrap the same underlying {@link Parameter} object.
467    * This delegates to the underlying {@link Parameter#equals(Object)} method.
468    *
469    * <p>
470    * This method makes ParameterInfo suitable for use as keys in hash-based collections such as {@link HashMap}
471    * and {@link HashSet}.
472    *
473    * @param obj The object to compare with.
474    * @return <jk>true</jk> if the objects are equal, <jk>false</jk> otherwise.
475    */
476   @Override
477   public boolean equals(Object obj) {
478      return obj instanceof ParameterInfo other && eq(this, other, (x, y) -> eq(x.inner, y.inner));
479   }
480
481   /**
482    * Returns a hash code value for this ParameterInfo.
483    *
484    * <p>
485    * This delegates to the underlying {@link Parameter#hashCode()} method.
486    *
487    * <p>
488    * This method makes ParameterInfo suitable for use as keys in hash-based collections such as {@link HashMap}
489    * and {@link HashSet}.
490    *
491    * @return A hash code value for this ParameterInfo.
492    */
493   @Override
494   public int hashCode() {
495      return inner.hashCode();
496   }
497
498   @Override
499   public boolean is(ElementFlag flag) {
500      return switch (flag) {
501         case SYNTHETIC -> isSynthetic();
502         case NOT_SYNTHETIC -> ! isSynthetic();  // HTT
503         case VARARGS -> isVarArgs();
504         case NOT_VARARGS -> ! isVarArgs();
505         default -> super.is(flag);
506      };
507   }
508
509   /**
510    * Returns <jk>true</jk> if this parameter is implicitly declared in source code.
511    *
512    * <p>
513    * Returns <jk>true</jk> if this parameter is neither explicitly nor implicitly declared in source code.
514    *
515    * <p>
516    * Same as calling {@link Parameter#isImplicit()}.
517    *
518    * <h5 class='section'>Example:</h5>
519    * <p class='bjava'>
520    *    <jc>// Filter out implicit parameters</jc>
521    *    ParameterInfo <jv>pi</jv> = ...;
522    *    <jk>if</jk> (! <jv>pi</jv>.isImplicit()) {
523    *       <jc>// Process explicit parameter</jc>
524    *    }
525    * </p>
526    *
527    * @return <jk>true</jk> if this parameter is implicitly declared.
528    * @see Parameter#isImplicit()
529    */
530   public boolean isImplicit() { return inner.isImplicit(); }
531
532   /**
533    * Returns <jk>true</jk> if the parameter has a name according to the <c>.class</c> file.
534    *
535    * <p>
536    * Same as calling {@link Parameter#isNamePresent()}.
537    *
538    * <p>
539    * <b>Note:</b> This method is different from {@link #hasName()} which also checks for the presence
540    * of a <c>@Name</c> annotation. This method only checks if the name is present in the bytecode.
541    *
542    * <h5 class='section'>Example:</h5>
543    * <p class='bjava'>
544    *    <jc>// Check if parameter name is in bytecode</jc>
545    *    ParameterInfo <jv>pi</jv> = ...;
546    *    <jk>if</jk> (<jv>pi</jv>.isNamePresent()) {
547    *       String <jv>name</jv> = <jv>pi</jv>.getName();
548    *    }
549    * </p>
550    *
551    * @return <jk>true</jk> if the parameter has a name in the bytecode.
552    * @see Parameter#isNamePresent()
553    * @see #hasName()
554    */
555   public boolean isNamePresent() { return inner.isNamePresent(); }
556
557   /**
558    * Returns <jk>true</jk> if this parameter is a synthetic construct as defined by the Java Language Specification.
559    *
560    * <p>
561    * Same as calling {@link Parameter#isSynthetic()}.
562    *
563    * <h5 class='section'>Example:</h5>
564    * <p class='bjava'>
565    *    <jc>// Filter out compiler-generated parameters</jc>
566    *    ParameterInfo <jv>pi</jv> = ...;
567    *    <jk>if</jk> (! <jv>pi</jv>.isSynthetic()) {
568    *       <jc>// Process real parameter</jc>
569    *    }
570    * </p>
571    *
572    * @return <jk>true</jk> if this parameter is a synthetic construct.
573    * @see Parameter#isSynthetic()
574    */
575   public boolean isSynthetic() { return inner.isSynthetic(); }
576
577   /**
578    * Returns <jk>true</jk> if the parameter type is an exact match for the specified class.
579    *
580    * @param c The type to check.
581    * @return <jk>true</jk> if the parameter type is an exact match for the specified class.
582    */
583   public boolean isType(Class<?> c) {
584      return getParameterType().is(c);
585   }
586   //-----------------------------------------------------------------------------------------------------------------
587   // High Priority Methods (direct Parameter API compatibility)
588   //-----------------------------------------------------------------------------------------------------------------
589
590   /**
591    * Returns <jk>true</jk> if this parameter represents a variable argument list.
592    *
593    * <p>
594    * Same as calling {@link Parameter#isVarArgs()}.
595    *
596    * <p>
597    * Only returns <jk>true</jk> for the last parameter of a variable arity method.
598    *
599    * <h5 class='section'>Example:</h5>
600    * <p class='bjava'>
601    *    <jc>// Check if this is a varargs parameter</jc>
602    *    ParameterInfo <jv>pi</jv> = ...;
603    *    <jk>if</jk> (<jv>pi</jv>.isVarArgs()) {
604    *       <jc>// Handle variable arguments</jc>
605    *    }
606    * </p>
607    *
608    * @return <jk>true</jk> if this parameter represents a variable argument list.
609    * @see Parameter#isVarArgs()
610    */
611   public boolean isVarArgs() { return inner.isVarArgs(); }
612
613   @Override
614   public String toString() {
615      return (executable.getSimpleName()) + "[" + index + "]";
616   }
617
618   private List<ParameterInfo> findMatchingParameters() {
619      if (executable instanceof ConstructorInfo executable2) {
620         // For constructors: search parent class constructors for parameters with matching index and type
621         // Note: We match by index and type only, not by name, to avoid circular dependency
622         // (getName() needs getMatchingParameters() which needs getName())
623         var list = new ArrayList<ParameterInfo>();
624
625         // Add this parameter first
626         list.add(this);
627
628         // Search parent classes for matching parameters
629         var cc = executable2.getDeclaringClass().getSuperclass();
630         while (nn(cc)) {
631            // Check all constructors in parent class
632            for (var pc : cc.getDeclaredConstructors()) {
633               // Check if constructor has parameter at this index with matching type
634               var params = pc.getParameters();
635               if (index < params.size() && getParameterType().is(params.get(index).getParameterType())) {
636                  list.add(params.get(index));
637               }
638            }
639            cc = cc.getSuperclass();
640         }
641
642         return list;
643      }
644      // For methods: use matching methods from parent classes
645      return ((MethodInfo)executable).getMatchingMethods().stream().map(m -> m.getParameter(index)).toList();
646   }
647
648   //-----------------------------------------------------------------------------------------------------------------
649   // Annotatable interface methods
650   //-----------------------------------------------------------------------------------------------------------------
651
652   private String findNameInternal() {
653      // Search through matching parameters in hierarchy for @Name annotations only.
654      // Note: We intentionally prioritize @Name annotations over bytecode parameter names
655      // because bytecode names are unreliable - users may or may not compile with -parameters flag.
656      for (var mp : getMatchingParameters()) {
657         for (var ai : mp.getAnnotations()) {
658            if (ai.hasSimpleName("Name")) {
659               var value = ai.getValue().orElse(null);
660               if (value != null)  // HTT
661                  return value;
662            }
663         }
664      }
665
666      return opt(inner).filter(x -> x.isNamePresent()).filter(x -> ! DISABLE_PARAM_NAME_DETECTION.get()).map(x -> x.getName()).orElse(null);
667   }
668
669   private String findQualifierInternal() {
670      // Search through matching parameters in hierarchy for @Named or javax.inject.Qualifier annotations
671      // @formatter:off
672      return getMatchingParameters().stream()
673         .flatMap(mp -> mp.getAnnotations().stream())
674         .filter(ai -> ai.hasSimpleName("Named") || ai.hasSimpleName("Qualifier"))
675         .map(ai -> ai.getValue().orElse(null))
676         .filter(Objects::nonNull)
677         .findFirst()
678         .orElse(null);
679      // @formatter:on
680   }
681}