GriffonClassUtils.java
0001 /*
0002  * Copyright 2008-2017 the original author or authors.
0003  *
0004  * Licensed under the Apache License, Version 2.0 (the "License");
0005  * you may not use this file except in compliance with the License.
0006  * You may obtain a copy of the License at
0007  *
0008  *     http://www.apache.org/licenses/LICENSE-2.0
0009  *
0010  * Unless required by applicable law or agreed to in writing, software
0011  * distributed under the License is distributed on an "AS IS" BASIS,
0012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013  * See the License for the specific language governing permissions and
0014  * limitations under the License.
0015  */
0016 package griffon.util;
0017 
0018 import griffon.core.Observable;
0019 import griffon.core.Vetoable;
0020 import griffon.core.artifact.GriffonArtifact;
0021 import griffon.core.artifact.GriffonMvcArtifact;
0022 import griffon.core.event.EventPublisher;
0023 import griffon.core.i18n.MessageSource;
0024 import griffon.core.mvc.MVCHandler;
0025 import griffon.core.resources.ResourceHandler;
0026 import griffon.core.resources.ResourceResolver;
0027 import griffon.core.threading.ThreadingHandler;
0028 import griffon.exceptions.BeanInstantiationException;
0029 import griffon.exceptions.FieldException;
0030 import griffon.exceptions.InstanceMethodInvocationException;
0031 import griffon.exceptions.PropertyException;
0032 import griffon.exceptions.StaticMethodInvocationException;
0033 
0034 import javax.annotation.Nonnull;
0035 import javax.annotation.Nullable;
0036 import java.beans.BeanInfo;
0037 import java.beans.IntrospectionException;
0038 import java.beans.Introspector;
0039 import java.beans.PropertyDescriptor;
0040 import java.lang.annotation.Annotation;
0041 import java.lang.reflect.Field;
0042 import java.lang.reflect.InvocationTargetException;
0043 import java.lang.reflect.Method;
0044 import java.lang.reflect.Modifier;
0045 import java.security.AccessController;
0046 import java.security.PrivilegedAction;
0047 import java.util.ArrayList;
0048 import java.util.Arrays;
0049 import java.util.Collection;
0050 import java.util.HashMap;
0051 import java.util.HashSet;
0052 import java.util.LinkedHashMap;
0053 import java.util.LinkedHashSet;
0054 import java.util.List;
0055 import java.util.Map;
0056 import java.util.Set;
0057 import java.util.SortedSet;
0058 import java.util.TreeSet;
0059 import java.util.regex.Pattern;
0060 
0061 import static griffon.util.GriffonNameUtils.requireNonBlank;
0062 import static griffon.util.MethodUtils.invokeExactMethod;
0063 import static griffon.util.MethodUtils.invokeMethod;
0064 import static java.util.Objects.requireNonNull;
0065 
0066 /**
0067  * Class containing utility methods for dealing with Griffon class artifacts.<p>
0068  * Contains utility methods copied from commons-lang and commons-beanutils in order
0069  * to reduce dependencies on external libraries.<p>
0070  <p>
0071  <b>Contains code copied from commons-beanutils and commons-langs</b>
0072  *
0073  @author Graeme Rocher (Grails 0.1)
0074  */
0075 public class GriffonClassUtils {
0076     public static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0];
0077     public static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
0078     public static final Object[] EMPTY_ARGS = EMPTY_OBJECT_ARRAY;
0079 
0080     private static final String PROPERTY_GET_PREFIX = "get";
0081     private static final String PROPERTY_IS_PREFIX = "is";
0082     private static final String PROPERTY_SET_PREFIX = "set";
0083     private static final String ON_SHUTDOWN_METHOD_NAME = "onShutdown";
0084     public static final Map<Class<?>, Class<?>> PRIMITIVE_TYPE_COMPATIBLE_CLASSES = new LinkedHashMap<>();
0085     public static final Map<String, String> PRIMITIVE_TYPE_COMPATIBLE_TYPES = new LinkedHashMap<>();
0086 
0087     private static final Pattern EVENT_HANDLER_PATTERN = Pattern.compile("^on[A-Z][\\w]*$");
0088     private static final Pattern CONTRIBUTION_PATTERN = Pattern.compile("^with[A-Z][a-z0-9_]*[\\w]*$");
0089     private static final Pattern GETTER_PATTERN_1 = Pattern.compile("^get[A-Z][\\w]*$");
0090     private static final Pattern GETTER_PATTERN_2 = Pattern.compile("^is[A-Z][\\w]*$");
0091     private static final Pattern SETTER_PATTERN = Pattern.compile("^set[A-Z][\\w]*$");
0092     private static final Set<MethodDescriptor> BASIC_METHODS = new TreeSet<>();
0093     private static final Set<MethodDescriptor> ARTIFACT_METHODS = new TreeSet<>();
0094     private static final Set<MethodDescriptor> MVC_METHODS = new TreeSet<>();
0095     private static final Set<MethodDescriptor> THREADING_METHODS = new TreeSet<>();
0096     private static final Set<MethodDescriptor> EVENT_PUBLISHER_METHODS = new TreeSet<>();
0097     private static final Set<MethodDescriptor> OBSERVABLE_METHODS = new TreeSet<>();
0098     private static final Set<MethodDescriptor> RESOURCE_HANDLER_METHODS = new TreeSet<>();
0099     private static final Set<MethodDescriptor> MESSAGE_SOURCE_METHODS = new TreeSet<>();
0100     private static final Set<MethodDescriptor> RESOURCE_RESOLVER_METHODS = new TreeSet<>();
0101     private static final String ERROR_TYPE_NULL = "Argument 'type' must not be null";
0102     private static final String ERROR_METHOD_NAME_BLANK = "Argument 'methodName' must not be blank";
0103     private static final String ERROR_OBJECT_NULL = "Argument 'object' must not be null";
0104     private static final String ERROR_CLAZZ_NULL = "Argument 'clazz' must not be null";
0105     private static final String ERROR_DESCRIPTOR_NULL = "Argument 'descriptor' must not be null";
0106     private static final String ERROR_BEAN_NULL = "Argument 'bean' must not be null";
0107     private static final String ERROR_NAME_BLANK = "Argument 'name' must not be blank";
0108     private static final String ERROR_PROPERTIES_NULL = "Argument 'properties' must not be null";
0109     private static final String ERROR_FIELDS_NULL = "Argument 'fields' must not be null";
0110     private static final String ERROR_PROPERTY_NAME_BLANK = "Argument 'propertyName' must not be blank";
0111     private static final String ERROR_METHOD_NULL = "Argument 'method' must not be null";
0112     private static final String MESSAGE = "message";
0113 
0114     /**
0115      * Just add two entries to the class compatibility map
0116      *
0117      @param left
0118      @param right
0119      */
0120     private static void registerPrimitiveClassPair(Class<?> left, Class<?> right) {
0121         PRIMITIVE_TYPE_COMPATIBLE_CLASSES.put(left, right);
0122         PRIMITIVE_TYPE_COMPATIBLE_CLASSES.put(right, left);
0123         PRIMITIVE_TYPE_COMPATIBLE_TYPES.put(left.getName(), right.getName());
0124         PRIMITIVE_TYPE_COMPATIBLE_TYPES.put(right.getName(), left.getName());
0125     }
0126 
0127     static {
0128         registerPrimitiveClassPair(Boolean.class, boolean.class);
0129         registerPrimitiveClassPair(Integer.class, int.class);
0130         registerPrimitiveClassPair(Short.class, short.class);
0131         registerPrimitiveClassPair(Byte.class, byte.class);
0132         registerPrimitiveClassPair(Character.class, char.class);
0133         registerPrimitiveClassPair(Long.class, long.class);
0134         registerPrimitiveClassPair(Float.class, float.class);
0135         registerPrimitiveClassPair(Double.class, double.class);
0136 
0137         for (Method method : Object.class.getMethods()) {
0138             MethodDescriptor md = MethodDescriptor.forMethod(method);
0139             if (!BASIC_METHODS.contains(md)) {
0140                 BASIC_METHODS.add(md);
0141             }
0142         }
0143 
0144         try {
0145             Class<?> groovyObjectClass = GriffonClassUtils.class.getClassLoader().loadClass("groovy.lang.GroovyObject");
0146             for (Method method : groovyObjectClass.getMethods()) {
0147                 MethodDescriptor md = MethodDescriptor.forMethod(method, true);
0148                 if (!BASIC_METHODS.contains(md)) {
0149                     BASIC_METHODS.add(md);
0150                 }
0151             }
0152         catch (ClassNotFoundException cnfe) {
0153             // ignore
0154         }
0155 
0156         try {
0157             Class<?> groovyObjectClass = GriffonClassUtils.class.getClassLoader().loadClass("groovy.lang.GroovyObjectSupport");
0158             for (Method method : groovyObjectClass.getMethods()) {
0159                 MethodDescriptor md = MethodDescriptor.forMethod(method);
0160                 if (!BASIC_METHODS.contains(md)) {
0161                     BASIC_METHODS.add(md);
0162                 }
0163             }
0164         catch (ClassNotFoundException cnfe) {
0165             // ignore
0166         }
0167 
0168         for (Method method : GriffonArtifact.class.getMethods()) {
0169             MethodDescriptor md = MethodDescriptor.forMethod(method, true);
0170             if (!ARTIFACT_METHODS.contains(md)) {
0171                 ARTIFACT_METHODS.add(md);
0172             }
0173         }
0174 
0175         // MVC_METHODS.add(new MethodDescriptor("getMvcGroup"));
0176         for (Method method : MVCHandler.class.getMethods()) {
0177             MethodDescriptor md = MethodDescriptor.forMethod(method, true);
0178             if (!MVC_METHODS.contains(md)) {
0179                 MVC_METHODS.add(md);
0180             }
0181         }
0182         for (Method method : GriffonMvcArtifact.class.getMethods()) {
0183             MethodDescriptor md = MethodDescriptor.forMethod(method, true);
0184             if (!MVC_METHODS.contains(md)) {
0185                 MVC_METHODS.add(md);
0186             }
0187         }
0188 
0189         // GriffonView
0190         MVC_METHODS.add(new MethodDescriptor("initUI"));
0191         // GriffonController
0192         MVC_METHODS.add(new MethodDescriptor("invokeAction"new Class<?>[]{String.class, Object[].class}));
0193         MVC_METHODS.add(new MethodDescriptor("invokeAction"new Class<?>[]{String.class, Object[].class}, Modifier.PUBLIC | Modifier.TRANSIENT));
0194 
0195         for (Method method : ThreadingHandler.class.getMethods()) {
0196             MethodDescriptor md = MethodDescriptor.forMethod(method, true);
0197             if (!THREADING_METHODS.contains(md)) {
0198                 THREADING_METHODS.add(md);
0199             }
0200         }
0201         // Special case due to the usage of varargs
0202         //THREADING_METHODS.add(new MethodDescriptor("runFuture", new Class<?>[]{Object[].class}));
0203 
0204         for (Method method : EventPublisher.class.getMethods()) {
0205             MethodDescriptor md = MethodDescriptor.forMethod(method, true);
0206             if (!EVENT_PUBLISHER_METHODS.contains(md)) {
0207                 EVENT_PUBLISHER_METHODS.add(md);
0208             }
0209         }
0210 
0211         for (Method method : Observable.class.getMethods()) {
0212             MethodDescriptor md = MethodDescriptor.forMethod(method, true);
0213             if (!OBSERVABLE_METHODS.contains(md)) {
0214                 OBSERVABLE_METHODS.add(md);
0215             }
0216         }
0217         for (Method method : Vetoable.class.getMethods()) {
0218             MethodDescriptor md = MethodDescriptor.forMethod(method, true);
0219             if (!OBSERVABLE_METHODS.contains(md)) {
0220                 OBSERVABLE_METHODS.add(md);
0221             }
0222         }
0223 
0224         for (Method method : ResourceHandler.class.getMethods()) {
0225             MethodDescriptor md = MethodDescriptor.forMethod(method, true);
0226             if (!RESOURCE_HANDLER_METHODS.contains(md)) {
0227                 RESOURCE_HANDLER_METHODS.add(md);
0228             }
0229         }
0230 
0231         for (Method method : MessageSource.class.getMethods()) {
0232             MethodDescriptor md = MethodDescriptor.forMethod(method, true);
0233             if (!MESSAGE_SOURCE_METHODS.contains(md)) {
0234                 MESSAGE_SOURCE_METHODS.add(md);
0235             }
0236         }
0237 
0238         for (Method method : ResourceResolver.class.getMethods()) {
0239             MethodDescriptor md = MethodDescriptor.forMethod(method, true);
0240             if (!RESOURCE_RESOLVER_METHODS.contains(md)) {
0241                 RESOURCE_RESOLVER_METHODS.add(md);
0242             }
0243         }
0244     }
0245 
0246     private GriffonClassUtils() {
0247         // prevent instantiation
0248     }
0249 
0250     /**
0251      * Checks that the specified condition is met. This method is designed
0252      * primarily for doing parameter validation in methods and constructors,
0253      * as demonstrated below:
0254      <blockquote><pre>
0255      * public Foo(int[] array) {
0256      *     GriffonClassUtils.requireState(array.length > 0);
0257      * }
0258      </pre></blockquote>
0259      *
0260      @param condition the condition to check
0261      *
0262      @throws IllegalStateException if {@code condition} evaluates to false
0263      */
0264     public static void requireState(boolean condition) {
0265         if (!condition) {
0266             throw new IllegalStateException();
0267         }
0268     }
0269 
0270     /**
0271      * Checks that the specified condition is met and throws a customized
0272      {@link IllegalStateException} if it is. This method is designed primarily
0273      * for doing parameter validation in methods and constructors with multiple
0274      * parameters, as demonstrated below:
0275      <blockquote><pre>
0276      * public Foo(int[] array) {
0277      *     GriffonClassUtils.requireState(array.length > 0, "array must not be empty");
0278      * }
0279      </pre></blockquote>
0280      *
0281      @param condition the condition to check
0282      @param message   detail message to be used in the event that a {@code
0283      *                  IllegalStateException} is thrown
0284      *
0285      @throws IllegalStateException if {@code condition} evaluates to false
0286      */
0287     public static void requireState(boolean condition, String message) {
0288         if (!condition) {
0289             throw new IllegalStateException(message);
0290         }
0291     }
0292 
0293     /**
0294      * Checks that the specified array is not empty, throwing a
0295      {@link IllegalStateException} if it is.
0296      *
0297      @param array the array to check
0298      *
0299      @throws NullPointerException  if {@code array} is null
0300      @throws IllegalStateException if {@code array} is empty
0301      */
0302     public static byte[] requireNonEmpty(@Nonnull byte[] array) {
0303         requireNonNull(array);
0304         requireState(array.length != 0);
0305         return array;
0306     }
0307 
0308     /**
0309      * Checks that the specified array is not empty, throwing a customized
0310      {@link IllegalStateException} if it is.
0311      *
0312      @param array   the array to check
0313      @param message detail message to be used in the event that a {@code
0314      *                IllegalStateException} is thrown
0315      *
0316      @throws NullPointerException     if {@code array} is null
0317      @throws IllegalArgumentException if {@code message} is {@code blank}
0318      @throws IllegalStateException    if {@code array} is empty
0319      */
0320     public static byte[] requireNonEmpty(@Nonnull byte[] array, @Nonnull String message) {
0321         requireNonNull(array);
0322         requireState(array.length != 0, requireNonBlank(message, MESSAGE));
0323         return array;
0324     }
0325 
0326     /**
0327      * Checks that the specified array is not empty, throwing a
0328      {@link IllegalStateException} if it is.
0329      *
0330      @param array the array to check
0331      *
0332      @throws NullPointerException  if {@code array} is null
0333      @throws IllegalStateException if {@code array} is empty
0334      */
0335     public static short[] requireNonEmpty(@Nonnull short[] array) {
0336         requireNonNull(array);
0337         requireState(array.length != 0);
0338         return array;
0339     }
0340 
0341     /**
0342      * Checks that the specified array is not empty, throwing a customized
0343      {@link IllegalStateException} if it is.
0344      *
0345      @param array   the array to check
0346      @param message detail message to be used in the event that a {@code
0347      *                IllegalStateException} is thrown
0348      *
0349      @throws NullPointerException     if {@code array} is null
0350      @throws IllegalArgumentException if {@code message} is {@code blank}
0351      @throws IllegalStateException    if {@code array} is empty
0352      */
0353     public static short[] requireNonEmpty(@Nonnull short[] array, @Nonnull String message) {
0354         requireNonNull(array);
0355         requireState(array.length != 0, requireNonBlank(message, MESSAGE));
0356         return array;
0357     }
0358 
0359     /**
0360      * Checks that the specified array is not empty, throwing a
0361      {@link IllegalStateException} if it is.
0362      *
0363      @param array the array to check
0364      *
0365      @throws NullPointerException  if {@code array} is null
0366      @throws IllegalStateException if {@code array} is empty
0367      */
0368     public static int[] requireNonEmpty(@Nonnull int[] array) {
0369         requireNonNull(array);
0370         requireState(array.length != 0);
0371         return array;
0372     }
0373 
0374     /**
0375      * Checks that the specified array is not empty, throwing a customized
0376      {@link IllegalStateException} if it is.
0377      *
0378      @param array   the array to check
0379      @param message detail message to be used in the event that a {@code
0380      *                IllegalStateException} is thrown
0381      *
0382      @throws NullPointerException     if {@code array} is null
0383      @throws IllegalArgumentException if {@code message} is {@code blank}
0384      @throws IllegalStateException    if {@code array} is empty
0385      */
0386     public static int[] requireNonEmpty(@Nonnull int[] array, @Nonnull String message) {
0387         requireNonNull(array);
0388         requireState(array.length != 0, requireNonBlank(message, MESSAGE));
0389         return array;
0390     }
0391 
0392     /**
0393      * Checks that the specified array is not empty, throwing a
0394      {@link IllegalStateException} if it is.
0395      *
0396      @param array the array to check
0397      *
0398      @throws NullPointerException  if {@code array} is null
0399      @throws IllegalStateException if {@code array} is empty
0400      */
0401     public static long[] requireNonEmpty(@Nonnull long[] array) {
0402         requireNonNull(array);
0403         requireState(array.length != 0);
0404         return array;
0405     }
0406 
0407     /**
0408      * Checks that the specified array is not empty, throwing a customized
0409      {@link IllegalStateException} if it is.
0410      *
0411      @param array   the array to check
0412      @param message detail message to be used in the event that a {@code
0413      *                IllegalStateException} is thrown
0414      *
0415      @throws NullPointerException     if {@code array} is null
0416      @throws IllegalArgumentException if {@code message} is {@code blank}
0417      @throws IllegalStateException    if {@code array} is empty
0418      */
0419     public static long[] requireNonEmpty(@Nonnull long[] array, @Nonnull String message) {
0420         requireNonNull(array);
0421         requireState(array.length != 0, requireNonBlank(message, MESSAGE));
0422         return array;
0423     }
0424 
0425     /**
0426      * Checks that the specified array is not empty, throwing a
0427      {@link IllegalStateException} if it is.
0428      *
0429      @param array the array to check
0430      *
0431      @throws NullPointerException  if {@code array} is null
0432      @throws IllegalStateException if {@code array} is empty
0433      */
0434     public static float[] requireNonEmpty(@Nonnull float[] array) {
0435         requireNonNull(array);
0436         requireState(array.length != 0);
0437         return array;
0438     }
0439 
0440     /**
0441      * Checks that the specified array is not empty, throwing a customized
0442      {@link IllegalStateException} if it is.
0443      *
0444      @param array   the array to check
0445      @param message detail message to be used in the event that a {@code
0446      *                IllegalStateException} is thrown
0447      *
0448      @throws NullPointerException     if {@code array} is null
0449      @throws IllegalArgumentException if {@code message} is {@code blank}
0450      @throws IllegalStateException    if {@code array} is empty
0451      */
0452     public static float[] requireNonEmpty(@Nonnull float[] array, @Nonnull String message) {
0453         requireNonNull(array);
0454         requireState(array.length != 0, requireNonBlank(message, MESSAGE));
0455         return array;
0456     }
0457 
0458     /**
0459      * Checks that the specified array is not empty, throwing a
0460      {@link IllegalStateException} if it is.
0461      *
0462      @param array the array to check
0463      *
0464      @throws NullPointerException  if {@code array} is null
0465      @throws IllegalStateException if {@code array} is empty
0466      */
0467     public static double[] requireNonEmpty(@Nonnull double[] array) {
0468         requireNonNull(array);
0469         requireState(array.length != 0);
0470         return array;
0471     }
0472 
0473     /**
0474      * Checks that the specified array is not empty, throwing a customized
0475      {@link IllegalStateException} if it is.
0476      *
0477      @param array   the array to check
0478      @param message detail message to be used in the event that a {@code
0479      *                IllegalStateException} is thrown
0480      *
0481      @throws NullPointerException     if {@code array} is null
0482      @throws IllegalArgumentException if {@code message} is {@code blank}
0483      @throws IllegalStateException    if {@code array} is empty
0484      */
0485     public static double[] requireNonEmpty(@Nonnull double[] array, @Nonnull String message) {
0486         requireNonNull(array);
0487         requireState(array.length != 0, requireNonBlank(message, MESSAGE));
0488         return array;
0489     }
0490 
0491     /**
0492      * Checks that the specified array is not empty, throwing a
0493      {@link IllegalStateException} if it is.
0494      *
0495      @param array the array to check
0496      *
0497      @throws NullPointerException  if {@code array} is null
0498      @throws IllegalStateException if {@code array} is empty
0499      */
0500     public static char[] requireNonEmpty(@Nonnull char[] array) {
0501         requireNonNull(array);
0502         requireState(array.length != 0);
0503         return array;
0504     }
0505 
0506     /**
0507      * Checks that the specified array is not empty, throwing a customized
0508      {@link IllegalStateException} if it is.
0509      *
0510      @param array   the array to check
0511      @param message detail message to be used in the event that a {@code
0512      *                IllegalStateException} is thrown
0513      *
0514      @throws NullPointerException     if {@code array} is null
0515      @throws IllegalArgumentException if {@code message} is {@code blank}
0516      @throws IllegalStateException    if {@code array} is empty
0517      */
0518     public static char[] requireNonEmpty(@Nonnull char[] array, @Nonnull String message) {
0519         requireNonNull(array);
0520         requireState(array.length != 0, requireNonBlank(message, MESSAGE));
0521         return array;
0522     }
0523 
0524     /**
0525      * Checks that the specified array is not empty, throwing a
0526      {@link IllegalStateException} if it is.
0527      *
0528      @param array the array to check
0529      *
0530      @throws NullPointerException  if {@code array} is null
0531      @throws IllegalStateException if {@code array} is empty
0532      */
0533     public static boolean[] requireNonEmpty(@Nonnull boolean[] array) {
0534         requireNonNull(array);
0535         requireState(array.length != 0);
0536         return array;
0537     }
0538 
0539     /**
0540      * Checks that the specified array is not empty, throwing a customized
0541      {@link IllegalStateException} if it is.
0542      *
0543      @param array   the array to check
0544      @param message detail message to be used in the event that a {@code
0545      *                IllegalStateException} is thrown
0546      *
0547      @throws NullPointerException     if {@code array} is null
0548      @throws IllegalArgumentException if {@code message} is {@code blank}
0549      @throws IllegalStateException    if {@code array} is empty
0550      */
0551     public static boolean[] requireNonEmpty(@Nonnull boolean[] array, @Nonnull String message) {
0552         requireNonNull(array);
0553         requireState(array.length != 0, requireNonBlank(message, MESSAGE));
0554         return array;
0555     }
0556 
0557     /**
0558      * Checks that the specified array is not empty, throwing a
0559      {@link IllegalStateException} if it is.
0560      *
0561      @param array the array to check
0562      *
0563      @throws NullPointerException  if {@code array} is null
0564      @throws IllegalStateException if {@code array} is empty
0565      */
0566     public static <E> E[] requireNonEmpty(@Nonnull E[] array) {
0567         requireNonNull(array);
0568         requireState(array.length != 0);
0569         return array;
0570     }
0571 
0572     /**
0573      * Checks that the specified array is not empty, throwing a customized
0574      {@link IllegalStateException} if it is.
0575      *
0576      @param array   the array to check
0577      @param message detail message to be used in the event that a {@code
0578      *                IllegalStateException} is thrown
0579      *
0580      @throws NullPointerException     if {@code array} is null
0581      @throws IllegalArgumentException if {@code message} is {@code blank}
0582      @throws IllegalStateException    if {@code array} is empty
0583      */
0584     public static <E> E[] requireNonEmpty(@Nonnull E[] array, @Nonnull String message) {
0585         requireNonNull(array);
0586         requireState(array.length != 0, requireNonBlank(message, MESSAGE));
0587         return array;
0588     }
0589 
0590     /**
0591      * Checks that the specified collection is not empty, throwing a
0592      {@link IllegalStateException} if it is.
0593      *
0594      @param collection the collection to check
0595      *
0596      @throws NullPointerException  if {@code collection} is null
0597      @throws IllegalStateException if {@code collection} is empty
0598      */
0599     public static Collection<?> requireNonEmpty(@Nonnull Collection<?> collection) {
0600         requireNonNull(collection);
0601         requireState(!collection.isEmpty());
0602         return collection;
0603     }
0604 
0605     /**
0606      * Checks that the specified collection is not empty, throwing a customized
0607      {@link IllegalStateException} if it is.
0608      *
0609      @param collection the collection to check
0610      @param message    detail message to be used in the event that a {@code
0611      *                   IllegalStateException} is thrown
0612      *
0613      @throws NullPointerException     if {@code collection} is null
0614      @throws IllegalArgumentException if {@code message} is {@code blank}
0615      @throws IllegalStateException    if {@code collection} is empty
0616      */
0617     public static Collection<?> requireNonEmpty(@Nonnull Collection<?> collection, @Nonnull String message) {
0618         requireNonNull(collection);
0619         requireState(!collection.isEmpty(), requireNonBlank(message, MESSAGE));
0620         return collection;
0621     }
0622 
0623     /**
0624      * Checks that the specified map is not empty, throwing a
0625      {@link IllegalStateException} if it is.
0626      *
0627      @param map the map to check
0628      *
0629      @throws NullPointerException  if {@code map} is null
0630      @throws IllegalStateException if {@code map} is empty
0631      */
0632     public static Map<?, ?> requireNonEmpty(@Nonnull Map<?, ?> map) {
0633         requireNonNull(map);
0634         requireState(!map.isEmpty());
0635         return map;
0636     }
0637 
0638     /**
0639      * Checks that the specified map is not empty, throwing a customized
0640      {@link IllegalStateException} if it is.
0641      *
0642      @param map     the map to check
0643      @param message detail message to be used in the event that a {@code
0644      *                IllegalStateException} is thrown
0645      *
0646      @throws NullPointerException     if {@code map} is null
0647      @throws IllegalArgumentException if {@code message} is {@code blank}
0648      @throws IllegalStateException    if {@code map} is empty
0649      */
0650     public static Map<?, ?> requireNonEmpty(@Nonnull Map<?, ?> map, @Nonnull String message) {
0651         requireNonNull(map);
0652         requireState(!map.isEmpty(), requireNonBlank(message, MESSAGE));
0653         return map;
0654     }
0655 
0656     /**
0657      * Finds out if the given string represents the name of an
0658      * event handler by matching against the following pattern:
0659      * "^on[A-Z][\\w]*$"<p>
0660      <p>
0661      <pre>
0662      * isEventHandler("onBootstrapEnd") = true
0663      * isEventHandler("mvcGroupInit")   = false
0664      * isEventHandler("online")         = false
0665      </pre>
0666      *
0667      @param name the name of a possible event handler
0668      *
0669      @return true if the name matches the given event handler
0670      * pattern, false otherwise.
0671      */
0672     public static boolean isEventHandler(@Nonnull String name) {
0673         requireNonBlank(name, ERROR_NAME_BLANK);
0674         return EVENT_HANDLER_PATTERN.matcher(name).matches() &&
0675             !ON_SHUTDOWN_METHOD_NAME.equals(name);
0676     }
0677 
0678     /**
0679      * Finds out if the given Method represents an event handler
0680      * by matching its name against the following pattern:
0681      * "^on[A-Z][\\w]*$"<p>
0682      <pre>
0683      * // assuming getMethod() returns an appropriate Method reference
0684      * isEventHandler(getMethod("onBootstrapEnd")) = true
0685      * isEventHandler(getMethod("mvcGroupInit"))   = false
0686      * isEventHandler(getMethod("online"))         = false
0687      </pre>
0688      *
0689      @param method a Method reference
0690      *
0691      @return true if the method name matches the given event handler
0692      * pattern, false otherwise.
0693      */
0694     public static boolean isEventHandler(@Nonnull Method method) {
0695         return isEventHandler(method, false);
0696     }
0697 
0698     /**
0699      * Finds out if the given Method represents an event handler
0700      * by matching its name against the following pattern:
0701      * "^on[A-Z][\\w]*$"<p>
0702      <pre>
0703      * // assuming getMethod() returns an appropriate Method reference
0704      * isEventHandler(getMethod("onBootstrapEnd")) = true
0705      * isEventHandler(getMethod("mvcGroupInit"))   = false
0706      * isEventHandler(getMethod("online"))         = false
0707      </pre>
0708      *
0709      @param method a Method reference
0710      *
0711      @return true if the method name matches the given event handler
0712      * pattern, false otherwise.
0713      */
0714     public static boolean isEventHandler(@Nonnull Method method, boolean removeAbstractModifier) {
0715         requireNonNull(method, ERROR_METHOD_NULL);
0716         return isEventHandler(MethodDescriptor.forMethod(method, removeAbstractModifier));
0717     }
0718 
0719     /**
0720      * Finds out if the given Method represents an event handler
0721      * by matching its name against the following pattern:
0722      * "^on[A-Z][\\w]*$"<p>
0723      <pre>
0724      * // assuming getMethod() returns an appropriate MethodDescriptor reference
0725      * isEventHandler(getMethod("onBootstrapEnd")) = true
0726      * isEventHandler(getMethod("mvcGroupInit"))   = false
0727      * isEventHandler(getMethod("online"))         = false
0728      </pre>
0729      *
0730      @param method a MethodDescriptor reference
0731      *
0732      @return true if the method name matches the given event handler
0733      * pattern, false otherwise.
0734      */
0735     public static boolean isEventHandler(@Nonnull MethodDescriptor method) {
0736         requireNonNull(method, ERROR_METHOD_NULL);
0737         return isInstanceMethod(method&&
0738             isEventHandler(method.getName());
0739     }
0740 
0741     /**
0742      * Finds out if the given {@code Method} belongs either to the
0743      * {@code Object} class or the {@code GroovyObject} class.<p>
0744      *
0745      @param method a Method reference
0746      *
0747      @return true if the method belongs to {@code Object} or
0748      * {@code GroovyObject}, false otherwise.
0749      */
0750     public static boolean isBasicMethod(@Nonnull Method method) {
0751         return isBasicMethod(method, false);
0752     }
0753 
0754     /**
0755      * Finds out if the given {@code Method} belongs either to the
0756      * {@code Object} class or the {@code GroovyObject} class.<p>
0757      *
0758      @param method a Method reference
0759      *
0760      @return true if the method belongs to {@code Object} or
0761      * {@code GroovyObject}, false otherwise.
0762      */
0763     public static boolean isBasicMethod(@Nonnull Method method, boolean removeAbstractModifier) {
0764         requireNonNull(method, ERROR_METHOD_NULL);
0765         return isBasicMethod(MethodDescriptor.forMethod(method, removeAbstractModifier));
0766     }
0767 
0768     /**
0769      * Finds out if the given {@code MethodDescriptor} belongs either to the
0770      * {@code Object} class or the {@code GroovyObject} class.<p>
0771      *
0772      @param method a MethodDescriptor reference
0773      *
0774      @return true if the method belongs to {@code Object} or
0775      * {@code GroovyObject}, false otherwise.
0776      */
0777     public static boolean isBasicMethod(@Nonnull MethodDescriptor method) {
0778         requireNonNull(method, ERROR_METHOD_NULL);
0779         return isInstanceMethod(method&& BASIC_METHODS.contains(method);
0780     }
0781 
0782     /**
0783      * Finds out if the given string represents the name of a
0784      * contribution method by matching against the following pattern:
0785      * "^with[A-Z][a-z0-9_]*[\w]*$"<p>
0786      <p>
0787      <pre>
0788      * isContributionMethod("withRest")     = true
0789      * isContributionMethod("withMVCGroup") = false
0790      * isContributionMethod("without")      = false
0791      </pre>
0792      *
0793      @param name the name of a possible contribution method
0794      *
0795      @return true if the name matches the given contribution method
0796      * pattern, false otherwise.
0797      */
0798     public static boolean isContributionMethod(@Nonnull String name) {
0799         requireNonBlank(name, ERROR_NAME_BLANK);
0800         return CONTRIBUTION_PATTERN.matcher(name).matches();
0801     }
0802 
0803     /**
0804      * Finds out if the given Method represents a contribution method
0805      * by matching its name against the following pattern:
0806      * "^with[A-Z][a-z0-9_]*[\w]*$"<p>
0807      <pre>
0808      * // assuming getMethod() returns an appropriate Method reference
0809      * isContributionMethod(getMethod("withRest"))     = true
0810      * isContributionMethod(getMethod("withMVCGroup")) = false
0811      * isContributionMethod(getMethod("without"))      = false
0812      </pre>
0813      *
0814      @param method a Method reference
0815      *
0816      @return true if the method name matches the given contribution method
0817      * pattern, false otherwise.
0818      */
0819     public static boolean isContributionMethod(@Nonnull Method method) {
0820         return isContributionMethod(method, false);
0821     }
0822 
0823     /**
0824      * Finds out if the given Method represents a contribution method
0825      * by matching its name against the following pattern:
0826      * "^with[A-Z][a-z0-9_]*[\w]*$"<p>
0827      <pre>
0828      * // assuming getMethod() returns an appropriate Method reference
0829      * isContributionMethod(getMethod("withRest"))     = true
0830      * isContributionMethod(getMethod("withMVCGroup")) = false
0831      * isContributionMethod(getMethod("without"))      = false
0832      </pre>
0833      *
0834      @param method a Method reference
0835      *
0836      @return true if the method name matches the given contribution method
0837      * pattern, false otherwise.
0838      */
0839     public static boolean isContributionMethod(@Nonnull Method method, boolean removeAbstractModifier) {
0840         requireNonNull(method, ERROR_METHOD_NULL);
0841         return isContributionMethod(MethodDescriptor.forMethod(method, removeAbstractModifier));
0842     }
0843 
0844     /**
0845      * Finds out if the given Method represents a contribution method
0846      * by matching its name against the following pattern:
0847      * "^with[A-Z][a-z0-9_]*[\w]*$"<p>
0848      <pre>
0849      * // assuming getMethod() returns an appropriate MethodDescriptor reference
0850      * isContributionMethod(getMethod("withRest"))     = true
0851      * isContributionMethod(getMethod("withMVCGroup")) = false
0852      * isContributionMethod(getMethod("without"))      = false
0853      </pre>
0854      *
0855      @param method a MethodDescriptor reference
0856      *
0857      @return true if the method name matches the given contribution method
0858      * pattern, false otherwise.
0859      */
0860     public static boolean isContributionMethod(@Nonnull MethodDescriptor method) {
0861         requireNonNull(method, ERROR_METHOD_NULL);
0862         return isInstanceMethod(method&&
0863             CONTRIBUTION_PATTERN.matcher(method.getName()).matches();
0864     }
0865 
0866     /**
0867      * Finds out if the given {@code Method} was injected by the Groovy
0868      * compiler.<p>
0869      * Performs a basic checks against the method's name, returning true
0870      * if the name starts with either "super$" or "this$".
0871      *
0872      @param method a Method reference
0873      *
0874      @return true if the method matches the given criteria, false otherwise.
0875      */
0876     public static boolean isGroovyInjectedMethod(@Nonnull Method method) {
0877         return isGroovyInjectedMethod(method, false);
0878     }
0879 
0880     /**
0881      * Finds out if the given {@code Method} was injected by the Groovy
0882      * compiler.<p>
0883      * Performs a basic checks against the method's name, returning true
0884      * if the name starts with either "super$" or "this$".
0885      *
0886      @param method a Method reference
0887      *
0888      @return true if the method matches the given criteria, false otherwise.
0889      */
0890     public static boolean isGroovyInjectedMethod(@Nonnull Method method, boolean removeAbstractModifier) {
0891         requireNonNull(method, ERROR_METHOD_NULL);
0892         return isGroovyInjectedMethod(MethodDescriptor.forMethod(method, removeAbstractModifier));
0893     }
0894 
0895     /**
0896      * Finds out if the given {@code MethodDescriptor} was injected by the Groovy
0897      * compiler.<p>
0898      * Performs a basic checks against the method's name, returning true
0899      * if the name starts with either "super$" or "this$".
0900      *
0901      @param method a MethodDescriptor reference
0902      *
0903      @return true if the method matches the given criteria, false otherwise.
0904      */
0905     public static boolean isGroovyInjectedMethod(@Nonnull MethodDescriptor method) {
0906         requireNonNull(method, ERROR_METHOD_NULL);
0907         return isInstanceMethod(method&&
0908             (method.getName().startsWith("super$"|| method.getName().startsWith("this$"));
0909     }
0910 
0911     /**
0912      * Finds out if the given {@code Method} is a getter method.
0913      <p>
0914      <pre>
0915      * // assuming getMethod() returns an appropriate Method reference
0916      * isGetterMethod(getMethod("getFoo"))       = true
0917      * isGetterMethod(getMethod("getfoo") )      = false
0918      * isGetterMethod(getMethod("mvcGroupInit")) = false
0919      * isGetterMethod(getMethod("isFoo"))        = true
0920      * isGetterMethod(getMethod("island"))       = false
0921      </pre>
0922      *
0923      @param method a Method reference
0924      *
0925      @return true if the method is a getter, false otherwise.
0926      */
0927     public static boolean isGetterMethod(@Nonnull Method method) {
0928         return isGetterMethod(method, false);
0929     }
0930 
0931     /**
0932      * Finds out if the given {@code Method} is a getter method.
0933      <p>
0934      <pre>
0935      * // assuming getMethod() returns an appropriate Method reference
0936      * isGetterMethod(getMethod("getFoo"))       = true
0937      * isGetterMethod(getMethod("getfoo") )      = false
0938      * isGetterMethod(getMethod("mvcGroupInit")) = false
0939      * isGetterMethod(getMethod("isFoo"))        = true
0940      * isGetterMethod(getMethod("island"))       = false
0941      </pre>
0942      *
0943      @param method a Method reference
0944      *
0945      @return true if the method is a getter, false otherwise.
0946      */
0947     public static boolean isGetterMethod(@Nonnull Method method, boolean removeAbstractModifier) {
0948         requireNonNull(method, ERROR_METHOD_NULL);
0949         return isGetterMethod(MethodDescriptor.forMethod(method, removeAbstractModifier));
0950     }
0951 
0952     /**
0953      * Finds out if the given {@code MetaMethod} is a getter method.
0954      <p>
0955      <pre>
0956      * // assuming getMethod() returns an appropriate MethodDescriptor reference
0957      * isGetterMethod(getMethod("getFoo"))       = true
0958      * isGetterMethod(getMethod("getfoo") )      = false
0959      * isGetterMethod(getMethod("mvcGroupInit")) = false
0960      * isGetterMethod(getMethod("isFoo"))        = true
0961      * isGetterMethod(getMethod("island"))       = false
0962      </pre>
0963      *
0964      @param method a MethodDescriptor reference
0965      *
0966      @return true if the method is a getter, false otherwise.
0967      */
0968     public static boolean isGetterMethod(@Nonnull MethodDescriptor method) {
0969         requireNonNull(method, ERROR_METHOD_NULL);
0970         return isInstanceMethod(method&&
0971             (GETTER_PATTERN_1.matcher(method.getName()).matches() || GETTER_PATTERN_2.matcher(method.getName()).matches());
0972     }
0973 
0974     /**
0975      * Finds out if the given {@code Method} is a setter method.
0976      <p>
0977      <pre>
0978      * // assuming getMethod() returns an appropriate Method reference
0979      * isGetterMethod(getMethod("setFoo"))       = true
0980      * isGetterMethod(getMethod("setfoo"))       = false
0981      * isGetterMethod(getMethod("mvcGroupInit")) = false
0982      </pre>
0983      *
0984      @param method a Method reference
0985      *
0986      @return true if the method is a setter, false otherwise.
0987      */
0988     public static boolean isSetterMethod(@Nonnull Method method) {
0989         return isSetterMethod(method, false);
0990     }
0991 
0992     /**
0993      * Finds out if the given {@code Method} is a setter method.
0994      <p>
0995      <pre>
0996      * // assuming getMethod() returns an appropriate Method reference
0997      * isGetterMethod(getMethod("setFoo"))       = true
0998      * isGetterMethod(getMethod("setfoo"))       = false
0999      * isGetterMethod(getMethod("mvcGroupInit")) = false
1000      </pre>
1001      *
1002      @param method a Method reference
1003      *
1004      @return true if the method is a setter, false otherwise.
1005      */
1006     public static boolean isSetterMethod(@Nonnull Method method, boolean removeAbstractModifier) {
1007         requireNonNull(method, ERROR_METHOD_NULL);
1008         return isSetterMethod(MethodDescriptor.forMethod(method, removeAbstractModifier));
1009     }
1010 
1011     /**
1012      * Finds out if the given {@code MethodDescriptor} is a setter method.
1013      <p>
1014      <pre>
1015      * // assuming getMethod() returns an appropriate MethodDescriptor reference
1016      * isGetterMethod(getMethod("setFoo"))       = true
1017      * isGetterMethod(getMethod("setfoo"))       = false
1018      * isGetterMethod(getMethod("mvcGroupInit")) = false
1019      </pre>
1020      *
1021      @param method a MethodDescriptor reference
1022      *
1023      @return true if the method is a setter, false otherwise.
1024      */
1025     public static boolean isSetterMethod(@Nonnull MethodDescriptor method) {
1026         requireNonNull(method, ERROR_METHOD_NULL);
1027         return isInstanceMethod(method&& SETTER_PATTERN.matcher(method.getName()).matches();
1028     }
1029 
1030     /**
1031      * Finds out if the given {@code Method} belongs to the set of
1032      * predefined Artifact methods by convention.
1033      <p>
1034      <pre>
1035      * // assuming getMethod() returns an appropriate Method reference
1036      * isArtifactMethod(getMethod("newInstance"))    = true
1037      * isArtifactMethod(getMethod("griffonDestroy")) = false
1038      * isArtifactMethod(getMethod("foo"))            = false
1039      </pre>
1040      *
1041      @param method a Method reference
1042      *
1043      @return true if the method is an Artifact method, false otherwise.
1044      */
1045     public static boolean isArtifactMethod(@Nonnull Method method) {
1046         return isArtifactMethod(method, false);
1047     }
1048 
1049     /**
1050      * Finds out if the given {@code Method} belongs to the set of
1051      * predefined Artifact methods by convention.
1052      <p>
1053      <pre>
1054      * // assuming getMethod() returns an appropriate Method reference
1055      * isArtifactMethod(getMethod("newInstance"))    = true
1056      * isArtifactMethod(getMethod("griffonDestroy")) = false
1057      * isArtifactMethod(getMethod("foo"))            = false
1058      </pre>
1059      *
1060      @param method a Method reference
1061      *
1062      @return true if the method is an Artifact method, false otherwise.
1063      */
1064     public static boolean isArtifactMethod(@Nonnull Method method, boolean removeAbstractModifier) {
1065         requireNonNull(method, ERROR_METHOD_NULL);
1066         return isArtifactMethod(MethodDescriptor.forMethod(method, removeAbstractModifier));
1067     }
1068 
1069     /**
1070      * Finds out if the given {@code MethodDescriptor} belongs to the set of
1071      * predefined Artifact methods by convention.
1072      <p>
1073      <pre>
1074      * // assuming getMethod() returns an appropriate MethodDescriptor reference
1075      * isArtifactMethod(getMethod("newInstance"))    = true
1076      * isArtifactMethod(getMethod("griffonDestroy")) = false
1077      * isArtifactMethod(getMethod("foo"))            = false
1078      </pre>
1079      *
1080      @param method a MethodDescriptor reference
1081      *
1082      @return true if the method is an Artifact method, false otherwise.
1083      */
1084     public static boolean isArtifactMethod(@Nonnull MethodDescriptor method) {
1085         requireNonNull(method, ERROR_METHOD_NULL);
1086         return isInstanceMethod(method&&
1087             ARTIFACT_METHODS.contains(method);
1088     }
1089 
1090     /**
1091      * Finds out if the given {@code Method} belongs to the set of
1092      * predefined MVC methods by convention.
1093      <p>
1094      <pre>
1095      * // assuming getMethod() returns an appropriate Method reference
1096      * isMvcMethod(getMethod("mvcGroupInit"))    = true
1097      * isMvcMethod(getMethod("mvcGroupDestroy")) = true
1098      * isMvcMethod(getMethod("foo"))             = false
1099      </pre>
1100      *
1101      @param method a Method reference
1102      *
1103      @return true if the method is an MVC method, false otherwise.
1104      */
1105     public static boolean isMvcMethod(@Nonnull Method method) {
1106         return isMvcMethod(method, false);
1107     }
1108 
1109     /**
1110      * Finds out if the given {@code Method} belongs to the set of
1111      * predefined MVC methods by convention.
1112      <p>
1113      <pre>
1114      * // assuming getMethod() returns an appropriate Method reference
1115      * isMvcMethod(getMethod("mvcGroupInit"))    = true
1116      * isMvcMethod(getMethod("mvcGroupDestroy")) = true
1117      * isMvcMethod(getMethod("foo"))             = false
1118      </pre>
1119      *
1120      @param method a Method reference
1121      *
1122      @return true if the method is an MVC method, false otherwise.
1123      */
1124     public static boolean isMvcMethod(@Nonnull Method method, boolean removeAbstractModifier) {
1125         requireNonNull(method, ERROR_METHOD_NULL);
1126         return isMvcMethod(MethodDescriptor.forMethod(method, removeAbstractModifier));
1127     }
1128 
1129     /**
1130      * Finds out if the given {@code MethodDescriptor} belongs to the set of
1131      * predefined MVC methods by convention.
1132      <p>
1133      <pre>
1134      * // assuming getMethod() returns an appropriate MethodDescriptor reference
1135      * isMvcMethod(getMethod("mvcGroupInit"))    = true
1136      * isMvcMethod(getMethod("mvcGroupDestroy")) = true
1137      * isMvcMethod(getMethod("foo"))             = false
1138      </pre>
1139      *
1140      @param method a MethodDescriptor reference
1141      *
1142      @return true if the method is an MVC method, false otherwise.
1143      */
1144     public static boolean isMvcMethod(@Nonnull MethodDescriptor method) {
1145         requireNonNull(method, ERROR_METHOD_NULL);
1146         return isInstanceMethod(method&&
1147             MVC_METHODS.contains(method);
1148     }
1149 
1150     /**
1151      * Finds out if the given {@code Method} belongs to the set of
1152      * predefined threading methods by convention.
1153      <p>
1154      <pre>
1155      * // assuming getMethod() returns an appropriate Method reference
1156      * isThreadingMethod(getMethod("execOutsideUI"))    = true
1157      * isThreadingMethod(getMethod("doLater"))          = true
1158      * isThreadingMethod(getMethod("foo"))              = false
1159      </pre>
1160      *
1161      @param method a Method reference
1162      *
1163      @return true if the method is a threading method, false otherwise.
1164      */
1165     public static boolean isThreadingMethod(@Nonnull Method method) {
1166         return isThreadingMethod(method, false);
1167     }
1168 
1169     /**
1170      * Finds out if the given {@code Method} belongs to the set of
1171      * predefined threading methods by convention.
1172      <p>
1173      <pre>
1174      * // assuming getMethod() returns an appropriate Method reference
1175      * isThreadingMethod(getMethod("execOutsideUI"))    = true
1176      * isThreadingMethod(getMethod("doLater"))          = true
1177      * isThreadingMethod(getMethod("foo"))              = false
1178      </pre>
1179      *
1180      @param method a Method reference
1181      *
1182      @return true if the method is a threading method, false otherwise.
1183      */
1184     public static boolean isThreadingMethod(@Nonnull Method method, boolean removeAbstractModifier) {
1185         requireNonNull(method, ERROR_METHOD_NULL);
1186         return isThreadingMethod(MethodDescriptor.forMethod(method, removeAbstractModifier));
1187     }
1188 
1189     /**
1190      * Finds out if the given {@code MethodDescriptor} belongs to the set of
1191      * predefined threading methods by convention.
1192      <p>
1193      <pre>
1194      * // assuming getMethod() returns an appropriate MethodDescriptor reference
1195      * isThreadingMethod(getMethod("execOutsideUI"))    = true
1196      * isThreadingMethod(getMethod("doLater"))          = true
1197      * isThreadingMethod(getMethod("foo"))              = false
1198      </pre>
1199      *
1200      @param method a MethodDescriptor reference
1201      *
1202      @return true if the method is a threading method, false otherwise.
1203      */
1204     public static boolean isThreadingMethod(@Nonnull MethodDescriptor method) {
1205         requireNonNull(method, ERROR_METHOD_NULL);
1206         return isInstanceMethod(method&&
1207             THREADING_METHODS.contains(method);
1208     }
1209 
1210     /**
1211      * Finds out if the given {@code Method} belongs to the set of
1212      * predefined event publisher methods by convention.
1213      <p>
1214      <pre>
1215      * // assuming getMethod() returns an appropriate Method reference
1216      * isEventPublisherMethod(getMethod("addEventPublisher"))  = true
1217      * isEventPublisherMethod(getMethod("publishEvent"))       = true
1218      * isEventPublisherMethod(getMethod("foo"))                = false
1219      </pre>
1220      *
1221      @param method a Method reference
1222      *
1223      @return true if the method is an @EventPublisher method, false otherwise.
1224      */
1225     public static boolean isEventPublisherMethod(@Nonnull Method method) {
1226         return isEventPublisherMethod(method, false);
1227     }
1228 
1229     /**
1230      * Finds out if the given {@code Method} belongs to the set of
1231      * predefined event publisher methods by convention.
1232      <p>
1233      <pre>
1234      * // assuming getMethod() returns an appropriate Method reference
1235      * isEventPublisherMethod(getMethod("addEventPublisher"))  = true
1236      * isEventPublisherMethod(getMethod("publishEvent"))       = true
1237      * isEventPublisherMethod(getMethod("foo"))                = false
1238      </pre>
1239      *
1240      @param method a Method reference
1241      *
1242      @return true if the method is an @EventPublisher method, false otherwise.
1243      */
1244     public static boolean isEventPublisherMethod(@Nonnull Method method, boolean removeAbstractModifier) {
1245         requireNonNull(method, ERROR_METHOD_NULL);
1246         return isEventPublisherMethod(MethodDescriptor.forMethod(method, removeAbstractModifier));
1247     }
1248 
1249     /**
1250      * Finds out if the given {@code MethodDescriptor} belongs to the set of
1251      * predefined event publisher methods by convention.
1252      <p>
1253      <pre>
1254      * // assuming getMethod() returns an appropriate MethodDescriptor reference
1255      * isEventPublisherMethod(getMethod("addEventPublisher"))  = true
1256      * isEventPublisherMethod(getMethod("publishEvent"))       = true
1257      * isEventPublisherMethod(getMethod("foo"))                = false
1258      </pre>
1259      *
1260      @param method a MethodDescriptor reference
1261      *
1262      @return true if the method is an @EventPublisher method, false otherwise.
1263      */
1264     public static boolean isEventPublisherMethod(@Nonnull MethodDescriptor method) {
1265         requireNonNull(method, ERROR_METHOD_NULL);
1266         return isInstanceMethod(method&&
1267             EVENT_PUBLISHER_METHODS.contains(method);
1268     }
1269 
1270     /**
1271      * Finds out if the given {@code Method} belongs to the set of
1272      * predefined observable methods by convention.
1273      <p>
1274      <pre>
1275      * // assuming getMethod() returns an appropriate Method reference
1276      * isObservableMethod(getMethod("addPropertyChangeListener"))  = true
1277      * isObservableMethod(getMethod("getPropertyChangeListeners")) = true
1278      * isObservableMethod(getMethod("foo"))                        = false
1279      </pre>
1280      *
1281      @param method a Method reference
1282      *
1283      @return true if the method is an Observable method, false otherwise.
1284      */
1285     public static boolean isObservableMethod(@Nonnull Method method) {
1286         return isObservableMethod(method, false);
1287     }
1288 
1289     /**
1290      * Finds out if the given {@code Method} belongs to the set of
1291      * predefined observable methods by convention.
1292      <p>
1293      <pre>
1294      * // assuming getMethod() returns an appropriate Method reference
1295      * isObservableMethod(getMethod("addPropertyChangeListener"))  = true
1296      * isObservableMethod(getMethod("getPropertyChangeListeners")) = true
1297      * isObservableMethod(getMethod("foo"))                        = false
1298      </pre>
1299      *
1300      @param method a Method reference
1301      *
1302      @return true if the method is an Observable method, false otherwise.
1303      */
1304     public static boolean isObservableMethod(@Nonnull Method method, boolean removeAbstractModifier) {
1305         requireNonNull(method, ERROR_METHOD_NULL);
1306         return isObservableMethod(MethodDescriptor.forMethod(method, removeAbstractModifier));
1307     }
1308 
1309     /**
1310      * Finds out if the given {@code MethodDescriptor} belongs to the set of
1311      * predefined observable methods by convention.
1312      <p>
1313      <pre>
1314      * // assuming getMethod() returns an appropriate MethodDescriptor reference
1315      * isObservableMethod(getMethod("addPropertyChangeListener"))  = true
1316      * isObservableMethod(getMethod("getPropertyChangeListeners")) = true
1317      * isObservableMethod(getMethod("foo"))                        = false
1318      </pre>
1319      *
1320      @param method a MethodDescriptor reference
1321      *
1322      @return true if the method is an Observable method, false otherwise.
1323      */
1324     public static boolean isObservableMethod(@Nonnull MethodDescriptor method) {
1325         requireNonNull(method, ERROR_METHOD_NULL);
1326         return isInstanceMethod(method&&
1327             OBSERVABLE_METHODS.contains(method);
1328     }
1329 
1330     /**
1331      * Finds out if the given {@code Method} belongs to the set of
1332      * predefined resources methods by convention.
1333      <p>
1334      <pre>
1335      * // assuming getMethod() returns an appropriate Method reference
1336      * isResourceHandlerMethod(getMethod("getResourceAsURL"))    = true
1337      * isResourceHandlerMethod(getMethod("getResourceAsStream")) = true
1338      * isResourceHandlerMethod(getMethod("foo"))                 = false
1339      </pre>
1340      *
1341      @param method a Method reference
1342      *
1343      @return true if the method is an ResourceHandler method, false otherwise.
1344      */
1345     public static boolean isResourceHandlerMethod(@Nonnull Method method) {
1346         return isResourceHandlerMethod(method, false);
1347     }
1348 
1349     /**
1350      * Finds out if the given {@code Method} belongs to the set of
1351      * predefined resources methods by convention.
1352      <p>
1353      <pre>
1354      * // assuming getMethod() returns an appropriate Method reference
1355      * isResourceHandlerMethod(getMethod("getResourceAsURL"))    = true
1356      * isResourceHandlerMethod(getMethod("getResourceAsStream")) = true
1357      * isResourceHandlerMethod(getMethod("foo"))                 = false
1358      </pre>
1359      *
1360      @param method a Method reference
1361      *
1362      @return true if the method is an ResourceHandler method, false otherwise.
1363      */
1364     public static boolean isResourceHandlerMethod(@Nonnull Method method, boolean removeAbstractModifier) {
1365         requireNonNull(method, ERROR_METHOD_NULL);
1366         return isResourceHandlerMethod(MethodDescriptor.forMethod(method, removeAbstractModifier));
1367     }
1368 
1369     /**
1370      * Finds out if the given {@code MethodDescriptor} belongs to the set of
1371      * predefined resources methods by convention.
1372      <p>
1373      <pre>
1374      * // assuming getMethod() returns an appropriate MethodDescriptor reference
1375      * isResourceHandlerMethod(getMethod("getResourceAsURL"))    = true
1376      * isResourceHandlerMethod(getMethod("getResourceAsStream")) = true
1377      * isResourceHandlerMethod(getMethod("foo"))                 = false
1378      </pre>
1379      *
1380      @param method a MethodDescriptor reference
1381      *
1382      @return true if the method is an ResourceHandler method, false otherwise.
1383      */
1384     public static boolean isResourceHandlerMethod(@Nonnull MethodDescriptor method) {
1385         requireNonNull(method, ERROR_METHOD_NULL);
1386         return isInstanceMethod(method&&
1387             RESOURCE_HANDLER_METHODS.contains(method);
1388     }
1389 
1390     /**
1391      * Finds out if the given {@code Method} belongs to the set of
1392      * predefined message source methods by convention.
1393      <p>
1394      <pre>
1395      * // assuming getMethod() returns an appropriate Method reference
1396      * isMessageSourceMethod(getMethod("getMessage"))    = true
1397      * isMessageSourceMethod(getMethod("foo"))           = false
1398      </pre>
1399      *
1400      @param method a Method reference
1401      *
1402      @return true if the method is an MessageSource method, false otherwise.
1403      */
1404     public static boolean isMessageSourceMethod(@Nonnull Method method) {
1405         return isMessageSourceMethod(method, false);
1406     }
1407 
1408     /**
1409      * Finds out if the given {@code Method} belongs to the set of
1410      * predefined message source methods by convention.
1411      <p>
1412      <pre>
1413      * // assuming getMethod() returns an appropriate Method reference
1414      * isMessageSourceMethod(getMethod("getMessage"))    = true
1415      * isMessageSourceMethod(getMethod("foo"))           = false
1416      </pre>
1417      *
1418      @param method a Method reference
1419      *
1420      @return true if the method is an MessageSource method, false otherwise.
1421      */
1422     public static boolean isMessageSourceMethod(@Nonnull Method method, boolean removeAbstractModifier) {
1423         requireNonNull(method, ERROR_METHOD_NULL);
1424         return isMessageSourceMethod(MethodDescriptor.forMethod(method, removeAbstractModifier));
1425     }
1426 
1427     /**
1428      * Finds out if the given {@code MethodDescriptor} belongs to the set of
1429      * predefined message source methods by convention.
1430      <p>
1431      <pre>
1432      * // assuming getMethod() returns an appropriate MethodDescriptor reference
1433      * isMessageSourceMethod(getMethod("getMessage"))    = true
1434      * isMessageSourceMethod(getMethod("foo"))           = false
1435      </pre>
1436      *
1437      @param method a MethodDescriptor reference
1438      *
1439      @return true if the method is an MessageSource method, false otherwise.
1440      */
1441     public static boolean isMessageSourceMethod(@Nonnull MethodDescriptor method) {
1442         requireNonNull(method, ERROR_METHOD_NULL);
1443         return isInstanceMethod(method&&
1444             MESSAGE_SOURCE_METHODS.contains(method);
1445     }
1446 
1447     /**
1448      * Finds out if the given {@code Method} belongs to the set of
1449      * predefined resource resolver methods by convention.
1450      <p>
1451      <pre>
1452      * // assuming getMethod() returns an appropriate Method reference
1453      * isResourceResolverMethod(getMethod("resolveResource")) = true
1454      * isResourceResolverMethod(getMethod("foo"))             = false
1455      </pre>
1456      *
1457      @param method a Method reference
1458      *
1459      @return true if the method is an ResourceResolver method, false otherwise.
1460      */
1461     public static boolean isResourceResolverMethod(@Nonnull Method method) {
1462         return isResourceResolverMethod(method, false);
1463     }
1464 
1465     /**
1466      * Finds out if the given {@code Method} belongs to the set of
1467      * predefined resource resolver methods by convention.
1468      <p>
1469      <pre>
1470      * // assuming getMethod() returns an appropriate Method reference
1471      * isResourceResolverMethod(getMethod("resolveResource")) = true
1472      * isResourceResolverMethod(getMethod("foo"))             = false
1473      </pre>
1474      *
1475      @param method a Method reference
1476      *
1477      @return true if the method is an ResourceResolver method, false otherwise.
1478      */
1479     public static boolean isResourceResolverMethod(@Nonnull Method method, boolean removeAbstractModifier) {
1480         requireNonNull(method, ERROR_METHOD_NULL);
1481         return isResourceResolverMethod(MethodDescriptor.forMethod(method, removeAbstractModifier));
1482     }
1483 
1484     /**
1485      * Finds out if the given {@code MethodDescriptor} belongs to the set of
1486      * predefined resource resolver methods by convention.
1487      <p>
1488      <pre>
1489      * // assuming getMethod() returns an appropriate MethodDescriptor reference
1490      * isResourceResolverMethod(getMethod("resolveResource")) = true
1491      * isResourceResolverMethod(getMethod("foo"))             = false
1492      </pre>
1493      *
1494      @param method a MethodDescriptor reference
1495      *
1496      @return true if the method is an ResourceResolver method, false otherwise.
1497      */
1498     public static boolean isResourceResolverMethod(@Nonnull MethodDescriptor method) {
1499         requireNonNull(method, ERROR_METHOD_NULL);
1500         return isInstanceMethod(method&&
1501             RESOURCE_RESOLVER_METHODS.contains(method);
1502     }
1503 
1504     /**
1505      * Finds out if the given {@code Method} is an instance method, i.e,
1506      * it is public and non-static.
1507      *
1508      @param method a Method reference
1509      *
1510      @return true if the method is an instance method, false otherwise.
1511      */
1512     public static boolean isInstanceMethod(@Nonnull Method method) {
1513         return isInstanceMethod(method, false);
1514     }
1515 
1516     /**
1517      * Finds out if the given {@code Method} is an instance method, i.e,
1518      * it is public and non-static.
1519      *
1520      @param method a Method reference
1521      *
1522      @return true if the method is an instance method, false otherwise.
1523      */
1524     public static boolean isInstanceMethod(@Nonnull Method method, boolean removeAbstractModifier) {
1525         requireNonNull(method, ERROR_METHOD_NULL);
1526         return isInstanceMethod(MethodDescriptor.forMethod(method, removeAbstractModifier));
1527     }
1528 
1529     /**
1530      * Finds out if the given {@code MethodDescriptor} is an instance method, i.e,
1531      * it is public and non-static.
1532      *
1533      @param method a MethodDescriptor reference
1534      *
1535      @return true if the method is an instance method, false otherwise.
1536      */
1537     public static boolean isInstanceMethod(@Nonnull MethodDescriptor method) {
1538         requireNonNull(method, ERROR_METHOD_NULL);
1539         int modifiers = method.getModifiers();
1540         return Modifier.isPublic(modifiers&&
1541             !Modifier.isAbstract(modifiers&&
1542             !Modifier.isStatic(modifiers);
1543     }
1544 
1545     /**
1546      * Finds out if the given {@code Method} matches the following criteria:<ul>
1547      <li>isInstanceMethod(method)</li>
1548      <li>! isBasicMethod(method)</li>
1549      <li>! isGroovyInjectedMethod(method)</li>
1550      <li>! isThreadingMethod(method)</li>
1551      <li>! isArtifactMethod(method)</li>
1552      <li>! isMvcMethod(method)</li>
1553      <li>! isServiceMethod(method)</li>
1554      <li>! isEventPublisherMethod(method)</li>
1555      <li>! isObservableMethod(method)</li>
1556      <li>! isResourceHandlerMethod(method)</li>
1557      <li>! isGetterMethod(method)</li>
1558      <li>! isSetterMethod(method)</li>
1559      <li>! isContributionMethod(method)</li>
1560      </ul>
1561      *
1562      @param method a Method reference
1563      *
1564      @return true if the method matches the given criteria, false otherwise.
1565      */
1566     public static boolean isPlainMethod(@Nonnull Method method) {
1567         return isPlainMethod(method, false);
1568     }
1569 
1570     /**
1571      * Finds out if the given {@code Method} matches the following criteria:<ul>
1572      <li>isInstanceMethod(method)</li>
1573      <li>! isBasicMethod(method)</li>
1574      <li>! isGroovyInjectedMethod(method)</li>
1575      <li>! isThreadingMethod(method)</li>
1576      <li>! isArtifactMethod(method)</li>
1577      <li>! isMvcMethod(method)</li>
1578      <li>! isServiceMethod(method)</li>
1579      <li>! isEventPublisherMethod(method)</li>
1580      <li>! isObservableMethod(method)</li>
1581      <li>! isResourceHandlerMethod(method)</li>
1582      <li>! isGetterMethod(method)</li>
1583      <li>! isSetterMethod(method)</li>
1584      <li>! isContributionMethod(method)</li>
1585      </ul>
1586      *
1587      @param method a Method reference
1588      *
1589      @return true if the method matches the given criteria, false otherwise.
1590      */
1591     public static boolean isPlainMethod(@Nonnull Method method, boolean removeAbstractModifier) {
1592         requireNonNull(method, ERROR_METHOD_NULL);
1593         return isPlainMethod(MethodDescriptor.forMethod(method, removeAbstractModifier));
1594     }
1595 
1596     /**
1597      * Finds out if the given {@code MethodDescriptor} matches the following criteria:<ul>
1598      <li>isInstanceMethod(method)</li>
1599      <li>! isBasicMethod(method)</li>
1600      <li>! isGroovyInjectedMethod(method)</li>
1601      <li>! isThreadingMethod(method)</li>
1602      <li>! isArtifactMethod(method)</li>
1603      <li>! isMvcMethod(method)</li>
1604      <li>! isServiceMethod(method)</li>
1605      <li>! isEventPublisherMethod(method)</li>
1606      <li>! isObservableMethod(method)</li>
1607      <li>! isResourceHandlerMethod(method)</li>
1608      <li>! isGetterMethod(method)</li>
1609      <li>! isSetterMethod(method)</li>
1610      <li>! isContributionMethod(method)</li>
1611      </ul>
1612      *
1613      @param method a MethodDescriptor reference
1614      *
1615      @return true if the method matches the given criteria, false otherwise.
1616      */
1617     public static boolean isPlainMethod(@Nonnull MethodDescriptor method) {
1618         requireNonNull(method, ERROR_METHOD_NULL);
1619         return isInstanceMethod(method&&
1620             !isBasicMethod(method&&
1621             !isGroovyInjectedMethod(method&&
1622             !isThreadingMethod(method&&
1623             !isArtifactMethod(method&&
1624             !isMvcMethod(method&&
1625             !isEventPublisherMethod(method&&
1626             !isObservableMethod(method&&
1627             !isResourceHandlerMethod(method&&
1628             !isGetterMethod(method&&
1629             !isSetterMethod(method&&
1630             !isContributionMethod(method);
1631     }
1632 
1633     /**
1634      * Returns true if the specified property in the specified class is of the specified type
1635      *
1636      @param clazz        The class which contains the property
1637      @param propertyName The property name
1638      @param type         The type to check
1639      *
1640      @return A boolean value
1641      */
1642     public static boolean isPropertyOfType(Class<?> clazz, String propertyName, Class<?> type) {
1643         try {
1644             Class<?> propType = getPropertyType(clazz, propertyName);
1645             return propType != null && propType.equals(type);
1646         catch (Exception e) {
1647             return false;
1648         }
1649     }
1650 
1651     /**
1652      * Instantiates a Class, wrapping any exceptions in a RuntimeException.
1653      *
1654      @param clazz target Class for which an object will be instantiated
1655      *
1656      @return the newly instantiated object.
1657      *
1658      @throws BeanInstantiationException if an error occurs when creating the object
1659      */
1660     @Nonnull
1661     public static Object instantiateClass(@Nonnull Class<?> clazz) {
1662         requireNonNull(clazz, ERROR_CLAZZ_NULL);
1663         try {
1664             return clazz.newInstance();
1665         catch (Exception e) {
1666             throw new BeanInstantiationException("Could not create an instance of " + clazz, e);
1667         }
1668     }
1669 
1670     @Nonnull
1671     public static Object instantiate(@Nonnull Class<?> clazz, @Nullable Object[] args) {
1672         requireNonNull(clazz, ERROR_CLAZZ_NULL);
1673         try {
1674             if (args == null) {
1675                 args = EMPTY_OBJECT_ARRAY;
1676             }
1677             int arguments = args.length;
1678             Class<?>[] parameterTypes = new Class<?>[arguments];
1679             for (int i = 0; i < arguments; i++) {
1680                 parameterTypes[i= args[i].getClass();
1681             }
1682             return clazz.getDeclaredConstructor(parameterTypes).newInstance(args);
1683         catch (Exception e) {
1684             throw new BeanInstantiationException("Could not create an instance of " + clazz, e);
1685         }
1686     }
1687 
1688     /**
1689      * Returns the value of the specified property and type from an instance of the specified Griffon class
1690      *
1691      @param clazz        The name of the class which contains the property
1692      @param propertyName The property name
1693      @param propertyType The property type
1694      *
1695      @return The value of the property or null if none exists
1696      */
1697     @Nullable
1698     public static Object getPropertyValueOfNewInstance(@Nullable Class<?> clazz, @Nullable String propertyName, Class<?> propertyType) {
1699         // validate
1700         if (clazz == null || GriffonNameUtils.isBlank(propertyName)) {
1701             return null;
1702         }
1703 
1704         Object instance;
1705         try {
1706             instance = instantiateClass(clazz);
1707         catch (BeanInstantiationException e) {
1708             return null;
1709         }
1710 
1711         return getPropertyOrStaticPropertyOrFieldValue(instance, propertyName);
1712     }
1713 
1714     /**
1715      * Returns the value of the specified property and type from an instance of the specified Griffon class
1716      *
1717      @param clazz        The name of the class which contains the property
1718      @param propertyName The property name
1719      *
1720      @return The value of the property or null if none exists
1721      */
1722     public static Object getPropertyValueOfNewInstance(Class<?> clazz, String propertyName) {
1723         // validate
1724         if (clazz == null || GriffonNameUtils.isBlank(propertyName)) {
1725             return null;
1726         }
1727 
1728         Object instance;
1729         try {
1730             instance = instantiateClass(clazz);
1731         catch (BeanInstantiationException e) {
1732             return null;
1733         }
1734 
1735         return getPropertyOrStaticPropertyOrFieldValue(instance, propertyName);
1736     }
1737 
1738     /**
1739      * Retrieves a PropertyDescriptor for the specified instance and property value
1740      *
1741      @param instance      The instance
1742      @param propertyValue The value of the property
1743      *
1744      @return The PropertyDescriptor
1745      */
1746     public static PropertyDescriptor getPropertyDescriptorForValue(Object instance, Object propertyValue) {
1747         if (instance == null || propertyValue == null) { return null}
1748 
1749         PropertyDescriptor[] descriptors = getPropertyDescriptors(instance.getClass());
1750 
1751         for (PropertyDescriptor pd : descriptors) {
1752             if (isAssignableOrConvertibleFrom(pd.getPropertyType(), propertyValue.getClass())) {
1753                 Object value;
1754                 try {
1755                     value = getReadMethod(pd).invoke(instance, (Object[]) null);
1756                 catch (Exception e) {
1757                     throw new RuntimeException("Problem calling readMethod of " + pd, e);
1758                 }
1759                 if (propertyValue.equals(value)) { return pd; }
1760             }
1761         }
1762         return null;
1763     }
1764 
1765     /**
1766      * Returns the type of the given property contained within the specified class
1767      *
1768      @param clazz        The class which contains the property
1769      @param propertyName The name of the property
1770      *
1771      @return The property type or null if none exists
1772      */
1773     @Nullable
1774     public static Class<?> getPropertyType(@Nullable Class<?> clazz, @Nullable String propertyName) {
1775         if (clazz == null || GriffonNameUtils.isBlank(propertyName)) {
1776             return null;
1777         }
1778 
1779         try {
1780             PropertyDescriptor desc = getPropertyDescriptor(clazz, propertyName);
1781             if (desc != null) {
1782                 return desc.getPropertyType();
1783             else {
1784                 return null;
1785             }
1786         catch (Exception e) {
1787             // if there are any errors in instantiating just return null for the moment
1788             return null;
1789         }
1790     }
1791 
1792     /**
1793      * Retrieves all the properties of the given class for the given type
1794      *
1795      @param clazz        The class to retrieve the properties from
1796      @param propertyType The type of the properties you wish to retrieve
1797      *
1798      @return An array of PropertyDescriptor instances
1799      */
1800     @Nonnull
1801     public static PropertyDescriptor[] getPropertiesOfType(@Nullable Class<?> clazz, @Nullable Class<?> propertyType) {
1802         if (clazz == null || propertyType == null) {
1803             return new PropertyDescriptor[0];
1804         }
1805 
1806         Set<PropertyDescriptor> properties = new HashSet<>();
1807         try {
1808             PropertyDescriptor[] descriptors = getPropertyDescriptors(clazz);
1809 
1810             for (PropertyDescriptor descriptor : descriptors) {
1811                 Class<?> currentPropertyType = descriptor.getPropertyType();
1812                 if (isTypeInstanceOfPropertyType(propertyType, currentPropertyType)) {
1813                     properties.add(descriptor);
1814                 }
1815             }
1816         catch (Exception e) {
1817             // if there are any errors in instantiating just return null for the moment
1818             return new PropertyDescriptor[0];
1819         }
1820         return properties.toArray(new PropertyDescriptor[properties.size()]);
1821     }
1822 
1823     private static boolean isTypeInstanceOfPropertyType(Class<?> type, Class<?> propertyType) {
1824         return propertyType.isAssignableFrom(type&& !propertyType.equals(Object.class);
1825     }
1826 
1827     /**
1828      * Retrieves all the properties of the given class which are assignable to the given type
1829      *
1830      @param clazz             The class to retrieve the properties from
1831      @param propertySuperType The type of the properties you wish to retrieve
1832      *
1833      @return An array of PropertyDescriptor instances
1834      */
1835     public static PropertyDescriptor[] getPropertiesAssignableToType(Class<?> clazz, Class<?> propertySuperType) {
1836         if (clazz == null || propertySuperType == null) { return new PropertyDescriptor[0]}
1837 
1838         Set<PropertyDescriptor> properties = new HashSet<>();
1839         try {
1840             PropertyDescriptor[] descriptors = getPropertyDescriptors(clazz);
1841 
1842             for (PropertyDescriptor descriptor : descriptors) {
1843                 if (propertySuperType.isAssignableFrom(descriptor.getPropertyType())) {
1844                     properties.add(descriptor);
1845                 }
1846             }
1847         catch (Exception e) {
1848             return new PropertyDescriptor[0];
1849         }
1850         return properties.toArray(new PropertyDescriptor[properties.size()]);
1851     }
1852 
1853     /**
1854      * Retrieves a property of the given class of the specified name and type
1855      *
1856      @param clazz        The class to retrieve the property from
1857      @param propertyName The name of the property
1858      @param propertyType The type of the property
1859      *
1860      @return A PropertyDescriptor instance or null if none exists
1861      */
1862     public static PropertyDescriptor getProperty(Class<?> clazz, String propertyName, Class<?> propertyType) {
1863         if (clazz == null || propertyName == null || propertyType == null) { return null}
1864 
1865         try {
1866             PropertyDescriptor pd = getPropertyDescriptor(clazz, propertyName);
1867             if (pd != null && pd.getPropertyType().equals(propertyType)) {
1868                 return pd;
1869             else {
1870                 return null;
1871             }
1872         catch (Exception e) {
1873             // if there are any errors in instantiating just return null for the moment
1874             return null;
1875         }
1876     }
1877 
1878     /**
1879      * Convenience method for converting a collection to an Object[]
1880      *
1881      @param c The collection
1882      *
1883      @return An object array
1884      */
1885     public static Object[] collectionToObjectArray(Collection<?> c) {
1886         if (c == null) { return EMPTY_OBJECT_ARRAY; }
1887         return c.toArray(new Object[c.size()]);
1888     }
1889 
1890     /**
1891      * Detect if left and right types are matching types. In particular,
1892      * test if one is a primitive type and the other is the corresponding
1893      * Java wrapper type. Primitive and wrapper classes may be passed to
1894      * either arguments.
1895      *
1896      @param leftType
1897      @param rightType
1898      *
1899      @return true if one of the classes is a native type and the other the object representation
1900      * of the same native type
1901      */
1902     public static boolean isMatchBetweenPrimitiveAndWrapperTypes(@Nonnull Class<?> leftType, @Nonnull Class<?> rightType) {
1903         requireNonNull(leftType, "Left type is null!");
1904         requireNonNull(rightType, "Right type is null!");
1905         return isMatchBetweenPrimitiveAndWrapperTypes(leftType.getName(), rightType.getName());
1906     }
1907 
1908     /**
1909      * Detect if left and right types are matching types. In particular,
1910      * test if one is a primitive type and the other is the corresponding
1911      * Java wrapper type. Primitive and wrapper classes may be passed to
1912      * either arguments.
1913      *
1914      @param leftType
1915      @param rightType
1916      *
1917      @return true if one of the classes is a native type and the other the object representation
1918      * of the same native type
1919      */
1920     public static boolean isMatchBetweenPrimitiveAndWrapperTypes(@Nonnull String leftType, @Nonnull String rightType) {
1921         requireNonBlank(leftType, "Left type is null!");
1922         requireNonBlank(rightType, "Right type is null!");
1923         String r = PRIMITIVE_TYPE_COMPATIBLE_TYPES.get(leftType);
1924         return r != null && r.equals(rightType);
1925     }
1926 
1927     @Nullable
1928     @SuppressWarnings("ConstantConditions")
1929     private static Method findDeclaredMethod(@Nonnull Class<?> clazz, @Nonnull String methodName, Class[] parameterTypes) {
1930         requireNonNull(clazz, ERROR_CLAZZ_NULL);
1931         requireNonBlank(methodName, ERROR_METHOD_NAME_BLANK);
1932         while (clazz != null) {
1933             try {
1934                 Method method = clazz.getDeclaredMethod(methodName, parameterTypes);
1935                 if (method != null) { return method; }
1936             catch (NoSuchMethodException | SecurityException e) {
1937                 // skip
1938             }
1939             clazz = clazz.getSuperclass();
1940         }
1941 
1942         return null;
1943     }
1944 
1945     /**
1946      <p>Work out if the specified property is readable and static. Java introspection does not
1947      * recognize this concept of static properties but Groovy does. We also consider public static fields
1948      * as static properties with no getters/setters</p>
1949      *
1950      @param clazz        The class to check for static property
1951      @param propertyName The property name
1952      *
1953      @return true if the property with name propertyName has a static getter method
1954      */
1955     public static boolean isStaticProperty(@Nonnull Class<?> clazz, @Nonnull String propertyName) {
1956         requireNonNull(clazz, ERROR_CLAZZ_NULL);
1957         requireNonBlank(propertyName, ERROR_PROPERTY_NAME_BLANK);
1958         Method getter = findDeclaredMethod(clazz, getGetterName(propertyName)null);
1959         if (getter != null) {
1960             return isPublicStatic(getter);
1961         else {
1962             try {
1963                 Field f = clazz.getDeclaredField(propertyName);
1964                 if (f != null) {
1965                     return isPublicStatic(f);
1966                 }
1967             catch (NoSuchFieldException ignore) {
1968                 //ignore
1969             }
1970         }
1971 
1972         return false;
1973     }
1974 
1975     /**
1976      * Determine whether the method is declared public static
1977      *
1978      @param m the method to be tested
1979      *
1980      @return True if the method is declared public static
1981      */
1982     public static boolean isPublicStatic(@Nonnull Method m) {
1983         requireNonNull(m, "Argument 'method' must not be null");
1984         final int modifiers = m.getModifiers();
1985         return Modifier.isPublic(modifiers&& Modifier.isStatic(modifiers);
1986     }
1987 
1988     /**
1989      * Determine whether the field is declared public static
1990      *
1991      @param f the field to be tested
1992      *
1993      @return True if the field is declared public static
1994      */
1995     public static boolean isPublicStatic(@Nonnull Field f) {
1996         requireNonNull(f, "Argument 'field' must not be null");
1997         final int modifiers = f.getModifiers();
1998         return Modifier.isPublic(modifiers&& Modifier.isStatic(modifiers);
1999     }
2000 
2001     /**
2002      * Calculate the name for a getter method to retrieve the specified property
2003      *
2004      @param propertyName the name of the property
2005      *
2006      @return The name for the getter method for this property, if it were to exist, i.e. getConstraints
2007      */
2008     @Nonnull
2009     public static String getGetterName(@Nonnull String propertyName) {
2010         requireNonBlank(propertyName, ERROR_PROPERTY_NAME_BLANK);
2011         return PROPERTY_GET_PREFIX + Character.toUpperCase(propertyName.charAt(0))
2012             + propertyName.substring(1);
2013     }
2014 
2015     /**
2016      <p>Get a static property value, which has a public static getter or is just a public static field.</p>
2017      *
2018      @param clazz The class to check for static property
2019      @param name  The property name
2020      *
2021      @return The value if there is one, or null if unset OR there is no such property
2022      */
2023     @Nullable
2024     public static Object getStaticPropertyValue(@Nonnull Class<?> clazz, @Nonnull String name) {
2025         requireNonNull(clazz, ERROR_CLAZZ_NULL);
2026         requireNonBlank(name, ERROR_NAME_BLANK);
2027         Method getter = findDeclaredMethod(clazz, getGetterName(name)null);
2028         try {
2029             if (getter != null) {
2030                 return getter.invoke(null, (Object[]) null);
2031             else {
2032                 Field f = clazz.getDeclaredField(name);
2033                 if (f != null) {
2034                     return f.get(null);
2035                 }
2036             }
2037         catch (Exception ignore) {
2038             //ignore
2039         }
2040         return null;
2041     }
2042 
2043     /**
2044      <p>Looks for a property of the reference instance with a given name.</p>
2045      <p>If found its value is returned. We follow the Java bean conventions with augmentation for groovy support
2046      * and static fields/properties. We will therefore match, in this order:
2047      </p>
2048      <ol>
2049      <li>Standard public bean property (with getter or just public field, using normal introspection)
2050      <li>Public static property with getter method
2051      <li>Public static field
2052      </ol>
2053      *
2054      @return property value or null if no property found
2055      */
2056     @Nullable
2057     public static Object getPropertyOrStaticPropertyOrFieldValue(@Nonnull Object obj, @Nonnull String name) {
2058         requireNonNull(obj, ERROR_OBJECT_NULL);
2059         requireNonBlank(name, ERROR_NAME_BLANK);
2060         if (isReadable(obj, name)) {
2061             try {
2062                 return getProperty(obj, name);
2063             catch (Exception e) {
2064                 throw new PropertyException(obj, name);
2065             }
2066         else {
2067             // Look for public fields
2068             if (isPublicField(obj, name)) {
2069                 return getFieldValue(obj, name);
2070             }
2071 
2072             // Look for statics
2073             Class<?> clazz = obj.getClass();
2074             if (isStaticProperty(clazz, name)) {
2075                 return getStaticPropertyValue(clazz, name);
2076             else {
2077                 return null;
2078             }
2079         }
2080     }
2081 
2082     /**
2083      * Get the value of a declared field on an object
2084      *
2085      @param obj  the instance that owns the field
2086      @param name the name of the file to lookup
2087      *
2088      @return The object value or null if there is no such field or access problems
2089      */
2090     @Nullable
2091     public static Object getFieldValue(@Nonnull Object obj, @Nonnull String name) {
2092         requireNonNull(obj, ERROR_OBJECT_NULL);
2093         requireNonBlank(name, ERROR_NAME_BLANK);
2094 
2095         Class<?> clazz = obj.getClass();
2096         Class<?> c = clazz;
2097         while (c != null && !c.equals(Object.class)) {
2098             Field field = null;
2099             boolean wasAccessible = false;
2100             try {
2101                 field = c.getDeclaredField(name);
2102                 wasAccessible = field.isAccessible();
2103                 field.setAccessible(true);
2104                 return field.get(obj);
2105             catch (Exception e) {
2106                 // ignore
2107             finally {
2108                 if (field != null) {
2109                     field.setAccessible(wasAccessible);
2110                 }
2111             }
2112             c = c.getSuperclass();
2113         }
2114 
2115         return null;
2116     }
2117 
2118     /**
2119      * Get the a declared field on an object
2120      *
2121      @param obj  the instance that owns the field
2122      @param name the name of the file to lookup
2123      *
2124      @return The field or null if there is no such field or access problems
2125      */
2126     @Nullable
2127     public static Field getField(@Nonnull Object obj, @Nonnull String name) {
2128         requireNonNull(obj, ERROR_OBJECT_NULL);
2129         requireNonBlank(name, ERROR_NAME_BLANK);
2130         return getField(obj.getClass(), name);
2131     }
2132 
2133     /**
2134      * Get the a declared field on a class
2135      *
2136      @param clazz the clazz that owns the field
2137      @param name  the name of the file to lookup
2138      *
2139      @return The field or null if there is no such field or access problems
2140      */
2141     @Nullable
2142     public static Field getField(@Nonnull Class<?> clazz, @Nonnull String name) {
2143         requireNonNull(clazz, ERROR_CLAZZ_NULL);
2144         requireNonBlank(name, ERROR_NAME_BLANK);
2145 
2146         Class<?> c = clazz;
2147         while (c != null && !c.equals(Object.class)) {
2148             try {
2149                 return c.getDeclaredField(name);
2150             catch (Exception e) {
2151                 // ignore
2152             }
2153             c = c.getSuperclass();
2154         }
2155         return null;
2156     }
2157 
2158     /**
2159      * Returns an array of {@code Field} objects reflecting all the fields
2160      * declared by the class and its hierarchy, represented by this
2161      * {@code Class} object. This includes public, protected, default
2162      * (package) access, and private fields, but excludes inherited fields.
2163      <p>
2164      <p> The elements in the returned array are not sorted and are not in any
2165      * particular order.
2166      *
2167      @param clazz the clazz that will be queried.
2168      *
2169      @return the array of {@code Field} objects representing all the
2170      * declared fields of this class and its hierarchy
2171      */
2172     public static Field[] getAllDeclaredFields(@Nonnull Class<?> clazz) {
2173         requireNonNull(clazz, ERROR_CLAZZ_NULL);
2174 
2175         List<Field> fields = new ArrayList<>();
2176 
2177         Class<?> c = clazz;
2178         while (c != null && !c.equals(Object.class)) {
2179             Field[] declaredFields = c.getDeclaredFields();
2180             if (declaredFields != null && declaredFields.length > 0) {
2181                 fields.addAll(Arrays.asList(declaredFields));
2182             }
2183             c = c.getSuperclass();
2184         }
2185 
2186         return fields.toArray(new Field[fields.size()]);
2187     }
2188 
2189     /**
2190      * Work out if the specified object has a public field with the name supplied.
2191      *
2192      @param obj  the instance that owns the field
2193      @param name the name of the file to lookup
2194      *
2195      @return True if a public field with the name exists
2196      */
2197     public static boolean isPublicField(@Nonnull Object obj, @Nonnull String name) {
2198         requireNonNull(obj, ERROR_OBJECT_NULL);
2199         requireNonBlank(name, ERROR_NAME_BLANK);
2200         Class<?> clazz = obj.getClass();
2201         Field f;
2202         try {
2203             f = clazz.getDeclaredField(name);
2204             return Modifier.isPublic(f.getModifiers());
2205         catch (NoSuchFieldException e) {
2206             return false;
2207         }
2208     }
2209 
2210     /**
2211      * Checks whether the specified property is inherited from a super class
2212      *
2213      @param clz          The class to check
2214      @param propertyName The property name
2215      *
2216      @return True if the property is inherited
2217      */
2218     public static boolean isPropertyInherited(@Nullable Class<?> clz, @Nonnull String propertyName) {
2219         if (clz == null) { return false}
2220         requireNonBlank(propertyName, ERROR_PROPERTY_NAME_BLANK);
2221         Class<?> superClass = clz.getSuperclass();
2222 
2223         PropertyDescriptor pd;
2224         try {
2225             pd = getPropertyDescriptor(superClass, propertyName);
2226         catch (Exception e) {
2227             throw new PropertyException(superClass, propertyName, e);
2228         }
2229         return pd != null && pd.getReadMethod() != null;
2230     }
2231 
2232     /**
2233      * Creates a concrete collection for the supplied interface
2234      *
2235      @param interfaceType The interface
2236      *
2237      @return ArrayList for List, TreeSet for SortedSet, HashSet for Set etc.
2238      */
2239     @Nonnull
2240     public static Collection<?> createConcreteCollection(@Nonnull Class<?> interfaceType) {
2241         requireNonNull(interfaceType, ERROR_TYPE_NULL);
2242         Collection<?> elements;
2243         if (interfaceType.equals(List.class)) {
2244             elements = new ArrayList<>();
2245         else if (interfaceType.equals(SortedSet.class)) {
2246             elements = new TreeSet<>();
2247         else {
2248             elements = new LinkedHashSet<>();
2249         }
2250         return elements;
2251     }
2252 
2253     /**
2254      * Retrieves the name of a setter for the specified property name
2255      *
2256      @param propertyName The property name
2257      *
2258      @return The setter equivalent
2259      */
2260     @Nonnull
2261     public static String getSetterName(@Nonnull String propertyName) {
2262         requireNonBlank(propertyName, ERROR_PROPERTY_NAME_BLANK);
2263         return PROPERTY_SET_PREFIX + propertyName.substring(01).toUpperCase() + propertyName.substring(1);
2264     }
2265 
2266     /**
2267      * Returns true if the name of the method specified and the number of arguments make it a javabean property
2268      *
2269      @param name True if its a Javabean property
2270      @param args The arguments
2271      *
2272      @return True if it is a javabean property method
2273      */
2274     @SuppressWarnings("ConstantConditions")
2275     public static boolean isGetter(@Nullable String name, @Nullable Class[] args) {
2276         if (GriffonNameUtils.isBlank(name|| args == null) { return false}
2277         if (args.length != 0) { return false}
2278 
2279         if (name.startsWith(PROPERTY_GET_PREFIX)) {
2280             name = name.substring(3);
2281             if (name.length() && Character.isUpperCase(name.charAt(0))) { return true}
2282         else if (name.startsWith(PROPERTY_IS_PREFIX)) {
2283             name = name.substring(2);
2284             if (name.length() && Character.isUpperCase(name.charAt(0))) { return true}
2285         }
2286         return false;
2287     }
2288 
2289     /**
2290      * Returns a property name equivalent for the given getter name or null if it is not a getter
2291      *
2292      @param getterName The getter name
2293      *
2294      @return The property name equivalent
2295      */
2296     @Nullable
2297     @SuppressWarnings("ConstantConditions")
2298     public static String getPropertyForGetter(@Nullable String getterName) {
2299         if (GriffonNameUtils.isBlank(getterName)) { return null}
2300 
2301         if (getterName.startsWith(PROPERTY_GET_PREFIX)) {
2302             String prop = getterName.substring(3);
2303             return convertPropertyName(prop);
2304         else if (getterName.startsWith(PROPERTY_IS_PREFIX)) {
2305             String prop = getterName.substring(2);
2306             return convertPropertyName(prop);
2307         }
2308         return null;
2309     }
2310 
2311     @Nonnull
2312     private static String convertPropertyName(@Nonnull String prop) {
2313         if (Character.isUpperCase(prop.charAt(0)) && Character.isUpperCase(prop.charAt(1))) {
2314             return prop;
2315         else if (Character.isDigit(prop.charAt(0))) {
2316             return prop;
2317         else {
2318             return Character.toLowerCase(prop.charAt(0)) + prop.substring(1);
2319         }
2320     }
2321 
2322     /**
2323      * Returns a property name equivalent for the given setter name or null if it is not a getter
2324      *
2325      @param setterName The setter name
2326      *
2327      @return The property name equivalent
2328      */
2329     @Nullable
2330     @SuppressWarnings("ConstantConditions")
2331     public static String getPropertyForSetter(@Nullable String setterName) {
2332         if (GriffonNameUtils.isBlank(setterName)) { return null}
2333 
2334         if (setterName.startsWith(PROPERTY_SET_PREFIX)) {
2335             String prop = setterName.substring(3);
2336             return convertPropertyName(prop);
2337         }
2338         return null;
2339     }
2340 
2341     @SuppressWarnings("ConstantConditions")
2342     public static boolean isSetter(@Nullable String name, @Nullable Class[] args) {
2343         if (GriffonNameUtils.isBlank(name|| args == null) { return false}
2344 
2345         if (name.startsWith(PROPERTY_SET_PREFIX)) {
2346             if (args.length != 1) { return false}
2347             name = name.substring(3);
2348             if (name.length() && Character.isUpperCase(name.charAt(0))) { return true}
2349         }
2350 
2351         return false;
2352     }
2353 
2354     /**
2355      * Returns true if the specified clazz parameter is either the same as, or is a superclass or super interface
2356      * of, the specified type parameter. Converts primitive types to compatible class automatically.
2357      *
2358      @param clazz
2359      @param type
2360      *
2361      @return True if the class is a taglib
2362      *
2363      @see java.lang.Class#isAssignableFrom(Class)
2364      */
2365     public static boolean isAssignableOrConvertibleFrom(@Nullable Class<?> clazz, @Nullable Class<?> type) {
2366         if (type == null || clazz == null) {
2367             return false;
2368         else if (type.isPrimitive()) {
2369             // convert primitive type to compatible class
2370             Class<?> primitiveClass = PRIMITIVE_TYPE_COMPATIBLE_CLASSES.get(type);
2371             return primitiveClass != null && clazz.isAssignableFrom(primitiveClass);
2372         else {
2373             return clazz.isAssignableFrom(type);
2374         }
2375     }
2376 
2377     /**
2378      * Retrieves a boolean value from a Map for the given key
2379      *
2380      @param key The key that references the boolean value
2381      @param map The map to look in
2382      *
2383      @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
2384      */
2385     public static boolean getBooleanFromMap(@Nullable String key, @Nullable Map<String, Object> map) {
2386         if (map == null) { return false}
2387         if (map.containsKey(key)) {
2388             Object o = map.get(key);
2389             if (o == null) { return falseelse if (instanceof Boolean) {
2390                 return (Booleano;
2391             else {
2392                 return Boolean.valueOf(o.toString());
2393             }
2394         }
2395         return false;
2396     }
2397 
2398     /**
2399      * Returns whether the specified class is either within one of the specified packages or
2400      * within a subpackage of one of the packages
2401      *
2402      @param clazz       The class
2403      @param packageList The list of packages
2404      *
2405      @return True if it is within the list of specified packages
2406      */
2407     public static boolean isClassBelowPackage(@Nonnull Class<?> clazz, @Nonnull List<?> packageList) {
2408         requireNonNull(clazz, ERROR_CLAZZ_NULL);
2409         requireNonNull(packageList, "Argument 'packageList' must not be null");
2410         String classPackage = clazz.getPackage().getName();
2411         for (Object packageName : packageList) {
2412             if (packageName != null) {
2413                 if (classPackage.startsWith(packageName.toString())) {
2414                     return true;
2415                 }
2416             }
2417         }
2418         return false;
2419     }
2420 
2421     /**
2422      * Sets or updates an object's properties.
2423      <p>
2424      * This method will attempt setting a property using a matching
2425      * {@code PropertyDescriptor}; next it will try direct field
2426      * access if the property was not found.
2427      *
2428      @param bean       the target object on which properties will be set
2429      @param properties names and values for properties to be set
2430      *
2431      @throws PropertyException if a property could be found
2432      @since 2.1.0
2433      */
2434     public static void setPropertiesOrFields(@Nonnull Object bean, @Nonnull Map<String, Object> propertiesthrows PropertyException {
2435         requireNonNull(bean, ERROR_BEAN_NULL);
2436         requireNonNull(properties, ERROR_PROPERTIES_NULL);
2437         for (Map.Entry<String, Object> entry : properties.entrySet()) {
2438             setPropertyOrFieldValue(bean, entry.getKey(), entry.getValue());
2439         }
2440     }
2441 
2442     /**
2443      * Sets or updates an object's properties.
2444      <p>
2445      * This method will attempt setting a property using a matching
2446      * {@code PropertyDescriptor}; next it will try direct field
2447      * access if the property was not found.
2448      *
2449      @param bean       the target object on which properties will be set
2450      @param properties names and values for properties to be set
2451      *
2452      @since 2.1.0
2453      */
2454     public static void setPropertiesOrFieldsNoException(@Nonnull Object bean, @Nonnull Map<String, Object> properties) {
2455         requireNonNull(bean, ERROR_BEAN_NULL);
2456         requireNonNull(properties, ERROR_PROPERTIES_NULL);
2457         for (Map.Entry<String, Object> entry : properties.entrySet()) {
2458             try {
2459                 setPropertyOrFieldValue(bean, entry.getKey(), entry.getValue());
2460             catch (PropertyException pe) {
2461                 // ignore
2462             }
2463         }
2464     }
2465 
2466     /**
2467      * Sets or updates an object's property.
2468      <p>
2469      * This method will attempt setting a property using a matching
2470      * {@code PropertyDescriptor}; next it will try direct field
2471      * access if the property was not found.
2472      *
2473      @param bean  the target object on which the property will be set
2474      @param name  the name of the property to set
2475      @param value the value to be set
2476      *
2477      @throws PropertyException if the property could not be found
2478      @since 2.1.0
2479      */
2480     public static void setPropertyOrFieldValue(@Nonnull Object bean, @Nonnull String name, @Nullable Object valuethrows PropertyException {
2481         try {
2482             setPropertyValue(bean, name, value);
2483         catch (PropertyException pe) {
2484             try {
2485                 setFieldValue(bean, name, value);
2486             catch (FieldException fe) {
2487                 throw pe;
2488             }
2489         }
2490     }
2491 
2492     /**
2493      * Sets or updates an object's property.
2494      <p>
2495      * This method will attempt setting a property using a matching
2496      * {@code PropertyDescriptor}; next it will try direct field
2497      * access if the property was not found.
2498      *
2499      @param bean  the target object on which the property will be set
2500      @param name  the name of the property to set
2501      @param value the value to be set
2502      *
2503      @throws PropertyException if the property could not be found
2504      @since 2.4.0
2505      */
2506     public static void setPropertyOrFieldValueNoException(@Nonnull Object bean, @Nonnull String name, @Nullable Object value) {
2507         try {
2508             setPropertyOrFieldValue(bean, name, value);
2509         catch (PropertyException pe) {
2510             // ignore
2511         }
2512     }
2513 
2514     /**
2515      * Sets or updates an object's properties.
2516      <p>
2517      * This method will attempt setting a property using direct field
2518      * access; next it will try a {@code PropertyDescriptor} if a
2519      * matching field name was not found.
2520      *
2521      @param bean       the target object on which properties will be set
2522      @param properties names and values for properties to be set
2523      *
2524      @throws FieldException if the field could not be found
2525      @since 2.1.0
2526      */
2527     public static void setFieldsOrProperties(@Nonnull Object bean, @Nonnull Map<String, Object> propertiesthrows FieldException {
2528         requireNonNull(bean, ERROR_BEAN_NULL);
2529         requireNonNull(properties, ERROR_PROPERTIES_NULL);
2530         for (Map.Entry<String, Object> entry : properties.entrySet()) {
2531             setFieldOrPropertyValue(bean, entry.getKey(), entry.getValue());
2532         }
2533     }
2534 
2535     /**
2536      * Sets or updates an object's properties.
2537      <p>
2538      * This method will attempt setting a property using direct field
2539      * access; next it will try a {@code PropertyDescriptor} if a
2540      * matching field name was not found.
2541      *
2542      @param bean       the target object on which properties will be set
2543      @param properties names and values for properties to be set
2544      *
2545      @since 2.1.0
2546      */
2547     public static void setFieldsOrPropertiesNoException(@Nonnull Object bean, @Nonnull Map<String, Object> properties) {
2548         requireNonNull(bean, ERROR_BEAN_NULL);
2549         requireNonNull(properties, ERROR_PROPERTIES_NULL);
2550         for (Map.Entry<String, Object> entry : properties.entrySet()) {
2551             try {
2552                 setFieldOrPropertyValue(bean, entry.getKey(), entry.getValue());
2553             catch (FieldException pe) {
2554                 // ignore
2555             }
2556         }
2557     }
2558 
2559     /**
2560      * Sets or updates an object's property.
2561      <p>
2562      * This method will attempt setting a property using direct field
2563      * access; next it will try a {@code PropertyDescriptor} if a
2564      * matching field name was not found.
2565      *
2566      @param bean  the target object on which the property will be set
2567      @param name  the name of the property to set
2568      @param value the value to be set
2569      *
2570      @throws FieldException if the property could not be found
2571      @since 2.1.0
2572      */
2573     public static void setFieldOrPropertyValue(@Nonnull Object bean, @Nonnull String name, @Nullable Object valuethrows FieldException {
2574         try {
2575             setFieldValue(bean, name, value);
2576         catch (FieldException fe) {
2577             try {
2578                 setPropertyValue(bean, name, value);
2579             catch (PropertyException pe) {
2580                 throw fe;
2581             }
2582         }
2583     }
2584 
2585     /**
2586      * Sets or updates field values on an object.
2587      *
2588      @param bean   the target object on which field values will be set
2589      @param fields names and values of fields to be set
2590      *
2591      @throws FieldException if a field could not be found
2592      @since 2.1.0
2593      */
2594     public static void setFields(@Nonnull Object bean, @Nonnull Map<String, Object> fieldsthrows FieldException {
2595         requireNonNull(bean, ERROR_BEAN_NULL);
2596         requireNonNull(fields, ERROR_FIELDS_NULL);
2597         for (Map.Entry<String, Object> entry : fields.entrySet()) {
2598             setFieldValue(bean, entry.getKey(), entry.getValue());
2599         }
2600     }
2601 
2602     /**
2603      * Sets or updates field values on an object.
2604      *
2605      @param bean   the target object on which field values will be set
2606      @param fields names and values of fields to be set
2607      *
2608      @since 2.1.0
2609      */
2610     public static void setFieldsNoException(@Nonnull Object bean, @Nonnull Map<String, Object> fields) {
2611         requireNonNull(bean, ERROR_BEAN_NULL);
2612         requireNonNull(fields, ERROR_FIELDS_NULL);
2613         for (Map.Entry<String, Object> entry : fields.entrySet()) {
2614             try {
2615                 setFieldValue(bean, entry.getKey(), entry.getValue());
2616             catch (FieldException e) {
2617                 // ignore
2618             }
2619         }
2620     }
2621 
2622     /**
2623      * Sets or updates an object's field.
2624      *
2625      @param bean  the target object on which the field will be set
2626      @param name  the name of the field to set
2627      @param value the value to be set
2628      *
2629      @throws FieldException if the field could not be found
2630      @since 2.1.0
2631      */
2632     public static void setFieldValue(@Nonnull Object bean, @Nonnull String name, @Nullable Object valuethrows FieldException {
2633         requireNonNull(bean, ERROR_BEAN_NULL);
2634         requireNonBlank(name, ERROR_NAME_BLANK);
2635         try {
2636             setField(bean, name, value);
2637         catch (IllegalAccessException | NoSuchFieldException e) {
2638             throw new FieldException(bean, name, value, e);
2639         }
2640     }
2641 
2642     /**
2643      * Sets or updates an object's field.
2644      *
2645      @param bean  the target object on which the field will be set
2646      @param name  the name of the field to set
2647      @param value the value to be set
2648      *
2649      @throws FieldException if the field could not be found
2650      @since 2.4.0
2651      */
2652     public static void setFieldValueNoException(@Nonnull Object bean, @Nonnull String name, @Nullable Object value) {
2653         try {
2654             setFieldValue(bean, name, value);
2655         catch (FieldException e) {
2656             // ignore
2657         }
2658     }
2659 
2660     /**
2661      * Sets or updates properties on an object.
2662      *
2663      @param bean       the target object on which properties will be set
2664      @param properties names and values of properties to be set
2665      *
2666      @throws PropertyException if a property could not be found
2667      */
2668     public static void setProperties(@Nonnull Object bean, @Nonnull Map<String, Object> propertiesthrows PropertyException {
2669         requireNonNull(bean, ERROR_BEAN_NULL);
2670         requireNonNull(properties, ERROR_PROPERTIES_NULL);
2671         for (Map.Entry<String, Object> entry : properties.entrySet()) {
2672             setPropertyValue(bean, entry.getKey(), entry.getValue());
2673         }
2674     }
2675 
2676     /**
2677      * Sets or updates properties on an object.
2678      *
2679      @param bean       the target object on which properties will be set
2680      @param properties names and values of properties to be set
2681      */
2682     public static void setPropertiesNoException(@Nonnull Object bean, @Nonnull Map<String, Object> properties) {
2683         requireNonNull(bean, ERROR_BEAN_NULL);
2684         requireNonNull(properties, ERROR_PROPERTIES_NULL);
2685         for (Map.Entry<String, Object> entry : properties.entrySet()) {
2686             try {
2687                 setPropertyValue(bean, entry.getKey(), entry.getValue());
2688             catch (PropertyException e) {
2689                 // ignore
2690             }
2691         }
2692     }
2693 
2694     /**
2695      * /**
2696      * Sets or updates a property on an object.
2697      *
2698      @param bean  the target object on which the property will be set
2699      @param name  the name of the property to set
2700      @param value the value to be set
2701      *
2702      @throws PropertyException if the property could not be found
2703      */
2704     public static void setPropertyValue(@Nonnull Object bean, @Nonnull String name, @Nullable Object valuethrows PropertyException {
2705         requireNonNull(bean, ERROR_BEAN_NULL);
2706         requireNonBlank(name, ERROR_NAME_BLANK);
2707         try {
2708             setProperty(bean, name, value);
2709         catch (IllegalAccessException | NoSuchMethodException e) {
2710             throw new PropertyException(bean, name, value, e);
2711         catch (InvocationTargetException e) {
2712             throw new PropertyException(bean, name, value, e.getTargetException());
2713         }
2714     }
2715 
2716     /**
2717      * Returns the value of a property.
2718      *
2719      @param bean the owner of the property
2720      @param name the name of the property to retrieve
2721      *
2722      @return the value read from the matching property
2723      *
2724      @throws PropertyException if the property could not be found
2725      */
2726     @Nullable
2727     public static Object getPropertyValue(@Nonnull Object bean, @Nonnull String namethrows PropertyException {
2728         requireNonNull(bean, ERROR_BEAN_NULL);
2729         requireNonBlank(name, ERROR_NAME_BLANK);
2730         try {
2731             return getProperty(bean, name);
2732         catch (IllegalAccessException | NoSuchMethodException e) {
2733             throw new PropertyException(bean, name, e);
2734         catch (InvocationTargetException e) {
2735             throw new PropertyException(bean, name, e.getTargetException());
2736         }
2737     }
2738 
2739     // -- The following methods and properties were copied from commons-beanutils
2740 
2741     private static final Map<String, PropertyDescriptor[]> descriptorsCache = new LinkedHashMap<>();
2742 
2743     /**
2744      <p>Retrieve the property descriptor for the specified property of the
2745      * specified bean, or return <code>null</code> if there is no such
2746      * descriptor.</p>
2747      * This method does not resolve index, nested nor mapped properties.<p>
2748      *
2749      @param bean Bean for which a property descriptor is requested
2750      @param name name of the property for which a property descriptor
2751      *             is requested
2752      *
2753      @return the property descriptor or null if the bean does not have
2754      * a property that matches the specified name.
2755      *
2756      @throws IllegalAccessException    if the caller does not have
2757      *                                   access to the property accessor method
2758      @throws IllegalArgumentException  if <code>bean</code> or
2759      *                                   <code>name</code> is null
2760      @throws InvocationTargetException if the property accessor method
2761      *                                   throws an exception
2762      @throws NoSuchMethodException     if an accessor method for this
2763      *                                   property cannot be found
2764      */
2765     @Nullable
2766     public static PropertyDescriptor getPropertyDescriptor(@Nonnull Object bean,
2767                                                            @Nonnull String name)
2768         throws IllegalAccessException, InvocationTargetException,
2769         NoSuchMethodException {
2770         requireNonNull(bean, ERROR_BEAN_NULL);
2771         requireNonBlank(name, ERROR_NAME_BLANK);
2772 
2773         return getPropertyDescriptor(bean instanceof Class ? (Class<?>bean : bean.getClass(), name);
2774     }
2775 
2776     /**
2777      <p>Retrieve the property descriptor for the specified property of the
2778      * specified class, or return <code>null</code> if there is no such
2779      * descriptor.</p>
2780      * This method does not resolve index, nested nor mapped properties.<p>
2781      *
2782      @param clazz class for which a property descriptor is requested
2783      @param name  name of the property for which a property descriptor
2784      *              is requested
2785      *
2786      @return the property descriptor or null if the bean does not have
2787      * a property that matches the specified name.
2788      *
2789      @throws IllegalAccessException    if the caller does not have
2790      *                                   access to the property accessor method
2791      @throws IllegalArgumentException  if <code>bean</code> or
2792      *                                   <code>name</code> is null
2793      @throws InvocationTargetException if the property accessor method
2794      *                                   throws an exception
2795      @throws NoSuchMethodException     if an accessor method for this
2796      *                                   property cannot be found
2797      */
2798     @Nullable
2799     public static PropertyDescriptor getPropertyDescriptor(@Nonnull Class<?> clazz,
2800                                                            @Nonnull String name)
2801         throws IllegalAccessException, InvocationTargetException,
2802         NoSuchMethodException {
2803         requireNonNull(clazz, ERROR_CLAZZ_NULL);
2804         requireNonBlank(name, ERROR_NAME_BLANK);
2805 
2806         PropertyDescriptor[] descriptors = getPropertyDescriptors(clazz);
2807         for (PropertyDescriptor descriptor : descriptors) {
2808             if (name.equals(descriptor.getName())) {
2809                 return (descriptor);
2810             }
2811         }
2812 
2813         return null;
2814     }
2815 
2816     /**
2817      <p>Retrieve the property descriptors for the specified class,
2818      * introspecting and caching them the first time a particular bean class
2819      * is encountered.</p>
2820      *
2821      @param beanClass Bean class for which property descriptors are requested
2822      *
2823      @return the property descriptors
2824      *
2825      @throws IllegalArgumentException if <code>beanClass</code> is null
2826      */
2827     @Nonnull
2828     public static PropertyDescriptor[] getPropertyDescriptors(@Nonnull Class<?> beanClass) {
2829         requireNonNull(beanClass, ERROR_CLAZZ_NULL);
2830 
2831         // Look up any cached descriptors for this bean class
2832         PropertyDescriptor[] descriptors;
2833         descriptors = descriptorsCache.get(beanClass.getName());
2834         if (descriptors != null) {
2835             return descriptors;
2836         }
2837 
2838         // Introspect the bean and cache the generated descriptors
2839         BeanInfo beanInfo;
2840         try {
2841             beanInfo = Introspector.getBeanInfo(beanClass);
2842         catch (IntrospectionException e) {
2843             return (new PropertyDescriptor[0]);
2844         }
2845         descriptors = beanInfo.getPropertyDescriptors();
2846         if (descriptors == null) {
2847             descriptors = new PropertyDescriptor[0];
2848         }
2849 
2850         descriptorsCache.put(beanClass.getName(), descriptors);
2851         return descriptors;
2852     }
2853 
2854     /**
2855      <p>Return an accessible property getter method for this property,
2856      * if there is one; otherwise return <code>null</code>.</p>
2857      *
2858      @param descriptor Property descriptor to return a getter for
2859      *
2860      @return The read method
2861      */
2862     @Nullable
2863     public static Method getReadMethod(@Nonnull PropertyDescriptor descriptor) {
2864         requireNonNull(descriptor, ERROR_DESCRIPTOR_NULL);
2865         return (MethodUtils.getAccessibleMethod(descriptor.getReadMethod()));
2866     }
2867 
2868     /**
2869      <p>Return <code>true</code> if the specified property name identifies
2870      * a readable property on the specified bean; otherwise, return
2871      <code>false</code>.
2872      *
2873      @param bean Bean to be examined
2874      @param name Property name to be evaluated
2875      *
2876      @return <code>true</code> if the property is readable,
2877      * otherwise <code>false</code>
2878      *
2879      @throws IllegalArgumentException if <code>bean</code>
2880      *                                  or <code>name</code> is <code>null</code>
2881      @since BeanUtils 1.6
2882      */
2883     public static boolean isReadable(@Nonnull Object bean, @Nonnull String name) {
2884         // Validate method parameters
2885         requireNonNull(bean, ERROR_BEAN_NULL);
2886         requireNonBlank(name, ERROR_NAME_BLANK);
2887 
2888         try {
2889             PropertyDescriptor desc = getPropertyDescriptor(bean, name);
2890             if (desc != null) {
2891                 Method readMethod = getReadMethod(bean.getClass(), desc);
2892                 if (readMethod != null) {
2893                     readMethod = MethodUtils.getAccessibleMethod(bean.getClass(), readMethod);
2894                 }
2895                 return (readMethod != null);
2896             else {
2897                 return false;
2898             }
2899         catch (IllegalAccessException e) {
2900             return false;
2901         catch (InvocationTargetException e) {
2902             return false;
2903         catch (NoSuchMethodException e) {
2904             return false;
2905         }
2906     }
2907 
2908     /**
2909      <p>Return an accessible property setter method for this property,
2910      * if there is one; otherwise return <code>null</code>.</p>
2911      *
2912      @param descriptor Property descriptor to return a setter for
2913      *
2914      @return The write method
2915      */
2916     @Nullable
2917     public static Method getWriteMethod(@Nonnull PropertyDescriptor descriptor) {
2918         requireNonNull(descriptor, ERROR_DESCRIPTOR_NULL);
2919         return (MethodUtils.getAccessibleMethod(descriptor.getWriteMethod()));
2920     }
2921 
2922     /**
2923      <p>Return <code>true</code> if the specified property name identifies
2924      * a writable property on the specified bean; otherwise, return
2925      <code>false</code>.
2926      *
2927      @param bean Bean to be examined
2928      @param name Property name to be evaluated
2929      *
2930      @return <code>true</code> if the property is writable,
2931      * otherwise <code>false</code>
2932      *
2933      @throws IllegalArgumentException if <code>bean</code>
2934      *                                  or <code>name</code> is <code>null</code>
2935      */
2936     public static boolean isWritable(@Nonnull Object bean, @Nonnull String name) {
2937         // Validate method parameters
2938         requireNonNull(bean, ERROR_BEAN_NULL);
2939         requireNonBlank(name, ERROR_NAME_BLANK);
2940 
2941         try {
2942             PropertyDescriptor desc = getPropertyDescriptor(bean, name);
2943             if (desc != null) {
2944                 Method writeMethod = getWriteMethod(bean.getClass(), desc);
2945                 if (writeMethod != null) {
2946                     writeMethod = MethodUtils.getAccessibleMethod(bean.getClass(), writeMethod);
2947                 }
2948                 return (writeMethod != null);
2949             else {
2950                 return false;
2951             }
2952         catch (IllegalAccessException e) {
2953             return false;
2954         catch (InvocationTargetException e) {
2955             return false;
2956         catch (NoSuchMethodException e) {
2957             return false;
2958         }
2959     }
2960 
2961     /**
2962      * Sets the value of the specified field of the specified bean.
2963      *
2964      @param bean  Bean whose field is to be mutated
2965      @param name  Name of the field to be mutated
2966      @param value The value to be set on the property
2967      *
2968      @throws IllegalAccessException   if the caller does not have
2969      *                                  access to the field
2970      @throws IllegalArgumentException if <code>bean</code> or
2971      *                                  <code>name</code> is null
2972      @throws NoSuchFieldException     if the named field cannot be found
2973      @throws FieldException           if the field cannot be set
2974      @since 2.1.0
2975      */
2976     public static void setField(@Nonnull Object bean, @Nonnull String name, @Nullable Object value)
2977         throws NoSuchFieldException, IllegalAccessException, FieldException {
2978         requireNonNull(bean, ERROR_BEAN_NULL);
2979         requireNonBlank(name, ERROR_NAME_BLANK);
2980 
2981         Class<?> declaringClass = bean.getClass();
2982         while (declaringClass != null) {
2983             try {
2984                 Field field = declaringClass.getDeclaredField(name);
2985 
2986                 // type conversion needed?
2987                 Class<?> propertyType = field.getType();
2988                 if (value != null && !propertyType.isAssignableFrom(value.getClass())) {
2989                     value = TypeUtils.convertValue(propertyType, value);
2990                 }
2991 
2992                 field.setAccessible(true);
2993                 try {
2994                     field.set(bean, value);
2995                     return;
2996                 catch (IllegalArgumentException iae) {
2997                     throw new FieldException(bean, name, value, iae);
2998                 }
2999             catch (NoSuchFieldException nsfe) {
3000                 declaringClass = declaringClass.getSuperclass();
3001             }
3002         }
3003         throw new NoSuchFieldException(name);
3004     }
3005 
3006     /**
3007      * Sets the value of the specified property of the specified bean.
3008      *
3009      @param bean  Bean whose property is to be mutated
3010      @param name  Name of the property to be mutated
3011      @param value The value to be set on the property
3012      *
3013      @throws IllegalAccessException    if the caller does not have
3014      *                                   access to the property accessor method
3015      @throws IllegalArgumentException  if <code>bean</code> or
3016      *                                   <code>name</code> is null
3017      @throws InvocationTargetException if the property accessor method
3018      *                                   throws an exception
3019      @throws NoSuchMethodException     if an accessor method for this
3020      *                                   property cannot be found
3021      @throws PropertyException         if the property cannot be set
3022      */
3023     public static void setProperty(@Nonnull Object bean, @Nonnull String name, @Nullable Object value)
3024         throws IllegalAccessException, InvocationTargetException,
3025         NoSuchMethodException, PropertyException {
3026         requireNonNull(bean, ERROR_BEAN_NULL);
3027         requireNonBlank(name, ERROR_NAME_BLANK);
3028 
3029         // Retrieve the property setter method for the specified property
3030         PropertyDescriptor descriptor = getPropertyDescriptor(bean, name);
3031         if (descriptor == null) {
3032             throw new NoSuchMethodException("Unknown property '" +
3033                 name + "' on class '" + bean.getClass() "'");
3034         }
3035         Method writeMethod = getWriteMethod(bean.getClass(), descriptor);
3036         if (writeMethod == null) {
3037             throw new NoSuchMethodException("Property '" + name +
3038                 "' has no setter method in class '" + bean.getClass() "'");
3039         }
3040 
3041         // type conversion needed?
3042         Class<?> propertyType = descriptor.getPropertyType();
3043         if (value != null && !propertyType.isAssignableFrom(value.getClass())) {
3044             value = TypeUtils.convertValue(propertyType, value);
3045         }
3046 
3047         // Call the property setter
3048         try {
3049             writeMethod.invoke(bean, value);
3050         catch (IllegalArgumentException iae) {
3051             throw new PropertyException(bean, name, value, iae);
3052         }
3053     }
3054 
3055     /**
3056      * Return the value of the specified property of the specified bean,
3057      * no matter which property reference format is used, with no
3058      * type conversions.
3059      *
3060      @param bean Bean whose property is to be extracted
3061      @param name Possibly indexed and/or nested name of the property
3062      *             to be extracted
3063      *
3064      @return the property value
3065      *
3066      @throws IllegalAccessException    if the caller does not have
3067      *                                   access to the property accessor method
3068      @throws IllegalArgumentException  if <code>bean</code> or
3069      *                                   <code>name</code> is null
3070      @throws InvocationTargetException if the property accessor method
3071      *                                   throws an exception
3072      @throws NoSuchMethodException     if an accessor method for this
3073      *                                   property cannot be found
3074      */
3075     @Nullable
3076     public static Object getProperty(@Nonnull Object bean, @Nonnull String name)
3077         throws IllegalAccessException, InvocationTargetException,
3078         NoSuchMethodException {
3079         requireNonNull(bean, ERROR_BEAN_NULL);
3080         requireNonBlank(name, ERROR_NAME_BLANK);
3081 
3082         // Retrieve the property getter method for the specified property
3083         PropertyDescriptor descriptor = getPropertyDescriptor(bean, name);
3084         if (descriptor == null) {
3085             throw new NoSuchMethodException("Unknown property '" +
3086                 name + "' on class '" + bean.getClass() "'");
3087         }
3088         Method readMethod = getReadMethod(bean.getClass(), descriptor);
3089         if (readMethod == null) {
3090             throw new NoSuchMethodException("Property '" + name +
3091                 "' has no getter method in class '" + bean.getClass() "'");
3092         }
3093 
3094         // Call the property getter and return the value
3095         return readMethod.invoke(bean, EMPTY_OBJECT_ARRAY);
3096     }
3097 
3098     /**
3099      <p>Return an accessible property getter method for this property,
3100      * if there is one; otherwise return <code>null</code>.</p>
3101      *
3102      @param clazz      The class of the read method will be invoked on
3103      @param descriptor Property descriptor to return a getter for
3104      *
3105      @return The read method
3106      */
3107     @Nullable
3108     public static Method getReadMethod(@Nonnull Class<?> clazz, @Nonnull PropertyDescriptor descriptor) {
3109         requireNonNull(clazz, ERROR_CLAZZ_NULL);
3110         requireNonNull(descriptor, ERROR_DESCRIPTOR_NULL);
3111         return (MethodUtils.getAccessibleMethod(clazz, descriptor.getReadMethod()));
3112     }
3113 
3114     /**
3115      <p>Return an accessible property setter method for this property,
3116      * if there is one; otherwise return <code>null</code>.</p>
3117      *
3118      @param clazz      The class of the write method will be invoked on
3119      @param descriptor Property descriptor to return a setter for
3120      *
3121      @return The write method
3122      */
3123     @Nullable
3124     public static Method getWriteMethod(@Nonnull Class<?> clazz, @Nonnull PropertyDescriptor descriptor) {
3125         requireNonNull(clazz, ERROR_CLAZZ_NULL);
3126         requireNonNull(descriptor, ERROR_DESCRIPTOR_NULL);
3127         return (MethodUtils.getAccessibleMethod(clazz, descriptor.getWriteMethod()));
3128     }
3129 
3130     // -- The following methods and properties were copied from commons-lang
3131 
3132     /**
3133      <p>Validate that the argument condition is <code>true</code>; otherwise
3134      * throwing an exception with the specified message. This method is useful when
3135      * validating according to an arbitrary boolean expression, such as validating a
3136      * primitive number or using your own custom validation expression.</p>
3137      <p>
3138      <pre>
3139      * isTrue( (i > 0), "The value must be greater than zero");
3140      * isTrue( myObject.isOk(), "The object is not OK");
3141      </pre>
3142      *
3143      @param expression the boolean expression to check
3144      @param message    the exception message if invalid
3145      *
3146      @throws IllegalArgumentException if expression is <code>false</code>
3147      */
3148     public static void isTrue(boolean expression, String message) {
3149         if (expression) {
3150             throw new IllegalArgumentException(message);
3151         }
3152     }
3153 
3154     @Nullable
3155     public static Object invokeInstanceMethod(@Nonnull Object object, @Nonnull String methodName) {
3156         return invokeInstanceMethod(object, methodName, EMPTY_ARGS);
3157     }
3158 
3159     @Nullable
3160     public static Object invokeInstanceMethod(@Nonnull Object object, @Nonnull String methodName, Object arg) {
3161         return invokeInstanceMethod(object, methodName, new Object[]{arg});
3162     }
3163 
3164     @Nullable
3165     public static Object invokeInstanceMethod(@Nonnull Object object, @Nonnull String methodName, Object... args) {
3166         requireNonNull(object, ERROR_OBJECT_NULL);
3167         requireNonBlank(methodName, ERROR_METHOD_NAME_BLANK);
3168         try {
3169             return invokeMethod(object, methodName, args);
3170         catch (NoSuchMethodException | IllegalAccessException e) {
3171             throw new InstanceMethodInvocationException(object, methodName, args, e);
3172         catch (InvocationTargetException e) {
3173             throw new InstanceMethodInvocationException(object, methodName, args, e.getTargetException());
3174         }
3175     }
3176 
3177     @Nullable
3178     public static Object invokeExactInstanceMethod(@Nonnull Object object, @Nonnull String methodName) {
3179         return invokeExactInstanceMethod(object, methodName, EMPTY_ARGS);
3180     }
3181 
3182     @Nullable
3183     public static Object invokeExactInstanceMethod(@Nonnull Object object, @Nonnull String methodName, Object arg) {
3184         return invokeExactInstanceMethod(object, methodName, new Object[]{arg});
3185     }
3186 
3187     @Nullable
3188     public static Object invokeExactInstanceMethod(@Nonnull Object object, @Nonnull String methodName, Object... args) {
3189         requireNonNull(object, ERROR_OBJECT_NULL);
3190         requireNonBlank(methodName, ERROR_METHOD_NAME_BLANK);
3191         try {
3192             return invokeExactMethod(object, methodName, args);
3193         catch (NoSuchMethodException | IllegalAccessException e) {
3194             throw new InstanceMethodInvocationException(object, methodName, args, e);
3195         catch (InvocationTargetException e) {
3196             throw new InstanceMethodInvocationException(object, methodName, args, e.getTargetException());
3197         }
3198     }
3199 
3200     @Nullable
3201     public static Object invokeStaticMethod(@Nonnull Class<?> type, @Nonnull String methodName) {
3202         return invokeStaticMethod(type, methodName, EMPTY_ARGS);
3203     }
3204 
3205     @Nullable
3206     public static Object invokeStaticMethod(@Nonnull Class<?> type, @Nonnull String methodName, Object arg) {
3207         return invokeStaticMethod(type, methodName, new Object[]{arg});
3208     }
3209 
3210     @Nullable
3211     public static Object invokeStaticMethod(@Nonnull Class<?> type, @Nonnull String methodName, Object... args) {
3212         requireNonNull(type, ERROR_TYPE_NULL);
3213         requireNonBlank(methodName, ERROR_METHOD_NAME_BLANK);
3214         try {
3215             return MethodUtils.invokeStaticMethod(type, methodName, args);
3216         catch (NoSuchMethodException | IllegalAccessException e) {
3217             throw new StaticMethodInvocationException(type, methodName, args, e);
3218         catch (InvocationTargetException e) {
3219             throw new StaticMethodInvocationException(type, methodName, args, e.getTargetException());
3220         }
3221     }
3222 
3223     @Nullable
3224     public static Object invokeExactStaticMethod(@Nonnull Class<?> type, @Nonnull String methodName) {
3225         return invokeExactStaticMethod(type, methodName, EMPTY_ARGS);
3226     }
3227 
3228     @Nullable
3229     public static Object invokeExactStaticMethod(@Nonnull Class<?> type, @Nonnull String methodName, Object arg) {
3230         return invokeExactStaticMethod(type, methodName, new Object[]{arg});
3231     }
3232 
3233     @Nullable
3234     public static Object invokeExactStaticMethod(@Nonnull Class<?> type, @Nonnull String methodName, Object... args) {
3235         requireNonNull(type, ERROR_TYPE_NULL);
3236         requireNonBlank(methodName, ERROR_METHOD_NAME_BLANK);
3237         try {
3238             return MethodUtils.invokeExactStaticMethod(type, methodName, args);
3239         catch (NoSuchMethodException | IllegalAccessException e) {
3240             throw new StaticMethodInvocationException(type, methodName, args, e);
3241         catch (InvocationTargetException e) {
3242             throw new StaticMethodInvocationException(type, methodName, args, e.getTargetException());
3243         }
3244     }
3245 
3246     public static boolean hasMethodAnnotatedwith(@Nonnull final Object instance, @Nonnull final Class<? extends Annotation> annotation) {
3247         Class<?> klass = instance.getClass();
3248         while (klass != null) {
3249             for (Method method : klass.getDeclaredMethods()) {
3250                 if (method.isAnnotationPresent(annotation&&
3251                     method.getParameterTypes().length == 0) {
3252                     return true;
3253                 }
3254             }
3255 
3256             klass = klass.getSuperclass();
3257         }
3258 
3259         return false;
3260     }
3261 
3262     public static void invokeAnnotatedMethod(@Nonnull final Object instance, @Nonnull final Class<? extends Annotation> annotation) {
3263         List<Method> methods = new ArrayList<>();
3264         Class<?> klass = instance.getClass();
3265         while (klass != null) {
3266             boolean found = false;
3267             for (Method method : klass.getDeclaredMethods()) {
3268                 if (method.isAnnotationPresent(annotation&&
3269                     method.getParameterTypes().length == 0) {
3270                     if (found) {
3271                         throw new InstanceMethodInvocationException(instance, method, buildCause(instance.getClass(), method, methods, annotation));
3272                     }
3273                     methods.add(method);
3274                     found = true;
3275                 }
3276             }
3277 
3278             klass = klass.getSuperclass();
3279         }
3280 
3281         for (final Method method : methods) {
3282             AccessController.doPrivileged(new PrivilegedAction<Object>() {
3283                 @Override
3284                 public Object run() {
3285                     boolean wasAccessible = method.isAccessible();
3286                     try {
3287                         method.setAccessible(true);
3288                         return method.invoke(instance);
3289                     catch (IllegalAccessException | IllegalArgumentException e) {
3290                         throw new InstanceMethodInvocationException(instance, method.getName(), null, e);
3291                     catch (InvocationTargetException e) {
3292                         throw new InstanceMethodInvocationException(instance, method.getName(), null, e.getTargetException());
3293                     finally {
3294                         method.setAccessible(wasAccessible);
3295                     }
3296                 }
3297             });
3298         }
3299     }
3300 
3301     @Nonnull
3302     private static Throwable buildCause(@Nonnull Class<?> clazz, @Nonnull Method method, @Nonnull List<Method> methods, @Nonnull final Class<? extends Annotation> annotation) {
3303         StringBuilder b = new StringBuilder("The following methods were found annotated with @" + annotation.getSimpleName() " on ")
3304             .append(clazz);
3305         for (Method m : methods) {
3306             b.append("\n  ").append(m.toGenericString());
3307         }
3308         b.append("\n  ").append(method.toGenericString());
3309         return new IllegalStateException(b.toString());
3310     }
3311 
3312     private static final String EMPTY_STRING = "";
3313 
3314     /**
3315      <p>The package separator character: <code>'&#x2e;' == {@value}</code>.</p>
3316      */
3317     public static final char PACKAGE_SEPARATOR_CHAR = '.';
3318 
3319     /**
3320      <p>The package separator String: <code>"&#x2e;"</code>.</p>
3321      */
3322     public static final String PACKAGE_SEPARATOR = String.valueOf(PACKAGE_SEPARATOR_CHAR);
3323 
3324     /**
3325      <p>The inner class separator character: <code>'$' == {@value}</code>.</p>
3326      */
3327     public static final char INNER_CLASS_SEPARATOR_CHAR = '$';
3328 
3329     /**
3330      <p>The inner class separator String: <code>"$"</code>.</p>
3331      */
3332     public static final String INNER_CLASS_SEPARATOR = String.valueOf(INNER_CLASS_SEPARATOR_CHAR);
3333 
3334     /**
3335      * Maps a primitive class name to its corresponding abbreviation used in array class names.
3336      */
3337     private static final Map<String, String> abbreviationMap = new HashMap<>();
3338 
3339     /**
3340      * Maps an abbreviation used in array class names to corresponding primitive class name.
3341      */
3342     private static final Map<String, String> reverseAbbreviationMap = new HashMap<>();
3343 
3344     /**
3345      * Add primitive type abbreviation to maps of abbreviations.
3346      *
3347      @param primitive    Canonical name of primitive type
3348      @param abbreviation Corresponding abbreviation of primitive type
3349      */
3350     private static void addAbbreviation(String primitive, String abbreviation) {
3351         abbreviationMap.put(primitive, abbreviation);
3352         reverseAbbreviationMap.put(abbreviation, primitive);
3353     }
3354 
3355     /**
3356      * Feed abbreviation maps
3357      */
3358     static {
3359         addAbbreviation("int""I");
3360         addAbbreviation("boolean""Z");
3361         addAbbreviation("float""F");
3362         addAbbreviation("long""J");
3363         addAbbreviation("short""S");
3364         addAbbreviation("byte""B");
3365         addAbbreviation("double""D");
3366         addAbbreviation("char""C");
3367     }
3368 
3369     // ----------------------------------------------------------------------
3370 
3371     /**
3372      <p>Gets the class name minus the package name for an <code>Object</code>.</p>
3373      *
3374      @param object      the class to get the short name for, may be null
3375      @param valueIfNull the value to return if null
3376      *
3377      @return the class name of the object without the package name, or the null value
3378      */
3379     @Nonnull
3380     public static String getShortClassName(@Nullable Object object, @Nonnull String valueIfNull) {
3381         if (object == null) {
3382             return valueIfNull;
3383         }
3384         return getShortClassName(object.getClass());
3385     }
3386 
3387     /**
3388      <p>Gets the class name minus the package name from a <code>Class</code>.</p>
3389      *
3390      @param cls the class to get the short name for.
3391      *
3392      @return the class name without the package name or an empty string
3393      */
3394     @Nonnull
3395     public static String getShortClassName(@Nullable Class<?> cls) {
3396         if (cls == null) {
3397             return EMPTY_STRING;
3398         }
3399         return getShortClassName(cls.getName());
3400     }
3401 
3402     /**
3403      <p>Gets the class name minus the package name from a String.</p>
3404      <p/>
3405      <p>The string passed in is assumed to be a class name - it is not checked.</p>
3406      *
3407      @param className the className to get the short name for
3408      *
3409      @return the class name of the class without the package name or an empty string
3410      */
3411     @Nonnull
3412     public static String getShortClassName(@Nullable String className) {
3413         if (className == null) {
3414             return EMPTY_STRING;
3415         }
3416         if (className.length() == 0) {
3417             return EMPTY_STRING;
3418         }
3419 
3420         StringBuilder arrayPrefix = new StringBuilder();
3421 
3422         // Handle array encoding
3423         if (className.startsWith("[")) {
3424             while (className.charAt(0== '[') {
3425                 className = className.substring(1);
3426                 arrayPrefix.append("[]");
3427             }
3428             // Strip Object type encoding
3429             if (className.charAt(0== 'L' && className.charAt(className.length() 1== ';') {
3430                 className = className.substring(1, className.length() 1);
3431             }
3432         }
3433 
3434         if (reverseAbbreviationMap.containsKey(className)) {
3435             className = reverseAbbreviationMap.get(className);
3436         }
3437 
3438         int lastDotIdx = className.lastIndexOf(PACKAGE_SEPARATOR_CHAR);
3439         int innerIdx = className.indexOf(
3440             INNER_CLASS_SEPARATOR_CHAR, lastDotIdx == -: lastDotIdx + 1);
3441         String out = className.substring(lastDotIdx + 1);
3442         if (innerIdx != -1) {
3443             out = out.replace(INNER_CLASS_SEPARATOR_CHAR, PACKAGE_SEPARATOR_CHAR);
3444         }
3445         return out + arrayPrefix;
3446     }
3447 
3448     // Package name
3449     // ----------------------------------------------------------------------
3450 
3451     /**
3452      <p>Gets the package name of an <code>Object</code>.</p>
3453      *
3454      @param object      the class to get the package name for, may be null
3455      @param valueIfNull the value to return if null
3456      *
3457      @return the package name of the object, or the null value
3458      */
3459     @Nonnull
3460     public static String getPackageName(@Nullable Object object, @Nonnull String valueIfNull) {
3461         if (object == null) {
3462             return valueIfNull;
3463         }
3464         return getPackageName(object.getClass());
3465     }
3466 
3467     /**
3468      <p>Gets the package name of a <code>Class</code>.</p>
3469      *
3470      @param cls the class to get the package name for, may be <code>null</code>.
3471      *
3472      @return the package name or an empty string
3473      */
3474     @Nonnull
3475     public static String getPackageName(@Nullable Class<?> cls) {
3476         if (cls == null) {
3477             return EMPTY_STRING;
3478         }
3479         return getPackageName(cls.getName());
3480     }
3481 
3482     /**
3483      <p>Gets the package name from a <code>String</code>.</p>
3484      <p/>
3485      <p>The string passed in is assumed to be a class name - it is not checked.</p>
3486      <p>If the class is unpackaged, return an empty string.</p>
3487      *
3488      @param className the className to get the package name for, may be <code>null</code>
3489      *
3490      @return the package name or an empty string
3491      */
3492     @Nonnull
3493     public static String getPackageName(@Nullable String className) {
3494         if (className == null || className.length() == 0) {
3495             return EMPTY_STRING;
3496         }
3497 
3498         // Strip array encoding
3499         while (className.charAt(0== '[') {
3500             className = className.substring(1);
3501         }
3502         // Strip Object type encoding
3503         if (className.charAt(0== 'L' && className.charAt(className.length() 1== ';') {
3504             className = className.substring(1);
3505         }
3506 
3507         int i = className.lastIndexOf(PACKAGE_SEPARATOR_CHAR);
3508         if (i == -1) {
3509             return EMPTY_STRING;
3510         }
3511         return className.substring(0, i);
3512     }
3513 
3514     /**
3515      * param instance array to the type array
3516      *
3517      @param args the arguments
3518      *
3519      @return the types of the arguments
3520      */
3521     @Nullable
3522     public static Class<?>[] convertToTypeArray(@Nullable Object[] args) {
3523         if (args == null) {
3524             return null;
3525         }
3526         int s = args.length;
3527         Class<?>[] ans = new Class<?>[s];
3528         for (int i = 0; i < s; i++) {
3529             Object o = args[i];
3530             ans[i= o != null ? o.getClass() null;
3531         }
3532         return ans;
3533     }
3534 }