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