All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.juneau.reflect.ExecutableInfo Maven / Gradle / Ivy

// ***************************************************************************************************************************
// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
// * to you 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.apache.juneau.reflect;

import static org.apache.juneau.common.internal.StringUtils.*;
import static org.apache.juneau.internal.CollectionUtils.*;
import static org.apache.juneau.internal.ConsumerUtils.*;

import java.lang.annotation.*;
import java.lang.reflect.*;
import java.util.*;
import java.util.function.*;

import org.apache.juneau.*;
import org.apache.juneau.internal.*;

/**
 * Contains common methods between {@link ConstructorInfo} and {@link MethodInfo}.
 *
 * 
See Also:
    *
*/ @FluentSetters public abstract class ExecutableInfo { final ClassInfo declaringClass; final Executable e; final boolean isConstructor; private volatile ParamInfo[] params; private volatile ClassInfo[] paramTypes, exceptionInfos; private volatile Class[] rawParamTypes; private volatile Type[] rawGenericParamTypes; private volatile Parameter[] rawParameters; private volatile Annotation[][] parameterAnnotations; private volatile Annotation[] declaredAnnotations; /** * Constructor. * * @param declaringClass The class that declares this method or constructor. * @param e The constructor or method that this info represents. */ protected ExecutableInfo(ClassInfo declaringClass, Executable e) { this.declaringClass = declaringClass; this.e = e; this.isConstructor = e instanceof Constructor; } /** * Returns metadata about the class that declared this method or constructor. * * @return Metadata about the class that declared this method or constructor. */ public final ClassInfo getDeclaringClass() { return declaringClass; } /** * Returns true if this executable represents a {@link Constructor}. * * @return * true if this executable represents a {@link Constructor} and can be cast to {@link ConstructorInfo}. * false if this executable represents a {@link Method} and can be cast to {@link MethodInfo}. */ public final boolean isConstructor() { return isConstructor; } //----------------------------------------------------------------------------------------------------------------- // Parameters //----------------------------------------------------------------------------------------------------------------- /** * Returns the number of parameters in this executable. * *

* Same as calling {@link Executable#getParameterCount()}. * * @return The number of parameters in this executable. */ public final int getParamCount() { return e.getParameterCount(); } /** * Returns true if this executable has at least one parameter. * *

* Same as calling {@link Executable#getParameterCount()} and comparing with zero. * * @return true if this executable has at least one parameter. */ public final boolean hasParams() { return getParamCount() != 0; } /** * Returns true if this executable has no parameters. * *

* Same as calling {@link Executable#getParameterCount()} and comparing with zero. * * @return true if this executable has no parameters. */ public final boolean hasNoParams() { return getParamCount() == 0; } /** * Returns true if this executable has this number of arguments. * *

* Same as calling {@link Executable#getParameterCount()} and comparing the count. * * @param number The number of expected arguments. * @return true if this executable has this number of arguments. */ public final boolean hasNumParams(int number) { return getParamCount() == number; } /** * Returns the parameters defined on this executable. * *

* Same as calling {@link Executable#getParameters()} but wraps the results * * @return An array of parameter information, never null. */ public final List getParams() { return ulist(_getParams()); } /** * Performs an action on every parameter that matches the specified filter. * * @param filter The filter, can be null. * @param action The action to perform. * @return This object. */ public ExecutableInfo forEachParam(Predicate filter, Consumer action) { for (ParamInfo pi : _getParams()) if (test(filter, pi)) action.accept(pi); return this; } /** * Returns parameter information at the specified index. * * @param index The parameter index. * @return The parameter information, never null. */ public final ParamInfo getParam(int index) { checkIndex(index); return _getParams()[index]; } /** * Returns the parameter types on this executable. * * @return The parameter types on this executable. */ public final List getParamTypes() { return ulist(_getParameterTypes()); } /** * Returns the parameter type of the parameter at the specified index. * * @param index The parameter index. * @return The parameter type of the parameter at the specified index. */ public final ClassInfo getParamType(int index) { checkIndex(index); return _getParameterTypes()[index]; } /** * Returns the raw parameter types on this executable. * * @return The raw parameter types on this executable. */ public final List> getRawParamTypes() { return ulist(_getRawParamTypes()); } /** * Returns the raw parameter type of the parameter at the specified index. * * @param index The parameter index. * @return The raw parameter type of the parameter at the specified index. */ public final Class getRawParamType(int index) { checkIndex(index); return _getRawParamTypes()[index]; } /** * Returns the raw generic parameter types on this executable. * * @return The raw generic parameter types on this executable. */ public final List getRawGenericParamTypes() { return ulist(_getRawGenericParamTypes()); } /** * Returns the raw generic parameter type of the parameter at the specified index. * * @param index The parameter index. * @return The raw generic parameter type of the parameter at the specified index. */ public final Type getRawGenericParamType(int index) { checkIndex(index); return _getRawGenericParamTypes()[index]; } /** * Returns an array of raw {@link Parameter} objects that represent all the parameters to the underlying executable represented by this object. * * @return An array of raw {@link Parameter} objects, or an empty array if executable has no parameters. * @see Executable#getParameters() */ public final List getRawParameters() { return ulist(_getRawParameters()); } /** * Returns the raw {@link Parameter} object that represents the parameter at the specified index. * * @param index The parameter index. * @return The raw {@link Parameter} object that represents the parameter at the specified index. * @see Executable#getParameters() */ public final Parameter getRawParameter(int index) { checkIndex(index); return _getRawParameters()[index]; } final ParamInfo[] _getParams() { if (params == null) { synchronized(this) { Parameter[] rp = _getRawParameters(); ParamInfo[] l = new ParamInfo[rp.length]; for (int i = 0; i < rp.length; i++) l[i] = new ParamInfo(this, rp[i], i); params = l; } } return params; } final ClassInfo[] _getParameterTypes() { if (paramTypes == null) { synchronized(this) { Class[] ptc = _getRawParamTypes(); // Note that due to a bug involving Enum constructors, getGenericParameterTypes() may // always return an empty array. This appears to be fixed in Java 8 b75. Type[] ptt = _getRawGenericParamTypes(); if (ptt.length != ptc.length) { // Bug in javac: generic type array excludes enclosing instance parameter // for inner classes with at least one generic constructor parameter. if (ptt.length + 1 == ptc.length) { Type[] ptt2 = new Type[ptc.length]; ptt2[0] = ptc[0]; for (int i = 0; i < ptt.length; i++) ptt2[i+1] = ptt[i]; ptt = ptt2; } else { ptt = ptc; } } ClassInfo[] l = new ClassInfo[ptc.length]; for (int i = 0; i < ptc.length; i++) l[i] = ClassInfo.of(ptc[i], ptt[i]); paramTypes = l; } } return paramTypes; } Class[] _getRawParamTypes() { if (rawParamTypes == null) { synchronized(this) { rawParamTypes = e.getParameterTypes(); } } return rawParamTypes; } final Type[] _getRawGenericParamTypes() { if (rawGenericParamTypes == null) { synchronized(this) { rawGenericParamTypes = e.getGenericParameterTypes(); } } return rawGenericParamTypes; } final Parameter[] _getRawParameters() { if (rawParameters == null) { synchronized(this) { rawParameters = e.getParameters(); } } return rawParameters; } private void checkIndex(int index) { int pc = getParamCount(); if (pc == 0) throw new IndexOutOfBoundsException(format("Invalid index ''{0}''. No parameters.", index)); if (index < 0 || index >= pc) throw new IndexOutOfBoundsException(format("Invalid index ''{0}''. Parameter count: {1}", index, pc)); } //----------------------------------------------------------------------------------------------------------------- // Annotations //----------------------------------------------------------------------------------------------------------------- /** * Performs an action on all matching parameter annotations at the specified parameter index. * * @param The annotation type. * @param index The parameter index. * @param type The annotation type. * @param predicate The predicate. * @param consumer The consumer. * @return This object. */ public final ExecutableInfo forEachParameterAnnotation(int index, Class type, Predicate predicate, Consumer consumer) { for (Annotation a : _getParameterAnnotations(index)) if (type.isInstance(a)) consume(predicate, consumer, type.cast(a)); return this; } final Annotation[][] _getParameterAnnotations() { if (parameterAnnotations == null) { synchronized(this) { parameterAnnotations = e.getParameterAnnotations(); } } return parameterAnnotations; } final Annotation[] _getParameterAnnotations(int index) { checkIndex(index); Annotation[][] x = _getParameterAnnotations(); int c = e.getParameterCount(); if (c != x.length) { // Seems to be a JVM bug where getParameterAnnotations() don't take mandated parameters into account. Annotation[][] x2 = new Annotation[c][]; int diff = c - x.length; for (int i = 0; i < diff; i++) x2[i] = new Annotation[0]; for (int i = diff; i < c; i++) x2[i] = x[i-diff]; x = x2; } return x[index]; } final Annotation[] _getDeclaredAnnotations() { if (declaredAnnotations == null) { synchronized(this) { declaredAnnotations = e.getDeclaredAnnotations(); } } return declaredAnnotations; } //----------------------------------------------------------------------------------------------------------------- // Exceptions //----------------------------------------------------------------------------------------------------------------- /** * Returns the exception types on this executable. * * @return The exception types on this executable. */ public final List getExceptionTypes() { return ulist(_getExceptionTypes()); } final ClassInfo[] _getExceptionTypes() { if (exceptionInfos == null) { synchronized(this) { Class[] exceptionTypes = e.getExceptionTypes(); ClassInfo[] l = new ClassInfo[exceptionTypes.length]; for (int i = 0; i < exceptionTypes.length; i++) l[i] = ClassInfo.of(exceptionTypes[i]); exceptionInfos = l; } } return exceptionInfos; } //----------------------------------------------------------------------------------------------------------------- // Characteristics //----------------------------------------------------------------------------------------------------------------- /** * Returns true if all specified flags are applicable to this method. * * @param flags The flags to test for. * @return true if all specified flags are applicable to this method. */ public final boolean isAll(ReflectFlags...flags) { for (ReflectFlags f : flags) { switch (f) { case DEPRECATED: if (isNotDeprecated()) return false; break; case NOT_DEPRECATED: if (isDeprecated()) return false; break; case HAS_PARAMS: if (hasNoParams()) return false; break; case HAS_NO_PARAMS: if (hasParams()) return false; break; case PUBLIC: if (isNotPublic()) return false; break; case NOT_PUBLIC: if (isPublic()) return false; break; case PROTECTED: if (isNotProtected()) return false; break; case NOT_PROTECTED: if (isProtected()) return false; break; case STATIC: if (isNotStatic()) return false; break; case NOT_STATIC: if (isStatic()) return false; break; case ABSTRACT: if (isNotAbstract()) return false; break; case NOT_ABSTRACT: if (isAbstract()) return false; break; default: throw new BasicRuntimeException("Invalid flag for executable: {0}", f); } } return true; } /** * Returns true if all specified flags are applicable to this field. * * @param flags The flags to test for. * @return true if all specified flags are applicable to this field. */ public final boolean is(ReflectFlags...flags) { return isAll(flags); } /** * Returns true if all specified flags are applicable to this method. * * @param flags The flags to test for. * @return true if all specified flags are applicable to this method. */ public final boolean isAny(ReflectFlags...flags) { for (ReflectFlags f : flags) { switch (f) { case DEPRECATED: if (isDeprecated()) return true; break; case NOT_DEPRECATED: if (isNotDeprecated()) return true; break; case HAS_PARAMS: if (hasParams()) return true; break; case HAS_NO_PARAMS: if (hasNoParams()) return true; break; case PUBLIC: if (isPublic()) return true; break; case NOT_PUBLIC: if (isNotPublic()) return true; break; case PROTECTED: if (isProtected()) return true; break; case NOT_PROTECTED: if (isNotProtected()) return true; break; case STATIC: if (isStatic()) return true; break; case NOT_STATIC: if (isNotStatic()) return true; break; case ABSTRACT: if (isAbstract()) return true; break; case NOT_ABSTRACT: if (isNotAbstract()) return true; break; default: throw new BasicRuntimeException("Invalid flag for executable: {0}", f); } } return false; } /** * Returns true if this method has the specified arguments. * * @param args The arguments to test for. * @return true if this method has this arguments in the exact order. */ public final boolean hasParamTypes(Class...args) { Class[] pt = _getRawParamTypes(); if (pt.length == args.length) { for (int i = 0; i < pt.length; i++) if (! pt[i].equals(args[i])) return false; return true; } return false; } /** * Returns true if this method has the specified arguments. * * @param args The arguments to test for. * @return true if this method has this arguments in the exact order. */ public final boolean hasParamTypes(ClassInfo...args) { Class[] pt = _getRawParamTypes(); if (pt.length == args.length) { for (int i = 0; i < pt.length; i++) if (! pt[i].equals(args[i].inner())) return false; return true; } return false; } /** * Returns true if this method has the specified argument parent classes. * * @param args The arguments to test for. * @return true if this method has this arguments in the exact order. */ public final boolean hasMatchingParamTypes(Class...args) { ClassInfo[] pt = _getParameterTypes(); if (pt.length != args.length) return false; for (ClassInfo element : pt) { boolean matched = false; for (Class arg : args) if (element.isParentOfFuzzyPrimitives(arg)) matched = true; if (! matched) return false; } return true; } /** * Returns true if this method has the specified argument parent classes. * * @param args The arguments to test for. * @return true if this method has this arguments in the exact order. */ public final boolean hasMatchingParamTypes(ClassInfo...args) { ClassInfo[] pt = _getParameterTypes(); if (pt.length != args.length) return false; for (ClassInfo element : pt) { boolean matched = false; for (ClassInfo arg : args) if (element.isParentOfFuzzyPrimitives(arg.inner())) matched = true; if (! matched) return false; } return true; } /** * Returns true if this method has at most only this arguments in any order. * * @param args The arguments to test for. * @return true if this method has at most only this arguments in any order. */ public final boolean hasFuzzyParamTypes(Class...args) { return fuzzyArgsMatch(args) != -1; } /** * Returns how well this method matches the specified arg types. * *

* The number returned is the number of method arguments that match the passed in arg types. *
Returns -1 if the method cannot take in one or more of the specified arguments. * * @param argTypes The arg types to check against. * @return How many parameters match or -1 if method cannot handle one or more of the arguments. */ public final int fuzzyArgsMatch(Class... argTypes) { int matches = 0; outer: for (ClassInfo pi : getParamTypes()) { for (Class a : argTypes) { if (pi.isParentOfFuzzyPrimitives(a)) { matches++; continue outer; } } return -1; } return matches; } /** * Returns how well this method matches the specified arg types. * *

* The number returned is the number of method arguments that match the passed in arg types. *
Returns -1 if the method cannot take in one or more of the specified arguments. * * @param argTypes The arg types to check against. * @return How many parameters match or -1 if method cannot handle one or more of the arguments. */ public final int fuzzyArgsMatch(Object... argTypes) { int matches = 0; outer: for (ClassInfo pi : getParamTypes()) { for (Object a : argTypes) { if (pi.canAcceptArg(a)) { matches++; continue outer; } } return -1; } return matches; } /** * Returns true if this method has at most only this arguments in any order. * * @param args The arguments to test for. * @return true if this method has at most only this arguments in any order. */ public final boolean hasFuzzyParamTypes(ClassInfo...args) { return fuzzyArgsMatch(args) != -1; } /** * Returns how well this method matches the specified arg types. * *

* The number returned is the number of method arguments that match the passed in arg types. *
Returns -1 if the method cannot take in one or more of the specified arguments. * * @param argTypes The arg types to check against. * @return How many parameters match or -1 if method cannot handle one or more of the arguments. */ public final int fuzzyArgsMatch(ClassInfo... argTypes) { int matches = 0; outer: for (ClassInfo pi : getParamTypes()) { for (ClassInfo a : argTypes) { if (pi.isParentOfFuzzyPrimitives(a)) { matches++; continue outer; } } return -1; } return matches; } /** * Returns true if this method has the {@link Deprecated @Deprecated} annotation on it. * * @return true if this method has the {@link Deprecated @Deprecated} annotation on it. */ public final boolean isDeprecated() { return e.isAnnotationPresent(Deprecated.class); } /** * Returns true if this method doesn't have the {@link Deprecated @Deprecated} annotation on it. * * @return true if this method doesn't have the {@link Deprecated @Deprecated} annotation on it. */ public final boolean isNotDeprecated() { return ! e.isAnnotationPresent(Deprecated.class); } /** * Returns true if this method is abstract. * * @return true if this method is abstract. */ public final boolean isAbstract() { return Modifier.isAbstract(e.getModifiers()); } /** * Returns true if this method is not abstract. * * @return true if this method is not abstract. */ public final boolean isNotAbstract() { return ! Modifier.isAbstract(e.getModifiers()); } /** * Returns true if this method is public. * * @return true if this method is public. */ public final boolean isPublic() { return Modifier.isPublic(e.getModifiers()); } /** * Returns true if this method is not public. * * @return true if this method is not public. */ public final boolean isNotPublic() { return ! Modifier.isPublic(e.getModifiers()); } /** * Returns true if this method is protected. * * @return true if this method is protected. */ public final boolean isProtected() { return Modifier.isProtected(e.getModifiers()); } /** * Returns true if this method is not protected. * * @return true if this method is not protected. */ public final boolean isNotProtected() { return ! Modifier.isProtected(e.getModifiers()); } /** * Returns true if this method is static. * * @return true if this method is static. */ public final boolean isStatic() { return Modifier.isStatic(e.getModifiers()); } /** * Returns true if this method is not static. * * @return true if this method is not static. */ public final boolean isNotStatic() { return ! Modifier.isStatic(e.getModifiers()); } //----------------------------------------------------------------------------------------------------------------- // Visibility //----------------------------------------------------------------------------------------------------------------- /** * Attempts to call x.setAccessible(true) and quietly ignores security exceptions. * * @return This object. */ @FluentSetter public ExecutableInfo accessible() { setAccessible(); return this; } /** * Attempts to call x.setAccessible(true) and quietly ignores security exceptions. * * @return true if call was successful. */ public final boolean setAccessible() { try { if (e != null) e.setAccessible(true); return true; } catch (SecurityException e) { return false; } } /** * Identifies if the specified visibility matches this method. * * @param v The visibility to validate against. * @return true if this visibility matches the modifier attribute of this method. */ public final boolean isVisible(Visibility v) { return v.isVisible(e); } //----------------------------------------------------------------------------------------------------------------- // Labels //----------------------------------------------------------------------------------------------------------------- /** * Returns true if this method has this name. * * @param name The name to test for. * @return true if this method has this name. */ public final boolean hasName(String name) { return getSimpleName().equals(name); } /** * Returns true if this method has a name in the specified list. * * @param names The names to test for. * @return true if this method has one of the names. */ public final boolean hasName(String...names) { for (String n : names) if (getSimpleName().equals(n)) return true; return false; } /** * Returns true if this method has a name in the specified set. * * @param names The names to test for. * @return true if this method has one of the names. */ public final boolean hasName(Set names) { return names.contains(getSimpleName()); } //----------------------------------------------------------------------------------------------------------------- // Labels //----------------------------------------------------------------------------------------------------------------- /** * Returns the full name of this executable. * *

Examples:
*
    *
  • "com.foo.MyClass.get(java.util.String)" - Method. *
  • "com.foo.MyClass(java.util.String)" - Constructor. *
* * @return The underlying executable name. */ public final String getFullName() { StringBuilder sb = new StringBuilder(128); ClassInfo dc = declaringClass; Package p = dc.getPackage(); if (p != null) sb.append(p.getName()).append('.'); dc.appendShortName(sb); if (! isConstructor) sb.append('.').append(getSimpleName()); sb.append('('); List pt = getParamTypes(); for (int i = 0; i < pt.size(); i++) { if (i > 0) sb.append(','); pt.get(i).appendFullName(sb); } sb.append(')'); return sb.toString(); } /** * Returns the short name of this executable. * *
Examples:
*
    *
  • "MyClass.get(String)" - Method. *
  • "MyClass(String)" - Constructor. *
* * @return The underlying executable name. */ public final String getShortName() { StringBuilder sb = new StringBuilder(64); sb.append(getSimpleName()).append('('); Class[] pt = _getRawParamTypes(); for (int i = 0; i < pt.length; i++) { if (i > 0) sb.append(','); sb.append(pt[i].getSimpleName()); } sb.append(')'); return sb.toString(); } /** * Returns the simple name of the underlying method. * * @return The simple name of the underlying method; */ public final String getSimpleName() { return isConstructor ? e.getDeclaringClass().getSimpleName() : e.getName(); } @Override public String toString() { return getShortName(); } // // }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy