/* * Copyright 2008-2017 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 griffon.util; import griffon.core.Observable; import griffon.core.Vetoable; import griffon.core.artifact.GriffonArtifact; import griffon.core.artifact.GriffonMvcArtifact; import griffon.core.event.EventPublisher; import griffon.core.i18n.MessageSource; import griffon.core.mvc.MVCHandler; import griffon.core.resources.ResourceHandler; import griffon.core.resources.ResourceResolver; import griffon.core.threading.ThreadingHandler; import griffon.exceptions.BeanInstantiationException; import griffon.exceptions.FieldException; import griffon.exceptions.InstanceMethodInvocationException; import griffon.exceptions.PropertyException; import griffon.exceptions.StaticMethodInvocationException; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.beans.BeanInfo; import java.beans.IntrospectionException; import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import java.util.regex.Pattern; import static griffon.util.GriffonNameUtils.requireNonBlank; import static griffon.util.MethodUtils.invokeExactMethod; import static griffon.util.MethodUtils.invokeMethod; import static java.util.Objects.requireNonNull; /** * Class containing utility methods for dealing with Griffon class artifacts.<p> * Contains utility methods copied from commons-lang and commons-beanutils in order * to reduce dependencies on external libraries.<p> * <p> * <b>Contains code copied from commons-beanutils and commons-langs</b> * * @author Graeme Rocher (Grails 0.1) */ public class GriffonClassUtils { public static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0]; public static final Object[] EMPTY_OBJECT_ARRAY = new Object[0]; public static final Object[] EMPTY_ARGS = EMPTY_OBJECT_ARRAY; private static final String PROPERTY_GET_PREFIX = "get"; private static final String PROPERTY_IS_PREFIX = "is"; private static final String PROPERTY_SET_PREFIX = "set"; private static final String ON_SHUTDOWN_METHOD_NAME = "onShutdown"; public static final Map<Class<?>, Class<?>> PRIMITIVE_TYPE_COMPATIBLE_CLASSES = new LinkedHashMap<>(); public static final Map<String, String> PRIMITIVE_TYPE_COMPATIBLE_TYPES = new LinkedHashMap<>(); private static final Pattern EVENT_HANDLER_PATTERN = Pattern.compile("^on[A-Z][\\w]*$"); private static final Pattern CONTRIBUTION_PATTERN = Pattern.compile("^with[A-Z][a-z0-9_]*[\\w]*$"); private static final Pattern GETTER_PATTERN_1 = Pattern.compile("^get[A-Z][\\w]*$"); private static final Pattern GETTER_PATTERN_2 = Pattern.compile("^is[A-Z][\\w]*$"); private static final Pattern SETTER_PATTERN = Pattern.compile("^set[A-Z][\\w]*$"); private static final Set<MethodDescriptor> BASIC_METHODS = new TreeSet<>(); private static final Set<MethodDescriptor> ARTIFACT_METHODS = new TreeSet<>(); private static final Set<MethodDescriptor> MVC_METHODS = new TreeSet<>(); private static final Set<MethodDescriptor> THREADING_METHODS = new TreeSet<>(); private static final Set<MethodDescriptor> EVENT_PUBLISHER_METHODS = new TreeSet<>(); private static final Set<MethodDescriptor> OBSERVABLE_METHODS = new TreeSet<>(); private static final Set<MethodDescriptor> RESOURCE_HANDLER_METHODS = new TreeSet<>(); private static final Set<MethodDescriptor> MESSAGE_SOURCE_METHODS = new TreeSet<>(); private static final Set<MethodDescriptor> RESOURCE_RESOLVER_METHODS = new TreeSet<>(); private static final String ERROR_TYPE_NULL = "Argument 'type' must not be null"; private static final String ERROR_METHOD_NAME_BLANK = "Argument 'methodName' must not be blank"; private static final String ERROR_OBJECT_NULL = "Argument 'object' must not be null"; private static final String ERROR_CLAZZ_NULL = "Argument 'clazz' must not be null"; private static final String ERROR_DESCRIPTOR_NULL = "Argument 'descriptor' must not be null"; private static final String ERROR_BEAN_NULL = "Argument 'bean' must not be null"; private static final String ERROR_NAME_BLANK = "Argument 'name' must not be blank"; private static final String ERROR_PROPERTIES_NULL = "Argument 'properties' must not be null"; private static final String ERROR_FIELDS_NULL = "Argument 'fields' must not be null"; private static final String ERROR_PROPERTY_NAME_BLANK = "Argument 'propertyName' must not be blank"; private static final String ERROR_METHOD_NULL = "Argument 'method' must not be null"; /** * Just add two entries to the class compatibility map * * @param left * @param right */ private static void registerPrimitiveClassPair(Class<?> left, Class<?> right) { PRIMITIVE_TYPE_COMPATIBLE_CLASSES.put(left, right); PRIMITIVE_TYPE_COMPATIBLE_CLASSES.put(right, left); PRIMITIVE_TYPE_COMPATIBLE_TYPES.put(left.getName(), right.getName()); PRIMITIVE_TYPE_COMPATIBLE_TYPES.put(right.getName(), left.getName()); } static { registerPrimitiveClassPair(Boolean.class, boolean.class); registerPrimitiveClassPair(Integer.class, int.class); registerPrimitiveClassPair(Short.class, short.class); registerPrimitiveClassPair(Byte.class, byte.class); registerPrimitiveClassPair(Character.class, char.class); registerPrimitiveClassPair(Long.class, long.class); registerPrimitiveClassPair(Float.class, float.class); registerPrimitiveClassPair(Double.class, double.class); for (Method method : Object.class.getMethods()) { MethodDescriptor md = MethodDescriptor.forMethod(method); if (!BASIC_METHODS.contains(md)) { BASIC_METHODS.add(md); } } try { Class groovyObjectClass = GriffonClassUtils.class.getClassLoader().loadClass("groovy.lang.GroovyObject"); for (Method method : groovyObjectClass.getMethods()) { MethodDescriptor md = MethodDescriptor.forMethod(method, true); if (!BASIC_METHODS.contains(md)) { BASIC_METHODS.add(md); } } } catch (ClassNotFoundException cnfe) { // ignore } try { Class groovyObjectClass = GriffonClassUtils.class.getClassLoader().loadClass("groovy.lang.GroovyObjectSupport"); for (Method method : groovyObjectClass.getMethods()) { MethodDescriptor md = MethodDescriptor.forMethod(method); if (!BASIC_METHODS.contains(md)) { BASIC_METHODS.add(md); } } } catch (ClassNotFoundException cnfe) { // ignore } for (Method method : GriffonArtifact.class.getMethods()) { MethodDescriptor md = MethodDescriptor.forMethod(method, true); if (!ARTIFACT_METHODS.contains(md)) { ARTIFACT_METHODS.add(md); } } // MVC_METHODS.add(new MethodDescriptor("getMvcGroup")); for (Method method : MVCHandler.class.getMethods()) { MethodDescriptor md = MethodDescriptor.forMethod(method, true); if (!MVC_METHODS.contains(md)) { MVC_METHODS.add(md); } } for (Method method : GriffonMvcArtifact.class.getMethods()) { MethodDescriptor md = MethodDescriptor.forMethod(method, true); if (!MVC_METHODS.contains(md)) { MVC_METHODS.add(md); } } // GriffonView MVC_METHODS.add(new MethodDescriptor("initUI")); // GriffonController MVC_METHODS.add(new MethodDescriptor("invokeAction", new Class<?>[]{String.class, Object[].class})); MVC_METHODS.add(new MethodDescriptor("invokeAction", new Class<?>[]{String.class, Object[].class}, Modifier.PUBLIC | Modifier.TRANSIENT)); for (Method method : ThreadingHandler.class.getMethods()) { MethodDescriptor md = MethodDescriptor.forMethod(method, true); if (!THREADING_METHODS.contains(md)) { THREADING_METHODS.add(md); } } // Special case due to the usage of varargs //THREADING_METHODS.add(new MethodDescriptor("runFuture", new Class<?>[]{Object[].class})); for (Method method : EventPublisher.class.getMethods()) { MethodDescriptor md = MethodDescriptor.forMethod(method, true); if (!EVENT_PUBLISHER_METHODS.contains(md)) { EVENT_PUBLISHER_METHODS.add(md); } } for (Method method : Observable.class.getMethods()) { MethodDescriptor md = MethodDescriptor.forMethod(method, true); if (!OBSERVABLE_METHODS.contains(md)) { OBSERVABLE_METHODS.add(md); } } for (Method method : Vetoable.class.getMethods()) { MethodDescriptor md = MethodDescriptor.forMethod(method, true); if (!OBSERVABLE_METHODS.contains(md)) { OBSERVABLE_METHODS.add(md); } } for (Method method : ResourceHandler.class.getMethods()) { MethodDescriptor md = MethodDescriptor.forMethod(method, true); if (!RESOURCE_HANDLER_METHODS.contains(md)) { RESOURCE_HANDLER_METHODS.add(md); } } for (Method method : MessageSource.class.getMethods()) { MethodDescriptor md = MethodDescriptor.forMethod(method, true); if (!MESSAGE_SOURCE_METHODS.contains(md)) { MESSAGE_SOURCE_METHODS.add(md); } } for (Method method : ResourceResolver.class.getMethods()) { MethodDescriptor md = MethodDescriptor.forMethod(method, true); if (!RESOURCE_RESOLVER_METHODS.contains(md)) { RESOURCE_RESOLVER_METHODS.add(md); } } } /** * Checks that the specified condition is met. This method is designed * primarily for doing parameter validation in methods and constructors, * as demonstrated below: * <blockquote><pre> * public Foo(int[] array) { * GriffonClassUtils.requireState(array.length > 0); * } * </pre></blockquote> * * @param condition the condition to check * @throws IllegalStateException if {@code condition} evaluates to false */ public static void requireState(boolean condition) { if (!condition) { throw new IllegalStateException(); } } /** * Checks that the specified condition is met and throws a customized * {@link IllegalStateException} if it is. This method is designed primarily * for doing parameter validation in methods and constructors with multiple * parameters, as demonstrated below: * <blockquote><pre> * public Foo(int[] array) { * GriffonClassUtils.requireState(array.length > 0, "array must not be empty"); * } * </pre></blockquote> * * @param condition the condition to check * @param message detail message to be used in the event that a {@code * IllegalStateException} is thrown * @throws IllegalStateException if {@code condition} evaluates to false */ public static void requireState(boolean condition, String message) { if (!condition) { throw new IllegalStateException(message); } } /** * Checks that the specified array is not empty, throwing a * {@link IllegalStateException} if it is. * * @param array the array to check * @throws NullPointerException if {@code array} is null * @throws IllegalStateException if {@code array} is empty */ public static byte[] requireNonEmpty(@Nonnull byte[] array) { requireNonNull(array); requireState(array.length != 0); return array; } /** * Checks that the specified array is not empty, throwing a customized * {@link IllegalStateException} if it is. * * @param array the array to check * @param message detail message to be used in the event that a {@code * IllegalStateException} is thrown * @throws NullPointerException if {@code array} is null * @throws IllegalArgumentException if {@code message} is {@code blank} * @throws IllegalStateException if {@code array} is empty */ public static byte[] requireNonEmpty(@Nonnull byte[] array, @Nonnull String message) { requireNonNull(array); requireState(array.length != 0, requireNonBlank(message, "message")); return array; } /** * Checks that the specified array is not empty, throwing a * {@link IllegalStateException} if it is. * * @param array the array to check * @throws NullPointerException if {@code array} is null * @throws IllegalStateException if {@code array} is empty */ public static short[] requireNonEmpty(@Nonnull short[] array) { requireNonNull(array); requireState(array.length != 0); return array; } /** * Checks that the specified array is not empty, throwing a customized * {@link IllegalStateException} if it is. * * @param array the array to check * @param message detail message to be used in the event that a {@code * IllegalStateException} is thrown * @throws NullPointerException if {@code array} is null * @throws IllegalArgumentException if {@code message} is {@code blank} * @throws IllegalStateException if {@code array} is empty */ public static short[] requireNonEmpty(@Nonnull short[] array, @Nonnull String message) { requireNonNull(array); requireState(array.length != 0, requireNonBlank(message, "message")); return array; } /** * Checks that the specified array is not empty, throwing a * {@link IllegalStateException} if it is. * * @param array the array to check * @throws NullPointerException if {@code array} is null * @throws IllegalStateException if {@code array} is empty */ public static int[] requireNonEmpty(@Nonnull int[] array) { requireNonNull(array); requireState(array.length != 0); return array; } /** * Checks that the specified array is not empty, throwing a customized * {@link IllegalStateException} if it is. * * @param array the array to check * @param message detail message to be used in the event that a {@code * IllegalStateException} is thrown * @throws NullPointerException if {@code array} is null * @throws IllegalArgumentException if {@code message} is {@code blank} * @throws IllegalStateException if {@code array} is empty */ public static int[] requireNonEmpty(@Nonnull int[] array, @Nonnull String message) { requireNonNull(array); requireState(array.length != 0, requireNonBlank(message, "message")); return array; } /** * Checks that the specified array is not empty, throwing a * {@link IllegalStateException} if it is. * * @param array the array to check * @throws NullPointerException if {@code array} is null * @throws IllegalStateException if {@code array} is empty */ public static long[] requireNonEmpty(@Nonnull long[] array) { requireNonNull(array); requireState(array.length != 0); return array; } /** * Checks that the specified array is not empty, throwing a customized * {@link IllegalStateException} if it is. * * @param array the array to check * @param message detail message to be used in the event that a {@code * IllegalStateException} is thrown * @throws NullPointerException if {@code array} is null * @throws IllegalArgumentException if {@code message} is {@code blank} * @throws IllegalStateException if {@code array} is empty */ public static long[] requireNonEmpty(@Nonnull long[] array, @Nonnull String message) { requireNonNull(array); requireState(array.length != 0, requireNonBlank(message, "message")); return array; } /** * Checks that the specified array is not empty, throwing a * {@link IllegalStateException} if it is. * * @param array the array to check * @throws NullPointerException if {@code array} is null * @throws IllegalStateException if {@code array} is empty */ public static float[] requireNonEmpty(@Nonnull float[] array) { requireNonNull(array); requireState(array.length != 0); return array; } /** * Checks that the specified array is not empty, throwing a customized * {@link IllegalStateException} if it is. * * @param array the array to check * @param message detail message to be used in the event that a {@code * IllegalStateException} is thrown * @throws NullPointerException if {@code array} is null * @throws IllegalArgumentException if {@code message} is {@code blank} * @throws IllegalStateException if {@code array} is empty */ public static float[] requireNonEmpty(@Nonnull float[] array, @Nonnull String message) { requireNonNull(array); requireState(array.length != 0, requireNonBlank(message, "message")); return array; } /** * Checks that the specified array is not empty, throwing a * {@link IllegalStateException} if it is. * * @param array the array to check * @throws NullPointerException if {@code array} is null * @throws IllegalStateException if {@code array} is empty */ public static double[] requireNonEmpty(@Nonnull double[] array) { requireNonNull(array); requireState(array.length != 0); return array; } /** * Checks that the specified array is not empty, throwing a customized * {@link IllegalStateException} if it is. * * @param array the array to check * @param message detail message to be used in the event that a {@code * IllegalStateException} is thrown * @throws NullPointerException if {@code array} is null * @throws IllegalArgumentException if {@code message} is {@code blank} * @throws IllegalStateException if {@code array} is empty */ public static double[] requireNonEmpty(@Nonnull double[] array, @Nonnull String message) { requireNonNull(array); requireState(array.length != 0, requireNonBlank(message, "message")); return array; } /** * Checks that the specified array is not empty, throwing a * {@link IllegalStateException} if it is. * * @param array the array to check * @throws NullPointerException if {@code array} is null * @throws IllegalStateException if {@code array} is empty */ public static char[] requireNonEmpty(@Nonnull char[] array) { requireNonNull(array); requireState(array.length != 0); return array; } /** * Checks that the specified array is not empty, throwing a customized * {@link IllegalStateException} if it is. * * @param array the array to check * @param message detail message to be used in the event that a {@code * IllegalStateException} is thrown * @throws NullPointerException if {@code array} is null * @throws IllegalArgumentException if {@code message} is {@code blank} * @throws IllegalStateException if {@code array} is empty */ public static char[] requireNonEmpty(@Nonnull char[] array, @Nonnull String message) { requireNonNull(array); requireState(array.length != 0, requireNonBlank(message, "message")); return array; } /** * Checks that the specified array is not empty, throwing a * {@link IllegalStateException} if it is. * * @param array the array to check * @throws NullPointerException if {@code array} is null * @throws IllegalStateException if {@code array} is empty */ public static boolean[] requireNonEmpty(@Nonnull boolean[] array) { requireNonNull(array); requireState(array.length != 0); return array; } /** * Checks that the specified array is not empty, throwing a customized * {@link IllegalStateException} if it is. * * @param array the array to check * @param message detail message to be used in the event that a {@code * IllegalStateException} is thrown * @throws NullPointerException if {@code array} is null * @throws IllegalArgumentException if {@code message} is {@code blank} * @throws IllegalStateException if {@code array} is empty */ public static boolean[] requireNonEmpty(@Nonnull boolean[] array, @Nonnull String message) { requireNonNull(array); requireState(array.length != 0, requireNonBlank(message, "message")); return array; } /** * Checks that the specified array is not empty, throwing a * {@link IllegalStateException} if it is. * * @param array the array to check * @throws NullPointerException if {@code array} is null * @throws IllegalStateException if {@code array} is empty */ public static <E> E[] requireNonEmpty(@Nonnull E[] array) { requireNonNull(array); requireState(array.length != 0); return array; } /** * Checks that the specified array is not empty, throwing a customized * {@link IllegalStateException} if it is. * * @param array the array to check * @param message detail message to be used in the event that a {@code * IllegalStateException} is thrown * @throws NullPointerException if {@code array} is null * @throws IllegalArgumentException if {@code message} is {@code blank} * @throws IllegalStateException if {@code array} is empty */ public static <E> E[] requireNonEmpty(@Nonnull E[] array, @Nonnull String message) { requireNonNull(array); requireState(array.length != 0, requireNonBlank(message, "message")); return array; } /** * Checks that the specified collection is not empty, throwing a * {@link IllegalStateException} if it is. * * @param collection the collection to check * @throws NullPointerException if {@code collection} is null * @throws IllegalStateException if {@code collection} is empty */ public static Collection<?> requireNonEmpty(@Nonnull Collection<?> collection) { requireNonNull(collection); requireState(!collection.isEmpty()); return collection; } /** * Checks that the specified collection is not empty, throwing a customized * {@link IllegalStateException} if it is. * * @param collection the collection to check * @param message detail message to be used in the event that a {@code * IllegalStateException} is thrown * @throws NullPointerException if {@code collection} is null * @throws IllegalArgumentException if {@code message} is {@code blank} * @throws IllegalStateException if {@code collection} is empty */ public static Collection<?> requireNonEmpty(@Nonnull Collection<?> collection, @Nonnull String message) { requireNonNull(collection); requireState(!collection.isEmpty(), requireNonBlank(message, "message")); return collection; } /** * Checks that the specified map is not empty, throwing a * {@link IllegalStateException} if it is. * * @param map the map to check * @throws NullPointerException if {@code map} is null * @throws IllegalStateException if {@code map} is empty */ public static Map<?, ?> requireNonEmpty(@Nonnull Map<?, ?> map) { requireNonNull(map); requireState(!map.isEmpty()); return map; } /** * Checks that the specified map is not empty, throwing a customized * {@link IllegalStateException} if it is. * * @param map the map to check * @param message detail message to be used in the event that a {@code * IllegalStateException} is thrown * @throws NullPointerException if {@code map} is null * @throws IllegalArgumentException if {@code message} is {@code blank} * @throws IllegalStateException if {@code map} is empty */ public static Map<?, ?> requireNonEmpty(@Nonnull Map<?, ?> map, @Nonnull String message) { requireNonNull(map); requireState(!map.isEmpty(), requireNonBlank(message, "message")); return map; } /** * Finds out if the given string represents the name of an * event handler by matching against the following pattern: * "^on[A-Z][\\w]*$"<p> * <p> * <pre> * isEventHandler("onBootstrapEnd") = true * isEventHandler("mvcGroupInit") = false * isEventHandler("online") = false * </pre> * * @param name the name of a possible event handler * @return true if the name matches the given event handler * pattern, false otherwise. */ public static boolean isEventHandler(@Nonnull String name) { requireNonBlank(name, ERROR_NAME_BLANK); return EVENT_HANDLER_PATTERN.matcher(name).matches() && !ON_SHUTDOWN_METHOD_NAME.equals(name); } /** * Finds out if the given Method represents an event handler * by matching its name against the following pattern: * "^on[A-Z][\\w]*$"<p> * <pre> * // assuming getMethod() returns an appropriate Method reference * isEventHandler(getMethod("onBootstrapEnd")) = true * isEventHandler(getMethod("mvcGroupInit")) = false * isEventHandler(getMethod("online")) = false * </pre> * * @param method a Method reference * @return true if the method name matches the given event handler * pattern, false otherwise. */ public static boolean isEventHandler(@Nonnull Method method) { return isEventHandler(method, false); } /** * Finds out if the given Method represents an event handler * by matching its name against the following pattern: * "^on[A-Z][\\w]*$"<p> * <pre> * // assuming getMethod() returns an appropriate Method reference * isEventHandler(getMethod("onBootstrapEnd")) = true * isEventHandler(getMethod("mvcGroupInit")) = false * isEventHandler(getMethod("online")) = false * </pre> * * @param method a Method reference * @return true if the method name matches the given event handler * pattern, false otherwise. */ public static boolean isEventHandler(@Nonnull Method method, boolean removeAbstractModifier) { requireNonNull(method, ERROR_METHOD_NULL); return isEventHandler(MethodDescriptor.forMethod(method, removeAbstractModifier)); } /** * Finds out if the given Method represents an event handler * by matching its name against the following pattern: * "^on[A-Z][\\w]*$"<p> * <pre> * // assuming getMethod() returns an appropriate MethodDescriptor reference * isEventHandler(getMethod("onBootstrapEnd")) = true * isEventHandler(getMethod("mvcGroupInit")) = false * isEventHandler(getMethod("online")) = false * </pre> * * @param method a MethodDescriptor reference * @return true if the method name matches the given event handler * pattern, false otherwise. */ public static boolean isEventHandler(@Nonnull MethodDescriptor method) { requireNonNull(method, ERROR_METHOD_NULL); return isInstanceMethod(method) && isEventHandler(method.getName()); } /** * Finds out if the given {@code Method} belongs either to the * {@code Object} class or the {@code GroovyObject} class.<p> * * @param method a Method reference * @return true if the method belongs to {@code Object} or * {@code GroovyObject}, false otherwise. */ public static boolean isBasicMethod(@Nonnull Method method) { return isBasicMethod(method, false); } /** * Finds out if the given {@code Method} belongs either to the * {@code Object} class or the {@code GroovyObject} class.<p> * * @param method a Method reference * @return true if the method belongs to {@code Object} or * {@code GroovyObject}, false otherwise. */ public static boolean isBasicMethod(@Nonnull Method method, boolean removeAbstractModifier) { requireNonNull(method, ERROR_METHOD_NULL); return isBasicMethod(MethodDescriptor.forMethod(method, removeAbstractModifier)); } /** * Finds out if the given {@code MethodDescriptor} belongs either to the * {@code Object} class or the {@code GroovyObject} class.<p> * * @param method a MethodDescriptor reference * @return true if the method belongs to {@code Object} or * {@code GroovyObject}, false otherwise. */ public static boolean isBasicMethod(@Nonnull MethodDescriptor method) { requireNonNull(method, ERROR_METHOD_NULL); return isInstanceMethod(method) && BASIC_METHODS.contains(method); } /** * Finds out if the given string represents the name of a * contribution method by matching against the following pattern: * "^with[A-Z][a-z0-9_]*[\w]*$"<p> * <p> * <pre> * isContributionMethod("withRest") = true * isContributionMethod("withMVCGroup") = false * isContributionMethod("without") = false * </pre> * * @param name the name of a possible contribution method * @return true if the name matches the given contribution method * pattern, false otherwise. */ public static boolean isContributionMethod(@Nonnull String name) { requireNonBlank(name, ERROR_NAME_BLANK); return CONTRIBUTION_PATTERN.matcher(name).matches(); } /** * Finds out if the given Method represents a contribution method * by matching its name against the following pattern: * "^with[A-Z][a-z0-9_]*[\w]*$"<p> * <pre> * // assuming getMethod() returns an appropriate Method reference * isContributionMethod(getMethod("withRest")) = true * isContributionMethod(getMethod("withMVCGroup")) = false * isContributionMethod(getMethod("without")) = false * </pre> * * @param method a Method reference * @return true if the method name matches the given contribution method * pattern, false otherwise. */ public static boolean isContributionMethod(@Nonnull Method method) { return isContributionMethod(method, false); } /** * Finds out if the given Method represents a contribution method * by matching its name against the following pattern: * "^with[A-Z][a-z0-9_]*[\w]*$"<p> * <pre> * // assuming getMethod() returns an appropriate Method reference * isContributionMethod(getMethod("withRest")) = true * isContributionMethod(getMethod("withMVCGroup")) = false * isContributionMethod(getMethod("without")) = false * </pre> * * @param method a Method reference * @return true if the method name matches the given contribution method * pattern, false otherwise. */ public static boolean isContributionMethod(@Nonnull Method method, boolean removeAbstractModifier) { requireNonNull(method, ERROR_METHOD_NULL); return isContributionMethod(MethodDescriptor.forMethod(method, removeAbstractModifier)); } /** * Finds out if the given Method represents a contribution method * by matching its name against the following pattern: * "^with[A-Z][a-z0-9_]*[\w]*$"<p> * <pre> * // assuming getMethod() returns an appropriate MethodDescriptor reference * isContributionMethod(getMethod("withRest")) = true * isContributionMethod(getMethod("withMVCGroup")) = false * isContributionMethod(getMethod("without")) = false * </pre> * * @param method a MethodDescriptor reference * @return true if the method name matches the given contribution method * pattern, false otherwise. */ public static boolean isContributionMethod(@Nonnull MethodDescriptor method) { requireNonNull(method, ERROR_METHOD_NULL); return isInstanceMethod(method) && CONTRIBUTION_PATTERN.matcher(method.getName()).matches(); } /** * Finds out if the given {@code Method} was injected by the Groovy * compiler.<p> * Performs a basic checks against the method's name, returning true * if the name starts with either "super$" or "this$". * * @param method a Method reference * @return true if the method matches the given criteria, false otherwise. */ public static boolean isGroovyInjectedMethod(@Nonnull Method method) { return isGroovyInjectedMethod(method, false); } /** * Finds out if the given {@code Method} was injected by the Groovy * compiler.<p> * Performs a basic checks against the method's name, returning true * if the name starts with either "super$" or "this$". * * @param method a Method reference * @return true if the method matches the given criteria, false otherwise. */ public static boolean isGroovyInjectedMethod(@Nonnull Method method, boolean removeAbstractModifier) { requireNonNull(method, ERROR_METHOD_NULL); return isGroovyInjectedMethod(MethodDescriptor.forMethod(method, removeAbstractModifier)); } /** * Finds out if the given {@code MethodDescriptor} was injected by the Groovy * compiler.<p> * Performs a basic checks against the method's name, returning true * if the name starts with either "super$" or "this$". * * @param method a MethodDescriptor reference * @return true if the method matches the given criteria, false otherwise. */ public static boolean isGroovyInjectedMethod(@Nonnull MethodDescriptor method) { requireNonNull(method, ERROR_METHOD_NULL); return isInstanceMethod(method) && (method.getName().startsWith("super$") || method.getName().startsWith("this$")); } /** * Finds out if the given {@code Method} is a getter method. * <p> * <pre> * // assuming getMethod() returns an appropriate Method reference * isGetterMethod(getMethod("getFoo")) = true * isGetterMethod(getMethod("getfoo") ) = false * isGetterMethod(getMethod("mvcGroupInit")) = false * isGetterMethod(getMethod("isFoo")) = true * isGetterMethod(getMethod("island")) = false * </pre> * * @param method a Method reference * @return true if the method is a getter, false otherwise. */ public static boolean isGetterMethod(@Nonnull Method method) { return isGetterMethod(method, false); } /** * Finds out if the given {@code Method} is a getter method. * <p> * <pre> * // assuming getMethod() returns an appropriate Method reference * isGetterMethod(getMethod("getFoo")) = true * isGetterMethod(getMethod("getfoo") ) = false * isGetterMethod(getMethod("mvcGroupInit")) = false * isGetterMethod(getMethod("isFoo")) = true * isGetterMethod(getMethod("island")) = false * </pre> * * @param method a Method reference * @return true if the method is a getter, false otherwise. */ public static boolean isGetterMethod(@Nonnull Method method, boolean removeAbstractModifier) { requireNonNull(method, ERROR_METHOD_NULL); return isGetterMethod(MethodDescriptor.forMethod(method, removeAbstractModifier)); } /** * Finds out if the given {@code MetaMethod} is a getter method. * <p> * <pre> * // assuming getMethod() returns an appropriate MethodDescriptor reference * isGetterMethod(getMethod("getFoo")) = true * isGetterMethod(getMethod("getfoo") ) = false * isGetterMethod(getMethod("mvcGroupInit")) = false * isGetterMethod(getMethod("isFoo")) = true * isGetterMethod(getMethod("island")) = false * </pre> * * @param method a MethodDescriptor reference * @return true if the method is a getter, false otherwise. */ public static boolean isGetterMethod(@Nonnull MethodDescriptor method) { requireNonNull(method, ERROR_METHOD_NULL); return isInstanceMethod(method) && (GETTER_PATTERN_1.matcher(method.getName()).matches() || GETTER_PATTERN_2.matcher(method.getName()).matches()); } /** * Finds out if the given {@code Method} is a setter method. * <p> * <pre> * // assuming getMethod() returns an appropriate Method reference * isGetterMethod(getMethod("setFoo")) = true * isGetterMethod(getMethod("setfoo")) = false * isGetterMethod(getMethod("mvcGroupInit")) = false * </pre> * * @param method a Method reference * @return true if the method is a setter, false otherwise. */ public static boolean isSetterMethod(@Nonnull Method method) { return isSetterMethod(method, false); } /** * Finds out if the given {@code Method} is a setter method. * <p> * <pre> * // assuming getMethod() returns an appropriate Method reference * isGetterMethod(getMethod("setFoo")) = true * isGetterMethod(getMethod("setfoo")) = false * isGetterMethod(getMethod("mvcGroupInit")) = false * </pre> * * @param method a Method reference * @return true if the method is a setter, false otherwise. */ public static boolean isSetterMethod(@Nonnull Method method, boolean removeAbstractModifier) { requireNonNull(method, ERROR_METHOD_NULL); return isSetterMethod(MethodDescriptor.forMethod(method, removeAbstractModifier)); } /** * Finds out if the given {@code MethodDescriptor} is a setter method. * <p> * <pre> * // assuming getMethod() returns an appropriate MethodDescriptor reference * isGetterMethod(getMethod("setFoo")) = true * isGetterMethod(getMethod("setfoo")) = false * isGetterMethod(getMethod("mvcGroupInit")) = false * </pre> * * @param method a MethodDescriptor reference * @return true if the method is a setter, false otherwise. */ public static boolean isSetterMethod(@Nonnull MethodDescriptor method) { requireNonNull(method, ERROR_METHOD_NULL); return isInstanceMethod(method) && SETTER_PATTERN.matcher(method.getName()).matches(); } /** * Finds out if the given {@code Method} belongs to the set of * predefined Artifact methods by convention. * <p> * <pre> * // assuming getMethod() returns an appropriate Method reference * isArtifactMethod(getMethod("newInstance")) = true * isArtifactMethod(getMethod("griffonDestroy")) = false * isArtifactMethod(getMethod("foo")) = false * </pre> * * @param method a Method reference * @return true if the method is an Artifact method, false otherwise. */ public static boolean isArtifactMethod(@Nonnull Method method) { return isArtifactMethod(method, false); } /** * Finds out if the given {@code Method} belongs to the set of * predefined Artifact methods by convention. * <p> * <pre> * // assuming getMethod() returns an appropriate Method reference * isArtifactMethod(getMethod("newInstance")) = true * isArtifactMethod(getMethod("griffonDestroy")) = false * isArtifactMethod(getMethod("foo")) = false * </pre> * * @param method a Method reference * @return true if the method is an Artifact method, false otherwise. */ public static boolean isArtifactMethod(@Nonnull Method method, boolean removeAbstractModifier) { requireNonNull(method, ERROR_METHOD_NULL); return isArtifactMethod(MethodDescriptor.forMethod(method, removeAbstractModifier)); } /** * Finds out if the given {@code MethodDescriptor} belongs to the set of * predefined Artifact methods by convention. * <p> * <pre> * // assuming getMethod() returns an appropriate MethodDescriptor reference * isArtifactMethod(getMethod("newInstance")) = true * isArtifactMethod(getMethod("griffonDestroy")) = false * isArtifactMethod(getMethod("foo")) = false * </pre> * * @param method a MethodDescriptor reference * @return true if the method is an Artifact method, false otherwise. */ public static boolean isArtifactMethod(@Nonnull MethodDescriptor method) { requireNonNull(method, ERROR_METHOD_NULL); return isInstanceMethod(method) && ARTIFACT_METHODS.contains(method); } /** * Finds out if the given {@code Method} belongs to the set of * predefined MVC methods by convention. * <p> * <pre> * // assuming getMethod() returns an appropriate Method reference * isMvcMethod(getMethod("mvcGroupInit")) = true * isMvcMethod(getMethod("mvcGroupDestroy")) = true * isMvcMethod(getMethod("foo")) = false * </pre> * * @param method a Method reference * @return true if the method is an MVC method, false otherwise. */ public static boolean isMvcMethod(@Nonnull Method method) { return isMvcMethod(method, false); } /** * Finds out if the given {@code Method} belongs to the set of * predefined MVC methods by convention. * <p> * <pre> * // assuming getMethod() returns an appropriate Method reference * isMvcMethod(getMethod("mvcGroupInit")) = true * isMvcMethod(getMethod("mvcGroupDestroy")) = true * isMvcMethod(getMethod("foo")) = false * </pre> * * @param method a Method reference * @return true if the method is an MVC method, false otherwise. */ public static boolean isMvcMethod(@Nonnull Method method, boolean removeAbstractModifier) { requireNonNull(method, ERROR_METHOD_NULL); return isMvcMethod(MethodDescriptor.forMethod(method, removeAbstractModifier)); } /** * Finds out if the given {@code MethodDescriptor} belongs to the set of * predefined MVC methods by convention. * <p> * <pre> * // assuming getMethod() returns an appropriate MethodDescriptor reference * isMvcMethod(getMethod("mvcGroupInit")) = true * isMvcMethod(getMethod("mvcGroupDestroy")) = true * isMvcMethod(getMethod("foo")) = false * </pre> * * @param method a MethodDescriptor reference * @return true if the method is an MVC method, false otherwise. */ public static boolean isMvcMethod(@Nonnull MethodDescriptor method) { requireNonNull(method, ERROR_METHOD_NULL); return isInstanceMethod(method) && MVC_METHODS.contains(method); } /** * Finds out if the given {@code Method} belongs to the set of * predefined threading methods by convention. * <p> * <pre> * // assuming getMethod() returns an appropriate Method reference * isThreadingMethod(getMethod("execOutsideUI")) = true * isThreadingMethod(getMethod("doLater")) = true * isThreadingMethod(getMethod("foo")) = false * </pre> * * @param method a Method reference * @return true if the method is a threading method, false otherwise. */ public static boolean isThreadingMethod(@Nonnull Method method) { return isThreadingMethod(method, false); } /** * Finds out if the given {@code Method} belongs to the set of * predefined threading methods by convention. * <p> * <pre> * // assuming getMethod() returns an appropriate Method reference * isThreadingMethod(getMethod("execOutsideUI")) = true * isThreadingMethod(getMethod("doLater")) = true * isThreadingMethod(getMethod("foo")) = false * </pre> * * @param method a Method reference * @return true if the method is a threading method, false otherwise. */ public static boolean isThreadingMethod(@Nonnull Method method, boolean removeAbstractModifier) { requireNonNull(method, ERROR_METHOD_NULL); return isThreadingMethod(MethodDescriptor.forMethod(method, removeAbstractModifier)); } /** * Finds out if the given {@code MethodDescriptor} belongs to the set of * predefined threading methods by convention. * <p> * <pre> * // assuming getMethod() returns an appropriate MethodDescriptor reference * isThreadingMethod(getMethod("execOutsideUI")) = true * isThreadingMethod(getMethod("doLater")) = true * isThreadingMethod(getMethod("foo")) = false * </pre> * * @param method a MethodDescriptor reference * @return true if the method is a threading method, false otherwise. */ public static boolean isThreadingMethod(@Nonnull MethodDescriptor method) { requireNonNull(method, ERROR_METHOD_NULL); return isInstanceMethod(method) && THREADING_METHODS.contains(method); } /** * Finds out if the given {@code Method} belongs to the set of * predefined event publisher methods by convention. * <p> * <pre> * // assuming getMethod() returns an appropriate Method reference * isEventPublisherMethod(getMethod("addEventPublisher")) = true * isEventPublisherMethod(getMethod("publishEvent")) = true * isEventPublisherMethod(getMethod("foo")) = false * </pre> * * @param method a Method reference * @return true if the method is an @EventPublisher method, false otherwise. */ public static boolean isEventPublisherMethod(@Nonnull Method method) { return isEventPublisherMethod(method, false); } /** * Finds out if the given {@code Method} belongs to the set of * predefined event publisher methods by convention. * <p> * <pre> * // assuming getMethod() returns an appropriate Method reference * isEventPublisherMethod(getMethod("addEventPublisher")) = true * isEventPublisherMethod(getMethod("publishEvent")) = true * isEventPublisherMethod(getMethod("foo")) = false * </pre> * * @param method a Method reference * @return true if the method is an @EventPublisher method, false otherwise. */ public static boolean isEventPublisherMethod(@Nonnull Method method, boolean removeAbstractModifier) { requireNonNull(method, ERROR_METHOD_NULL); return isEventPublisherMethod(MethodDescriptor.forMethod(method, removeAbstractModifier)); } /** * Finds out if the given {@code MethodDescriptor} belongs to the set of * predefined event publisher methods by convention. * <p> * <pre> * // assuming getMethod() returns an appropriate MethodDescriptor reference * isEventPublisherMethod(getMethod("addEventPublisher")) = true * isEventPublisherMethod(getMethod("publishEvent")) = true * isEventPublisherMethod(getMethod("foo")) = false * </pre> * * @param method a MethodDescriptor reference * @return true if the method is an @EventPublisher method, false otherwise. */ public static boolean isEventPublisherMethod(@Nonnull MethodDescriptor method) { requireNonNull(method, ERROR_METHOD_NULL); return isInstanceMethod(method) && EVENT_PUBLISHER_METHODS.contains(method); } /** * Finds out if the given {@code Method} belongs to the set of * predefined observable methods by convention. * <p> * <pre> * // assuming getMethod() returns an appropriate Method reference * isObservableMethod(getMethod("addPropertyChangeListener")) = true * isObservableMethod(getMethod("getPropertyChangeListeners")) = true * isObservableMethod(getMethod("foo")) = false * </pre> * * @param method a Method reference * @return true if the method is an Observable method, false otherwise. */ public static boolean isObservableMethod(@Nonnull Method method) { return isObservableMethod(method, false); } /** * Finds out if the given {@code Method} belongs to the set of * predefined observable methods by convention. * <p> * <pre> * // assuming getMethod() returns an appropriate Method reference * isObservableMethod(getMethod("addPropertyChangeListener")) = true * isObservableMethod(getMethod("getPropertyChangeListeners")) = true * isObservableMethod(getMethod("foo")) = false * </pre> * * @param method a Method reference * @return true if the method is an Observable method, false otherwise. */ public static boolean isObservableMethod(@Nonnull Method method, boolean removeAbstractModifier) { requireNonNull(method, ERROR_METHOD_NULL); return isObservableMethod(MethodDescriptor.forMethod(method, removeAbstractModifier)); } /** * Finds out if the given {@code MethodDescriptor} belongs to the set of * predefined observable methods by convention. * <p> * <pre> * // assuming getMethod() returns an appropriate MethodDescriptor reference * isObservableMethod(getMethod("addPropertyChangeListener")) = true * isObservableMethod(getMethod("getPropertyChangeListeners")) = true * isObservableMethod(getMethod("foo")) = false * </pre> * * @param method a MethodDescriptor reference * @return true if the method is an Observable method, false otherwise. */ public static boolean isObservableMethod(@Nonnull MethodDescriptor method) { requireNonNull(method, ERROR_METHOD_NULL); return isInstanceMethod(method) && OBSERVABLE_METHODS.contains(method); } /** * Finds out if the given {@code Method} belongs to the set of * predefined resources methods by convention. * <p> * <pre> * // assuming getMethod() returns an appropriate Method reference * isResourceHandlerMethod(getMethod("getResourceAsURL")) = true * isResourceHandlerMethod(getMethod("getResourceAsStream")) = true * isResourceHandlerMethod(getMethod("foo")) = false * </pre> * * @param method a Method reference * @return true if the method is an ResourceHandler method, false otherwise. */ public static boolean isResourceHandlerMethod(@Nonnull Method method) { return isResourceHandlerMethod(method, false); } /** * Finds out if the given {@code Method} belongs to the set of * predefined resources methods by convention. * <p> * <pre> * // assuming getMethod() returns an appropriate Method reference * isResourceHandlerMethod(getMethod("getResourceAsURL")) = true * isResourceHandlerMethod(getMethod("getResourceAsStream")) = true * isResourceHandlerMethod(getMethod("foo")) = false * </pre> * * @param method a Method reference * @return true if the method is an ResourceHandler method, false otherwise. */ public static boolean isResourceHandlerMethod(@Nonnull Method method, boolean removeAbstractModifier) { requireNonNull(method, ERROR_METHOD_NULL); return isResourceHandlerMethod(MethodDescriptor.forMethod(method, removeAbstractModifier)); } /** * Finds out if the given {@code MethodDescriptor} belongs to the set of * predefined resources methods by convention. * <p> * <pre> * // assuming getMethod() returns an appropriate MethodDescriptor reference * isResourceHandlerMethod(getMethod("getResourceAsURL")) = true * isResourceHandlerMethod(getMethod("getResourceAsStream")) = true * isResourceHandlerMethod(getMethod("foo")) = false * </pre> * * @param method a MethodDescriptor reference * @return true if the method is an ResourceHandler method, false otherwise. */ public static boolean isResourceHandlerMethod(@Nonnull MethodDescriptor method) { requireNonNull(method, ERROR_METHOD_NULL); return isInstanceMethod(method) && RESOURCE_HANDLER_METHODS.contains(method); } /** * Finds out if the given {@code Method} belongs to the set of * predefined message source methods by convention. * <p> * <pre> * // assuming getMethod() returns an appropriate Method reference * isMessageSourceMethod(getMethod("getMessage")) = true * isMessageSourceMethod(getMethod("foo")) = false * </pre> * * @param method a Method reference * @return true if the method is an MessageSource method, false otherwise. */ public static boolean isMessageSourceMethod(@Nonnull Method method) { return isMessageSourceMethod(method, false); } /** * Finds out if the given {@code Method} belongs to the set of * predefined message source methods by convention. * <p> * <pre> * // assuming getMethod() returns an appropriate Method reference * isMessageSourceMethod(getMethod("getMessage")) = true * isMessageSourceMethod(getMethod("foo")) = false * </pre> * * @param method a Method reference * @return true if the method is an MessageSource method, false otherwise. */ public static boolean isMessageSourceMethod(@Nonnull Method method, boolean removeAbstractModifier) { requireNonNull(method, ERROR_METHOD_NULL); return isMessageSourceMethod(MethodDescriptor.forMethod(method, removeAbstractModifier)); } /** * Finds out if the given {@code MethodDescriptor} belongs to the set of * predefined message source methods by convention. * <p> * <pre> * // assuming getMethod() returns an appropriate MethodDescriptor reference * isMessageSourceMethod(getMethod("getMessage")) = true * isMessageSourceMethod(getMethod("foo")) = false * </pre> * * @param method a MethodDescriptor reference * @return true if the method is an MessageSource method, false otherwise. */ public static boolean isMessageSourceMethod(@Nonnull MethodDescriptor method) { requireNonNull(method, ERROR_METHOD_NULL); return isInstanceMethod(method) && MESSAGE_SOURCE_METHODS.contains(method); } /** * Finds out if the given {@code Method} belongs to the set of * predefined resource resolver methods by convention. * <p> * <pre> * // assuming getMethod() returns an appropriate Method reference * isResourceResolverMethod(getMethod("resolveResource")) = true * isResourceResolverMethod(getMethod("foo")) = false * </pre> * * @param method a Method reference * @return true if the method is an ResourceResolver method, false otherwise. */ public static boolean isResourceResolverMethod(@Nonnull Method method) { return isResourceResolverMethod(method, false); } /** * Finds out if the given {@code Method} belongs to the set of * predefined resource resolver methods by convention. * <p> * <pre> * // assuming getMethod() returns an appropriate Method reference * isResourceResolverMethod(getMethod("resolveResource")) = true * isResourceResolverMethod(getMethod("foo")) = false * </pre> * * @param method a Method reference * @return true if the method is an ResourceResolver method, false otherwise. */ public static boolean isResourceResolverMethod(@Nonnull Method method, boolean removeAbstractModifier) { requireNonNull(method, ERROR_METHOD_NULL); return isResourceResolverMethod(MethodDescriptor.forMethod(method, removeAbstractModifier)); } /** * Finds out if the given {@code MethodDescriptor} belongs to the set of * predefined resource resolver methods by convention. * <p> * <pre> * // assuming getMethod() returns an appropriate MethodDescriptor reference * isResourceResolverMethod(getMethod("resolveResource")) = true * isResourceResolverMethod(getMethod("foo")) = false * </pre> * * @param method a MethodDescriptor reference * @return true if the method is an ResourceResolver method, false otherwise. */ public static boolean isResourceResolverMethod(@Nonnull MethodDescriptor method) { requireNonNull(method, ERROR_METHOD_NULL); return isInstanceMethod(method) && RESOURCE_RESOLVER_METHODS.contains(method); } /** * Finds out if the given {@code Method} is an instance method, i.e, * it is public and non-static. * * @param method a Method reference * @return true if the method is an instance method, false otherwise. */ public static boolean isInstanceMethod(@Nonnull Method method) { return isInstanceMethod(method, false); } /** * Finds out if the given {@code Method} is an instance method, i.e, * it is public and non-static. * * @param method a Method reference * @return true if the method is an instance method, false otherwise. */ public static boolean isInstanceMethod(@Nonnull Method method, boolean removeAbstractModifier) { requireNonNull(method, ERROR_METHOD_NULL); return isInstanceMethod(MethodDescriptor.forMethod(method, removeAbstractModifier)); } /** * Finds out if the given {@code MethodDescriptor} is an instance method, i.e, * it is public and non-static. * * @param method a MethodDescriptor reference * @return true if the method is an instance method, false otherwise. */ public static boolean isInstanceMethod(@Nonnull MethodDescriptor method) { requireNonNull(method, ERROR_METHOD_NULL); int modifiers = method.getModifiers(); return Modifier.isPublic(modifiers) && !Modifier.isAbstract(modifiers) && !Modifier.isStatic(modifiers); } /** * Finds out if the given {@code Method} matches the following criteria:<ul> * <li>isInstanceMethod(method)</li> * <li>! isBasicMethod(method)</li> * <li>! isGroovyInjectedMethod(method)</li> * <li>! isThreadingMethod(method)</li> * <li>! isArtifactMethod(method)</li> * <li>! isMvcMethod(method)</li> * <li>! isServiceMethod(method)</li> * <li>! isEventPublisherMethod(method)</li> * <li>! isObservableMethod(method)</li> * <li>! isResourceHandlerMethod(method)</li> * <li>! isGetterMethod(method)</li> * <li>! isSetterMethod(method)</li> * <li>! isContributionMethod(method)</li> * </ul> * * @param method a Method reference * @return true if the method matches the given criteria, false otherwise. */ public static boolean isPlainMethod(@Nonnull Method method) { return isPlainMethod(method, false); } /** * Finds out if the given {@code Method} matches the following criteria:<ul> * <li>isInstanceMethod(method)</li> * <li>! isBasicMethod(method)</li> * <li>! isGroovyInjectedMethod(method)</li> * <li>! isThreadingMethod(method)</li> * <li>! isArtifactMethod(method)</li> * <li>! isMvcMethod(method)</li> * <li>! isServiceMethod(method)</li> * <li>! isEventPublisherMethod(method)</li> * <li>! isObservableMethod(method)</li> * <li>! isResourceHandlerMethod(method)</li> * <li>! isGetterMethod(method)</li> * <li>! isSetterMethod(method)</li> * <li>! isContributionMethod(method)</li> * </ul> * * @param method a Method reference * @return true if the method matches the given criteria, false otherwise. */ public static boolean isPlainMethod(@Nonnull Method method, boolean removeAbstractModifier) { requireNonNull(method, ERROR_METHOD_NULL); return isPlainMethod(MethodDescriptor.forMethod(method, removeAbstractModifier)); } /** * Finds out if the given {@code MethodDescriptor} matches the following criteria:<ul> * <li>isInstanceMethod(method)</li> * <li>! isBasicMethod(method)</li> * <li>! isGroovyInjectedMethod(method)</li> * <li>! isThreadingMethod(method)</li> * <li>! isArtifactMethod(method)</li> * <li>! isMvcMethod(method)</li> * <li>! isServiceMethod(method)</li> * <li>! isEventPublisherMethod(method)</li> * <li>! isObservableMethod(method)</li> * <li>! isResourceHandlerMethod(method)</li> * <li>! isGetterMethod(method)</li> * <li>! isSetterMethod(method)</li> * <li>! isContributionMethod(method)</li> * </ul> * * @param method a MethodDescriptor reference * @return true if the method matches the given criteria, false otherwise. */ public static boolean isPlainMethod(@Nonnull MethodDescriptor method) { requireNonNull(method, ERROR_METHOD_NULL); return isInstanceMethod(method) && !isBasicMethod(method) && !isGroovyInjectedMethod(method) && !isThreadingMethod(method) && !isArtifactMethod(method) && !isMvcMethod(method) && !isEventPublisherMethod(method) && !isObservableMethod(method) && !isResourceHandlerMethod(method) && !isGetterMethod(method) && !isSetterMethod(method) && !isContributionMethod(method); } /** * Returns true if the specified property in the specified class is of the specified type * * @param clazz The class which contains the property * @param propertyName The property name * @param type The type to check * @return A boolean value */ public static boolean isPropertyOfType(Class<?> clazz, String propertyName, Class<?> type) { try { Class<?> propType = getPropertyType(clazz, propertyName); return propType != null && propType.equals(type); } catch (Exception e) { return false; } } /** * Instantiates a Class, wrapping any exceptions in a RuntimeException. * * @param clazz target Class for which an object will be instantiated * @return the newly instantiated object. * @throws BeanInstantiationException if an error occurs when creating the object */ @Nonnull public static Object instantiateClass(@Nonnull Class<?> clazz) { requireNonNull(clazz, ERROR_CLAZZ_NULL); try { return clazz.newInstance(); } catch (Exception e) { throw new BeanInstantiationException("Could not create an instance of " + clazz, e); } } @Nonnull public static Object instantiate(@Nonnull Class<?> clazz, @Nullable Object[] args) { requireNonNull(clazz, ERROR_CLAZZ_NULL); try { if (args == null) { args = EMPTY_OBJECT_ARRAY; } int arguments = args.length; Class<?>[] parameterTypes = new Class<?>[arguments]; for (int i = 0; i < arguments; i++) { parameterTypes[i] = args[i].getClass(); } return clazz.getDeclaredConstructor(parameterTypes).newInstance(args); } catch (Exception e) { throw new BeanInstantiationException("Could not create an instance of " + clazz, e); } } /** * Returns the value of the specified property and type from an instance of the specified Griffon class * * @param clazz The name of the class which contains the property * @param propertyName The property name * @param propertyType The property type * @return The value of the property or null if none exists */ @Nullable public static Object getPropertyValueOfNewInstance(@Nullable Class<?> clazz, @Nullable String propertyName, Class<?> propertyType) { // validate if (clazz == null || GriffonNameUtils.isBlank(propertyName)) { return null; } Object instance; try { instance = instantiateClass(clazz); } catch (BeanInstantiationException e) { return null; } return getPropertyOrStaticPropertyOrFieldValue(instance, propertyName); } /** * Returns the value of the specified property and type from an instance of the specified Griffon class * * @param clazz The name of the class which contains the property * @param propertyName The property name * @return The value of the property or null if none exists */ public static Object getPropertyValueOfNewInstance(Class<?> clazz, String propertyName) { // validate if (clazz == null || GriffonNameUtils.isBlank(propertyName)) { return null; } Object instance; try { instance = instantiateClass(clazz); } catch (BeanInstantiationException e) { return null; } return getPropertyOrStaticPropertyOrFieldValue(instance, propertyName); } /** * Retrieves a PropertyDescriptor for the specified instance and property value * * @param instance The instance * @param propertyValue The value of the property * @return The PropertyDescriptor */ public static PropertyDescriptor getPropertyDescriptorForValue(Object instance, Object propertyValue) { if (instance == null || propertyValue == null) return null; PropertyDescriptor[] descriptors = getPropertyDescriptors(instance.getClass()); for (PropertyDescriptor pd : descriptors) { if (isAssignableOrConvertibleFrom(pd.getPropertyType(), propertyValue.getClass())) { Object value; try { value = getReadMethod(pd).invoke(instance, (Object[]) null); } catch (Exception e) { throw new RuntimeException("Problem calling readMethod of " + pd, e); } if (propertyValue.equals(value)) return pd; } } return null; } /** * Returns the type of the given property contained within the specified class * * @param clazz The class which contains the property * @param propertyName The name of the property * @return The property type or null if none exists */ @Nullable public static Class<?> getPropertyType(@Nullable Class<?> clazz, @Nullable String propertyName) { if (clazz == null || GriffonNameUtils.isBlank(propertyName)) { return null; } try { PropertyDescriptor desc = getPropertyDescriptor(clazz, propertyName); if (desc != null) { return desc.getPropertyType(); } else { return null; } } catch (Exception e) { // if there are any errors in instantiating just return null for the moment return null; } } /** * Retrieves all the properties of the given class for the given type * * @param clazz The class to retrieve the properties from * @param propertyType The type of the properties you wish to retrieve * @return An array of PropertyDescriptor instances */ @Nonnull public static PropertyDescriptor[] getPropertiesOfType(@Nullable Class<?> clazz, @Nullable Class<?> propertyType) { if (clazz == null || propertyType == null) { return new PropertyDescriptor[0]; } Set<PropertyDescriptor> properties = new HashSet<>(); try { PropertyDescriptor[] descriptors = getPropertyDescriptors(clazz); for (PropertyDescriptor descriptor : descriptors) { Class<?> currentPropertyType = descriptor.getPropertyType(); if (isTypeInstanceOfPropertyType(propertyType, currentPropertyType)) { properties.add(descriptor); } } } catch (Exception e) { // if there are any errors in instantiating just return null for the moment return new PropertyDescriptor[0]; } return properties.toArray(new PropertyDescriptor[properties.size()]); } private static boolean isTypeInstanceOfPropertyType(Class<?> type, Class<?> propertyType) { return propertyType.isAssignableFrom(type) && !propertyType.equals(Object.class); } /** * Retrieves all the properties of the given class which are assignable to the given type * * @param clazz The class to retrieve the properties from * @param propertySuperType The type of the properties you wish to retrieve * @return An array of PropertyDescriptor instances */ public static PropertyDescriptor[] getPropertiesAssignableToType(Class<?> clazz, Class<?> propertySuperType) { if (clazz == null || propertySuperType == null) return new PropertyDescriptor[0]; Set<PropertyDescriptor> properties = new HashSet<>(); try { PropertyDescriptor[] descriptors = getPropertyDescriptors(clazz); for (PropertyDescriptor descriptor : descriptors) { if (propertySuperType.isAssignableFrom(descriptor.getPropertyType())) { properties.add(descriptor); } } } catch (Exception e) { return new PropertyDescriptor[0]; } return properties.toArray(new PropertyDescriptor[properties.size()]); } /** * Retrieves a property of the given class of the specified name and type * * @param clazz The class to retrieve the property from * @param propertyName The name of the property * @param propertyType The type of the property * @return A PropertyDescriptor instance or null if none exists */ public static PropertyDescriptor getProperty(Class<?> clazz, String propertyName, Class<?> propertyType) { if (clazz == null || propertyName == null || propertyType == null) return null; try { PropertyDescriptor pd = getPropertyDescriptor(clazz, propertyName); if (pd.getPropertyType().equals(propertyType)) { return pd; } else { return null; } } catch (Exception e) { // if there are any errors in instantiating just return null for the moment return null; } } /** * Convenience method for converting a collection to an Object[] * * @param c The collection * @return An object array */ public static Object[] collectionToObjectArray(Collection<?> c) { if (c == null) return EMPTY_OBJECT_ARRAY; return c.toArray(new Object[c.size()]); } /** * Detect if left and right types are matching types. In particular, * test if one is a primitive type and the other is the corresponding * Java wrapper type. Primitive and wrapper classes may be passed to * either arguments. * * @param leftType * @param rightType * @return true if one of the classes is a native type and the other the object representation * of the same native type */ public static boolean isMatchBetweenPrimitiveAndWrapperTypes(@Nonnull Class<?> leftType, @Nonnull Class<?> rightType) { requireNonNull(leftType, "Left type is null!"); requireNonNull(rightType, "Right type is null!"); return isMatchBetweenPrimitiveAndWrapperTypes(leftType.getName(), rightType.getName()); } /** * Detect if left and right types are matching types. In particular, * test if one is a primitive type and the other is the corresponding * Java wrapper type. Primitive and wrapper classes may be passed to * either arguments. * * @param leftType * @param rightType * @return true if one of the classes is a native type and the other the object representation * of the same native type */ public static boolean isMatchBetweenPrimitiveAndWrapperTypes(@Nonnull String leftType, @Nonnull String rightType) { requireNonBlank(leftType, "Left type is null!"); requireNonBlank(rightType, "Right type is null!"); String r = PRIMITIVE_TYPE_COMPATIBLE_TYPES.get(leftType); return r != null && r.equals(rightType); } @Nullable @SuppressWarnings("ConstantConditions") private static Method findDeclaredMethod(@Nonnull Class<?> clazz, @Nonnull String methodName, Class[] parameterTypes) { requireNonNull(clazz, ERROR_CLAZZ_NULL); requireNonBlank(methodName, ERROR_METHOD_NAME_BLANK); while (clazz != null) { try { Method method = clazz.getDeclaredMethod(methodName, parameterTypes); if (method != null) return method; } catch (NoSuchMethodException | SecurityException e) { // skip } clazz = clazz.getSuperclass(); } return null; } /** * <p>Work out if the specified property is readable and static. Java introspection does not * recognize this concept of static properties but Groovy does. We also consider public static fields * as static properties with no getters/setters</p> * * @param clazz The class to check for static property * @param propertyName The property name * @return true if the property with name propertyName has a static getter method */ public static boolean isStaticProperty(@Nonnull Class<?> clazz, @Nonnull String propertyName) { requireNonNull(clazz, ERROR_CLAZZ_NULL); requireNonBlank(propertyName, ERROR_PROPERTY_NAME_BLANK); Method getter = findDeclaredMethod(clazz, getGetterName(propertyName), null); if (getter != null) { return isPublicStatic(getter); } else { try { Field f = clazz.getDeclaredField(propertyName); if (f != null) { return isPublicStatic(f); } } catch (NoSuchFieldException ignore) { //ignore } } return false; } /** * Determine whether the method is declared public static * * @param m the method to be tested * @return True if the method is declared public static */ public static boolean isPublicStatic(@Nonnull Method m) { requireNonNull(m, "Argument 'method' must not be null"); final int modifiers = m.getModifiers(); return Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers); } /** * Determine whether the field is declared public static * * @param f the field to be tested * @return True if the field is declared public static */ public static boolean isPublicStatic(@Nonnull Field f) { requireNonNull(f, "Argument 'field' must not be null"); final int modifiers = f.getModifiers(); return Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers); } /** * Calculate the name for a getter method to retrieve the specified property * * @param propertyName the name of the property * @return The name for the getter method for this property, if it were to exist, i.e. getConstraints */ @Nonnull public static String getGetterName(@Nonnull String propertyName) { requireNonBlank(propertyName, ERROR_PROPERTY_NAME_BLANK); return PROPERTY_GET_PREFIX + Character.toUpperCase(propertyName.charAt(0)) + propertyName.substring(1); } /** * <p>Get a static property value, which has a public static getter or is just a public static field.</p> * * @param clazz The class to check for static property * @param name The property name * @return The value if there is one, or null if unset OR there is no such property */ @Nullable public static Object getStaticPropertyValue(@Nonnull Class<?> clazz, @Nonnull String name) { requireNonNull(clazz, ERROR_CLAZZ_NULL); requireNonBlank(name, ERROR_NAME_BLANK); Method getter = findDeclaredMethod(clazz, getGetterName(name), null); try { if (getter != null) { return getter.invoke(null, (Object[]) null); } else { Field f = clazz.getDeclaredField(name); if (f != null) { return f.get(null); } } } catch (Exception ignore) { //ignore } return null; } /** * <p>Looks for a property of the reference instance with a given name.</p> * <p>If found its value is returned. We follow the Java bean conventions with augmentation for groovy support * and static fields/properties. We will therefore match, in this order: * </p> * <ol> * <li>Standard public bean property (with getter or just public field, using normal introspection) * <li>Public static property with getter method * <li>Public static field * </ol> * * @return property value or null if no property found */ @Nullable public static Object getPropertyOrStaticPropertyOrFieldValue(@Nonnull Object obj, @Nonnull String name) { requireNonNull(obj, ERROR_OBJECT_NULL); requireNonBlank(name, ERROR_NAME_BLANK); if (isReadable(obj, name)) { try { return getProperty(obj, name); } catch (Exception e) { throw new PropertyException(obj, name); } } else { // Look for public fields if (isPublicField(obj, name)) { return getFieldValue(obj, name); } // Look for statics Class<?> clazz = obj.getClass(); if (isStaticProperty(clazz, name)) { return getStaticPropertyValue(clazz, name); } else { return null; } } } /** * Get the value of a declared field on an object * * @param obj the instance that owns the field * @param name the name of the file to lookup * @return The object value or null if there is no such field or access problems */ @Nullable public static Object getFieldValue(@Nonnull Object obj, @Nonnull String name) { requireNonNull(obj, ERROR_OBJECT_NULL); requireNonBlank(name, ERROR_NAME_BLANK); Class<?> clazz = obj.getClass(); Class c = clazz; while (c != null && !c.equals(Object.class)) { Field field = null; boolean wasAccessible = false; try { field = c.getDeclaredField(name); wasAccessible = field.isAccessible(); field.setAccessible(true); return field.get(obj); } catch (Exception e) { // ignore } finally { if (field != null) { field.setAccessible(wasAccessible); } } c = c.getSuperclass(); } return null; } /** * Get the a declared field on an object * * @param obj the instance that owns the field * @param name the name of the file to lookup * @return The field or null if there is no such field or access problems */ @Nullable public static Field getField(@Nonnull Object obj, @Nonnull String name) { requireNonNull(obj, ERROR_OBJECT_NULL); requireNonBlank(name, ERROR_NAME_BLANK); return getField(obj.getClass(), name); } /** * Get the a declared field on a class * * @param clazz the clazz that owns the field * @param name the name of the file to lookup * @return The field or null if there is no such field or access problems */ @Nullable public static Field getField(@Nonnull Class<?> clazz, @Nonnull String name) { requireNonNull(clazz, ERROR_CLAZZ_NULL); requireNonBlank(name, ERROR_NAME_BLANK); Class c = clazz; while (c != null && !c.equals(Object.class)) { Field field = null; try { return c.getDeclaredField(name); } catch (Exception e) { // ignore } c = c.getSuperclass(); } return null; } /** * Returns an array of {@code Field} objects reflecting all the fields * declared by the class and its hierarchy, represented by this * {@code Class} object. This includes public, protected, default * (package) access, and private fields, but excludes inherited fields. * <p> * <p> The elements in the returned array are not sorted and are not in any * particular order. * * @param clazz the clazz that will be queried. * @return the array of {@code Field} objects representing all the * declared fields of this class and its hierarchy */ public static Field[] getAllDeclaredFields(@Nonnull Class<?> clazz) { requireNonNull(clazz, ERROR_CLAZZ_NULL); List<Field> fields = new ArrayList<>(); Class c = clazz; while (c != null && !c.equals(Object.class)) { Field[] declaredFields = c.getDeclaredFields(); if (declaredFields != null && declaredFields.length > 0) { fields.addAll(Arrays.asList(declaredFields)); } c = c.getSuperclass(); } return fields.toArray(new Field[fields.size()]); } /** * Work out if the specified object has a public field with the name supplied. * * @param obj the instance that owns the field * @param name the name of the file to lookup * @return True if a public field with the name exists */ public static boolean isPublicField(@Nonnull Object obj, @Nonnull String name) { requireNonNull(obj, ERROR_OBJECT_NULL); requireNonBlank(name, ERROR_NAME_BLANK); Class<?> clazz = obj.getClass(); Field f; try { f = clazz.getDeclaredField(name); return Modifier.isPublic(f.getModifiers()); } catch (NoSuchFieldException e) { return false; } } /** * Checks whether the specified property is inherited from a super class * * @param clz The class to check * @param propertyName The property name * @return True if the property is inherited */ public static boolean isPropertyInherited(@Nullable Class<?> clz, @Nonnull String propertyName) { if (clz == null) return false; requireNonBlank(propertyName, ERROR_PROPERTY_NAME_BLANK); Class<?> superClass = clz.getSuperclass(); PropertyDescriptor pd; try { pd = getPropertyDescriptor(superClass, propertyName); } catch (Exception e) { throw new PropertyException(superClass, propertyName, e); } return pd != null && pd.getReadMethod() != null; } /** * Creates a concrete collection for the supplied interface * * @param interfaceType The interface * @return ArrayList for List, TreeSet for SortedSet, HashSet for Set etc. */ @Nonnull public static Collection<?> createConcreteCollection(@Nonnull Class<?> interfaceType) { requireNonNull(interfaceType, ERROR_TYPE_NULL); Collection<?> elements; if (interfaceType.equals(List.class)) { elements = new ArrayList<>(); } else if (interfaceType.equals(SortedSet.class)) { elements = new TreeSet<>(); } else { elements = new LinkedHashSet<>(); } return elements; } /** * Retrieves the name of a setter for the specified property name * * @param propertyName The property name * @return The setter equivalent */ @Nonnull public static String getSetterName(@Nonnull String propertyName) { requireNonBlank(propertyName, ERROR_PROPERTY_NAME_BLANK); return PROPERTY_SET_PREFIX + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1); } /** * Returns true if the name of the method specified and the number of arguments make it a javabean property * * @param name True if its a Javabean property * @param args The arguments * @return True if it is a javabean property method */ @SuppressWarnings("ConstantConditions") public static boolean isGetter(@Nullable String name, @Nullable Class[] args) { if (GriffonNameUtils.isBlank(name) || args == null) return false; if (args.length != 0) return false; if (name.startsWith(PROPERTY_GET_PREFIX)) { name = name.substring(3); if (name.length() > 0 && Character.isUpperCase(name.charAt(0))) return true; } else if (name.startsWith(PROPERTY_IS_PREFIX)) { name = name.substring(2); if (name.length() > 0 && Character.isUpperCase(name.charAt(0))) return true; } return false; } /** * Returns a property name equivalent for the given getter name or null if it is not a getter * * @param getterName The getter name * @return The property name equivalent */ @Nullable @SuppressWarnings("ConstantConditions") public static String getPropertyForGetter(@Nullable String getterName) { if (GriffonNameUtils.isBlank(getterName)) return null; if (getterName.startsWith(PROPERTY_GET_PREFIX)) { String prop = getterName.substring(3); return convertPropertyName(prop); } else if (getterName.startsWith(PROPERTY_IS_PREFIX)) { String prop = getterName.substring(2); return convertPropertyName(prop); } return null; } @Nonnull private static String convertPropertyName(@Nonnull String prop) { if (Character.isUpperCase(prop.charAt(0)) && Character.isUpperCase(prop.charAt(1))) { return prop; } else if (Character.isDigit(prop.charAt(0))) { return prop; } else { return Character.toLowerCase(prop.charAt(0)) + prop.substring(1); } } /** * Returns a property name equivalent for the given setter name or null if it is not a getter * * @param setterName The setter name * @return The property name equivalent */ @Nullable @SuppressWarnings("ConstantConditions") public static String getPropertyForSetter(@Nullable String setterName) { if (GriffonNameUtils.isBlank(setterName)) return null; if (setterName.startsWith(PROPERTY_SET_PREFIX)) { String prop = setterName.substring(3); return convertPropertyName(prop); } return null; } @SuppressWarnings("ConstantConditions") public static boolean isSetter(@Nullable String name, @Nullable Class[] args) { if (GriffonNameUtils.isBlank(name) || args == null) return false; if (name.startsWith(PROPERTY_SET_PREFIX)) { if (args.length != 1) return false; name = name.substring(3); if (name.length() > 0 && Character.isUpperCase(name.charAt(0))) return true; } return false; } /** * Returns true if the specified clazz parameter is either the same as, or is a superclass or super interface * of, the specified type parameter. Converts primitive types to compatible class automatically. * * @param clazz * @param type * @return True if the class is a taglib * @see java.lang.Class#isAssignableFrom(Class) */ public static boolean isAssignableOrConvertibleFrom(@Nullable Class<?> clazz, @Nullable Class<?> type) { if (type == null || clazz == null) { return false; } else if (type.isPrimitive()) { // convert primitive type to compatible class Class<?> primitiveClass = PRIMITIVE_TYPE_COMPATIBLE_CLASSES.get(type); return primitiveClass != null && clazz.isAssignableFrom(primitiveClass); } else { return clazz.isAssignableFrom(type); } } /** * Retrieves a boolean value from a Map for the given key * * @param key The key that references the boolean value * @param map The map to look in * @return A boolean value which will be false if the map is null, the map doesn't contain the key or the value is false */ public static boolean getBooleanFromMap(@Nullable String key, @Nullable Map<String, Object> map) { if (map == null) return false; if (map.containsKey(key)) { Object o = map.get(key); if (o == null) return false; else if (o instanceof Boolean) { return (Boolean) o; } else { return Boolean.valueOf(o.toString()); } } return false; } /** * Returns whether the specified class is either within one of the specified packages or * within a subpackage of one of the packages * * @param clazz The class * @param packageList The list of packages * @return True if it is within the list of specified packages */ public static boolean isClassBelowPackage(@Nonnull Class<?> clazz, @Nonnull List<?> packageList) { requireNonNull(clazz, ERROR_CLAZZ_NULL); requireNonNull(packageList, "Argument 'packageList' must not be null"); String classPackage = clazz.getPackage().getName(); for (Object packageName : packageList) { if (packageName != null) { if (classPackage.startsWith(packageName.toString())) { return true; } } } return false; } /** * Sets or updates an object's properties. * <p> * This method will attempt setting a property using a matching * {@code PropertyDescriptor}; next it will try direct field * access if the property was not found. * * @param bean the target object on which properties will be set * @param properties names and values for properties to be set * @throws PropertyException if a property could be found * @since 2.1.0 */ public static void setPropertiesOrFields(@Nonnull Object bean, @Nonnull Map<String, Object> properties) throws PropertyException { requireNonNull(bean, ERROR_BEAN_NULL); requireNonNull(properties, ERROR_PROPERTIES_NULL); for (Map.Entry<String, Object> entry : properties.entrySet()) { setPropertyOrFieldValue(bean, entry.getKey(), entry.getValue()); } } /** * Sets or updates an object's properties. * <p> * This method will attempt setting a property using a matching * {@code PropertyDescriptor}; next it will try direct field * access if the property was not found. * * @param bean the target object on which properties will be set * @param properties names and values for properties to be set * @since 2.1.0 */ public static void setPropertiesOrFieldsNoException(@Nonnull Object bean, @Nonnull Map<String, Object> properties) { requireNonNull(bean, ERROR_BEAN_NULL); requireNonNull(properties, ERROR_PROPERTIES_NULL); for (Map.Entry<String, Object> entry : properties.entrySet()) { try { setPropertyOrFieldValue(bean, entry.getKey(), entry.getValue()); } catch (PropertyException pe) { // ignore } } } /** * Sets or updates an object's property. * <p> * This method will attempt setting a property using a matching * {@code PropertyDescriptor}; next it will try direct field * access if the property was not found. * * @param bean the target object on which the property will be set * @param name the name of the property to set * @param value the value to be set * @throws PropertyException if the property could not be found * @since 2.1.0 */ public static void setPropertyOrFieldValue(@Nonnull Object bean, @Nonnull String name, @Nullable Object value) throws PropertyException { try { setPropertyValue(bean, name, value); } catch (PropertyException pe) { try { setFieldValue(bean, name, value); } catch (FieldException fe) { throw pe; } } } /** * Sets or updates an object's property. * <p> * This method will attempt setting a property using a matching * {@code PropertyDescriptor}; next it will try direct field * access if the property was not found. * * @param bean the target object on which the property will be set * @param name the name of the property to set * @param value the value to be set * @throws PropertyException if the property could not be found * @since 2.4.0 */ public static void setPropertyOrFieldValueNoException(@Nonnull Object bean, @Nonnull String name, @Nullable Object value) { try { setPropertyOrFieldValue(bean, name, value); } catch (PropertyException pe) { // ignore } } /** * Sets or updates an object's properties. * <p> * This method will attempt setting a property using direct field * access; next it will try a {@code PropertyDescriptor} if a * matching field name was not found. * * @param bean the target object on which properties will be set * @param properties names and values for properties to be set * @throws FieldException if the field could not be found * @since 2.1.0 */ public static void setFieldsOrProperties(@Nonnull Object bean, @Nonnull Map<String, Object> properties) throws FieldException { requireNonNull(bean, ERROR_BEAN_NULL); requireNonNull(properties, ERROR_PROPERTIES_NULL); for (Map.Entry<String, Object> entry : properties.entrySet()) { setFieldOrPropertyValue(bean, entry.getKey(), entry.getValue()); } } /** * Sets or updates an object's properties. * <p> * This method will attempt setting a property using direct field * access; next it will try a {@code PropertyDescriptor} if a * matching field name was not found. * * @param bean the target object on which properties will be set * @param properties names and values for properties to be set * @since 2.1.0 */ public static void setFieldsOrPropertiesNoException(@Nonnull Object bean, @Nonnull Map<String, Object> properties) { requireNonNull(bean, ERROR_BEAN_NULL); requireNonNull(properties, ERROR_PROPERTIES_NULL); for (Map.Entry<String, Object> entry : properties.entrySet()) { try { setFieldOrPropertyValue(bean, entry.getKey(), entry.getValue()); } catch (FieldException pe) { // ignore } } } /** * Sets or updates an object's property. * <p> * This method will attempt setting a property using direct field * access; next it will try a {@code PropertyDescriptor} if a * matching field name was not found. * * @param bean the target object on which the property will be set * @param name the name of the property to set * @param value the value to be set * @throws FieldException if the property could not be found * @since 2.1.0 */ public static void setFieldOrPropertyValue(@Nonnull Object bean, @Nonnull String name, @Nullable Object value) throws FieldException { try { setFieldValue(bean, name, value); } catch (FieldException fe) { try { setPropertyValue(bean, name, value); } catch (PropertyException pe) { throw fe; } } } /** * Sets or updates field values on an object. * * @param bean the target object on which field values will be set * @param fields names and values of fields to be set * @throws FieldException if a field could not be found * @since 2.1.0 */ public static void setFields(@Nonnull Object bean, @Nonnull Map<String, Object> fields) throws FieldException { requireNonNull(bean, ERROR_BEAN_NULL); requireNonNull(fields, ERROR_FIELDS_NULL); for (Map.Entry<String, Object> entry : fields.entrySet()) { setFieldValue(bean, entry.getKey(), entry.getValue()); } } /** * Sets or updates field values on an object. * * @param bean the target object on which field values will be set * @param fields names and values of fields to be set * @since 2.1.0 */ public static void setFieldsNoException(@Nonnull Object bean, @Nonnull Map<String, Object> fields) { requireNonNull(bean, ERROR_BEAN_NULL); requireNonNull(fields, ERROR_FIELDS_NULL); for (Map.Entry<String, Object> entry : fields.entrySet()) { try { setFieldValue(bean, entry.getKey(), entry.getValue()); } catch (FieldException e) { // ignore } } } /** * Sets or updates an object's field. * * @param bean the target object on which the field will be set * @param name the name of the field to set * @param value the value to be set * @throws FieldException if the field could not be found * @since 2.1.0 */ public static void setFieldValue(@Nonnull Object bean, @Nonnull String name, @Nullable Object value) throws FieldException { requireNonNull(bean, ERROR_BEAN_NULL); requireNonBlank(name, ERROR_NAME_BLANK); try { setField(bean, name, value); } catch (IllegalAccessException | NoSuchFieldException e) { throw new FieldException(bean, name, value, e); } } /** * Sets or updates an object's field. * * @param bean the target object on which the field will be set * @param name the name of the field to set * @param value the value to be set * @throws FieldException if the field could not be found * @since 2.4.0 */ public static void setFieldValueNoException(@Nonnull Object bean, @Nonnull String name, @Nullable Object value) { try { setFieldValue(bean, name, value); } catch (FieldException e) { // ignore } } /** * Sets or updates properties on an object. * * @param bean the target object on which properties will be set * @param properties names and values of properties to be set * @throws PropertyException if a property could not be found */ public static void setProperties(@Nonnull Object bean, @Nonnull Map<String, Object> properties) throws PropertyException { requireNonNull(bean, ERROR_BEAN_NULL); requireNonNull(properties, ERROR_PROPERTIES_NULL); for (Map.Entry<String, Object> entry : properties.entrySet()) { setPropertyValue(bean, entry.getKey(), entry.getValue()); } } /** * Sets or updates properties on an object. * * @param bean the target object on which properties will be set * @param properties names and values of properties to be set */ public static void setPropertiesNoException(@Nonnull Object bean, @Nonnull Map<String, Object> properties) { requireNonNull(bean, ERROR_BEAN_NULL); requireNonNull(properties, ERROR_PROPERTIES_NULL); for (Map.Entry<String, Object> entry : properties.entrySet()) { try { setPropertyValue(bean, entry.getKey(), entry.getValue()); } catch (PropertyException e) { // ignore } } } /** * /** * Sets or updates a property on an object. * * @param bean the target object on which the property will be set * @param name the name of the property to set * @param value the value to be set * @throws PropertyException if the property could not be found */ public static void setPropertyValue(@Nonnull Object bean, @Nonnull String name, @Nullable Object value) throws PropertyException { requireNonNull(bean, ERROR_BEAN_NULL); requireNonBlank(name, ERROR_NAME_BLANK); try { setProperty(bean, name, value); } catch (IllegalAccessException | NoSuchMethodException e) { throw new PropertyException(bean, name, value, e); } catch (InvocationTargetException e) { throw new PropertyException(bean, name, value, e.getTargetException()); } } /** * Returns the value of a property. * * @param bean the owner of the property * @param name the name of the property to retrieve * @return the value read from the matching property * @throws PropertyException if the property could not be found */ @Nullable public static Object getPropertyValue(@Nonnull Object bean, @Nonnull String name) throws PropertyException { requireNonNull(bean, ERROR_BEAN_NULL); requireNonBlank(name, ERROR_NAME_BLANK); try { return getProperty(bean, name); } catch (IllegalAccessException | NoSuchMethodException e) { throw new PropertyException(bean, name, e); } catch (InvocationTargetException e) { throw new PropertyException(bean, name, e.getTargetException()); } } // -- The following methods and properties were copied from commons-beanutils private static final Map<String, PropertyDescriptor[]> descriptorsCache = new LinkedHashMap<>(); /** * <p>Retrieve the property descriptor for the specified property of the * specified bean, or return <code>null</code> if there is no such * descriptor.</p> * This method does not resolve index, nested nor mapped properties.<p> * * @param bean Bean for which a property descriptor is requested * @param name name of the property for which a property descriptor * is requested * @return the property descriptor or null if the bean does not have * a property that matches the specified name. * @throws IllegalAccessException if the caller does not have * access to the property accessor method * @throws IllegalArgumentException if <code>bean</code> or * <code>name</code> is null * @throws InvocationTargetException if the property accessor method * throws an exception * @throws NoSuchMethodException if an accessor method for this * property cannot be found */ @Nullable public static PropertyDescriptor getPropertyDescriptor(@Nonnull Object bean, @Nonnull String name) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { requireNonNull(bean, ERROR_BEAN_NULL); requireNonBlank(name, ERROR_NAME_BLANK); return getPropertyDescriptor(bean instanceof Class ? (Class<?>) bean : bean.getClass(), name); } /** * <p>Retrieve the property descriptor for the specified property of the * specified class, or return <code>null</code> if there is no such * descriptor.</p> * This method does not resolve index, nested nor mapped properties.<p> * * @param clazz class for which a property descriptor is requested * @param name name of the property for which a property descriptor * is requested * @return the property descriptor or null if the bean does not have * a property that matches the specified name. * @throws IllegalAccessException if the caller does not have * access to the property accessor method * @throws IllegalArgumentException if <code>bean</code> or * <code>name</code> is null * @throws InvocationTargetException if the property accessor method * throws an exception * @throws NoSuchMethodException if an accessor method for this * property cannot be found */ @Nullable public static PropertyDescriptor getPropertyDescriptor(@Nonnull Class<?> clazz, @Nonnull String name) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { requireNonNull(clazz, ERROR_CLAZZ_NULL); requireNonBlank(name, ERROR_NAME_BLANK); PropertyDescriptor[] descriptors = getPropertyDescriptors(clazz); for (PropertyDescriptor descriptor : descriptors) { if (name.equals(descriptor.getName())) { return (descriptor); } } return null; } /** * <p>Retrieve the property descriptors for the specified class, * introspecting and caching them the first time a particular bean class * is encountered.</p> * * @param beanClass Bean class for which property descriptors are requested * @return the property descriptors * @throws IllegalArgumentException if <code>beanClass</code> is null */ @Nonnull public static PropertyDescriptor[] getPropertyDescriptors(@Nonnull Class<?> beanClass) { requireNonNull(beanClass, ERROR_CLAZZ_NULL); // Look up any cached descriptors for this bean class PropertyDescriptor[] descriptors; descriptors = descriptorsCache.get(beanClass.getName()); if (descriptors != null) { return descriptors; } // Introspect the bean and cache the generated descriptors BeanInfo beanInfo; try { beanInfo = Introspector.getBeanInfo(beanClass); } catch (IntrospectionException e) { return (new PropertyDescriptor[0]); } descriptors = beanInfo.getPropertyDescriptors(); if (descriptors == null) { descriptors = new PropertyDescriptor[0]; } descriptorsCache.put(beanClass.getName(), descriptors); return descriptors; } /** * <p>Return an accessible property getter method for this property, * if there is one; otherwise return <code>null</code>.</p> * * @param descriptor Property descriptor to return a getter for * @return The read method */ @Nullable public static Method getReadMethod(@Nonnull PropertyDescriptor descriptor) { requireNonNull(descriptor, ERROR_DESCRIPTOR_NULL); return (MethodUtils.getAccessibleMethod(descriptor.getReadMethod())); } /** * <p>Return <code>true</code> if the specified property name identifies * a readable property on the specified bean; otherwise, return * <code>false</code>. * * @param bean Bean to be examined * @param name Property name to be evaluated * @return <code>true</code> if the property is readable, * otherwise <code>false</code> * @throws IllegalArgumentException if <code>bean</code> * or <code>name</code> is <code>null</code> * @since BeanUtils 1.6 */ public static boolean isReadable(@Nonnull Object bean, @Nonnull String name) { // Validate method parameters requireNonNull(bean, ERROR_BEAN_NULL); requireNonBlank(name, ERROR_NAME_BLANK); try { PropertyDescriptor desc = getPropertyDescriptor(bean, name); if (desc != null) { Method readMethod = getReadMethod(bean.getClass(), desc); if (readMethod != null) { readMethod = MethodUtils.getAccessibleMethod(bean.getClass(), readMethod); } return (readMethod != null); } else { return false; } } catch (IllegalAccessException e) { return false; } catch (InvocationTargetException e) { return false; } catch (NoSuchMethodException e) { return false; } } /** * <p>Return an accessible property setter method for this property, * if there is one; otherwise return <code>null</code>.</p> * * @param descriptor Property descriptor to return a setter for * @return The write method */ @Nullable public static Method getWriteMethod(@Nonnull PropertyDescriptor descriptor) { requireNonNull(descriptor, ERROR_DESCRIPTOR_NULL); return (MethodUtils.getAccessibleMethod(descriptor.getWriteMethod())); } /** * <p>Return <code>true</code> if the specified property name identifies * a writable property on the specified bean; otherwise, return * <code>false</code>. * * @param bean Bean to be examined * @param name Property name to be evaluated * @return <code>true</code> if the property is writable, * otherwise <code>false</code> * @throws IllegalArgumentException if <code>bean</code> * or <code>name</code> is <code>null</code> */ public static boolean isWritable(@Nonnull Object bean, @Nonnull String name) { // Validate method parameters requireNonNull(bean, ERROR_BEAN_NULL); requireNonBlank(name, ERROR_NAME_BLANK); try { PropertyDescriptor desc = getPropertyDescriptor(bean, name); if (desc != null) { Method writeMethod = getWriteMethod(bean.getClass(), desc); if (writeMethod != null) { writeMethod = MethodUtils.getAccessibleMethod(bean.getClass(), writeMethod); } return (writeMethod != null); } else { return false; } } catch (IllegalAccessException e) { return false; } catch (InvocationTargetException e) { return false; } catch (NoSuchMethodException e) { return false; } } /** * Sets the value of the specified field of the specified bean. * * @param bean Bean whose field is to be mutated * @param name Name of the field to be mutated * @param value The value to be set on the property * @throws IllegalAccessException if the caller does not have * access to the field * @throws IllegalArgumentException if <code>bean</code> or * <code>name</code> is null * @throws NoSuchFieldException if the named field cannot be found * @throws FieldException if the field cannot be set * @since 2.1.0 */ public static void setField(@Nonnull Object bean, @Nonnull String name, @Nullable Object value) throws NoSuchFieldException, IllegalAccessException, FieldException { requireNonNull(bean, ERROR_BEAN_NULL); requireNonBlank(name, ERROR_NAME_BLANK); Class<?> declaringClass = bean.getClass(); while (declaringClass != null) { try { Field field = declaringClass.getDeclaredField(name); // type conversion needed? Class<?> propertyType = field.getType(); if (value != null && !propertyType.isAssignableFrom(value.getClass())) { value = TypeUtils.convertValue(propertyType, value); } field.setAccessible(true); try { field.set(bean, value); return; } catch (IllegalArgumentException iae) { throw new FieldException(bean, name, value, iae); } } catch (NoSuchFieldException nsfe) { declaringClass = declaringClass.getSuperclass(); } } throw new NoSuchFieldException(name); } /** * Sets the value of the specified property of the specified bean. * * @param bean Bean whose property is to be mutated * @param name Name of the property to be mutated * @param value The value to be set on the property * @throws IllegalAccessException if the caller does not have * access to the property accessor method * @throws IllegalArgumentException if <code>bean</code> or * <code>name</code> is null * @throws InvocationTargetException if the property accessor method * throws an exception * @throws NoSuchMethodException if an accessor method for this * property cannot be found * @throws PropertyException if the property cannot be set */ public static void setProperty(@Nonnull Object bean, @Nonnull String name, @Nullable Object value) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException, PropertyException { requireNonNull(bean, ERROR_BEAN_NULL); requireNonBlank(name, ERROR_NAME_BLANK); // Retrieve the property setter method for the specified property PropertyDescriptor descriptor = getPropertyDescriptor(bean, name); if (descriptor == null) { throw new NoSuchMethodException("Unknown property '" + name + "' on class '" + bean.getClass() + "'"); } Method writeMethod = getWriteMethod(bean.getClass(), descriptor); if (writeMethod == null) { throw new NoSuchMethodException("Property '" + name + "' has no setter method in class '" + bean.getClass() + "'"); } // type conversion needed? Class<?> propertyType = descriptor.getPropertyType(); if (value != null && !propertyType.isAssignableFrom(value.getClass())) { value = TypeUtils.convertValue(propertyType, value); } // Call the property setter try { writeMethod.invoke(bean, value); } catch (IllegalArgumentException iae) { throw new PropertyException(bean, name, value, iae); } } /** * Return the value of the specified property of the specified bean, * no matter which property reference format is used, with no * type conversions. * * @param bean Bean whose property is to be extracted * @param name Possibly indexed and/or nested name of the property * to be extracted * @return the property value * @throws IllegalAccessException if the caller does not have * access to the property accessor method * @throws IllegalArgumentException if <code>bean</code> or * <code>name</code> is null * @throws InvocationTargetException if the property accessor method * throws an exception * @throws NoSuchMethodException if an accessor method for this * property cannot be found */ @Nullable public static Object getProperty(@Nonnull Object bean, @Nonnull String name) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { requireNonNull(bean, ERROR_BEAN_NULL); requireNonBlank(name, ERROR_NAME_BLANK); // Retrieve the property getter method for the specified property PropertyDescriptor descriptor = getPropertyDescriptor(bean, name); if (descriptor == null) { throw new NoSuchMethodException("Unknown property '" + name + "' on class '" + bean.getClass() + "'"); } Method readMethod = getReadMethod(bean.getClass(), descriptor); if (readMethod == null) { throw new NoSuchMethodException("Property '" + name + "' has no getter method in class '" + bean.getClass() + "'"); } // Call the property getter and return the value return readMethod.invoke(bean, EMPTY_OBJECT_ARRAY); } /** * <p>Return an accessible property getter method for this property, * if there is one; otherwise return <code>null</code>.</p> * * @param clazz The class of the read method will be invoked on * @param descriptor Property descriptor to return a getter for * @return The read method */ @Nullable public static Method getReadMethod(@Nonnull Class<?> clazz, @Nonnull PropertyDescriptor descriptor) { requireNonNull(clazz, ERROR_CLAZZ_NULL); requireNonNull(descriptor, ERROR_DESCRIPTOR_NULL); return (MethodUtils.getAccessibleMethod(clazz, descriptor.getReadMethod())); } /** * <p>Return an accessible property setter method for this property, * if there is one; otherwise return <code>null</code>.</p> * * @param clazz The class of the write method will be invoked on * @param descriptor Property descriptor to return a setter for * @return The write method */ @Nullable public static Method getWriteMethod(@Nonnull Class<?> clazz, @Nonnull PropertyDescriptor descriptor) { requireNonNull(clazz, ERROR_CLAZZ_NULL); requireNonNull(descriptor, ERROR_DESCRIPTOR_NULL); return (MethodUtils.getAccessibleMethod(clazz, descriptor.getWriteMethod())); } // -- The following methods and properties were copied from commons-lang /** * <p>Validate that the argument condition is <code>true</code>; otherwise * throwing an exception with the specified message. This method is useful when * validating according to an arbitrary boolean expression, such as validating a * primitive number or using your own custom validation expression.</p> * <p> * <pre> * isTrue( (i > 0), "The value must be greater than zero"); * isTrue( myObject.isOk(), "The object is not OK"); * </pre> * * @param expression the boolean expression to check * @param message the exception message if invalid * @throws IllegalArgumentException if expression is <code>false</code> */ public static void isTrue(boolean expression, String message) { if (expression) { throw new IllegalArgumentException(message); } } @Nullable public static Object invokeInstanceMethod(@Nonnull Object object, @Nonnull String methodName) { return invokeInstanceMethod(object, methodName, EMPTY_ARGS); } @Nullable public static Object invokeInstanceMethod(@Nonnull Object object, @Nonnull String methodName, Object arg) { return invokeInstanceMethod(object, methodName, new Object[]{arg}); } @Nullable public static Object invokeInstanceMethod(@Nonnull Object object, @Nonnull String methodName, Object... args) { requireNonNull(object, ERROR_OBJECT_NULL); requireNonBlank(methodName, ERROR_METHOD_NAME_BLANK); try { return invokeMethod(object, methodName, args); } catch (NoSuchMethodException | IllegalAccessException e) { throw new InstanceMethodInvocationException(object, methodName, args, e); } catch (InvocationTargetException e) { throw new InstanceMethodInvocationException(object, methodName, args, e.getTargetException()); } } @Nullable public static Object invokeExactInstanceMethod(@Nonnull Object object, @Nonnull String methodName) { return invokeExactInstanceMethod(object, methodName, EMPTY_ARGS); } @Nullable public static Object invokeExactInstanceMethod(@Nonnull Object object, @Nonnull String methodName, Object arg) { return invokeExactInstanceMethod(object, methodName, new Object[]{arg}); } @Nullable public static Object invokeExactInstanceMethod(@Nonnull Object object, @Nonnull String methodName, Object... args) { requireNonNull(object, ERROR_OBJECT_NULL); requireNonBlank(methodName, ERROR_METHOD_NAME_BLANK); try { return invokeExactMethod(object, methodName, args); } catch (NoSuchMethodException | IllegalAccessException e) { throw new InstanceMethodInvocationException(object, methodName, args, e); } catch (InvocationTargetException e) { throw new InstanceMethodInvocationException(object, methodName, args, e.getTargetException()); } } @Nullable public static Object invokeStaticMethod(@Nonnull Class<?> type, @Nonnull String methodName) { return invokeStaticMethod(type, methodName, EMPTY_ARGS); } @Nullable public static Object invokeStaticMethod(@Nonnull Class<?> type, @Nonnull String methodName, Object arg) { return invokeStaticMethod(type, methodName, new Object[]{arg}); } @Nullable public static Object invokeStaticMethod(@Nonnull Class<?> type, @Nonnull String methodName, Object... args) { requireNonNull(type, ERROR_TYPE_NULL); requireNonBlank(methodName, ERROR_METHOD_NAME_BLANK); try { return MethodUtils.invokeStaticMethod(type, methodName, args); } catch (NoSuchMethodException | IllegalAccessException e) { throw new StaticMethodInvocationException(type, methodName, args, e); } catch (InvocationTargetException e) { throw new StaticMethodInvocationException(type, methodName, args, e.getTargetException()); } } @Nullable public static Object invokeExactStaticMethod(@Nonnull Class<?> type, @Nonnull String methodName) { return invokeExactStaticMethod(type, methodName, EMPTY_ARGS); } @Nullable public static Object invokeExactStaticMethod(@Nonnull Class<?> type, @Nonnull String methodName, Object arg) { return invokeExactStaticMethod(type, methodName, new Object[]{arg}); } @Nullable public static Object invokeExactStaticMethod(@Nonnull Class<?> type, @Nonnull String methodName, Object... args) { requireNonNull(type, ERROR_TYPE_NULL); requireNonBlank(methodName, ERROR_METHOD_NAME_BLANK); try { return MethodUtils.invokeExactStaticMethod(type, methodName, args); } catch (NoSuchMethodException | IllegalAccessException e) { throw new StaticMethodInvocationException(type, methodName, args, e); } catch (InvocationTargetException e) { throw new StaticMethodInvocationException(type, methodName, args, e.getTargetException()); } } public static boolean hasMethodAnnotatedwith(@Nonnull final Object instance, @Nonnull final Class<? extends Annotation> annotation) { Class<?> klass = instance.getClass(); while (klass != null) { boolean found = false; for (Method method : klass.getDeclaredMethods()) { if (method.isAnnotationPresent(annotation) && method.getParameterTypes().length == 0) { return true; } } klass = klass.getSuperclass(); } return false; } public static void invokeAnnotatedMethod(@Nonnull final Object instance, @Nonnull final Class<? extends Annotation> annotation) { List<Method> methods = new ArrayList<>(); Class<?> klass = instance.getClass(); while (klass != null) { boolean found = false; for (Method method : klass.getDeclaredMethods()) { if (method.isAnnotationPresent(annotation) && method.getParameterTypes().length == 0) { if (found) { throw new InstanceMethodInvocationException(instance, method, buildCause(instance.getClass(), method, methods, annotation)); } methods.add(method); found = true; } } klass = klass.getSuperclass(); } for (final Method method : methods) { AccessController.doPrivileged(new PrivilegedAction<Object>() { @Override public Object run() { boolean wasAccessible = method.isAccessible(); try { method.setAccessible(true); return method.invoke(instance); } catch (IllegalAccessException | IllegalArgumentException e) { throw new InstanceMethodInvocationException(instance, method.getName(), null, e); } catch (InvocationTargetException e) { throw new InstanceMethodInvocationException(instance, method.getName(), null, e.getTargetException()); } finally { method.setAccessible(wasAccessible); } } }); } } @Nonnull private static Throwable buildCause(@Nonnull Class<?> clazz, @Nonnull Method method, @Nonnull List<Method> methods, @Nonnull final Class<? extends Annotation> annotation) { StringBuilder b = new StringBuilder("The following methods were found annotated with @" + annotation.getSimpleName() + " on ") .append(clazz); for (Method m : methods) { b.append("\n ").append(m.toGenericString()); } b.append("\n ").append(method.toGenericString()); return new IllegalStateException(b.toString()); } private static final String EMPTY_STRING = ""; /** * <p>The package separator character: <code>'.' == {@value}</code>.</p> */ public static final char PACKAGE_SEPARATOR_CHAR = '.'; /** * <p>The package separator String: <code>"."</code>.</p> */ public static final String PACKAGE_SEPARATOR = String.valueOf(PACKAGE_SEPARATOR_CHAR); /** * <p>The inner class separator character: <code>'$' == {@value}</code>.</p> */ public static final char INNER_CLASS_SEPARATOR_CHAR = '$'; /** * <p>The inner class separator String: <code>"$"</code>.</p> */ public static final String INNER_CLASS_SEPARATOR = String.valueOf(INNER_CLASS_SEPARATOR_CHAR); /** * Maps a primitive class name to its corresponding abbreviation used in array class names. */ private static final Map<String, String> abbreviationMap = new HashMap<>(); /** * Maps an abbreviation used in array class names to corresponding primitive class name. */ private static final Map<String, String> reverseAbbreviationMap = new HashMap<>(); /** * Add primitive type abbreviation to maps of abbreviations. * * @param primitive Canonical name of primitive type * @param abbreviation Corresponding abbreviation of primitive type */ private static void addAbbreviation(String primitive, String abbreviation) { abbreviationMap.put(primitive, abbreviation); reverseAbbreviationMap.put(abbreviation, primitive); } /** * Feed abbreviation maps */ static { addAbbreviation("int", "I"); addAbbreviation("boolean", "Z"); addAbbreviation("float", "F"); addAbbreviation("long", "J"); addAbbreviation("short", "S"); addAbbreviation("byte", "B"); addAbbreviation("double", "D"); addAbbreviation("char", "C"); } // ---------------------------------------------------------------------- /** * <p>Gets the class name minus the package name for an <code>Object</code>.</p> * * @param object the class to get the short name for, may be null * @param valueIfNull the value to return if null * @return the class name of the object without the package name, or the null value */ @Nonnull public static String getShortClassName(@Nullable Object object, @Nonnull String valueIfNull) { if (object == null) { return valueIfNull; } return getShortClassName(object.getClass()); } /** * <p>Gets the class name minus the package name from a <code>Class</code>.</p> * * @param cls the class to get the short name for. * @return the class name without the package name or an empty string */ @Nonnull public static String getShortClassName(@Nullable Class<?> cls) { if (cls == null) { return EMPTY_STRING; } return getShortClassName(cls.getName()); } /** * <p>Gets the class name minus the package name from a String.</p> * <p/> * <p>The string passed in is assumed to be a class name - it is not checked.</p> * * @param className the className to get the short name for * @return the class name of the class without the package name or an empty string */ @Nonnull public static String getShortClassName(@Nullable String className) { if (className == null) { return EMPTY_STRING; } if (className.length() == 0) { return EMPTY_STRING; } StringBuilder arrayPrefix = new StringBuilder(); // Handle array encoding if (className.startsWith("[")) { while (className.charAt(0) == '[') { className = className.substring(1); arrayPrefix.append("[]"); } // Strip Object type encoding if (className.charAt(0) == 'L' && className.charAt(className.length() - 1) == ';') { className = className.substring(1, className.length() - 1); } } if (reverseAbbreviationMap.containsKey(className)) { className = reverseAbbreviationMap.get(className); } int lastDotIdx = className.lastIndexOf(PACKAGE_SEPARATOR_CHAR); int innerIdx = className.indexOf( INNER_CLASS_SEPARATOR_CHAR, lastDotIdx == -1 ? 0 : lastDotIdx + 1); String out = className.substring(lastDotIdx + 1); if (innerIdx != -1) { out = out.replace(INNER_CLASS_SEPARATOR_CHAR, PACKAGE_SEPARATOR_CHAR); } return out + arrayPrefix; } // Package name // ---------------------------------------------------------------------- /** * <p>Gets the package name of an <code>Object</code>.</p> * * @param object the class to get the package name for, may be null * @param valueIfNull the value to return if null * @return the package name of the object, or the null value */ @Nonnull public static String getPackageName(@Nullable Object object, @Nonnull String valueIfNull) { if (object == null) { return valueIfNull; } return getPackageName(object.getClass()); } /** * <p>Gets the package name of a <code>Class</code>.</p> * * @param cls the class to get the package name for, may be <code>null</code>. * @return the package name or an empty string */ @Nonnull public static String getPackageName(@Nullable Class<?> cls) { if (cls == null) { return EMPTY_STRING; } return getPackageName(cls.getName()); } /** * <p>Gets the package name from a <code>String</code>.</p> * <p/> * <p>The string passed in is assumed to be a class name - it is not checked.</p> * <p>If the class is unpackaged, return an empty string.</p> * * @param className the className to get the package name for, may be <code>null</code> * @return the package name or an empty string */ @Nonnull public static String getPackageName(@Nullable String className) { if (className == null || className.length() == 0) { return EMPTY_STRING; } // Strip array encoding while (className.charAt(0) == '[') { className = className.substring(1); } // Strip Object type encoding if (className.charAt(0) == 'L' && className.charAt(className.length() - 1) == ';') { className = className.substring(1); } int i = className.lastIndexOf(PACKAGE_SEPARATOR_CHAR); if (i == -1) { return EMPTY_STRING; } return className.substring(0, i); } /** * param instance array to the type array * * @param args the arguments * @return the types of the arguments */ @Nullable public static Class<?>[] convertToTypeArray(@Nullable Object[] args) { if (args == null) { return null; } int s = args.length; Class<?>[] ans = new Class<?>[s]; for (int i = 0; i < s; i++) { Object o = args[i]; ans[i] = o != null ? o.getClass() : null; } return ans; } }