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