001/* 002 * Copyright 2002-2020 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.core; 018 019import java.lang.annotation.Annotation; 020import java.lang.reflect.AnnotatedElement; 021import java.lang.reflect.Constructor; 022import java.lang.reflect.Executable; 023import java.lang.reflect.Member; 024import java.lang.reflect.Method; 025import java.lang.reflect.Parameter; 026import java.lang.reflect.ParameterizedType; 027import java.lang.reflect.Type; 028import java.util.HashMap; 029import java.util.Map; 030import java.util.Optional; 031import java.util.function.Predicate; 032 033import kotlin.Unit; 034import kotlin.reflect.KFunction; 035import kotlin.reflect.KParameter; 036import kotlin.reflect.jvm.ReflectJvmMapping; 037 038import org.springframework.lang.Nullable; 039import org.springframework.util.Assert; 040import org.springframework.util.ClassUtils; 041import org.springframework.util.ObjectUtils; 042 043/** 044 * Helper class that encapsulates the specification of a method parameter, i.e. a {@link Method} 045 * or {@link Constructor} plus a parameter index and a nested type index for a declared generic 046 * type. Useful as a specification object to pass along. 047 * 048 * <p>As of 4.2, there is a {@link org.springframework.core.annotation.SynthesizingMethodParameter} 049 * subclass available which synthesizes annotations with attribute aliases. That subclass is used 050 * for web and message endpoint processing, in particular. 051 * 052 * @author Juergen Hoeller 053 * @author Rob Harrop 054 * @author Andy Clement 055 * @author Sam Brannen 056 * @author Sebastien Deleuze 057 * @author Phillip Webb 058 * @since 2.0 059 * @see org.springframework.core.annotation.SynthesizingMethodParameter 060 */ 061public class MethodParameter { 062 063 private static final Annotation[] EMPTY_ANNOTATION_ARRAY = new Annotation[0]; 064 065 066 private final Executable executable; 067 068 private final int parameterIndex; 069 070 @Nullable 071 private volatile Parameter parameter; 072 073 private int nestingLevel; 074 075 /** Map from Integer level to Integer type index. */ 076 @Nullable 077 Map<Integer, Integer> typeIndexesPerLevel; 078 079 /** The containing class. Could also be supplied by overriding {@link #getContainingClass()} */ 080 @Nullable 081 private volatile Class<?> containingClass; 082 083 @Nullable 084 private volatile Class<?> parameterType; 085 086 @Nullable 087 private volatile Type genericParameterType; 088 089 @Nullable 090 private volatile Annotation[] parameterAnnotations; 091 092 @Nullable 093 private volatile ParameterNameDiscoverer parameterNameDiscoverer; 094 095 @Nullable 096 private volatile String parameterName; 097 098 @Nullable 099 private volatile MethodParameter nestedMethodParameter; 100 101 102 /** 103 * Create a new {@code MethodParameter} for the given method, with nesting level 1. 104 * @param method the Method to specify a parameter for 105 * @param parameterIndex the index of the parameter: -1 for the method 106 * return type; 0 for the first method parameter; 1 for the second method 107 * parameter, etc. 108 */ 109 public MethodParameter(Method method, int parameterIndex) { 110 this(method, parameterIndex, 1); 111 } 112 113 /** 114 * Create a new {@code MethodParameter} for the given method. 115 * @param method the Method to specify a parameter for 116 * @param parameterIndex the index of the parameter: -1 for the method 117 * return type; 0 for the first method parameter; 1 for the second method 118 * parameter, etc. 119 * @param nestingLevel the nesting level of the target type 120 * (typically 1; e.g. in case of a List of Lists, 1 would indicate the 121 * nested List, whereas 2 would indicate the element of the nested List) 122 */ 123 public MethodParameter(Method method, int parameterIndex, int nestingLevel) { 124 Assert.notNull(method, "Method must not be null"); 125 this.executable = method; 126 this.parameterIndex = validateIndex(method, parameterIndex); 127 this.nestingLevel = nestingLevel; 128 } 129 130 /** 131 * Create a new MethodParameter for the given constructor, with nesting level 1. 132 * @param constructor the Constructor to specify a parameter for 133 * @param parameterIndex the index of the parameter 134 */ 135 public MethodParameter(Constructor<?> constructor, int parameterIndex) { 136 this(constructor, parameterIndex, 1); 137 } 138 139 /** 140 * Create a new MethodParameter for the given constructor. 141 * @param constructor the Constructor to specify a parameter for 142 * @param parameterIndex the index of the parameter 143 * @param nestingLevel the nesting level of the target type 144 * (typically 1; e.g. in case of a List of Lists, 1 would indicate the 145 * nested List, whereas 2 would indicate the element of the nested List) 146 */ 147 public MethodParameter(Constructor<?> constructor, int parameterIndex, int nestingLevel) { 148 Assert.notNull(constructor, "Constructor must not be null"); 149 this.executable = constructor; 150 this.parameterIndex = validateIndex(constructor, parameterIndex); 151 this.nestingLevel = nestingLevel; 152 } 153 154 /** 155 * Internal constructor used to create a {@link MethodParameter} with a 156 * containing class already set. 157 * @param executable the Executable to specify a parameter for 158 * @param parameterIndex the index of the parameter 159 * @param containingClass the containing class 160 * @since 5.2 161 */ 162 MethodParameter(Executable executable, int parameterIndex, @Nullable Class<?> containingClass) { 163 Assert.notNull(executable, "Executable must not be null"); 164 this.executable = executable; 165 this.parameterIndex = validateIndex(executable, parameterIndex); 166 this.nestingLevel = 1; 167 this.containingClass = containingClass; 168 } 169 170 /** 171 * Copy constructor, resulting in an independent MethodParameter object 172 * based on the same metadata and cache state that the original object was in. 173 * @param original the original MethodParameter object to copy from 174 */ 175 public MethodParameter(MethodParameter original) { 176 Assert.notNull(original, "Original must not be null"); 177 this.executable = original.executable; 178 this.parameterIndex = original.parameterIndex; 179 this.parameter = original.parameter; 180 this.nestingLevel = original.nestingLevel; 181 this.typeIndexesPerLevel = original.typeIndexesPerLevel; 182 this.containingClass = original.containingClass; 183 this.parameterType = original.parameterType; 184 this.genericParameterType = original.genericParameterType; 185 this.parameterAnnotations = original.parameterAnnotations; 186 this.parameterNameDiscoverer = original.parameterNameDiscoverer; 187 this.parameterName = original.parameterName; 188 } 189 190 191 /** 192 * Return the wrapped Method, if any. 193 * <p>Note: Either Method or Constructor is available. 194 * @return the Method, or {@code null} if none 195 */ 196 @Nullable 197 public Method getMethod() { 198 return (this.executable instanceof Method ? (Method) this.executable : null); 199 } 200 201 /** 202 * Return the wrapped Constructor, if any. 203 * <p>Note: Either Method or Constructor is available. 204 * @return the Constructor, or {@code null} if none 205 */ 206 @Nullable 207 public Constructor<?> getConstructor() { 208 return (this.executable instanceof Constructor ? (Constructor<?>) this.executable : null); 209 } 210 211 /** 212 * Return the class that declares the underlying Method or Constructor. 213 */ 214 public Class<?> getDeclaringClass() { 215 return this.executable.getDeclaringClass(); 216 } 217 218 /** 219 * Return the wrapped member. 220 * @return the Method or Constructor as Member 221 */ 222 public Member getMember() { 223 return this.executable; 224 } 225 226 /** 227 * Return the wrapped annotated element. 228 * <p>Note: This method exposes the annotations declared on the method/constructor 229 * itself (i.e. at the method/constructor level, not at the parameter level). 230 * @return the Method or Constructor as AnnotatedElement 231 */ 232 public AnnotatedElement getAnnotatedElement() { 233 return this.executable; 234 } 235 236 /** 237 * Return the wrapped executable. 238 * @return the Method or Constructor as Executable 239 * @since 5.0 240 */ 241 public Executable getExecutable() { 242 return this.executable; 243 } 244 245 /** 246 * Return the {@link Parameter} descriptor for method/constructor parameter. 247 * @since 5.0 248 */ 249 public Parameter getParameter() { 250 if (this.parameterIndex < 0) { 251 throw new IllegalStateException("Cannot retrieve Parameter descriptor for method return type"); 252 } 253 Parameter parameter = this.parameter; 254 if (parameter == null) { 255 parameter = getExecutable().getParameters()[this.parameterIndex]; 256 this.parameter = parameter; 257 } 258 return parameter; 259 } 260 261 /** 262 * Return the index of the method/constructor parameter. 263 * @return the parameter index (-1 in case of the return type) 264 */ 265 public int getParameterIndex() { 266 return this.parameterIndex; 267 } 268 269 /** 270 * Increase this parameter's nesting level. 271 * @see #getNestingLevel() 272 * @deprecated since 5.2 in favor of {@link #nested(Integer)} 273 */ 274 @Deprecated 275 public void increaseNestingLevel() { 276 this.nestingLevel++; 277 } 278 279 /** 280 * Decrease this parameter's nesting level. 281 * @see #getNestingLevel() 282 * @deprecated since 5.2 in favor of retaining the original MethodParameter and 283 * using {@link #nested(Integer)} if nesting is required 284 */ 285 @Deprecated 286 public void decreaseNestingLevel() { 287 getTypeIndexesPerLevel().remove(this.nestingLevel); 288 this.nestingLevel--; 289 } 290 291 /** 292 * Return the nesting level of the target type 293 * (typically 1; e.g. in case of a List of Lists, 1 would indicate the 294 * nested List, whereas 2 would indicate the element of the nested List). 295 */ 296 public int getNestingLevel() { 297 return this.nestingLevel; 298 } 299 300 /** 301 * Return a variant of this {@code MethodParameter} with the type 302 * for the current level set to the specified value. 303 * @param typeIndex the new type index 304 * @since 5.2 305 */ 306 public MethodParameter withTypeIndex(int typeIndex) { 307 return nested(this.nestingLevel, typeIndex); 308 } 309 310 /** 311 * Set the type index for the current nesting level. 312 * @param typeIndex the corresponding type index 313 * (or {@code null} for the default type index) 314 * @see #getNestingLevel() 315 * @deprecated since 5.2 in favor of {@link #withTypeIndex} 316 */ 317 @Deprecated 318 public void setTypeIndexForCurrentLevel(int typeIndex) { 319 getTypeIndexesPerLevel().put(this.nestingLevel, typeIndex); 320 } 321 322 /** 323 * Return the type index for the current nesting level. 324 * @return the corresponding type index, or {@code null} 325 * if none specified (indicating the default type index) 326 * @see #getNestingLevel() 327 */ 328 @Nullable 329 public Integer getTypeIndexForCurrentLevel() { 330 return getTypeIndexForLevel(this.nestingLevel); 331 } 332 333 /** 334 * Return the type index for the specified nesting level. 335 * @param nestingLevel the nesting level to check 336 * @return the corresponding type index, or {@code null} 337 * if none specified (indicating the default type index) 338 */ 339 @Nullable 340 public Integer getTypeIndexForLevel(int nestingLevel) { 341 return getTypeIndexesPerLevel().get(nestingLevel); 342 } 343 344 /** 345 * Obtain the (lazily constructed) type-indexes-per-level Map. 346 */ 347 private Map<Integer, Integer> getTypeIndexesPerLevel() { 348 if (this.typeIndexesPerLevel == null) { 349 this.typeIndexesPerLevel = new HashMap<>(4); 350 } 351 return this.typeIndexesPerLevel; 352 } 353 354 /** 355 * Return a variant of this {@code MethodParameter} which points to the 356 * same parameter but one nesting level deeper. 357 * @since 4.3 358 */ 359 public MethodParameter nested() { 360 return nested(null); 361 } 362 363 /** 364 * Return a variant of this {@code MethodParameter} which points to the 365 * same parameter but one nesting level deeper. 366 * @param typeIndex the type index for the new nesting level 367 * @since 5.2 368 */ 369 public MethodParameter nested(@Nullable Integer typeIndex) { 370 MethodParameter nestedParam = this.nestedMethodParameter; 371 if (nestedParam != null && typeIndex == null) { 372 return nestedParam; 373 } 374 nestedParam = nested(this.nestingLevel + 1, typeIndex); 375 if (typeIndex == null) { 376 this.nestedMethodParameter = nestedParam; 377 } 378 return nestedParam; 379 } 380 381 private MethodParameter nested(int nestingLevel, @Nullable Integer typeIndex) { 382 MethodParameter copy = clone(); 383 copy.nestingLevel = nestingLevel; 384 if (this.typeIndexesPerLevel != null) { 385 copy.typeIndexesPerLevel = new HashMap<>(this.typeIndexesPerLevel); 386 } 387 if (typeIndex != null) { 388 copy.getTypeIndexesPerLevel().put(copy.nestingLevel, typeIndex); 389 } 390 copy.parameterType = null; 391 copy.genericParameterType = null; 392 return copy; 393 } 394 395 /** 396 * Return whether this method indicates a parameter which is not required: 397 * either in the form of Java 8's {@link java.util.Optional}, any variant 398 * of a parameter-level {@code Nullable} annotation (such as from JSR-305 399 * or the FindBugs set of annotations), or a language-level nullable type 400 * declaration or {@code Continuation} parameter in Kotlin. 401 * @since 4.3 402 */ 403 public boolean isOptional() { 404 return (getParameterType() == Optional.class || hasNullableAnnotation() || 405 (KotlinDetector.isKotlinReflectPresent() && 406 KotlinDetector.isKotlinType(getContainingClass()) && 407 KotlinDelegate.isOptional(this))); 408 } 409 410 /** 411 * Check whether this method parameter is annotated with any variant of a 412 * {@code Nullable} annotation, e.g. {@code javax.annotation.Nullable} or 413 * {@code edu.umd.cs.findbugs.annotations.Nullable}. 414 */ 415 private boolean hasNullableAnnotation() { 416 for (Annotation ann : getParameterAnnotations()) { 417 if ("Nullable".equals(ann.annotationType().getSimpleName())) { 418 return true; 419 } 420 } 421 return false; 422 } 423 424 /** 425 * Return a variant of this {@code MethodParameter} which points to 426 * the same parameter but one nesting level deeper in case of a 427 * {@link java.util.Optional} declaration. 428 * @since 4.3 429 * @see #isOptional() 430 * @see #nested() 431 */ 432 public MethodParameter nestedIfOptional() { 433 return (getParameterType() == Optional.class ? nested() : this); 434 } 435 436 /** 437 * Return a variant of this {@code MethodParameter} which refers to the 438 * given containing class. 439 * @param containingClass a specific containing class (potentially a 440 * subclass of the declaring class, e.g. substituting a type variable) 441 * @since 5.2 442 * @see #getParameterType() 443 */ 444 public MethodParameter withContainingClass(@Nullable Class<?> containingClass) { 445 MethodParameter result = clone(); 446 result.containingClass = containingClass; 447 result.parameterType = null; 448 return result; 449 } 450 451 /** 452 * Set a containing class to resolve the parameter type against. 453 */ 454 @Deprecated 455 void setContainingClass(Class<?> containingClass) { 456 this.containingClass = containingClass; 457 this.parameterType = null; 458 } 459 460 /** 461 * Return the containing class for this method parameter. 462 * @return a specific containing class (potentially a subclass of the 463 * declaring class), or otherwise simply the declaring class itself 464 * @see #getDeclaringClass() 465 */ 466 public Class<?> getContainingClass() { 467 Class<?> containingClass = this.containingClass; 468 return (containingClass != null ? containingClass : getDeclaringClass()); 469 } 470 471 /** 472 * Set a resolved (generic) parameter type. 473 */ 474 @Deprecated 475 void setParameterType(@Nullable Class<?> parameterType) { 476 this.parameterType = parameterType; 477 } 478 479 /** 480 * Return the type of the method/constructor parameter. 481 * @return the parameter type (never {@code null}) 482 */ 483 public Class<?> getParameterType() { 484 Class<?> paramType = this.parameterType; 485 if (paramType != null) { 486 return paramType; 487 } 488 if (getContainingClass() != getDeclaringClass()) { 489 paramType = ResolvableType.forMethodParameter(this, null, 1).resolve(); 490 } 491 if (paramType == null) { 492 paramType = computeParameterType(); 493 } 494 this.parameterType = paramType; 495 return paramType; 496 } 497 498 /** 499 * Return the generic type of the method/constructor parameter. 500 * @return the parameter type (never {@code null}) 501 * @since 3.0 502 */ 503 public Type getGenericParameterType() { 504 Type paramType = this.genericParameterType; 505 if (paramType == null) { 506 if (this.parameterIndex < 0) { 507 Method method = getMethod(); 508 paramType = (method != null ? 509 (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(getContainingClass()) ? 510 KotlinDelegate.getGenericReturnType(method) : method.getGenericReturnType()) : void.class); 511 } 512 else { 513 Type[] genericParameterTypes = this.executable.getGenericParameterTypes(); 514 int index = this.parameterIndex; 515 if (this.executable instanceof Constructor && 516 ClassUtils.isInnerClass(this.executable.getDeclaringClass()) && 517 genericParameterTypes.length == this.executable.getParameterCount() - 1) { 518 // Bug in javac: type array excludes enclosing instance parameter 519 // for inner classes with at least one generic constructor parameter, 520 // so access it with the actual parameter index lowered by 1 521 index = this.parameterIndex - 1; 522 } 523 paramType = (index >= 0 && index < genericParameterTypes.length ? 524 genericParameterTypes[index] : computeParameterType()); 525 } 526 this.genericParameterType = paramType; 527 } 528 return paramType; 529 } 530 531 private Class<?> computeParameterType() { 532 if (this.parameterIndex < 0) { 533 Method method = getMethod(); 534 if (method == null) { 535 return void.class; 536 } 537 if (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(getContainingClass())) { 538 return KotlinDelegate.getReturnType(method); 539 } 540 return method.getReturnType(); 541 } 542 return this.executable.getParameterTypes()[this.parameterIndex]; 543 } 544 545 /** 546 * Return the nested type of the method/constructor parameter. 547 * @return the parameter type (never {@code null}) 548 * @since 3.1 549 * @see #getNestingLevel() 550 */ 551 public Class<?> getNestedParameterType() { 552 if (this.nestingLevel > 1) { 553 Type type = getGenericParameterType(); 554 for (int i = 2; i <= this.nestingLevel; i++) { 555 if (type instanceof ParameterizedType) { 556 Type[] args = ((ParameterizedType) type).getActualTypeArguments(); 557 Integer index = getTypeIndexForLevel(i); 558 type = args[index != null ? index : args.length - 1]; 559 } 560 // TODO: Object.class if unresolvable 561 } 562 if (type instanceof Class) { 563 return (Class<?>) type; 564 } 565 else if (type instanceof ParameterizedType) { 566 Type arg = ((ParameterizedType) type).getRawType(); 567 if (arg instanceof Class) { 568 return (Class<?>) arg; 569 } 570 } 571 return Object.class; 572 } 573 else { 574 return getParameterType(); 575 } 576 } 577 578 /** 579 * Return the nested generic type of the method/constructor parameter. 580 * @return the parameter type (never {@code null}) 581 * @since 4.2 582 * @see #getNestingLevel() 583 */ 584 public Type getNestedGenericParameterType() { 585 if (this.nestingLevel > 1) { 586 Type type = getGenericParameterType(); 587 for (int i = 2; i <= this.nestingLevel; i++) { 588 if (type instanceof ParameterizedType) { 589 Type[] args = ((ParameterizedType) type).getActualTypeArguments(); 590 Integer index = getTypeIndexForLevel(i); 591 type = args[index != null ? index : args.length - 1]; 592 } 593 } 594 return type; 595 } 596 else { 597 return getGenericParameterType(); 598 } 599 } 600 601 /** 602 * Return the annotations associated with the target method/constructor itself. 603 */ 604 public Annotation[] getMethodAnnotations() { 605 return adaptAnnotationArray(getAnnotatedElement().getAnnotations()); 606 } 607 608 /** 609 * Return the method/constructor annotation of the given type, if available. 610 * @param annotationType the annotation type to look for 611 * @return the annotation object, or {@code null} if not found 612 */ 613 @Nullable 614 public <A extends Annotation> A getMethodAnnotation(Class<A> annotationType) { 615 A annotation = getAnnotatedElement().getAnnotation(annotationType); 616 return (annotation != null ? adaptAnnotation(annotation) : null); 617 } 618 619 /** 620 * Return whether the method/constructor is annotated with the given type. 621 * @param annotationType the annotation type to look for 622 * @since 4.3 623 * @see #getMethodAnnotation(Class) 624 */ 625 public <A extends Annotation> boolean hasMethodAnnotation(Class<A> annotationType) { 626 return getAnnotatedElement().isAnnotationPresent(annotationType); 627 } 628 629 /** 630 * Return the annotations associated with the specific method/constructor parameter. 631 */ 632 public Annotation[] getParameterAnnotations() { 633 Annotation[] paramAnns = this.parameterAnnotations; 634 if (paramAnns == null) { 635 Annotation[][] annotationArray = this.executable.getParameterAnnotations(); 636 int index = this.parameterIndex; 637 if (this.executable instanceof Constructor && 638 ClassUtils.isInnerClass(this.executable.getDeclaringClass()) && 639 annotationArray.length == this.executable.getParameterCount() - 1) { 640 // Bug in javac in JDK <9: annotation array excludes enclosing instance parameter 641 // for inner classes, so access it with the actual parameter index lowered by 1 642 index = this.parameterIndex - 1; 643 } 644 paramAnns = (index >= 0 && index < annotationArray.length ? 645 adaptAnnotationArray(annotationArray[index]) : EMPTY_ANNOTATION_ARRAY); 646 this.parameterAnnotations = paramAnns; 647 } 648 return paramAnns; 649 } 650 651 /** 652 * Return {@code true} if the parameter has at least one annotation, 653 * {@code false} if it has none. 654 * @see #getParameterAnnotations() 655 */ 656 public boolean hasParameterAnnotations() { 657 return (getParameterAnnotations().length != 0); 658 } 659 660 /** 661 * Return the parameter annotation of the given type, if available. 662 * @param annotationType the annotation type to look for 663 * @return the annotation object, or {@code null} if not found 664 */ 665 @SuppressWarnings("unchecked") 666 @Nullable 667 public <A extends Annotation> A getParameterAnnotation(Class<A> annotationType) { 668 Annotation[] anns = getParameterAnnotations(); 669 for (Annotation ann : anns) { 670 if (annotationType.isInstance(ann)) { 671 return (A) ann; 672 } 673 } 674 return null; 675 } 676 677 /** 678 * Return whether the parameter is declared with the given annotation type. 679 * @param annotationType the annotation type to look for 680 * @see #getParameterAnnotation(Class) 681 */ 682 public <A extends Annotation> boolean hasParameterAnnotation(Class<A> annotationType) { 683 return (getParameterAnnotation(annotationType) != null); 684 } 685 686 /** 687 * Initialize parameter name discovery for this method parameter. 688 * <p>This method does not actually try to retrieve the parameter name at 689 * this point; it just allows discovery to happen when the application calls 690 * {@link #getParameterName()} (if ever). 691 */ 692 public void initParameterNameDiscovery(@Nullable ParameterNameDiscoverer parameterNameDiscoverer) { 693 this.parameterNameDiscoverer = parameterNameDiscoverer; 694 } 695 696 /** 697 * Return the name of the method/constructor parameter. 698 * @return the parameter name (may be {@code null} if no 699 * parameter name metadata is contained in the class file or no 700 * {@link #initParameterNameDiscovery ParameterNameDiscoverer} 701 * has been set to begin with) 702 */ 703 @Nullable 704 public String getParameterName() { 705 if (this.parameterIndex < 0) { 706 return null; 707 } 708 ParameterNameDiscoverer discoverer = this.parameterNameDiscoverer; 709 if (discoverer != null) { 710 String[] parameterNames = null; 711 if (this.executable instanceof Method) { 712 parameterNames = discoverer.getParameterNames((Method) this.executable); 713 } 714 else if (this.executable instanceof Constructor) { 715 parameterNames = discoverer.getParameterNames((Constructor<?>) this.executable); 716 } 717 if (parameterNames != null) { 718 this.parameterName = parameterNames[this.parameterIndex]; 719 } 720 this.parameterNameDiscoverer = null; 721 } 722 return this.parameterName; 723 } 724 725 726 /** 727 * A template method to post-process a given annotation instance before 728 * returning it to the caller. 729 * <p>The default implementation simply returns the given annotation as-is. 730 * @param annotation the annotation about to be returned 731 * @return the post-processed annotation (or simply the original one) 732 * @since 4.2 733 */ 734 protected <A extends Annotation> A adaptAnnotation(A annotation) { 735 return annotation; 736 } 737 738 /** 739 * A template method to post-process a given annotation array before 740 * returning it to the caller. 741 * <p>The default implementation simply returns the given annotation array as-is. 742 * @param annotations the annotation array about to be returned 743 * @return the post-processed annotation array (or simply the original one) 744 * @since 4.2 745 */ 746 protected Annotation[] adaptAnnotationArray(Annotation[] annotations) { 747 return annotations; 748 } 749 750 751 @Override 752 public boolean equals(@Nullable Object other) { 753 if (this == other) { 754 return true; 755 } 756 if (!(other instanceof MethodParameter)) { 757 return false; 758 } 759 MethodParameter otherParam = (MethodParameter) other; 760 return (getContainingClass() == otherParam.getContainingClass() && 761 ObjectUtils.nullSafeEquals(this.typeIndexesPerLevel, otherParam.typeIndexesPerLevel) && 762 this.nestingLevel == otherParam.nestingLevel && 763 this.parameterIndex == otherParam.parameterIndex && 764 this.executable.equals(otherParam.executable)); 765 } 766 767 @Override 768 public int hashCode() { 769 return (31 * this.executable.hashCode() + this.parameterIndex); 770 } 771 772 @Override 773 public String toString() { 774 Method method = getMethod(); 775 return (method != null ? "method '" + method.getName() + "'" : "constructor") + 776 " parameter " + this.parameterIndex; 777 } 778 779 @Override 780 public MethodParameter clone() { 781 return new MethodParameter(this); 782 } 783 784 /** 785 * Create a new MethodParameter for the given method or constructor. 786 * <p>This is a convenience factory method for scenarios where a 787 * Method or Constructor reference is treated in a generic fashion. 788 * @param methodOrConstructor the Method or Constructor to specify a parameter for 789 * @param parameterIndex the index of the parameter 790 * @return the corresponding MethodParameter instance 791 * @deprecated as of 5.0, in favor of {@link #forExecutable} 792 */ 793 @Deprecated 794 public static MethodParameter forMethodOrConstructor(Object methodOrConstructor, int parameterIndex) { 795 if (!(methodOrConstructor instanceof Executable)) { 796 throw new IllegalArgumentException( 797 "Given object [" + methodOrConstructor + "] is neither a Method nor a Constructor"); 798 } 799 return forExecutable((Executable) methodOrConstructor, parameterIndex); 800 } 801 802 /** 803 * Create a new MethodParameter for the given method or constructor. 804 * <p>This is a convenience factory method for scenarios where a 805 * Method or Constructor reference is treated in a generic fashion. 806 * @param executable the Method or Constructor to specify a parameter for 807 * @param parameterIndex the index of the parameter 808 * @return the corresponding MethodParameter instance 809 * @since 5.0 810 */ 811 public static MethodParameter forExecutable(Executable executable, int parameterIndex) { 812 if (executable instanceof Method) { 813 return new MethodParameter((Method) executable, parameterIndex); 814 } 815 else if (executable instanceof Constructor) { 816 return new MethodParameter((Constructor<?>) executable, parameterIndex); 817 } 818 else { 819 throw new IllegalArgumentException("Not a Method/Constructor: " + executable); 820 } 821 } 822 823 /** 824 * Create a new MethodParameter for the given parameter descriptor. 825 * <p>This is a convenience factory method for scenarios where a 826 * Java 8 {@link Parameter} descriptor is already available. 827 * @param parameter the parameter descriptor 828 * @return the corresponding MethodParameter instance 829 * @since 5.0 830 */ 831 public static MethodParameter forParameter(Parameter parameter) { 832 return forExecutable(parameter.getDeclaringExecutable(), findParameterIndex(parameter)); 833 } 834 835 protected static int findParameterIndex(Parameter parameter) { 836 Executable executable = parameter.getDeclaringExecutable(); 837 Parameter[] allParams = executable.getParameters(); 838 // Try first with identity checks for greater performance. 839 for (int i = 0; i < allParams.length; i++) { 840 if (parameter == allParams[i]) { 841 return i; 842 } 843 } 844 // Potentially try again with object equality checks in order to avoid race 845 // conditions while invoking java.lang.reflect.Executable.getParameters(). 846 for (int i = 0; i < allParams.length; i++) { 847 if (parameter.equals(allParams[i])) { 848 return i; 849 } 850 } 851 throw new IllegalArgumentException("Given parameter [" + parameter + 852 "] does not match any parameter in the declaring executable"); 853 } 854 855 private static int validateIndex(Executable executable, int parameterIndex) { 856 int count = executable.getParameterCount(); 857 Assert.isTrue(parameterIndex >= -1 && parameterIndex < count, 858 () -> "Parameter index needs to be between -1 and " + (count - 1)); 859 return parameterIndex; 860 } 861 862 863 /** 864 * Inner class to avoid a hard dependency on Kotlin at runtime. 865 */ 866 private static class KotlinDelegate { 867 868 /** 869 * Check whether the specified {@link MethodParameter} represents a nullable Kotlin type, 870 * an optional parameter (with a default value in the Kotlin declaration) or a 871 * {@code Continuation} parameter used in suspending functions. 872 */ 873 public static boolean isOptional(MethodParameter param) { 874 Method method = param.getMethod(); 875 int index = param.getParameterIndex(); 876 if (method != null && index == -1) { 877 KFunction<?> function = ReflectJvmMapping.getKotlinFunction(method); 878 return (function != null && function.getReturnType().isMarkedNullable()); 879 } 880 KFunction<?> function; 881 Predicate<KParameter> predicate; 882 if (method != null) { 883 if (param.getParameterType().getName().equals("kotlin.coroutines.Continuation")) { 884 return true; 885 } 886 function = ReflectJvmMapping.getKotlinFunction(method); 887 predicate = p -> KParameter.Kind.VALUE.equals(p.getKind()); 888 } 889 else { 890 Constructor<?> ctor = param.getConstructor(); 891 Assert.state(ctor != null, "Neither method nor constructor found"); 892 function = ReflectJvmMapping.getKotlinFunction(ctor); 893 predicate = p -> (KParameter.Kind.VALUE.equals(p.getKind()) || 894 KParameter.Kind.INSTANCE.equals(p.getKind())); 895 } 896 if (function != null) { 897 int i = 0; 898 for (KParameter kParameter : function.getParameters()) { 899 if (predicate.test(kParameter)) { 900 if (index == i++) { 901 return (kParameter.getType().isMarkedNullable() || kParameter.isOptional()); 902 } 903 } 904 } 905 } 906 return false; 907 } 908 909 /** 910 * Return the generic return type of the method, with support of suspending 911 * functions via Kotlin reflection. 912 */ 913 static private Type getGenericReturnType(Method method) { 914 try { 915 KFunction<?> function = ReflectJvmMapping.getKotlinFunction(method); 916 if (function != null && function.isSuspend()) { 917 return ReflectJvmMapping.getJavaType(function.getReturnType()); 918 } 919 } 920 catch (UnsupportedOperationException ex) { 921 // probably a synthetic class - let's use java reflection instead 922 } 923 return method.getGenericReturnType(); 924 } 925 926 /** 927 * Return the return type of the method, with support of suspending 928 * functions via Kotlin reflection. 929 */ 930 static private Class<?> getReturnType(Method method) { 931 try { 932 KFunction<?> function = ReflectJvmMapping.getKotlinFunction(method); 933 if (function != null && function.isSuspend()) { 934 Type paramType = ReflectJvmMapping.getJavaType(function.getReturnType()); 935 if (paramType == Unit.class) { 936 paramType = void.class; 937 } 938 return ResolvableType.forType(paramType).resolve(method.getReturnType()); 939 } 940 } 941 catch (UnsupportedOperationException ex) { 942 // probably a synthetic class - let's use java reflection instead 943 } 944 return method.getReturnType(); 945 } 946 } 947 948}