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.CollectionUtils.*;
21 import static org.apache.juneau.commons.utils.Utils.*;
22
23 import java.lang.annotation.*;
24 import java.lang.reflect.*;
25 import java.util.*;
26 import java.util.function.*;
27 import java.util.stream.*;
28
29 import org.apache.juneau.commons.function.*;
30 import org.apache.juneau.commons.utils.*;
31
32 /**
33 * Lightweight utility class for introspecting information about a method or constructor parameter.
34 *
35 * <p>
36 * This class provides a convenient wrapper around {@link Parameter} that extends the standard Java reflection
37 * API with additional functionality for parameter introspection, annotation handling, and name resolution.
38 * It supports resolving parameter names from bytecode (when compiled with <c>-parameters</c>) or from
39 * {@link org.apache.juneau.annotation.Name @Name} annotations.
40 *
41 * <h5 class='section'>Features:</h5>
42 * <ul class='spaced-list'>
43 * <li>Parameter introspection - access parameter metadata, type, annotations
44 * <li>Name resolution - resolve parameter names from bytecode or annotations
45 * <li>Qualifier support - extract qualifier names from annotations
46 * <li>Hierarchy traversal - find matching parameters in parent methods/constructors
47 * <li>Annotation support - get annotations declared on the parameter
48 * </ul>
49 *
50 * <h5 class='section'>Use Cases:</h5>
51 * <ul class='spaced-list'>
52 * <li>Introspecting parameter metadata for code generation or analysis
53 * <li>Resolving parameter names for dependency injection frameworks
54 * <li>Finding annotations on parameters
55 * <li>Working with parameter types and qualifiers
56 * <li>Building frameworks that need to analyze method/constructor signatures
57 * </ul>
58 *
59 * <h5 class='section'>Usage:</h5>
60 * <p class='bjava'>
61 * <jc>// Get ParameterInfo from a method</jc>
62 * MethodInfo <jv>mi</jv> = ...;
63 * ParameterInfo <jv>param</jv> = <jv>mi</jv>.getParameters().get(0);
64 *
65 * <jc>// Get parameter type</jc>
66 * ClassInfo <jv>type</jv> = <jv>param</jv>.getParameterType();
67 *
68 * <jc>// Get resolved name (from bytecode or @Name annotation)</jc>
69 * String <jv>name</jv> = <jv>param</jv>.getResolvedName();
70 *
71 * <jc>// Get annotations</jc>
72 * List<AnnotationInfo<MyAnnotation>> <jv>annotations</jv> =
73 * <jv>param</jv>.getAnnotations(MyAnnotation.<jk>class</jk>).toList();
74 * </p>
75 *
76 * <h5 class='section'>Parameter Name Resolution:</h5>
77 * <p>
78 * Parameter names are resolved in the following order:
79 * <ol class='spaced-list'>
80 * <li>{@link org.apache.juneau.annotation.Name @Name} annotation value (if present)
81 * <li>Bytecode parameter names (if compiled with <c>-parameters</c> flag)
82 * <li><c>arg0</c>, <c>arg1</c>, etc. (fallback if names unavailable)
83 * </ol>
84 *
85 * <h5 class='section'>See Also:</h5><ul>
86 * <li class='jc'>{@link MethodInfo} - Method introspection
87 * <li class='jc'>{@link ConstructorInfo} - Constructor introspection
88 * <li class='jc'>{@link ExecutableInfo} - Common executable functionality
89 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauCommonsReflection">Reflection Package</a>
90 * </ul>
91 */
92 public class ParameterInfo extends ElementInfo implements Annotatable {
93
94 /**
95 * Resettable supplier for the system property to disable bytecode parameter name detection.
96 *
97 * <p>
98 * When the value is <jk>true</jk>, parameter names will only come from {@link org.apache.juneau.annotation.Name @Name}
99 * 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<ParameterInfo> <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<String> 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 }