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