/*
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.core;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import kotlin.reflect.KFunction;
import kotlin.reflect.KParameter;
import kotlin.reflect.jvm.ReflectJvmMapping;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
Helper class that encapsulates the specification of a method parameter, i.e. a Method
or Constructor
plus a parameter index and a nested type index for a declared generic type. Useful as a specification object to pass along. As of 4.2, there is a SynthesizingMethodParameter
subclass available which synthesizes annotations with attribute aliases. That subclass is used for web and message endpoint processing, in particular.
Author: Juergen Hoeller, Rob Harrop, Andy Clement, Sam Brannen, Sebastien Deleuze See Also: Since: 2.0
/**
* Helper class that encapsulates the specification of a method parameter, i.e. a {@link Method}
* or {@link Constructor} plus a parameter index and a nested type index for a declared generic
* type. Useful as a specification object to pass along.
*
* <p>As of 4.2, there is a {@link org.springframework.core.annotation.SynthesizingMethodParameter}
* subclass available which synthesizes annotations with attribute aliases. That subclass is used
* for web and message endpoint processing, in particular.
*
* @author Juergen Hoeller
* @author Rob Harrop
* @author Andy Clement
* @author Sam Brannen
* @author Sebastien Deleuze
* @since 2.0
* @see org.springframework.core.annotation.SynthesizingMethodParameter
*/
public class MethodParameter {
private static final Annotation[] EMPTY_ANNOTATION_ARRAY = new Annotation[0];
private final Executable executable;
private final int parameterIndex;
@Nullable
private volatile Parameter parameter;
private int nestingLevel = 1;
Map from Integer level to Integer type index. /** Map from Integer level to Integer type index. */
@Nullable
Map<Integer, Integer> typeIndexesPerLevel;
@Nullable
private volatile Class<?> containingClass;
@Nullable
private volatile Class<?> parameterType;
@Nullable
private volatile Type genericParameterType;
@Nullable
private volatile Annotation[] parameterAnnotations;
@Nullable
private volatile ParameterNameDiscoverer parameterNameDiscoverer;
@Nullable
private volatile String parameterName;
@Nullable
private volatile MethodParameter nestedMethodParameter;
Create a new MethodParameter
for the given method, with nesting level 1. Params: - method – the Method to specify a parameter for
- parameterIndex – the index of the parameter: -1 for the method
return type; 0 for the first method parameter; 1 for the second method
parameter, etc.
/**
* Create a new {@code MethodParameter} for the given method, with nesting level 1.
* @param method the Method to specify a parameter for
* @param parameterIndex the index of the parameter: -1 for the method
* return type; 0 for the first method parameter; 1 for the second method
* parameter, etc.
*/
public MethodParameter(Method method, int parameterIndex) {
this(method, parameterIndex, 1);
}
Create a new MethodParameter
for the given method. Params: - method – the Method to specify a parameter for
- parameterIndex – the index of the parameter: -1 for the method
return type; 0 for the first method parameter; 1 for the second method
parameter, etc.
- nestingLevel – the nesting level of the target type
(typically 1; e.g. in case of a List of Lists, 1 would indicate the
nested List, whereas 2 would indicate the element of the nested List)
/**
* Create a new {@code MethodParameter} for the given method.
* @param method the Method to specify a parameter for
* @param parameterIndex the index of the parameter: -1 for the method
* return type; 0 for the first method parameter; 1 for the second method
* parameter, etc.
* @param nestingLevel the nesting level of the target type
* (typically 1; e.g. in case of a List of Lists, 1 would indicate the
* nested List, whereas 2 would indicate the element of the nested List)
*/
public MethodParameter(Method method, int parameterIndex, int nestingLevel) {
Assert.notNull(method, "Method must not be null");
this.executable = method;
this.parameterIndex = validateIndex(method, parameterIndex);
this.nestingLevel = nestingLevel;
}
Create a new MethodParameter for the given constructor, with nesting level 1.
Params: - constructor – the Constructor to specify a parameter for
- parameterIndex – the index of the parameter
/**
* Create a new MethodParameter for the given constructor, with nesting level 1.
* @param constructor the Constructor to specify a parameter for
* @param parameterIndex the index of the parameter
*/
public MethodParameter(Constructor<?> constructor, int parameterIndex) {
this(constructor, parameterIndex, 1);
}
Create a new MethodParameter for the given constructor.
Params: - constructor – the Constructor to specify a parameter for
- parameterIndex – the index of the parameter
- nestingLevel – the nesting level of the target type
(typically 1; e.g. in case of a List of Lists, 1 would indicate the
nested List, whereas 2 would indicate the element of the nested List)
/**
* Create a new MethodParameter for the given constructor.
* @param constructor the Constructor to specify a parameter for
* @param parameterIndex the index of the parameter
* @param nestingLevel the nesting level of the target type
* (typically 1; e.g. in case of a List of Lists, 1 would indicate the
* nested List, whereas 2 would indicate the element of the nested List)
*/
public MethodParameter(Constructor<?> constructor, int parameterIndex, int nestingLevel) {
Assert.notNull(constructor, "Constructor must not be null");
this.executable = constructor;
this.parameterIndex = validateIndex(constructor, parameterIndex);
this.nestingLevel = nestingLevel;
}
Copy constructor, resulting in an independent MethodParameter object
based on the same metadata and cache state that the original object was in.
Params: - original – the original MethodParameter object to copy from
/**
* Copy constructor, resulting in an independent MethodParameter object
* based on the same metadata and cache state that the original object was in.
* @param original the original MethodParameter object to copy from
*/
public MethodParameter(MethodParameter original) {
Assert.notNull(original, "Original must not be null");
this.executable = original.executable;
this.parameterIndex = original.parameterIndex;
this.parameter = original.parameter;
this.nestingLevel = original.nestingLevel;
this.typeIndexesPerLevel = original.typeIndexesPerLevel;
this.containingClass = original.containingClass;
this.parameterType = original.parameterType;
this.genericParameterType = original.genericParameterType;
this.parameterAnnotations = original.parameterAnnotations;
this.parameterNameDiscoverer = original.parameterNameDiscoverer;
this.parameterName = original.parameterName;
}
Return the wrapped Method, if any.
Note: Either Method or Constructor is available.
Returns: the Method, or null
if none
/**
* Return the wrapped Method, if any.
* <p>Note: Either Method or Constructor is available.
* @return the Method, or {@code null} if none
*/
@Nullable
public Method getMethod() {
return (this.executable instanceof Method ? (Method) this.executable : null);
}
Return the wrapped Constructor, if any.
Note: Either Method or Constructor is available.
Returns: the Constructor, or null
if none
/**
* Return the wrapped Constructor, if any.
* <p>Note: Either Method or Constructor is available.
* @return the Constructor, or {@code null} if none
*/
@Nullable
public Constructor<?> getConstructor() {
return (this.executable instanceof Constructor ? (Constructor<?>) this.executable : null);
}
Return the class that declares the underlying Method or Constructor.
/**
* Return the class that declares the underlying Method or Constructor.
*/
public Class<?> getDeclaringClass() {
return this.executable.getDeclaringClass();
}
Return the wrapped member.
Returns: the Method or Constructor as Member
/**
* Return the wrapped member.
* @return the Method or Constructor as Member
*/
public Member getMember() {
return this.executable;
}
Return the wrapped annotated element.
Note: This method exposes the annotations declared on the method/constructor
itself (i.e. at the method/constructor level, not at the parameter level).
Returns: the Method or Constructor as AnnotatedElement
/**
* Return the wrapped annotated element.
* <p>Note: This method exposes the annotations declared on the method/constructor
* itself (i.e. at the method/constructor level, not at the parameter level).
* @return the Method or Constructor as AnnotatedElement
*/
public AnnotatedElement getAnnotatedElement() {
return this.executable;
}
Return the wrapped executable.
Returns: the Method or Constructor as Executable Since: 5.0
/**
* Return the wrapped executable.
* @return the Method or Constructor as Executable
* @since 5.0
*/
public Executable getExecutable() {
return this.executable;
}
Return the Parameter
descriptor for method/constructor parameter. Since: 5.0
/**
* Return the {@link Parameter} descriptor for method/constructor parameter.
* @since 5.0
*/
public Parameter getParameter() {
if (this.parameterIndex < 0) {
throw new IllegalStateException("Cannot retrieve Parameter descriptor for method return type");
}
Parameter parameter = this.parameter;
if (parameter == null) {
parameter = getExecutable().getParameters()[this.parameterIndex];
this.parameter = parameter;
}
return parameter;
}
Return the index of the method/constructor parameter.
Returns: the parameter index (-1 in case of the return type)
/**
* Return the index of the method/constructor parameter.
* @return the parameter index (-1 in case of the return type)
*/
public int getParameterIndex() {
return this.parameterIndex;
}
Increase this parameter's nesting level.
See Also: - getNestingLevel()
/**
* Increase this parameter's nesting level.
* @see #getNestingLevel()
*/
public void increaseNestingLevel() {
this.nestingLevel++;
}
Decrease this parameter's nesting level.
See Also: - getNestingLevel()
/**
* Decrease this parameter's nesting level.
* @see #getNestingLevel()
*/
public void decreaseNestingLevel() {
getTypeIndexesPerLevel().remove(this.nestingLevel);
this.nestingLevel--;
}
Return the nesting level of the target type
(typically 1; e.g. in case of a List of Lists, 1 would indicate the
nested List, whereas 2 would indicate the element of the nested List).
/**
* Return the nesting level of the target type
* (typically 1; e.g. in case of a List of Lists, 1 would indicate the
* nested List, whereas 2 would indicate the element of the nested List).
*/
public int getNestingLevel() {
return this.nestingLevel;
}
Set the type index for the current nesting level.
Params: - typeIndex – the corresponding type index (or
null
for the default type index)
See Also:
/**
* Set the type index for the current nesting level.
* @param typeIndex the corresponding type index
* (or {@code null} for the default type index)
* @see #getNestingLevel()
*/
public void setTypeIndexForCurrentLevel(int typeIndex) {
getTypeIndexesPerLevel().put(this.nestingLevel, typeIndex);
}
Return the type index for the current nesting level.
See Also: Returns: the corresponding type index, or null
if none specified (indicating the default type index)
/**
* Return the type index for the current nesting level.
* @return the corresponding type index, or {@code null}
* if none specified (indicating the default type index)
* @see #getNestingLevel()
*/
@Nullable
public Integer getTypeIndexForCurrentLevel() {
return getTypeIndexForLevel(this.nestingLevel);
}
Return the type index for the specified nesting level.
Params: - nestingLevel – the nesting level to check
Returns: the corresponding type index, or null
if none specified (indicating the default type index)
/**
* Return the type index for the specified nesting level.
* @param nestingLevel the nesting level to check
* @return the corresponding type index, or {@code null}
* if none specified (indicating the default type index)
*/
@Nullable
public Integer getTypeIndexForLevel(int nestingLevel) {
return getTypeIndexesPerLevel().get(nestingLevel);
}
Obtain the (lazily constructed) type-indexes-per-level Map.
/**
* Obtain the (lazily constructed) type-indexes-per-level Map.
*/
private Map<Integer, Integer> getTypeIndexesPerLevel() {
if (this.typeIndexesPerLevel == null) {
this.typeIndexesPerLevel = new HashMap<>(4);
}
return this.typeIndexesPerLevel;
}
Return a variant of this MethodParameter
which points to the same parameter but one nesting level deeper. This is effectively the same as increaseNestingLevel()
, just with an independent MethodParameter
object (e.g. in case of the original being cached). Since: 4.3
/**
* Return a variant of this {@code MethodParameter} which points to the
* same parameter but one nesting level deeper. This is effectively the
* same as {@link #increaseNestingLevel()}, just with an independent
* {@code MethodParameter} object (e.g. in case of the original being cached).
* @since 4.3
*/
public MethodParameter nested() {
MethodParameter nestedParam = this.nestedMethodParameter;
if (nestedParam != null) {
return nestedParam;
}
nestedParam = clone();
nestedParam.nestingLevel = this.nestingLevel + 1;
this.nestedMethodParameter = nestedParam;
return nestedParam;
}
Return whether this method indicates a parameter which is not required: either in the form of Java 8's Optional
, any variant of a parameter-level Nullable
annotation (such as from JSR-305 or the FindBugs set of annotations), or a language-level nullable type declaration in Kotlin. Since: 4.3
/**
* Return whether this method indicates a parameter which is not required:
* either in the form of Java 8's {@link java.util.Optional}, any variant
* of a parameter-level {@code Nullable} annotation (such as from JSR-305
* or the FindBugs set of annotations), or a language-level nullable type
* declaration in Kotlin.
* @since 4.3
*/
public boolean isOptional() {
return (getParameterType() == Optional.class || hasNullableAnnotation() ||
(KotlinDetector.isKotlinReflectPresent() &&
KotlinDetector.isKotlinType(getContainingClass()) &&
KotlinDelegate.isOptional(this)));
}
Check whether this method parameter is annotated with any variant of a Nullable
annotation, e.g. javax.annotation.Nullable
or edu.umd.cs.findbugs.annotations.Nullable
. /**
* Check whether this method parameter is annotated with any variant of a
* {@code Nullable} annotation, e.g. {@code javax.annotation.Nullable} or
* {@code edu.umd.cs.findbugs.annotations.Nullable}.
*/
private boolean hasNullableAnnotation() {
for (Annotation ann : getParameterAnnotations()) {
if ("Nullable".equals(ann.annotationType().getSimpleName())) {
return true;
}
}
return false;
}
Return a variant of this MethodParameter
which points to the same parameter but one nesting level deeper in case of a Optional
declaration. See Also: Since: 4.3
/**
* Return a variant of this {@code MethodParameter} which points to
* the same parameter but one nesting level deeper in case of a
* {@link java.util.Optional} declaration.
* @since 4.3
* @see #isOptional()
* @see #nested()
*/
public MethodParameter nestedIfOptional() {
return (getParameterType() == Optional.class ? nested() : this);
}
Set a containing class to resolve the parameter type against.
/**
* Set a containing class to resolve the parameter type against.
*/
void setContainingClass(Class<?> containingClass) {
this.containingClass = containingClass;
}
public Class<?> getContainingClass() {
Class<?> containingClass = this.containingClass;
return (containingClass != null ? containingClass : getDeclaringClass());
}
Set a resolved (generic) parameter type.
/**
* Set a resolved (generic) parameter type.
*/
void setParameterType(@Nullable Class<?> parameterType) {
this.parameterType = parameterType;
}
Return the type of the method/constructor parameter.
Returns: the parameter type (never null
)
/**
* Return the type of the method/constructor parameter.
* @return the parameter type (never {@code null})
*/
public Class<?> getParameterType() {
Class<?> paramType = this.parameterType;
if (paramType == null) {
if (this.parameterIndex < 0) {
Method method = getMethod();
paramType = (method != null ? method.getReturnType() : void.class);
}
else {
paramType = this.executable.getParameterTypes()[this.parameterIndex];
}
this.parameterType = paramType;
}
return paramType;
}
Return the generic type of the method/constructor parameter.
Returns: the parameter type (never null
) Since: 3.0
/**
* Return the generic type of the method/constructor parameter.
* @return the parameter type (never {@code null})
* @since 3.0
*/
public Type getGenericParameterType() {
Type paramType = this.genericParameterType;
if (paramType == null) {
if (this.parameterIndex < 0) {
Method method = getMethod();
paramType = (method != null ? method.getGenericReturnType() : void.class);
}
else {
Type[] genericParameterTypes = this.executable.getGenericParameterTypes();
int index = this.parameterIndex;
if (this.executable instanceof Constructor &&
ClassUtils.isInnerClass(this.executable.getDeclaringClass()) &&
genericParameterTypes.length == this.executable.getParameterCount() - 1) {
// Bug in javac: type array excludes enclosing instance parameter
// for inner classes with at least one generic constructor parameter,
// so access it with the actual parameter index lowered by 1
index = this.parameterIndex - 1;
}
paramType = (index >= 0 && index < genericParameterTypes.length ?
genericParameterTypes[index] : getParameterType());
}
this.genericParameterType = paramType;
}
return paramType;
}
Return the nested type of the method/constructor parameter.
See Also: Returns: the parameter type (never null
) Since: 3.1
/**
* Return the nested type of the method/constructor parameter.
* @return the parameter type (never {@code null})
* @since 3.1
* @see #getNestingLevel()
*/
public Class<?> getNestedParameterType() {
if (this.nestingLevel > 1) {
Type type = getGenericParameterType();
for (int i = 2; i <= this.nestingLevel; i++) {
if (type instanceof ParameterizedType) {
Type[] args = ((ParameterizedType) type).getActualTypeArguments();
Integer index = getTypeIndexForLevel(i);
type = args[index != null ? index : args.length - 1];
}
// TODO: Object.class if unresolvable
}
if (type instanceof Class) {
return (Class<?>) type;
}
else if (type instanceof ParameterizedType) {
Type arg = ((ParameterizedType) type).getRawType();
if (arg instanceof Class) {
return (Class<?>) arg;
}
}
return Object.class;
}
else {
return getParameterType();
}
}
Return the nested generic type of the method/constructor parameter.
See Also: Returns: the parameter type (never null
) Since: 4.2
/**
* Return the nested generic type of the method/constructor parameter.
* @return the parameter type (never {@code null})
* @since 4.2
* @see #getNestingLevel()
*/
public Type getNestedGenericParameterType() {
if (this.nestingLevel > 1) {
Type type = getGenericParameterType();
for (int i = 2; i <= this.nestingLevel; i++) {
if (type instanceof ParameterizedType) {
Type[] args = ((ParameterizedType) type).getActualTypeArguments();
Integer index = getTypeIndexForLevel(i);
type = args[index != null ? index : args.length - 1];
}
}
return type;
}
else {
return getGenericParameterType();
}
}
Return the annotations associated with the target method/constructor itself.
/**
* Return the annotations associated with the target method/constructor itself.
*/
public Annotation[] getMethodAnnotations() {
return adaptAnnotationArray(getAnnotatedElement().getAnnotations());
}
Return the method/constructor annotation of the given type, if available.
Params: - annotationType – the annotation type to look for
Returns: the annotation object, or null
if not found
/**
* Return the method/constructor annotation of the given type, if available.
* @param annotationType the annotation type to look for
* @return the annotation object, or {@code null} if not found
*/
@Nullable
public <A extends Annotation> A getMethodAnnotation(Class<A> annotationType) {
A annotation = getAnnotatedElement().getAnnotation(annotationType);
return (annotation != null ? adaptAnnotation(annotation) : null);
}
Return whether the method/constructor is annotated with the given type.
Params: - annotationType – the annotation type to look for
See Also: Since: 4.3
/**
* Return whether the method/constructor is annotated with the given type.
* @param annotationType the annotation type to look for
* @since 4.3
* @see #getMethodAnnotation(Class)
*/
public <A extends Annotation> boolean hasMethodAnnotation(Class<A> annotationType) {
return getAnnotatedElement().isAnnotationPresent(annotationType);
}
Return the annotations associated with the specific method/constructor parameter.
/**
* Return the annotations associated with the specific method/constructor parameter.
*/
public Annotation[] getParameterAnnotations() {
Annotation[] paramAnns = this.parameterAnnotations;
if (paramAnns == null) {
Annotation[][] annotationArray = this.executable.getParameterAnnotations();
int index = this.parameterIndex;
if (this.executable instanceof Constructor &&
ClassUtils.isInnerClass(this.executable.getDeclaringClass()) &&
annotationArray.length == this.executable.getParameterCount() - 1) {
// Bug in javac in JDK <9: annotation array excludes enclosing instance parameter
// for inner classes, so access it with the actual parameter index lowered by 1
index = this.parameterIndex - 1;
}
paramAnns = (index >= 0 && index < annotationArray.length ?
adaptAnnotationArray(annotationArray[index]) : EMPTY_ANNOTATION_ARRAY);
this.parameterAnnotations = paramAnns;
}
return paramAnns;
}
Return true
if the parameter has at least one annotation, false
if it has none. See Also:
/**
* Return {@code true} if the parameter has at least one annotation,
* {@code false} if it has none.
* @see #getParameterAnnotations()
*/
public boolean hasParameterAnnotations() {
return (getParameterAnnotations().length != 0);
}
Return the parameter annotation of the given type, if available.
Params: - annotationType – the annotation type to look for
Returns: the annotation object, or null
if not found
/**
* Return the parameter annotation of the given type, if available.
* @param annotationType the annotation type to look for
* @return the annotation object, or {@code null} if not found
*/
@SuppressWarnings("unchecked")
@Nullable
public <A extends Annotation> A getParameterAnnotation(Class<A> annotationType) {
Annotation[] anns = getParameterAnnotations();
for (Annotation ann : anns) {
if (annotationType.isInstance(ann)) {
return (A) ann;
}
}
return null;
}
Return whether the parameter is declared with the given annotation type.
Params: - annotationType – the annotation type to look for
See Also:
/**
* Return whether the parameter is declared with the given annotation type.
* @param annotationType the annotation type to look for
* @see #getParameterAnnotation(Class)
*/
public <A extends Annotation> boolean hasParameterAnnotation(Class<A> annotationType) {
return (getParameterAnnotation(annotationType) != null);
}
Initialize parameter name discovery for this method parameter.
This method does not actually try to retrieve the parameter name at this point; it just allows discovery to happen when the application calls getParameterName()
(if ever).
/**
* Initialize parameter name discovery for this method parameter.
* <p>This method does not actually try to retrieve the parameter name at
* this point; it just allows discovery to happen when the application calls
* {@link #getParameterName()} (if ever).
*/
public void initParameterNameDiscovery(@Nullable ParameterNameDiscoverer parameterNameDiscoverer) {
this.parameterNameDiscoverer = parameterNameDiscoverer;
}
Return the name of the method/constructor parameter.
Returns: the parameter name (may be null
if no parameter name metadata is contained in the class file or no ParameterNameDiscoverer
has been set to begin with)
/**
* Return the name of the method/constructor parameter.
* @return the parameter name (may be {@code null} if no
* parameter name metadata is contained in the class file or no
* {@link #initParameterNameDiscovery ParameterNameDiscoverer}
* has been set to begin with)
*/
@Nullable
public String getParameterName() {
if (this.parameterIndex < 0) {
return null;
}
ParameterNameDiscoverer discoverer = this.parameterNameDiscoverer;
if (discoverer != null) {
String[] parameterNames = null;
if (this.executable instanceof Method) {
parameterNames = discoverer.getParameterNames((Method) this.executable);
}
else if (this.executable instanceof Constructor) {
parameterNames = discoverer.getParameterNames((Constructor<?>) this.executable);
}
if (parameterNames != null) {
this.parameterName = parameterNames[this.parameterIndex];
}
this.parameterNameDiscoverer = null;
}
return this.parameterName;
}
A template method to post-process a given annotation instance before
returning it to the caller.
The default implementation simply returns the given annotation as-is.
Params: - annotation – the annotation about to be returned
Returns: the post-processed annotation (or simply the original one) Since: 4.2
/**
* A template method to post-process a given annotation instance before
* returning it to the caller.
* <p>The default implementation simply returns the given annotation as-is.
* @param annotation the annotation about to be returned
* @return the post-processed annotation (or simply the original one)
* @since 4.2
*/
protected <A extends Annotation> A adaptAnnotation(A annotation) {
return annotation;
}
A template method to post-process a given annotation array before
returning it to the caller.
The default implementation simply returns the given annotation array as-is.
Params: - annotations – the annotation array about to be returned
Returns: the post-processed annotation array (or simply the original one) Since: 4.2
/**
* A template method to post-process a given annotation array before
* returning it to the caller.
* <p>The default implementation simply returns the given annotation array as-is.
* @param annotations the annotation array about to be returned
* @return the post-processed annotation array (or simply the original one)
* @since 4.2
*/
protected Annotation[] adaptAnnotationArray(Annotation[] annotations) {
return annotations;
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof MethodParameter)) {
return false;
}
MethodParameter otherParam = (MethodParameter) other;
return (this.parameterIndex == otherParam.parameterIndex && getExecutable().equals(otherParam.getExecutable()));
}
@Override
public int hashCode() {
return (getExecutable().hashCode() * 31 + this.parameterIndex);
}
@Override
public String toString() {
Method method = getMethod();
return (method != null ? "method '" + method.getName() + "'" : "constructor") +
" parameter " + this.parameterIndex;
}
@Override
public MethodParameter clone() {
return new MethodParameter(this);
}
Create a new MethodParameter for the given method or constructor.
This is a convenience factory method for scenarios where a
Method or Constructor reference is treated in a generic fashion.
Params: - methodOrConstructor – the Method or Constructor to specify a parameter for
- parameterIndex – the index of the parameter
Returns: the corresponding MethodParameter instance Deprecated: as of 5.0, in favor of forExecutable
/**
* Create a new MethodParameter for the given method or constructor.
* <p>This is a convenience factory method for scenarios where a
* Method or Constructor reference is treated in a generic fashion.
* @param methodOrConstructor the Method or Constructor to specify a parameter for
* @param parameterIndex the index of the parameter
* @return the corresponding MethodParameter instance
* @deprecated as of 5.0, in favor of {@link #forExecutable}
*/
@Deprecated
public static MethodParameter forMethodOrConstructor(Object methodOrConstructor, int parameterIndex) {
if (!(methodOrConstructor instanceof Executable)) {
throw new IllegalArgumentException(
"Given object [" + methodOrConstructor + "] is neither a Method nor a Constructor");
}
return forExecutable((Executable) methodOrConstructor, parameterIndex);
}
Create a new MethodParameter for the given method or constructor.
This is a convenience factory method for scenarios where a
Method or Constructor reference is treated in a generic fashion.
Params: - executable – the Method or Constructor to specify a parameter for
- parameterIndex – the index of the parameter
Returns: the corresponding MethodParameter instance Since: 5.0
/**
* Create a new MethodParameter for the given method or constructor.
* <p>This is a convenience factory method for scenarios where a
* Method or Constructor reference is treated in a generic fashion.
* @param executable the Method or Constructor to specify a parameter for
* @param parameterIndex the index of the parameter
* @return the corresponding MethodParameter instance
* @since 5.0
*/
public static MethodParameter forExecutable(Executable executable, int parameterIndex) {
if (executable instanceof Method) {
return new MethodParameter((Method) executable, parameterIndex);
}
else if (executable instanceof Constructor) {
return new MethodParameter((Constructor<?>) executable, parameterIndex);
}
else {
throw new IllegalArgumentException("Not a Method/Constructor: " + executable);
}
}
Create a new MethodParameter for the given parameter descriptor.
This is a convenience factory method for scenarios where a Java 8 Parameter
descriptor is already available.
Params: - parameter – the parameter descriptor
Returns: the corresponding MethodParameter instance Since: 5.0
/**
* Create a new MethodParameter for the given parameter descriptor.
* <p>This is a convenience factory method for scenarios where a
* Java 8 {@link Parameter} descriptor is already available.
* @param parameter the parameter descriptor
* @return the corresponding MethodParameter instance
* @since 5.0
*/
public static MethodParameter forParameter(Parameter parameter) {
return forExecutable(parameter.getDeclaringExecutable(), findParameterIndex(parameter));
}
protected static int findParameterIndex(Parameter parameter) {
Executable executable = parameter.getDeclaringExecutable();
Parameter[] allParams = executable.getParameters();
// Try first with identity checks for greater performance.
for (int i = 0; i < allParams.length; i++) {
if (parameter == allParams[i]) {
return i;
}
}
// Potentially try again with object equality checks in order to avoid race
// conditions while invoking java.lang.reflect.Executable.getParameters().
for (int i = 0; i < allParams.length; i++) {
if (parameter.equals(allParams[i])) {
return i;
}
}
throw new IllegalArgumentException("Given parameter [" + parameter +
"] does not match any parameter in the declaring executable");
}
private static int validateIndex(Executable executable, int parameterIndex) {
int count = executable.getParameterCount();
Assert.isTrue(parameterIndex >= -1 && parameterIndex < count,
() -> "Parameter index needs to be between -1 and " + (count - 1));
return parameterIndex;
}
Inner class to avoid a hard dependency on Kotlin at runtime.
/**
* Inner class to avoid a hard dependency on Kotlin at runtime.
*/
private static class KotlinDelegate {
Check whether the specified MethodParameter
represents a nullable Kotlin type or an optional parameter (with a default value in the Kotlin declaration). /**
* Check whether the specified {@link MethodParameter} represents a nullable Kotlin type
* or an optional parameter (with a default value in the Kotlin declaration).
*/
public static boolean isOptional(MethodParameter param) {
Method method = param.getMethod();
Constructor<?> ctor = param.getConstructor();
int index = param.getParameterIndex();
if (method != null && index == -1) {
KFunction<?> function = ReflectJvmMapping.getKotlinFunction(method);
return (function != null && function.getReturnType().isMarkedNullable());
}
else {
KFunction<?> function = null;
Predicate<KParameter> predicate = null;
if (method != null) {
function = ReflectJvmMapping.getKotlinFunction(method);
predicate = p -> KParameter.Kind.VALUE.equals(p.getKind());
}
else if (ctor != null) {
function = ReflectJvmMapping.getKotlinFunction(ctor);
predicate = p -> KParameter.Kind.VALUE.equals(p.getKind()) ||
KParameter.Kind.INSTANCE.equals(p.getKind());
}
if (function != null) {
List<KParameter> parameters = function.getParameters();
KParameter parameter = parameters
.stream()
.filter(predicate)
.collect(Collectors.toList())
.get(index);
return (parameter.getType().isMarkedNullable() || parameter.isOptional());
}
}
return false;
}
}
}