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