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