/* |
|
* Copyright (c) 2013, 2021, Oracle and/or its affiliates. All rights reserved. |
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
* |
|
* This code is free software; you can redistribute it and/or modify it |
|
* under the terms of the GNU General Public License version 2 only, as |
|
* published by the Free Software Foundation. Oracle designates this |
|
* particular file as subject to the "Classpath" exception as provided |
|
* by Oracle in the LICENSE file that accompanied this code. |
|
* |
|
* This code is distributed in the hope that it will be useful, but WITHOUT |
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
* version 2 for more details (a copy is included in the LICENSE file that |
|
* accompanied this code). |
|
* |
|
* You should have received a copy of the GNU General Public License version |
|
* 2 along with this work; if not, write to the Free Software Foundation, |
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
* |
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
* or visit www.oracle.com if you need additional information or have any |
|
* questions. |
|
*/ |
|
package java.lang.reflect; |
|
import java.lang.annotation.*; |
|
import java.util.HashMap; |
|
import java.util.Map; |
|
import java.util.Objects; |
|
import sun.reflect.annotation.AnnotationSupport; |
|
/** |
|
* Information about method parameters. |
|
* |
|
* A {@code Parameter} provides information about method parameters, |
|
* including its name and modifiers. It also provides an alternate |
|
* means of obtaining attributes for the parameter. |
|
* |
|
* @since 1.8 |
|
*/ |
|
public final class Parameter implements AnnotatedElement { |
|
private final String name; |
|
private final int modifiers; |
|
private final Executable executable; |
|
private final int index; |
|
/** |
|
* Package-private constructor for {@code Parameter}. |
|
* |
|
* If method parameter data is present in the classfile, then the |
|
* JVM creates {@code Parameter} objects directly. If it is |
|
* absent, however, then {@code Executable} uses this constructor |
|
* to synthesize them. |
|
* |
|
* @param name The name of the parameter. |
|
* @param modifiers The modifier flags for the parameter. |
|
* @param executable The executable which defines this parameter. |
|
* @param index The index of the parameter. |
|
*/ |
|
Parameter(String name, |
|
int modifiers, |
|
Executable executable, |
|
int index) { |
|
this.name = name; |
|
this.modifiers = modifiers; |
|
this.executable = executable; |
|
this.index = index; |
|
} |
|
/** |
|
* Compares based on the executable and the index. |
|
* |
|
* @param obj The object to compare. |
|
* @return Whether or not this is equal to the argument. |
|
*/ |
|
@Override |
|
public boolean equals(Object obj) { |
|
return (obj instanceof Parameter other) |
|
&& other.executable.equals(executable) |
|
&& other.index == index; |
|
} |
|
/** |
|
* Returns a hash code based on the executable's hash code and the |
|
* index. |
|
* |
|
* @return A hash code based on the executable's hash code. |
|
*/ |
|
@Override |
|
public int hashCode() { |
|
return executable.hashCode() ^ index; |
|
} |
|
/** |
|
* Returns true if the parameter has a name according to the class |
|
* file; returns false otherwise. Whether a parameter has a name |
|
* is determined by the {@literal MethodParameters} attribute of |
|
* the method which declares the parameter. |
|
* |
|
* @return true if and only if the parameter has a name according |
|
* to the class file. |
|
*/ |
|
public boolean isNamePresent() { |
|
return executable.hasRealParameterData() && name != null; |
|
} |
|
/** |
|
* Returns a string describing this parameter. The format is the |
|
* modifiers for the parameter, if any, in canonical order as |
|
* recommended by <cite>The Java Language |
|
* Specification</cite>, followed by the fully-qualified type of |
|
* the parameter (excluding the last [] if the parameter is |
|
* variable arity), followed by "..." if the parameter is variable |
|
* arity, followed by a space, followed by the name of the |
|
* parameter. |
|
* |
|
* @return A string representation of the parameter and associated |
|
* information. |
|
*/ |
|
@Override |
|
public String toString() { |
|
final StringBuilder sb = new StringBuilder(); |
|
final Type type = getParameterizedType(); |
|
final String typename = type.getTypeName(); |
|
sb.append(Modifier.toString(getModifiers())); |
|
if(0 != modifiers) |
|
sb.append(' '); |
|
if(isVarArgs()) |
|
sb.append(typename.replaceFirst("\\[\\]$", "...")); |
|
else |
|
sb.append(typename); |
|
sb.append(' '); |
|
sb.append(getName()); |
|
return sb.toString(); |
|
} |
|
/** |
|
* {@return the {@code Executable} declaring this parameter} |
|
*/ |
|
public Executable getDeclaringExecutable() { |
|
return executable; |
|
} |
|
/** |
|
* {@return the Java language {@linkplain Modifier modifiers} for |
|
* the parameter represented by this object} |
|
* |
|
* @jls 8.4.1 Formal Parameters |
|
* @see <a |
|
* href="{@docRoot}/java.base/java/lang/reflect/package-summary.html#LanguageJvmModel">Java |
|
* programming language and JVM modeling in core reflection</a> |
|
*/ |
|
public int getModifiers() { |
|
return modifiers; |
|
} |
|
/** |
|
* Returns the name of the parameter. If the parameter's name is |
|
* {@linkplain #isNamePresent() present}, then this method returns |
|
* the name provided by the class file. Otherwise, this method |
|
* synthesizes a name of the form argN, where N is the index of |
|
* the parameter in the descriptor of the method which declares |
|
* the parameter. |
|
* |
|
* @return The name of the parameter, either provided by the class |
|
* file or synthesized if the class file does not provide |
|
* a name. |
|
*/ |
|
public String getName() { |
|
// Note: empty strings as parameter names are now outlawed. |
|
// The .isEmpty() is for compatibility with current JVM |
|
// behavior. It may be removed at some point. |
|
if(name == null || name.isEmpty()) |
|
return "arg" + index; |
|
else |
|
return name; |
|
} |
|
// Package-private accessor to the real name field. |
|
String getRealName() { |
|
return name; |
|
} |
|
/** |
|
* Returns a {@code Type} object that identifies the parameterized |
|
* type for the parameter represented by this {@code Parameter} |
|
* object. |
|
* |
|
* @return a {@code Type} object identifying the parameterized |
|
* type of the parameter represented by this object |
|
*/ |
|
public Type getParameterizedType() { |
|
Type tmp = parameterTypeCache; |
|
if (null == tmp) { |
|
tmp = executable.getAllGenericParameterTypes()[index]; |
|
parameterTypeCache = tmp; |
|
} |
|
return tmp; |
|
} |
|
private transient volatile Type parameterTypeCache; |
|
/** |
|
* Returns a {@code Class} object that identifies the |
|
* declared type for the parameter represented by this |
|
* {@code Parameter} object. |
|
* |
|
* @return a {@code Class} object identifying the declared |
|
* type of the parameter represented by this object |
|
*/ |
|
public Class<?> getType() { |
|
Class<?> tmp = parameterClassCache; |
|
if (null == tmp) { |
|
tmp = executable.getParameterTypes()[index]; |
|
parameterClassCache = tmp; |
|
} |
|
return tmp; |
|
} |
|
/** |
|
* Returns an AnnotatedType object that represents the use of a type to |
|
* specify the type of the formal parameter represented by this Parameter. |
|
* |
|
* @return an {@code AnnotatedType} object representing the use of a type |
|
* to specify the type of the formal parameter represented by this |
|
* Parameter |
|
*/ |
|
public AnnotatedType getAnnotatedType() { |
|
// no caching for now |
|
return executable.getAnnotatedParameterTypes()[index]; |
|
} |
|
private transient volatile Class<?> parameterClassCache; |
|
/** |
|
* Returns {@code true} if this parameter is implicitly declared |
|
* in source code; returns {@code false} otherwise. |
|
* |
|
* @return true if and only if this parameter is implicitly |
|
* declared as defined by <cite>The Java Language |
|
* Specification</cite>. |
|
*/ |
|
public boolean isImplicit() { |
|
return Modifier.isMandated(getModifiers()); |
|
} |
|
/** |
|
* Returns {@code true} if this parameter is neither implicitly |
|
* nor explicitly declared in source code; returns {@code false} |
|
* otherwise. |
|
* |
|
* @return true if and only if this parameter is a synthetic |
|
* construct as defined by |
|
* <cite>The Java Language Specification</cite>. |
|
* @jls 13.1 The Form of a Binary |
|
* @see <a |
|
* href="{@docRoot}/java.base/java/lang/reflect/package-summary.html#LanguageJvmModel">Java |
|
* programming language and JVM modeling in core reflection</a> |
|
*/ |
|
public boolean isSynthetic() { |
|
return Modifier.isSynthetic(getModifiers()); |
|
} |
|
/** |
|
* Returns {@code true} if this parameter represents a variable |
|
* argument list; returns {@code false} otherwise. |
|
* |
|
* @return {@code true} if an only if this parameter represents a |
|
* variable argument list. |
|
*/ |
|
public boolean isVarArgs() { |
|
return executable.isVarArgs() && |
|
index == executable.getParameterCount() - 1; |
|
} |
|
/** |
|
* {@inheritDoc} |
|
* <p>Note that any annotation returned by this method is a |
|
* declaration annotation. |
|
* @throws NullPointerException {@inheritDoc} |
|
*/ |
|
@Override |
|
public <T extends Annotation> T getAnnotation(Class<T> annotationClass) { |
|
Objects.requireNonNull(annotationClass); |
|
return annotationClass.cast(declaredAnnotations().get(annotationClass)); |
|
} |
|
/** |
|
* {@inheritDoc} |
|
* <p>Note that any annotations returned by this method are |
|
* declaration annotations. |
|
* |
|
* @throws NullPointerException {@inheritDoc} |
|
*/ |
|
@Override |
|
public <T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass) { |
|
Objects.requireNonNull(annotationClass); |
|
return AnnotationSupport.getDirectlyAndIndirectlyPresent(declaredAnnotations(), annotationClass); |
|
} |
|
/** |
|
* {@inheritDoc} |
|
* <p>Note that any annotations returned by this method are |
|
* declaration annotations. |
|
*/ |
|
@Override |
|
public Annotation[] getDeclaredAnnotations() { |
|
return executable.getParameterAnnotations()[index]; |
|
} |
|
/** |
|
* {@inheritDoc} |
|
* <p>Note that any annotation returned by this method is a |
|
* declaration annotation. |
|
* |
|
* @throws NullPointerException {@inheritDoc} |
|
*/ |
|
@Override |
|
public <T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass) { |
|
// Only annotations on classes are inherited, for all other |
|
// objects getDeclaredAnnotation is the same as |
|
// getAnnotation. |
|
return getAnnotation(annotationClass); |
|
} |
|
/** |
|
* {@inheritDoc} |
|
* <p>Note that any annotations returned by this method are |
|
* declaration annotations. |
|
* |
|
* @throws NullPointerException {@inheritDoc} |
|
*/ |
|
@Override |
|
public <T extends Annotation> T[] getDeclaredAnnotationsByType(Class<T> annotationClass) { |
|
// Only annotations on classes are inherited, for all other |
|
// objects getDeclaredAnnotations is the same as |
|
// getAnnotations. |
|
return getAnnotationsByType(annotationClass); |
|
} |
|
/** |
|
* {@inheritDoc} |
|
* <p>Note that any annotations returned by this method are |
|
* declaration annotations. |
|
*/ |
|
@Override |
|
public Annotation[] getAnnotations() { |
|
return getDeclaredAnnotations(); |
|
} |
|
private transient Map<Class<? extends Annotation>, Annotation> declaredAnnotations; |
|
private synchronized Map<Class<? extends Annotation>, Annotation> declaredAnnotations() { |
|
if(null == declaredAnnotations) { |
|
declaredAnnotations = new HashMap<>(); |
|
for (Annotation a : getDeclaredAnnotations()) |
|
declaredAnnotations.put(a.annotationType(), a); |
|
} |
|
return declaredAnnotations; |
|
} |
|
} |