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