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}