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