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.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&lt;AnnotationInfo&lt;MyAnnotation&gt;&gt; <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&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 }