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