| // *************************************************************************************************************************** |
| // * 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.internal.StringUtils.*; |
| |
| import java.lang.annotation.*; |
| import java.lang.reflect.*; |
| import java.util.*; |
| |
| import org.apache.juneau.*; |
| import org.apache.juneau.internal.*; |
| |
| /** |
| * Contains common methods between {@link ConstructorInfo} and {@link MethodInfo}. |
| */ |
| public abstract class ExecutableInfo { |
| |
| final ClassInfo declaringClass; |
| final Executable e, re; |
| final boolean isConstructor; |
| |
| private List<ParamInfo> params; |
| private List<ClassInfo> paramTypes, exceptionInfos; |
| private Class<?>[] rawParamTypes, rawExceptionTypes; |
| private Type[] rawGenericParamTypes; |
| private Parameter[] rawParameters; |
| |
| /** |
| * Constructor. |
| * |
| * @param declaringClass The class that declares this method or constructor. |
| * @param e The constructor or method that this info represents. |
| * @param re The "real" constructor if the constructor above is defined against a CGLIB proxy. |
| */ |
| protected ExecutableInfo(ClassInfo declaringClass, Executable e, Executable re) { |
| this.declaringClass = declaringClass; |
| this.e = e; |
| this.re = re == null ? e : re; |
| 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 <jk>true</jk> if this executable represents a {@link Constructor}. |
| * |
| * @return |
| * <jk>true</jk> if this executable represents a {@link Constructor} and can be cast to {@link ConstructorInfo}. |
| * <jk>false</jk> 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. |
| * |
| * <p> |
| * Same as calling {@link Executable#getParameterCount()}. |
| * |
| * @return The number of parameters in this executable. |
| */ |
| public final int getParamCount() { |
| return e.getParameterCount(); |
| } |
| |
| /** |
| * Returns <jk>true</jk> if this executable has at least one parameter. |
| * |
| * <p> |
| * Same as calling {@link Executable#getParameterCount()} and comparing with zero. |
| * |
| * @return <jk>true</jk> if this executable has at least one parameter. |
| */ |
| public final boolean hasParams() { |
| return getParamCount() != 0; |
| } |
| |
| /** |
| * Returns <jk>true</jk> if this executable has no parameters. |
| * |
| * <p> |
| * Same as calling {@link Executable#getParameterCount()} and comparing with zero. |
| * |
| * @return <jk>true</jk> if this executable has no parameters. |
| */ |
| public final boolean hasNoParams() { |
| return getParamCount() == 0; |
| } |
| |
| /** |
| * Returns <jk>true</jk> if this executable has this number of arguments. |
| * |
| * <p> |
| * Same as calling {@link Executable#getParameterCount()} and comparing the count. |
| * |
| * @param number The number of expected arguments. |
| * @return <jk>true</jk> if this executable has this number of arguments. |
| */ |
| public final boolean hasNumParams(int number) { |
| return getParamCount() == number; |
| } |
| |
| /** |
| * Returns the parameters defined on this executable. |
| * |
| * <p> |
| * Same as calling {@link Executable#getParameters()} but wraps the results |
| * |
| * @return An array of parameter information, never <jk>null</jk>. |
| */ |
| public final List<ParamInfo> getParams() { |
| if (params == null) { |
| Parameter[] rp = rawParameters(); |
| List<ParamInfo> l = new ArrayList<>(rp.length); |
| for (int i = 0; i < rp.length; i++) |
| l.add(new ParamInfo(this, rp[i], i)); |
| params = Collections.unmodifiableList(l); |
| } |
| return params; |
| } |
| |
| /** |
| * Returns parameter information at the specified index. |
| * |
| * @param index The parameter index. |
| * @return The parameter information, never <jk>null</jk>. |
| */ |
| public final ParamInfo getParam(int index) { |
| checkIndex(index); |
| if (params != null) |
| return params.get(index); |
| return new ParamInfo(this, rawParameters()[index], index); |
| } |
| |
| /** |
| * Returns the parameter types on this executable. |
| * |
| * @return The parameter types on this executable. |
| */ |
| public final List<ClassInfo> getParamTypes() { |
| if (paramTypes == null) { |
| Class<?>[] ptc = rawParamTypes(); |
| // 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 = rawGenericParamTypes(); |
| if (ptt.length != ptc.length) |
| ptt = ptc; |
| List<ClassInfo> l = new ArrayList<>(ptc.length); |
| for (int i = 0; i < ptc.length; i++) |
| l.add(ClassInfo.of(ptc[i], ptt[i])); |
| paramTypes = Collections.unmodifiableList(l); |
| } |
| return paramTypes; |
| } |
| |
| /** |
| * 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); |
| if (paramTypes != null) |
| return getParamTypes().get(index); |
| return ClassInfo.of(getRawParamType(index), getRawGenericParamType(index)); |
| } |
| |
| /** |
| * Returns the raw parameter types on this executable. |
| * |
| * @return The raw parameter types on this executable. |
| */ |
| public final Class<?>[] getRawParamTypes() { |
| return rawParamTypes().clone(); |
| } |
| |
| /** |
| * 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 rawParamTypes()[index]; |
| } |
| |
| /** |
| * Returns the raw generic parameter types on this executable. |
| * |
| * @return The raw generic parameter types on this executable. |
| */ |
| public final Type[] getRawGenericParamTypes() { |
| return rawGenericParamTypes().clone(); |
| } |
| |
| /** |
| * 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 rawGenericParamTypes()[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 Parameter[] getRawParameters() { |
| return rawParameters().clone(); |
| } |
| |
| /** |
| * 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 rawParameters()[index]; |
| } |
| |
| Class<?>[] rawParamTypes() { |
| if (rawParamTypes == null) |
| rawParamTypes = e.getParameterTypes(); |
| return rawParamTypes; |
| } |
| |
| Type[] rawGenericParamTypes() { |
| if (rawGenericParamTypes == null) |
| rawGenericParamTypes = re.getGenericParameterTypes(); |
| return rawGenericParamTypes; |
| } |
| |
| Parameter[] rawParameters() { |
| if (rawParameters == null) |
| rawParameters = re.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 |
| //----------------------------------------------------------------------------------------------------------------- |
| |
| /** |
| * Returns the parameter annotations on this executable. |
| * |
| * @return The parameter annotations on this executable. |
| */ |
| public final Annotation[][] getParameterAnnotations() { |
| return e.getParameterAnnotations(); |
| } |
| |
| /** |
| * Returns the parameter annotations on the parameter at the specified index. |
| * |
| * @param index The parameter index. |
| * @return The parameter annotations on the parameter at the specified index. |
| */ |
| public final Annotation[] getParameterAnnotations(int index) { |
| checkIndex(index); |
| return e.getParameterAnnotations()[index]; |
| } |
| |
| //----------------------------------------------------------------------------------------------------------------- |
| // Exceptions |
| //----------------------------------------------------------------------------------------------------------------- |
| |
| /** |
| * Returns the exception types on this executable. |
| * |
| * @return The exception types on this executable. |
| */ |
| public final List<ClassInfo> getExceptionTypes() { |
| if (exceptionInfos == null) { |
| Class<?>[] exceptionTypes = rawExceptionTypes(); |
| List<ClassInfo> l = new ArrayList<>(exceptionTypes.length); |
| for (Class<?> et : exceptionTypes) |
| l.add(ClassInfo.of(et)); |
| exceptionInfos = Collections.unmodifiableList(l); |
| } |
| return exceptionInfos; |
| } |
| |
| /** |
| * Returns the raw exception types on this executable. |
| * |
| * @return The raw exception types on this executable. |
| */ |
| public final Class<?>[] getRawExceptionTypes() { |
| return rawExceptionTypes().clone(); |
| } |
| |
| private Class<?>[] rawExceptionTypes() { |
| if (rawExceptionTypes == null) |
| rawExceptionTypes = e.getExceptionTypes(); |
| return rawExceptionTypes; |
| } |
| |
| //----------------------------------------------------------------------------------------------------------------- |
| // Characteristics |
| //----------------------------------------------------------------------------------------------------------------- |
| |
| /** |
| * Returns <jk>true</jk> if all specified flags are applicable to this method. |
| * |
| * @param flags The flags to test for. |
| * @return <jk>true</jk> 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 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 RuntimeException("Invalid flag for executable: " + f); |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * Returns <jk>true</jk> if all specified flags are applicable to this method. |
| * |
| * @param flags The flags to test for. |
| * @return <jk>true</jk> 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 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 RuntimeException("Invalid flag for executable: " + f); |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Returns <jk>true</jk> if this method has the specified arguments. |
| * |
| * @param args The arguments to test for. |
| * @return <jk>true</jk> if this method has this arguments in the exact order. |
| */ |
| public final boolean hasParamTypes(Class<?>...args) { |
| Class<?>[] pt = rawParamTypes(); |
| 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 <jk>true</jk> if this method has the specified arguments. |
| * |
| * @param args The arguments to test for. |
| * @return <jk>true</jk> if this method has this arguments in the exact order. |
| */ |
| public final boolean hasParamTypes(ClassInfo...args) { |
| Class<?>[] pt = rawParamTypes(); |
| 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 <jk>true</jk> if this method has the specified argument parent classes. |
| * |
| * @param args The arguments to test for. |
| * @return <jk>true</jk> if this method has this arguments in the exact order. |
| */ |
| public final boolean hasParamTypeParents(Class<?>...args) { |
| Class<?>[] pt = rawParamTypes(); |
| if (pt.length == args.length) { |
| for (int i = 0; i < pt.length; i++) |
| if (! args[i].isAssignableFrom(pt[i])) |
| return false; |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Returns <jk>true</jk> if this method has the specified argument parent classes. |
| * |
| * @param args The arguments to test for. |
| * @return <jk>true</jk> if this method has this arguments in the exact order. |
| */ |
| public final boolean hasParamTypeParents(ClassInfo...args) { |
| Class<?>[] pt = rawParamTypes(); |
| if (pt.length == args.length) { |
| for (int i = 0; i < pt.length; i++) |
| if (! args[i].inner().isAssignableFrom(pt[i])) |
| return false; |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Returns <jk>true</jk> if this method has at most only this arguments in any order. |
| * |
| * @param args The arguments to test for. |
| * @return <jk>true</jk> if this method has at most only this arguments in any order. |
| */ |
| public final boolean hasFuzzyParamTypes(Class<?>...args) { |
| return ClassUtils.fuzzyArgsMatch(rawParamTypes(), args) != -1; |
| } |
| |
| /** |
| * Returns <jk>true</jk> if this method has at most only this arguments in any order. |
| * |
| * @param args The arguments to test for. |
| * @return <jk>true</jk> if this method has at most only this arguments in any order. |
| */ |
| public boolean hasFuzzyParamTypes(ClassInfo...args) { |
| return ClassUtils.fuzzyArgsMatch(rawParamTypes(), args) != -1; |
| } |
| |
| /** |
| * Returns <jk>true</jk> if this method has the {@link Deprecated @Deprecated} annotation on it. |
| * |
| * @return <jk>true</jk> if this method has the {@link Deprecated @Deprecated} annotation on it. |
| */ |
| public final boolean isDeprecated() { |
| return e.isAnnotationPresent(Deprecated.class); |
| |
| } |
| |
| /** |
| * Returns <jk>true</jk> if this method doesn't have the {@link Deprecated @Deprecated} annotation on it. |
| * |
| * @return <jk>true</jk> if this method doesn't have the {@link Deprecated @Deprecated} annotation on it. |
| */ |
| public final boolean isNotDeprecated() { |
| return ! e.isAnnotationPresent(Deprecated.class); |
| |
| } |
| |
| /** |
| * Returns <jk>true</jk> if this method is abstract. |
| * |
| * @return <jk>true</jk> if this method is abstract. |
| */ |
| public final boolean isAbstract() { |
| return Modifier.isAbstract(e.getModifiers()); |
| } |
| |
| /** |
| * Returns <jk>true</jk> if this method is not abstract. |
| * |
| * @return <jk>true</jk> if this method is not abstract. |
| */ |
| public final boolean isNotAbstract() { |
| return ! Modifier.isAbstract(e.getModifiers()); |
| } |
| |
| /** |
| * Returns <jk>true</jk> if this method is public. |
| * |
| * @return <jk>true</jk> if this method is public. |
| */ |
| public final boolean isPublic() { |
| return Modifier.isPublic(e.getModifiers()); |
| } |
| |
| /** |
| * Returns <jk>true</jk> if this method is not public. |
| * |
| * @return <jk>true</jk> if this method is not public. |
| */ |
| public final boolean isNotPublic() { |
| return ! Modifier.isPublic(e.getModifiers()); |
| } |
| |
| /** |
| * Returns <jk>true</jk> if this method is static. |
| * |
| * @return <jk>true</jk> if this method is static. |
| */ |
| public final boolean isStatic() { |
| return Modifier.isStatic(e.getModifiers()); |
| } |
| |
| /** |
| * Returns <jk>true</jk> if this method is not static. |
| * |
| * @return <jk>true</jk> if this method is not static. |
| */ |
| public final boolean isNotStatic() { |
| return ! Modifier.isStatic(e.getModifiers()); |
| } |
| |
| //----------------------------------------------------------------------------------------------------------------- |
| // Visibility |
| //----------------------------------------------------------------------------------------------------------------- |
| |
| /** |
| * Attempts to call <code>x.setAccessible(<jk>true</jk>)</code> and quietly ignores security exceptions. |
| * |
| * @return <jk>true</jk> if call was successful. |
| */ |
| public final boolean setAccessible() { |
| try { |
| if (! (e.isAccessible())) |
| 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 <jk>true</jk> if this visibility matches the modifier attribute of this method. |
| */ |
| public final boolean isVisible(Visibility v) { |
| return v.isVisible(e); |
| } |
| |
| //----------------------------------------------------------------------------------------------------------------- |
| // Labels |
| //----------------------------------------------------------------------------------------------------------------- |
| |
| /** |
| * Returns <jk>true</jk> if this method has this name. |
| * |
| * @param name The name to test for. |
| * @return <jk>true</jk> if this method has this name. |
| */ |
| public final boolean hasName(String name) { |
| return getSimpleName().equals(name); |
| } |
| |
| /** |
| * Returns <jk>true</jk> if this method has a name in the specified list. |
| * |
| * @param names The names to test for. |
| * @return <jk>true</jk> 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 <jk>true</jk> if this method has a name in the specified set. |
| * |
| * @param names The names to test for. |
| * @return <jk>true</jk> if this method has one of the names. |
| */ |
| public final boolean hasName(Set<String> names) { |
| return names.contains(getSimpleName()); |
| } |
| |
| //----------------------------------------------------------------------------------------------------------------- |
| // Labels |
| //----------------------------------------------------------------------------------------------------------------- |
| |
| /** |
| * Returns the full name of this executable. |
| * |
| * <h5 class='section'>Examples:</h5> |
| * <ul> |
| * <li><js>"com.foo.MyClass.get(java.util.String)"<js> - Method. |
| * <li><js>"com.foo.MyClass(java.util.String)"<js> - Constructor. |
| * </ul> |
| * |
| * @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<ClassInfo> 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. |
| * |
| * <h5 class='section'>Examples:</h5> |
| * <ul> |
| * <li><js>"MyClass.get(String)"<js> - Method. |
| * <li><js>"MyClass(String)"<js> - Constructor. |
| * </ul> |
| * |
| * @return The underlying executable name. |
| */ |
| public final String getShortName() { |
| StringBuilder sb = new StringBuilder(64); |
| sb.append(getSimpleName()).append('('); |
| Class<?>[] pt = rawParamTypes(); |
| 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(); |
| } |
| } |