GriffonClassUtils.java
0001 /* 
0002  * Copyright 2004-2012 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.MVCClosure;
0019 import griffon.exceptions.BeanException;
0020 import griffon.exceptions.BeanInstantiationException;
0021 import groovy.lang.*;
0022 import groovy.util.FactoryBuilderSupport;
0023 import org.codehaus.groovy.reflection.CachedClass;
0024 
0025 import java.beans.*;
0026 import java.lang.reflect.Field;
0027 import java.lang.reflect.InvocationTargetException;
0028 import java.lang.reflect.Method;
0029 import java.lang.reflect.Modifier;
0030 import java.util.*;
0031 import java.util.concurrent.Callable;
0032 import java.util.concurrent.ExecutorService;
0033 import java.util.regex.Pattern;
0034 
0035 import static griffon.util.MethodUtils.invokeExactMethod;
0036 import static griffon.util.MethodUtils.invokeMethod;
0037 
0038 /**
0039  * Class containing utility methods for dealing with Griffon class artifacts.<p>
0040  * Contains utility methods copied from commons-lang and commons-beanutils in order
0041  * to reduce dependencies on external libraries.<p>
0042  <p/>
0043  <b>Contains code copied from commons-beanutils and commons-langs</b>
0044  *
0045  @author Graeme Rocher (Grails 0.1)
0046  */
0047 public final class GriffonClassUtils {
0048     public static final Class[] EMPTY_CLASS_ARRAY = new Class[0];
0049     public static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
0050     public static final Class[] EMPTY_TYPES = EMPTY_CLASS_ARRAY;
0051     public static final Object[] EMPTY_ARGS = EMPTY_OBJECT_ARRAY;
0052 
0053     private static final String PROPERTY_GET_PREFIX = "get";
0054     private static final String PROPERTY_IS_PREFIX = "is";
0055     private static final String PROPERTY_SET_PREFIX = "set";
0056     public static final Map<Class, Class> PRIMITIVE_TYPE_COMPATIBLE_CLASSES = new HashMap<Class, Class>();
0057 
0058     private static final Pattern EVENT_HANDLER_PATTERN = Pattern.compile("^on[A-Z][\\w]*$");
0059     private static final Pattern GETTER_PATTERN_1 = Pattern.compile("^get[A-Z][\\w]*$");
0060     private static final Pattern GETTER_PATTERN_2 = Pattern.compile("^is[A-Z][\\w]*$");
0061     private static final Pattern SETTER_PATTERN = Pattern.compile("^set[A-Z][\\w]*$");
0062     private static final Set<MethodDescriptor> BASIC_METHODS = new TreeSet<MethodDescriptor>();
0063     private static final Set<MethodDescriptor> MVC_METHODS = new TreeSet<MethodDescriptor>();
0064     private static final Set<MethodDescriptor> THREADING_METHODS = new TreeSet<MethodDescriptor>();
0065     private static final Set<MethodDescriptor> EVENT_PUBLISHER_METHODS = new TreeSet<MethodDescriptor>();
0066     private static final Set<MethodDescriptor> OBSERVABLE_METHODS = new TreeSet<MethodDescriptor>();
0067 
0068     /**
0069      * Just add two entries to the class compatibility map
0070      *
0071      @param left
0072      @param right
0073      */
0074     private static void registerPrimitiveClassPair(Class<?> left, Class<?> right) {
0075         PRIMITIVE_TYPE_COMPATIBLE_CLASSES.put(left, right);
0076         PRIMITIVE_TYPE_COMPATIBLE_CLASSES.put(right, left);
0077     }
0078 
0079     static {
0080         registerPrimitiveClassPair(Boolean.class, boolean.class);
0081         registerPrimitiveClassPair(Integer.class, int.class);
0082         registerPrimitiveClassPair(Short.class, short.class);
0083         registerPrimitiveClassPair(Byte.class, byte.class);
0084         registerPrimitiveClassPair(Character.class, char.class);
0085         registerPrimitiveClassPair(Long.class, long.class);
0086         registerPrimitiveClassPair(Float.class, float.class);
0087         registerPrimitiveClassPair(Double.class, double.class);
0088 
0089         for (Method method : GroovyObject.class.getMethods()) {
0090             MethodDescriptor md = MethodDescriptor.forMethod(method);
0091             if (!BASIC_METHODS.contains(md)) {
0092                 BASIC_METHODS.add(md);
0093             }
0094         }
0095         for (Method method : GroovyObjectSupport.class.getMethods()) {
0096             MethodDescriptor md = MethodDescriptor.forMethod(method);
0097             if (!BASIC_METHODS.contains(md)) {
0098                 BASIC_METHODS.add(md);
0099             }
0100         }
0101         for (Method method : Object.class.getMethods()) {
0102             MethodDescriptor md = MethodDescriptor.forMethod(method);
0103             if (!BASIC_METHODS.contains(md)) {
0104                 BASIC_METHODS.add(md);
0105             }
0106         }
0107 
0108         MVC_METHODS.add(new MethodDescriptor("mvcGroupInit"new Class[]{Map.class}));
0109         MVC_METHODS.add(new MethodDescriptor("mvcGroupDestroy"));
0110         MVC_METHODS.add(new MethodDescriptor("newInstance"new Class[]{Class.class, String.class}));
0111         MVC_METHODS.add(new MethodDescriptor("buildMVCGroup"new Class[]{String.class}));
0112         MVC_METHODS.add(new MethodDescriptor("buildMVCGroup"new Class[]{String.class, Map.class}));
0113         MVC_METHODS.add(new MethodDescriptor("buildMVCGroup"new Class[]{Map.class, String.class}));
0114         MVC_METHODS.add(new MethodDescriptor("buildMVCGroup"new Class[]{String.class, String.class}));
0115         MVC_METHODS.add(new MethodDescriptor("buildMVCGroup"new Class[]{String.class, String.class, Map.class}));
0116         MVC_METHODS.add(new MethodDescriptor("buildMVCGroup"new Class[]{Map.class, String.class, String.class}));
0117         MVC_METHODS.add(new MethodDescriptor("createMVCGroup"new Class[]{String.class}));
0118         MVC_METHODS.add(new MethodDescriptor("createMVCGroup"new Class[]{String.class, Map.class}));
0119         MVC_METHODS.add(new MethodDescriptor("createMVCGroup"new Class[]{Map.class, String.class}));
0120         MVC_METHODS.add(new MethodDescriptor("createMVCGroup"new Class[]{String.class, String.class}));
0121         MVC_METHODS.add(new MethodDescriptor("createMVCGroup"new Class[]{String.class, String.class, Map.class}));
0122         MVC_METHODS.add(new MethodDescriptor("createMVCGroup"new Class[]{Map.class, String.class, String.class}));
0123         MVC_METHODS.add(new MethodDescriptor("destroyMVCGroup"new Class[]{String.class}));
0124         MVC_METHODS.add(new MethodDescriptor("withMVCGroup"new Class[]{String.class, Closure.class}));
0125         MVC_METHODS.add(new MethodDescriptor("withMVCGroup"new Class[]{String.class, Map.class, Closure.class}));
0126         MVC_METHODS.add(new MethodDescriptor("withMVCGroup"new Class[]{Map.class, String.class, Closure.class}));
0127         MVC_METHODS.add(new MethodDescriptor("withMVCGroup"new Class[]{String.class, String.class, Closure.class}));
0128         MVC_METHODS.add(new MethodDescriptor("withMVCGroup"new Class[]{String.class, String.class, Map.class, Closure.class}));
0129         MVC_METHODS.add(new MethodDescriptor("withMVCGroup"new Class[]{Map.class, String.class, String.class, Closure.class}));
0130         MVC_METHODS.add(new MethodDescriptor("withMVCGroup"new Class[]{String.class, MVCClosure.class}));
0131         MVC_METHODS.add(new MethodDescriptor("withMVCGroup"new Class[]{String.class, Map.class, MVCClosure.class}));
0132         MVC_METHODS.add(new MethodDescriptor("withMVCGroup"new Class[]{Map.class, String.class, MVCClosure.class}));
0133         MVC_METHODS.add(new MethodDescriptor("withMVCGroup"new Class[]{String.class, String.class, MVCClosure.class}));
0134         MVC_METHODS.add(new MethodDescriptor("withMVCGroup"new Class[]{String.class, String.class, Map.class, MVCClosure.class}));
0135         MVC_METHODS.add(new MethodDescriptor("withMVCGroup"new Class[]{Map.class, String.class, String.class, MVCClosure.class}));
0136 
0137         // Special cases due to the usage of varargs
0138         MVC_METHODS.add(new MethodDescriptor("newInstance"new Class[]{Object[].class}));
0139         MVC_METHODS.add(new MethodDescriptor("buildMVCGroup"new Class[]{Object[].class}));
0140         MVC_METHODS.add(new MethodDescriptor("createMVCGroup"new Class[]{Object[].class}));
0141         MVC_METHODS.add(new MethodDescriptor("withMVCGroup"new Class[]{Object[].class}));
0142 
0143         MVC_METHODS.add(new MethodDescriptor("getApp"));
0144         MVC_METHODS.add(new MethodDescriptor("getLog"));
0145         MVC_METHODS.add(new MethodDescriptor("getArtifactManager"));
0146         MVC_METHODS.add(new MethodDescriptor("getGriffonClass"));
0147         MVC_METHODS.add(new MethodDescriptor("setBuilder"new Class[]{FactoryBuilderSupport.class}));
0148 
0149         THREADING_METHODS.add(new MethodDescriptor("isUIThread"));
0150         THREADING_METHODS.add(new MethodDescriptor("execAsync"new Class[]{Runnable.class}));
0151         THREADING_METHODS.add(new MethodDescriptor("execAsync"new Class[]{Script.class}));
0152         THREADING_METHODS.add(new MethodDescriptor("execSync"new Class[]{Runnable.class}));
0153         THREADING_METHODS.add(new MethodDescriptor("execSync"new Class[]{Script.class}));
0154         THREADING_METHODS.add(new MethodDescriptor("execOutside"new Class[]{Runnable.class}));
0155         THREADING_METHODS.add(new MethodDescriptor("execOutside"new Class[]{Script.class}));
0156         THREADING_METHODS.add(new MethodDescriptor("execFuture"new Class[]{Closure.class}));
0157         THREADING_METHODS.add(new MethodDescriptor("execFuture"new Class[]{Callable.class}));
0158         THREADING_METHODS.add(new MethodDescriptor("execFuture"new Class[]{ExecutorService.class, Closure.class}));
0159         THREADING_METHODS.add(new MethodDescriptor("execFuture"new Class[]{ExecutorService.class, Callable.class}));
0160         THREADING_METHODS.add(new MethodDescriptor("edt"new Class[]{Runnable.class}));
0161         THREADING_METHODS.add(new MethodDescriptor("edt"new Class[]{Closure.class}));
0162         THREADING_METHODS.add(new MethodDescriptor("doLater"new Class[]{Runnable.class}));
0163         THREADING_METHODS.add(new MethodDescriptor("doLater"new Class[]{Closure.class}));
0164         THREADING_METHODS.add(new MethodDescriptor("doOutside"new Class[]{Runnable.class}));
0165         THREADING_METHODS.add(new MethodDescriptor("doOutside"new Class[]{Closure.class}));
0166         // Special case due to the usage of varargs
0167         THREADING_METHODS.add(new MethodDescriptor("execFuture"new Class[]{Object[].class}));
0168 
0169         EVENT_PUBLISHER_METHODS.add(new MethodDescriptor("addEventListener"new Class[]{Object.class}));
0170         EVENT_PUBLISHER_METHODS.add(new MethodDescriptor("addEventListener"new Class[]{String.class, Closure.class}));
0171         EVENT_PUBLISHER_METHODS.add(new MethodDescriptor("addEventListener"new Class[]{String.class, RunnableWithArgs.class}));
0172         EVENT_PUBLISHER_METHODS.add(new MethodDescriptor("removeEventListener"new Class[]{Object.class}));
0173         EVENT_PUBLISHER_METHODS.add(new MethodDescriptor("removeEventListener"new Class[]{String.class, Closure.class}));
0174         EVENT_PUBLISHER_METHODS.add(new MethodDescriptor("removeEventListener"new Class[]{String.class, RunnableWithArgs.class}));
0175         EVENT_PUBLISHER_METHODS.add(new MethodDescriptor("publishEvent"new Class[]{String.class}));
0176         EVENT_PUBLISHER_METHODS.add(new MethodDescriptor("publishEvent"new Class[]{String.class, List.class}));
0177         EVENT_PUBLISHER_METHODS.add(new MethodDescriptor("publishEventAsync"new Class[]{String.class}));
0178         EVENT_PUBLISHER_METHODS.add(new MethodDescriptor("publishEventAsync"new Class[]{String.class, List.class}));
0179         EVENT_PUBLISHER_METHODS.add(new MethodDescriptor("publishEventOutside"new Class[]{String.class}));
0180         EVENT_PUBLISHER_METHODS.add(new MethodDescriptor("publishEventOutside"new Class[]{String.class, List.class}));
0181 
0182         OBSERVABLE_METHODS.add(new MethodDescriptor("addPropertyChangeListener"new Class[]{PropertyChangeListener.class}));
0183         OBSERVABLE_METHODS.add(new MethodDescriptor("addPropertyChangeListener"new Class[]{String.class, PropertyChangeListener.class}));
0184         OBSERVABLE_METHODS.add(new MethodDescriptor("removePropertyChangeListener"new Class[]{PropertyChangeListener.class}));
0185         OBSERVABLE_METHODS.add(new MethodDescriptor("removePropertyChangeListener"new Class[]{String.class, PropertyChangeListener.class}));
0186         OBSERVABLE_METHODS.add(new MethodDescriptor("getPropertyChangeListeners"new Class[0]));
0187         OBSERVABLE_METHODS.add(new MethodDescriptor("getPropertyChangeListeners"new Class[]{String.class}));
0188     }
0189 
0190     /**
0191      * Finds out if the given string represents the name of an
0192      * event handler by matching against the following pattern:
0193      * "^on[A-Z][\\w]*$"<p>
0194      <p/>
0195      <pre>
0196      * GriffonClassUtils.isEventHandler("onBootstrapEnd") = true
0197      * GriffonClassUtils.isEventHandler("mvcGroupInit")   = false
0198      * GriffonClassUtils.isEventHandler("online")         = false
0199      </pre>
0200      *
0201      @param name the name of a possible event handler
0202      @return true if the name matches the given event handler
0203      *         pattern, false otherwise.
0204      */
0205     public static boolean isEventHandler(String name) {
0206         if (GriffonNameUtils.isBlank(name)) return false;
0207         return EVENT_HANDLER_PATTERN.matcher(name).matches();
0208     }
0209 
0210     /**
0211      * Finds out if the given Method represents an event handler
0212      * by matching its name against the following pattern:
0213      * "^on[A-Z][\\w]*$"<p>
0214      <pre>
0215      * GriffonClassUtils.isEventHandler("onBootstrapEnd") = true
0216      * GriffonClassUtils.isEventHandler("mvcGroupInit")   = false
0217      * GriffonClassUtils.isEventHandler("online")         = false
0218      </pre>
0219      *
0220      @param method a Method reference
0221      @return true if the method name matches the given event handler
0222      *         pattern, false otherwise.
0223      */
0224     public static boolean isEventHandler(Method method) {
0225         return isEventHandler(MethodDescriptor.forMethod(method));
0226     }
0227 
0228     /**
0229      * Finds out if the given Method represents an event handler
0230      * by matching its name against the following pattern:
0231      * "^on[A-Z][\\w]*$"<p>
0232      <pre>
0233      * GriffonClassUtils.isEventHandler("onBootstrapEnd") = true
0234      * GriffonClassUtils.isEventHandler("mvcGroupInit")   = false
0235      * GriffonClassUtils.isEventHandler("online")         = false
0236      </pre>
0237      *
0238      @param method a MetaMethod reference
0239      @return true if the method name matches the given event handler
0240      *         pattern, false otherwise.
0241      */
0242     public static boolean isEventHandler(MetaMethod method) {
0243         return isEventHandler(MethodDescriptor.forMethod(method));
0244     }
0245 
0246     /**
0247      * Finds out if the given Method represents an event handler
0248      * by matching its name against the following pattern:
0249      * "^on[A-Z][\\w]*$"<p>
0250      <pre>
0251      * GriffonClassUtils.isEventHandler("onBootstrapEnd") = true
0252      * GriffonClassUtils.isEventHandler("mvcGroupInit")   = false
0253      * GriffonClassUtils.isEventHandler("online")         = false
0254      </pre>
0255      *
0256      @param method a MethodDescriptor reference
0257      @return true if the method name matches the given event handler
0258      *         pattern, false otherwise.
0259      */
0260     public static boolean isEventHandler(MethodDescriptor method) {
0261         if (method == null || method.getModifiers() - Modifier.PUBLIC != 0return false;
0262         return EVENT_HANDLER_PATTERN.matcher(method.getName()).matches();
0263     }
0264 
0265     /**
0266      * Finds out if the given {@code Method} belongs either to the
0267      * {@code Object} class or the {@code GroovyObject} class.<p>
0268      *
0269      @param method a Method reference
0270      @return true if the method belongs to {@code Object} or
0271      *         {@code GroovyObject}, false otherwise.
0272      */
0273     public static boolean isBasicMethod(Method method) {
0274         return isBasicMethod(MethodDescriptor.forMethod(method));
0275     }
0276 
0277     /**
0278      * Finds out if the given {@code MetaMethod} belongs either to the
0279      * {@code Object} class or the {@code GroovyObject} class.<p>
0280      *
0281      @param method a MetaMethod reference
0282      @return true if the method belongs to {@code Object} or
0283      *         {@code GroovyObject}, false otherwise.
0284      */
0285     public static boolean isBasicMethod(MetaMethod method) {
0286         return isBasicMethod(MethodDescriptor.forMethod(method));
0287     }
0288 
0289     /**
0290      * Finds out if the given {@code MethodDescriptor} belongs either to the
0291      * {@code Object} class or the {@code GroovyObject} class.<p>
0292      *
0293      @param method a MethodDescriptor reference
0294      @return true if the method belongs to {@code Object} or
0295      *         {@code GroovyObject}, false otherwise.
0296      */
0297     public static boolean isBasicMethod(MethodDescriptor method) {
0298         if (method == null || !isInstanceMethod(method)) return false;
0299         return BASIC_METHODS.contains(method);
0300     }
0301 
0302     /**
0303      * Finds out if the given {@code Method} was injected by the Groovy
0304      * compiler.<p>
0305      * Performs a basic checks against the method's name, returning true
0306      * if the name starts with either "super$" or "this$".
0307      *
0308      @param method a Method reference
0309      @return true if the method matches the given criteria, false otherwise.
0310      */
0311     public static boolean isGroovyInjectedMethod(Method method) {
0312         return isGroovyInjectedMethod(MethodDescriptor.forMethod(method));
0313     }
0314 
0315     /**
0316      * Finds out if the given {@code MetaMethod} was injected by the Groovy
0317      * compiler.<p>
0318      * Performs a basic checks against the method's name, returning true
0319      * if the name starts with either "super$" or "this$".
0320      *
0321      @param method a MetaMethod reference
0322      @return true if the method matches the given criteria, false otherwise.
0323      */
0324     public static boolean isGroovyInjectedMethod(MetaMethod method) {
0325         return isGroovyInjectedMethod(MethodDescriptor.forMethod(method));
0326     }
0327 
0328     /**
0329      * Finds out if the given {@code MethodDescriptor} was injected by the Groovy
0330      * compiler.<p>
0331      * Performs a basic checks against the method's name, returning true
0332      * if the name starts with either "super$" or "this$".
0333      *
0334      @param method a MethodDescriptor reference
0335      @return true if the method matches the given criteria, false otherwise.
0336      */
0337     public static boolean isGroovyInjectedMethod(MethodDescriptor method) {
0338         if (method == null || !isInstanceMethod(method)) return false;
0339         return method.getName().startsWith("super$"||
0340                 method.getName().startsWith("this$");
0341     }
0342 
0343     /**
0344      * Finds out if the given {@code Method} is a getter method.
0345      <p/>
0346      <pre>
0347      * // assuming getMethod() returns an appropriate Method reference
0348      * isGetterMethod(getMethod("getFoo"))       = true
0349      * isGetterMethod(getMethod("getfoo") )      = false
0350      * isGetterMethod(getMethod("mvcGroupInit")) = false
0351      * isGetterMethod(getMethod("isFoo"))        = true
0352      * isGetterMethod(getMethod("island"))       = false
0353      </pre>
0354      *
0355      @param method a Method reference
0356      @return true if the method is a getter, false otherwise.
0357      */
0358     public static boolean isGetterMethod(Method method) {
0359         return isGetterMethod(MethodDescriptor.forMethod(method));
0360     }
0361 
0362     /**
0363      * Finds out if the given {@code MetaMethod} is a getter method.
0364      <p/>
0365      <pre>
0366      * // assuming getMethod() returns an appropriate MetaMethod reference
0367      * isGetterMethod(getMethod("getFoo"))       = true
0368      * isGetterMethod(getMethod("getfoo") )      = false
0369      * isGetterMethod(getMethod("mvcGroupInit")) = false
0370      * isGetterMethod(getMethod("isFoo"))        = true
0371      * isGetterMethod(getMethod("island"))       = false
0372      </pre>
0373      *
0374      @param method a Method reference
0375      @return true if the method is a getter, false otherwise.
0376      */
0377     public static boolean isGetterMethod(MetaMethod method) {
0378         return isGetterMethod(MethodDescriptor.forMethod(method));
0379     }
0380 
0381     /**
0382      * Finds out if the given {@code MetaMethod} is a getter method.
0383      <p/>
0384      <pre>
0385      * // assuming getMethod() returns an appropriate MethodDescriptor reference
0386      * isGetterMethod(getMethod("getFoo"))       = true
0387      * isGetterMethod(getMethod("getfoo") )      = false
0388      * isGetterMethod(getMethod("mvcGroupInit")) = false
0389      * isGetterMethod(getMethod("isFoo"))        = true
0390      * isGetterMethod(getMethod("island"))       = false
0391      </pre>
0392      *
0393      @param method a MethodDescriptor reference
0394      @return true if the method is a getter, false otherwise.
0395      */
0396     public static boolean isGetterMethod(MethodDescriptor method) {
0397         if (method == null || !isInstanceMethod(method)) return false;
0398         return GETTER_PATTERN_1.matcher(method.getName()).matches() ||
0399                 GETTER_PATTERN_2.matcher(method.getName()).matches();
0400     }
0401 
0402     /**
0403      * Finds out if the given {@code Method} is a setter method.
0404      <p/>
0405      <pre>
0406      * // assuming getMethod() returns an appropriate Method reference
0407      * isGetterMethod(getMethod("setFoo"))       = true
0408      * isGetterMethod(getMethod("setfoo"))       = false
0409      * isGetterMethod(getMethod("mvcGroupInit")) = false
0410      </pre>
0411      *
0412      @param method a Method reference
0413      @return true if the method is a setter, false otherwise.
0414      */
0415     public static boolean isSetterMethod(Method method) {
0416         return isSetterMethod(MethodDescriptor.forMethod(method));
0417     }
0418 
0419     /**
0420      * Finds out if the given {@code MetaMethod} is a setter method.
0421      <p/>
0422      <pre>
0423      * // assuming getMethod() returns an appropriate MetaMethod reference
0424      * isGetterMethod(getMethod("setFoo"))       = true
0425      * isGetterMethod(getMethod("setfoo"))       = false
0426      * isGetterMethod(getMethod("mvcGroupInit")) = false
0427      </pre>
0428      *
0429      @param method a MetaMethod reference
0430      @return true if the method is a setter, false otherwise.
0431      */
0432     public static boolean isSetterMethod(MetaMethod method) {
0433         return isSetterMethod(MethodDescriptor.forMethod(method));
0434     }
0435 
0436     /**
0437      * Finds out if the given {@code MethodDescriptor} is a setter method.
0438      <p/>
0439      <pre>
0440      * // assuming getMethod() returns an appropriate MethodDescriptor reference
0441      * isGetterMethod(getMethod("setFoo"))       = true
0442      * isGetterMethod(getMethod("setfoo"))       = false
0443      * isGetterMethod(getMethod("mvcGroupInit")) = false
0444      </pre>
0445      *
0446      @param method a MethodDescriptor reference
0447      @return true if the method is a setter, false otherwise.
0448      */
0449     public static boolean isSetterMethod(MethodDescriptor method) {
0450         if (method == null || !isInstanceMethod(method)) return false;
0451         return SETTER_PATTERN.matcher(method.getName()).matches();
0452     }
0453 
0454     /**
0455      * Finds out if the given {@code Method} belongs to the set of
0456      * predefined MVC methods by convention.
0457      <p/>
0458      <pre>
0459      * // assuming getMethod() returns an appropriate Method reference
0460      * isMvcMethod(getMethod("mvcGroupInit"))    = true
0461      * isMvcMethod(getMethod("mvcGroupDestroy")) = true
0462      * isMvcMethod(getMethod("foo"))             = false
0463      </pre>
0464      *
0465      @param method a Method reference
0466      @return true if the method is an MVC method, false otherwise.
0467      */
0468     public static boolean isMvcMethod(Method method) {
0469         return isMvcMethod(MethodDescriptor.forMethod(method));
0470     }
0471 
0472     /**
0473      * Finds out if the given {@code MetaMethod} belongs to the set of
0474      * predefined MVC methods by convention.
0475      <p/>
0476      <pre>
0477      * // assuming getMethod() returns an appropriate MetaMethod reference
0478      * isMvcMethod(getMethod("mvcGroupInit"))    = true
0479      * isMvcMethod(getMethod("mvcGroupDestroy")) = true
0480      * isMvcMethod(getMethod("foo"))             = false
0481      </pre>
0482      *
0483      @param method a Method reference
0484      @return true if the method is an MVC method, false otherwise.
0485      */
0486     public static boolean isMvcMethod(MetaMethod method) {
0487         return isMvcMethod(MethodDescriptor.forMethod(method));
0488     }
0489 
0490     /**
0491      * Finds out if the given {@code MethodDescriptor} belongs to the set of
0492      * predefined MVC methods by convention.
0493      <p/>
0494      <pre>
0495      * // assuming getMethod() returns an appropriate MethodDescriptor reference
0496      * isMvcMethod(getMethod("mvcGroupInit"))    = true
0497      * isMvcMethod(getMethod("mvcGroupDestroy")) = true
0498      * isMvcMethod(getMethod("foo"))             = false
0499      </pre>
0500      *
0501      @param method a Method reference
0502      @return true if the method is an MVC method, false otherwise.
0503      */
0504     public static boolean isMvcMethod(MethodDescriptor method) {
0505         if (method == null || !isInstanceMethod(method)) return false;
0506         return MVC_METHODS.contains(method);
0507     }
0508 
0509     /**
0510      * Finds out if the given {@code Method} belongs to the set of
0511      * predefined threading methods by convention.
0512      <p/>
0513      <pre>
0514      * // assuming getMethod() returns an appropriate Method reference
0515      * isThreadingMethod(getMethod("execOutside"))    = true
0516      * isThreadingMethod(getMethod("doLater"))        = true
0517      * isThreadingMethod(getMethod("foo"))            = false
0518      </pre>
0519      *
0520      @param method a Method reference
0521      @return true if the method is a threading method, false otherwise.
0522      */
0523     public static boolean isThreadingMethod(Method method) {
0524         return isThreadingMethod(MethodDescriptor.forMethod(method));
0525     }
0526 
0527     /**
0528      * Finds out if the given {@code MetaMethod} belongs to the set of
0529      * predefined threading methods by convention.
0530      <p/>
0531      <pre>
0532      * // assuming getMethod() returns an appropriate MetaMethod reference
0533      * isThreadingMethod(getMethod("execOutside"))    = true
0534      * isThreadingMethod(getMethod("doLater"))        = true
0535      * isThreadingMethod(getMethod("foo"))            = false
0536      </pre>
0537      *
0538      @param method a Method reference
0539      @return true if the method is a threading method, false otherwise.
0540      */
0541     public static boolean isThreadingMethod(MetaMethod method) {
0542         return isThreadingMethod(MethodDescriptor.forMethod(method));
0543     }
0544 
0545     /**
0546      * Finds out if the given {@code MethodDescriptor} belongs to the set of
0547      * predefined threading methods by convention.
0548      <p/>
0549      <pre>
0550      * // assuming getMethod() returns an appropriate MethodDescriptor reference
0551      * isThreadingMethod(getMethod("execOutside"))    = true
0552      * isThreadingMethod(getMethod("doLater"))        = true
0553      * isThreadingMethod(getMethod("foo"))            = false
0554      </pre>
0555      *
0556      @param method a Method reference
0557      @return true if the method is a threading method, false otherwise.
0558      */
0559     public static boolean isThreadingMethod(MethodDescriptor method) {
0560         if (method == null || !isInstanceMethod(method)) return false;
0561         return THREADING_METHODS.contains(method);
0562     }
0563 
0564     /**
0565      * Finds out if the given {@code Method} belongs to the set of
0566      * predefined EVENT_PUBLISHER methods by convention.
0567      <p/>
0568      <pre>
0569      * // assuming getMethod() returns an appropriate Method reference
0570      * isEventPublisherMethod(getMethod("addEventPublisher"))  = true
0571      * isEventPublisherMethod(getMethod("publishEvent"))       = true
0572      * isEventPublisherMethod(getMethod("foo"))                = false
0573      </pre>
0574      *
0575      @param method a Method reference
0576      @return true if the method is an @EventPublisher method, false otherwise.
0577      */
0578     public static boolean isEventPublisherMethod(Method method) {
0579         return isEventPublisherMethod(MethodDescriptor.forMethod(method));
0580     }
0581 
0582     /**
0583      * Finds out if the given {@code MetaMethod} belongs to the set of
0584      * predefined EVENT_PUBLISHER methods by convention.
0585      <p/>
0586      <pre>
0587      * // assuming getMethod() returns an appropriate MetaMethod reference
0588      * isEventPublisherMethod(getMethod("addEventPublisher"))  = true
0589      * isEventPublisherMethod(getMethod("publishEvent"))       = true
0590      * isEventPublisherMethod(getMethod("foo"))                = false
0591      </pre>
0592      *
0593      @param method a Method reference
0594      @return true if the method is an @EventPublisher method, false otherwise.
0595      */
0596     public static boolean isEventPublisherMethod(MetaMethod method) {
0597         return isEventPublisherMethod(MethodDescriptor.forMethod(method));
0598     }
0599 
0600     /**
0601      * Finds out if the given {@code MethodDescriptor} belongs to the set of
0602      * predefined EVENT_PUBLISHER methods by convention.
0603      <p/>
0604      <pre>
0605      * // assuming getMethod() returns an appropriate MethodDescriptor reference
0606      * isEventPublisherMethod(getMethod("addEventPublisher"))  = true
0607      * isEventPublisherMethod(getMethod("publishEvent"))       = true
0608      * isEventPublisherMethod(getMethod("foo"))                = false
0609      </pre>
0610      *
0611      @param method a Method reference
0612      @return true if the method is an @EventPublisher method, false otherwise.
0613      */
0614     public static boolean isEventPublisherMethod(MethodDescriptor method) {
0615         if (method == null || !isInstanceMethod(method)) return false;
0616         return EVENT_PUBLISHER_METHODS.contains(method);
0617     }
0618 
0619     /**
0620      * Finds out if the given {@code Method} belongs to the set of
0621      * predefined OBSERVABLE methods by convention.
0622      <p/>
0623      <pre>
0624      * // assuming getMethod() returns an appropriate Method reference
0625      * isObservableMethod(getMethod("addPropertyChangeListener"))  = true
0626      * isObservableMethod(getMethod("getPropertyChangeListeners")) = true
0627      * isObservableMethod(getMethod("foo"))                        = false
0628      </pre>
0629      *
0630      @param method a Method reference
0631      @return true if the method is an Observable method, false otherwise.
0632      */
0633     public static boolean isObservableMethod(Method method) {
0634         return isObservableMethod(MethodDescriptor.forMethod(method));
0635     }
0636 
0637     /**
0638      * Finds out if the given {@code MetaMethod} belongs to the set of
0639      * predefined OBSERVABLE methods by convention.
0640      <p/>
0641      <pre>
0642      * // assuming getMethod() returns an appropriate MetaMethod reference
0643      * isObservableMethod(getMethod("addPropertyChangeListener"))  = true
0644      * isObservableMethod(getMethod("getPropertyChangeListeners")) = true
0645      * isObservableMethod(getMethod("foo"))                        = false
0646      </pre>
0647      *
0648      @param method a Method reference
0649      @return true if the method is an Observable method, false otherwise.
0650      */
0651     public static boolean isObservableMethod(MetaMethod method) {
0652         return isObservableMethod(MethodDescriptor.forMethod(method));
0653     }
0654 
0655     /**
0656      * Finds out if the given {@code MethodDescriptor} belongs to the set of
0657      * predefined OBSERVABLE methods by convention.
0658      <p/>
0659      <pre>
0660      * // assuming getMethod() returns an appropriate MethodDescriptor reference
0661      * isObservableMethod(getMethod("addPropertyChangeListener"))  = true
0662      * isObservableMethod(getMethod("getPropertyChangeListeners")) = true
0663      * isObservableMethod(getMethod("foo"))                        = false
0664      </pre>
0665      *
0666      @param method a Method reference
0667      @return true if the method is an Observable method, false otherwise.
0668      */
0669     public static boolean isObservableMethod(MethodDescriptor method) {
0670         if (method == null || !isInstanceMethod(method)) return false;
0671         return OBSERVABLE_METHODS.contains(method);
0672     }
0673 
0674     /**
0675      * Finds out if the given {@code Method} is an instance method, i.e,
0676      * it is public and non-static.
0677      *
0678      @param method a Method reference
0679      @return true if the method is an instance method, false otherwise.
0680      */
0681     public static boolean isInstanceMethod(Method method) {
0682         return isInstanceMethod(MethodDescriptor.forMethod(method));
0683     }
0684 
0685     /**
0686      * Finds out if the given {@code MetaMethod} is an instance method, i.e,
0687      * it is public and non-static.
0688      *
0689      @param method a MetaMethod reference
0690      @return true if the method is an instance method, false otherwise.
0691      */
0692     public static boolean isInstanceMethod(MetaMethod method) {
0693         return isInstanceMethod(MethodDescriptor.forMethod(method));
0694     }
0695 
0696     /**
0697      * Finds out if the given {@code MethodDescriptor} is an instance method, i.e,
0698      * it is public and non-static.
0699      *
0700      @param method a MethodDescriptor reference
0701      @return true if the method is an instance method, false otherwise.
0702      */
0703     public static boolean isInstanceMethod(MethodDescriptor method) {
0704         if (method == nullreturn false;
0705         int modifiers = method.getModifiers();
0706         return Modifier.isPublic(modifiers&&
0707                 !Modifier.isStatic(modifiers);
0708     }
0709 
0710     /**
0711      * Finds out if the given {@code Method} matches the following criteria:<ul>
0712      <li>isInstanceMethod(method)</li>
0713      <li>! isBasicMethod(method)</li>
0714      <li>! isGroovyInjectedMethod(method)</li>
0715      <li>! isThreadingMethod(method)</li>
0716      <li>! isMvcMethod(method)</li>
0717      <li>! isEventPublisherMethod(method)</li>
0718      <li>! isObservableMethod(method)</li>
0719      <li>! isGetterMethod(method)</li>
0720      <li>! isSetterMethod(method)</li>
0721      </ul>
0722      *
0723      @param method a Method reference
0724      @return true if the method matches the given criteria, false otherwise.
0725      */
0726     public static boolean isPlainMethod(Method method) {
0727         return isPlainMethod(MethodDescriptor.forMethod(method));
0728     }
0729 
0730     /**
0731      * Finds out if the given {@code MetaMethod} matches the following criteria:<ul>
0732      <li>isInstanceMethod(method)</li>
0733      <li>! isBasicMethod(method)</li>
0734      <li>! isGroovyInjectedMethod(method)</li>
0735      <li>! isThreadingMethod(method)</li>
0736      <li>! isMvcMethod(method)</li>
0737      <li>! isEventPublisherMethod(method)</li>
0738      <li>! isObservableMethod(method)</li>
0739      <li>! isGetterMethod(method)</li>
0740      <li>! isSetterMethod(method)</li>
0741      </ul>
0742      *
0743      @param method a Method reference
0744      @return true if the method matches the given criteria, false otherwise.
0745      */
0746     public static boolean isPlainMethod(MetaMethod method) {
0747         return isPlainMethod(MethodDescriptor.forMethod(method));
0748     }
0749 
0750     /**
0751      * Finds out if the given {@code MethodDescriptor} matches the following criteria:<ul>
0752      <li>isInstanceMethod(method)</li>
0753      <li>! isBasicMethod(method)</li>
0754      <li>! isGroovyInjectedMethod(method)</li>
0755      <li>! isThreadingMethod(method)</li>
0756      <li>! isMvcMethod(method)</li>
0757      <li>! isEventPublisherMethod(method)</li>
0758      <li>! isObservableMethod(method)</li>
0759      <li>! isGetterMethod(method)</li>
0760      <li>! isSetterMethod(method)</li>
0761      </ul>
0762      *
0763      @param method a MethodDescriptor reference
0764      @return true if the method matches the given criteria, false otherwise.
0765      */
0766     public static boolean isPlainMethod(MethodDescriptor method) {
0767         return isInstanceMethod(method&&
0768                 !isBasicMethod(method&&
0769                 !isGroovyInjectedMethod(method&&
0770                 !isThreadingMethod(method&&
0771                 !isMvcMethod(method&&
0772                 !isEventPublisherMethod(method&&
0773                 !isObservableMethod(method&&
0774                 !isGetterMethod(method&&
0775                 !isSetterMethod(method);
0776     }
0777 
0778     public static boolean isGetter(MetaProperty property) {
0779         return isGetter(property, false);
0780     }
0781 
0782     public static boolean isGetter(MetaProperty property, boolean strict) {
0783         if (property == nullreturn false;
0784         return GETTER_PATTERN_1.matcher(property.getName()).matches() ||
0785                 (strict && GETTER_PATTERN_2.matcher(property.getName()).matches());
0786     }
0787 
0788     public static boolean isSetter(MetaProperty property) {
0789         if (property == nullreturn false;
0790         return SETTER_PATTERN.matcher(property.getName()).matches();
0791     }
0792 
0793     /**
0794      * Returns true if the specified property in the specified class is of the specified type
0795      *
0796      @param clazz        The class which contains the property
0797      @param propertyName The property name
0798      @param type         The type to check
0799      @return A boolean value
0800      */
0801     public static boolean isPropertyOfType(Class<?> clazz, String propertyName, Class<?> type) {
0802         try {
0803             Class propType = getPropertyType(clazz, propertyName);
0804             return propType != null && propType.equals(type);
0805         catch (Exception e) {
0806             return false;
0807         }
0808     }
0809 
0810     /**
0811      * Instantiates a Class, wrapping any exceptions in a RuntimeException.
0812      *
0813      @param clazz target Class for which an object will be instantiated
0814      @return the newly instantiated object.
0815      @throws BeanInstantiationException if an error occurs when creating the object
0816      */
0817     public static Object instantiateClass(Class<?> clazz) {
0818         try {
0819             return clazz.newInstance();
0820         catch (Exception e) {
0821             throw new BeanInstantiationException("Could not create an instance of " + clazz, e);
0822         }
0823     }
0824 
0825 /*
0826     public static Object instantiate(Class<?> clazz, Object arg) {
0827         return instantiate(clazz, new Object[]{arg});
0828     }
0829 */
0830 
0831     public static Object instantiate(Class<?> clazz, Object[] args) {
0832         try {
0833             if (args == null) {
0834                 args = EMPTY_OBJECT_ARRAY;
0835             }
0836             int arguments = args.length;
0837             Class[] parameterTypes = new Class[arguments];
0838             for (int i = 0; i < arguments; i++) {
0839                 parameterTypes[i= args[i].getClass();
0840             }
0841             return clazz.getDeclaredConstructor(parameterTypes).newInstance(args);
0842         catch (Exception e) {
0843             throw new BeanInstantiationException("Could not create an instance of " + clazz, e);
0844         }
0845     }
0846 
0847     /**
0848      * Returns the value of the specified property and type from an instance of the specified Griffon class
0849      *
0850      @param clazz        The name of the class which contains the property
0851      @param propertyName The property name
0852      @param propertyType The property type
0853      @return The value of the property or null if none exists
0854      */
0855     public static Object getPropertyValueOfNewInstance(Class<?> clazz, String propertyName, Class<?> propertyType) {
0856         // validate
0857         if (clazz == null || GriffonNameUtils.isBlank(propertyName))
0858             return null;
0859 
0860         Object instance = null;
0861         try {
0862             instance = instantiateClass(clazz);
0863         catch (BeanInstantiationException e) {
0864             return null;
0865         }
0866 
0867         return getPropertyOrStaticPropertyOrFieldValue(instance, propertyName);
0868     }
0869 
0870     /**
0871      * Returns the value of the specified property and type from an instance of the specified Griffon class
0872      *
0873      @param clazz        The name of the class which contains the property
0874      @param propertyName The property name
0875      @return The value of the property or null if none exists
0876      */
0877     public static Object getPropertyValueOfNewInstance(Class<?> clazz, String propertyName) {
0878         // validate
0879         if (clazz == null || GriffonNameUtils.isBlank(propertyName))
0880             return null;
0881 
0882         Object instance = null;
0883         try {
0884             instance = instantiateClass(clazz);
0885         catch (BeanInstantiationException e) {
0886             return null;
0887         }
0888 
0889         return getPropertyOrStaticPropertyOrFieldValue(instance, propertyName);
0890     }
0891 
0892     /**
0893      * Retrieves a PropertyDescriptor for the specified instance and property value
0894      *
0895      @param instance      The instance
0896      @param propertyValue The value of the property
0897      @return The PropertyDescriptor
0898      */
0899     public static PropertyDescriptor getPropertyDescriptorForValue(Object instance, Object propertyValue) {
0900         if (instance == null || propertyValue == null)
0901             return null;
0902 
0903         PropertyDescriptor[] descriptors = getPropertyDescriptors(instance.getClass());
0904 
0905         for (int i = 0; i < descriptors.length; i++) {
0906             PropertyDescriptor pd = descriptors[i];
0907             if (isAssignableOrConvertibleFrom(pd.getPropertyType(), propertyValue.getClass())) {
0908                 Object value;
0909                 try {
0910                     value = getReadMethod(pd).invoke(instance, (Object[]) null);
0911                 catch (Exception e) {
0912                     throw new RuntimeException("Problem calling readMethod of " + pd, e);
0913                 }
0914                 if (propertyValue.equals(value))
0915                     return pd;
0916             }
0917         }
0918         return null;
0919     }
0920 
0921     /**
0922      * Returns the type of the given property contained within the specified class
0923      *
0924      @param clazz        The class which contains the property
0925      @param propertyName The name of the property
0926      @return The property type or null if none exists
0927      */
0928     public static Class<?> getPropertyType(Class<?> clazz, String propertyName) {
0929         if (clazz == null || GriffonNameUtils.isBlank(propertyName))
0930             return null;
0931 
0932         try {
0933             PropertyDescriptor desc = getPropertyDescriptor(clazz, propertyName);
0934             if (desc != null) {
0935                 return desc.getPropertyType();
0936             else {
0937                 return null;
0938             }
0939         catch (Exception e) {
0940             // if there are any errors in instantiating just return null for the moment
0941             return null;
0942         }
0943     }
0944 
0945     /**
0946      * Retrieves all the properties of the given class for the given type
0947      *
0948      @param clazz        The class to retrieve the properties from
0949      @param propertyType The type of the properties you wish to retrieve
0950      @return An array of PropertyDescriptor instances
0951      */
0952     public static PropertyDescriptor[] getPropertiesOfType(Class<?> clazz, Class<?> propertyType) {
0953         if (clazz == null || propertyType == null)
0954             return new PropertyDescriptor[0];
0955 
0956         Set properties = new HashSet();
0957         try {
0958             PropertyDescriptor[] descriptors = getPropertyDescriptors(clazz);
0959 
0960             for (int i = 0; i < descriptors.length; i++) {
0961                 Class<?> currentPropertyType = descriptors[i].getPropertyType();
0962                 if (isTypeInstanceOfPropertyType(propertyType, currentPropertyType)) {
0963                     properties.add(descriptors[i]);
0964                 }
0965             }
0966         catch (Exception e) {
0967             // if there are any errors in instantiating just return null for the moment
0968             return new PropertyDescriptor[0];
0969         }
0970         return (PropertyDescriptor[]) properties.toArray(new PropertyDescriptor[properties.size()]);
0971     }
0972 
0973     private static boolean isTypeInstanceOfPropertyType(Class<?> type, Class<?> propertyType) {
0974         return propertyType.isAssignableFrom(type&& !propertyType.equals(Object.class);
0975     }
0976 
0977     /**
0978      * Retrieves all the properties of the given class which are assignable to the given type
0979      *
0980      @param clazz             The class to retrieve the properties from
0981      @param propertySuperType The type of the properties you wish to retrieve
0982      @return An array of PropertyDescriptor instances
0983      */
0984     public static PropertyDescriptor[] getPropertiesAssignableToType(Class<?> clazz, Class<?> propertySuperType) {
0985         if (clazz == null || propertySuperType == nullreturn new PropertyDescriptor[0];
0986 
0987         Set properties = new HashSet();
0988         try {
0989             PropertyDescriptor[] descriptors = getPropertyDescriptors(clazz);
0990 
0991             for (int i = 0; i < descriptors.length; i++) {
0992                 if (propertySuperType.isAssignableFrom(descriptors[i].getPropertyType())) {
0993                     properties.add(descriptors[i]);
0994                 }
0995             }
0996         catch (Exception e) {
0997             return new PropertyDescriptor[0];
0998         }
0999         return (PropertyDescriptor[]) properties.toArray(new PropertyDescriptor[properties.size()]);
1000     }
1001 
1002     /**
1003      * Retrieves a property of the given class of the specified name and type
1004      *
1005      @param clazz        The class to retrieve the property from
1006      @param propertyName The name of the property
1007      @param propertyType The type of the property
1008      @return A PropertyDescriptor instance or null if none exists
1009      */
1010     public static PropertyDescriptor getProperty(Class<?> clazz, String propertyName, Class<?> propertyType) {
1011         if (clazz == null || propertyName == null || propertyType == null)
1012             return null;
1013 
1014         try {
1015             PropertyDescriptor pd = getPropertyDescriptor(clazz, propertyName);
1016             if (pd.getPropertyType().equals(propertyType)) {
1017                 return pd;
1018             else {
1019                 return null;
1020             }
1021         catch (Exception e) {
1022             // if there are any errors in instantiating just return null for the moment
1023             return null;
1024         }
1025     }
1026 
1027     /**
1028      * Convenience method for converting a collection to an Object[]
1029      *
1030      @param c The collection
1031      @return An object array
1032      */
1033     public static Object[] collectionToObjectArray(Collection c) {
1034         if (c == nullreturn EMPTY_OBJECT_ARRAY;
1035 
1036         return c.toArray(new Object[c.size()]);
1037     }
1038 
1039 
1040     /**
1041      * Detect if left and right types are matching types. In particular,
1042      * test if one is a primitive type and the other is the corresponding
1043      * Java wrapper type. Primitive and wrapper classes may be passed to
1044      * either arguments.
1045      *
1046      @param leftType
1047      @param rightType
1048      @return true if one of the classes is a native type and the other the object representation
1049      *         of the same native type
1050      */
1051     public static boolean isMatchBetweenPrimitiveAndWrapperTypes(Class<?> leftType, Class<?> rightType) {
1052         if (leftType == null) {
1053             throw new NullPointerException("Left type is null!");
1054         else if (rightType == null) {
1055             throw new NullPointerException("Right type is null!");
1056         else {
1057             Class<?> r = (Class<?>PRIMITIVE_TYPE_COMPATIBLE_CLASSES.get(leftType);
1058             return r == rightType;
1059         }
1060     }
1061 
1062     /**
1063      <p>Tests whether or not the left hand type is compatible with the right hand type in Groovy
1064      * terms, i.e. can the left type be assigned a value of the right hand type in Groovy.</p>
1065      <p>This handles Java primitive type equivalence and uses isAssignableFrom for all other types,
1066      * with a bit of magic for native types and polymorphism i.e. Number assigned an int.
1067      * If either parameter is null an exception is thrown</p>
1068      *
1069      @param leftType  The type of the left hand part of a notional assignment
1070      @param rightType The type of the right hand part of a notional assignment
1071      @return True if values of the right hand type can be assigned in Groovy to variables of the left hand type.
1072      */
1073     public static boolean isGroovyAssignableFrom(Class<?> leftType, Class<?> rightType) {
1074         if (leftType == null) {
1075             throw new NullPointerException("Left type is null!");
1076         else if (rightType == null) {
1077             throw new NullPointerException("Right type is null!");
1078         else if (leftType == Object.class) {
1079             return true;
1080         else if (leftType == rightType) {
1081             return true;
1082         else {
1083             // check for primitive type equivalence
1084             Class<?> r = (Class<?>PRIMITIVE_TYPE_COMPATIBLE_CLASSES.get(leftType);
1085             boolean result = r == rightType;
1086 
1087             if (!result) {
1088                 // If no primitive <-> wrapper match, it may still be assignable
1089                 // from polymorphic primitives i.e. Number -> int (AKA Integer)
1090                 if (rightType.isPrimitive()) {
1091                     // see if incompatible
1092                     r = (Class<?>PRIMITIVE_TYPE_COMPATIBLE_CLASSES.get(rightType);
1093                     if (r != null) {
1094                         result = leftType.isAssignableFrom(r);
1095                     }
1096                 else {
1097                     // Otherwise it may just be assignable using normal Java polymorphism
1098                     result = leftType.isAssignableFrom(rightType);
1099                 }
1100             }
1101             return result;
1102         }
1103     }
1104 
1105     private static Method findDeclaredMethod(Class<?> clazz, String methodName, Class[] parameterTypes) {
1106         while (clazz != null) {
1107             try {
1108                 Method method = clazz.getDeclaredMethod(methodName, parameterTypes);
1109                 if (method != nullreturn method;
1110             catch (NoSuchMethodException e) {
1111                 // skip
1112             catch (SecurityException e) {
1113                 // skip
1114             }
1115             clazz = clazz.getSuperclass();
1116         }
1117 
1118         return null;
1119     }
1120 
1121     /**
1122      <p>Work out if the specified property is readable and static. Java introspection does not
1123      * recognize this concept of static properties but Groovy does. We also consider public static fields
1124      * as static properties with no getters/setters</p>
1125      *
1126      @param clazz        The class to check for static property
1127      @param propertyName The property name
1128      @return true if the property with name propertyName has a static getter method
1129      */
1130     public static boolean isStaticProperty(Class<?> clazz, String propertyName) {
1131         Method getter = findDeclaredMethod(clazz, getGetterName(propertyName)null);
1132         if (getter != null) {
1133             return isPublicStatic(getter);
1134         else {
1135             try {
1136                 Field f = clazz.getDeclaredField(propertyName);
1137                 if (f != null) {
1138                     return isPublicStatic(f);
1139                 }
1140             catch (NoSuchFieldException ignore) {
1141                 //ignore
1142             }
1143         }
1144 
1145         return false;
1146     }
1147 
1148     /**
1149      * Determine whether the method is declared public static
1150      *
1151      @param m
1152      @return True if the method is declared public static
1153      */
1154     public static boolean isPublicStatic(Method m) {
1155         final int modifiers = m.getModifiers();
1156         return Modifier.isPublic(modifiers&& Modifier.isStatic(modifiers);
1157     }
1158 
1159     /**
1160      * Determine whether the field is declared public static
1161      *
1162      @param f
1163      @return True if the field is declared public static
1164      */
1165     public static boolean isPublicStatic(Field f) {
1166         final int modifiers = f.getModifiers();
1167         return Modifier.isPublic(modifiers&& Modifier.isStatic(modifiers);
1168     }
1169 
1170     /**
1171      * Calculate the name for a getter method to retrieve the specified property
1172      *
1173      @param propertyName
1174      @return The name for the getter method for this property, if it were to exist, i.e. getConstraints
1175      */
1176     public static String getGetterName(String propertyName) {
1177         return PROPERTY_GET_PREFIX + Character.toUpperCase(propertyName.charAt(0))
1178                 + propertyName.substring(1);
1179     }
1180 
1181     /**
1182      <p>Get a static property value, which has a public static getter or is just a public static field.</p>
1183      *
1184      @param clazz The class to check for static property
1185      @param name  The property name
1186      @return The value if there is one, or null if unset OR there is no such property
1187      */
1188     public static Object getStaticPropertyValue(Class<?> clazz, String name) {
1189         Method getter = findDeclaredMethod(clazz, getGetterName(name)null);
1190         try {
1191             if (getter != null) {
1192                 return getter.invoke(null, (Object[]) null);
1193             else {
1194                 Field f = clazz.getDeclaredField(name);
1195                 if (f != null) {
1196                     return f.get(null);
1197                 }
1198             }
1199         catch (Exception ignore) {
1200             //ignore
1201         }
1202         return null;
1203     }
1204 
1205     /**
1206      <p>Looks for a property of the reference instance with a given name.</p>
1207      <p>If found its value is returned. We follow the Java bean conventions with augmentation for groovy support
1208      * and static fields/properties. We will therefore match, in this order:
1209      </p>
1210      <ol>
1211      <li>Standard public bean property (with getter or just public field, using normal introspection)
1212      <li>Public static property with getter method
1213      <li>Public static field
1214      </ol>
1215      *
1216      @return property value or null if no property found
1217      */
1218     public static Object getPropertyOrStaticPropertyOrFieldValue(Object obj, String name) {
1219         if (isReadable(obj, name)) {
1220             try {
1221                 return getProperty(obj, name);
1222             catch (Exception e) {
1223                 throw new BeanException("Error while reading value of property/field " + name, e);
1224             }
1225         else {
1226             // Look for public fields
1227             if (isPublicField(obj, name)) {
1228                 return getFieldValue(obj, name);
1229             }
1230 
1231             // Look for statics
1232             Class<?> clazz = obj.getClass();
1233             if (isStaticProperty(clazz, name)) {
1234                 return getStaticPropertyValue(clazz, name);
1235             else {
1236                 return null;
1237             }
1238         }
1239     }
1240 
1241     /**
1242      * Get the value of a declared field on an object
1243      *
1244      @param obj
1245      @param name
1246      @return The object value or null if there is no such field or access problems
1247      */
1248     public static Object getFieldValue(Object obj, String name) {
1249         Class<?> clazz = obj.getClass();
1250         Field f = null;
1251         try {
1252             f = clazz.getDeclaredField(name);
1253             return f.get(obj);
1254         catch (Exception e) {
1255             return null;
1256         }
1257     }
1258 
1259     /**
1260      * Work out if the specified object has a public field with the name supplied.
1261      *
1262      @param obj
1263      @param name
1264      @return True if a public field with the name exists
1265      */
1266     public static boolean isPublicField(Object obj, String name) {
1267         Class<?> clazz = obj.getClass();
1268         Field f = null;
1269         try {
1270             f = clazz.getDeclaredField(name);
1271             return Modifier.isPublic(f.getModifiers());
1272         catch (NoSuchFieldException e) {
1273             return false;
1274         }
1275     }
1276 
1277     /**
1278      * Checks whether the specified property is inherited from a super class
1279      *
1280      @param clz          The class to check
1281      @param propertyName The property name
1282      @return True if the property is inherited
1283      */
1284     public static boolean isPropertyInherited(Class<?> clz, String propertyName) {
1285         if (clz == nullreturn false;
1286         if (GriffonNameUtils.isBlank(propertyName))
1287             throw new IllegalArgumentException("Argument [propertyName] cannot be null or blank");
1288 
1289         Class<?> superClass = clz.getSuperclass();
1290 
1291         PropertyDescriptor pd = null;
1292         try {
1293             pd = getPropertyDescriptor(superClass, propertyName);
1294         catch (Exception e) {
1295             throw new BeanException("Could not read property descritptor for " + propertyName + " in " + superClass, e);
1296         }
1297         if (pd != null && pd.getReadMethod() != null) {
1298             return true;
1299         }
1300         return false;
1301     }
1302 
1303     /**
1304      * Creates a concrete collection for the suppied interface
1305      *
1306      @param interfaceType The interface
1307      @return ArrayList for List, TreeSet for SortedSet, HashSet for Set etc.
1308      */
1309     public static Collection createConcreteCollection(Class<?> interfaceType) {
1310         Collection elements;
1311         if (interfaceType.equals(List.class)) {
1312             elements = new ArrayList();
1313         else if (interfaceType.equals(SortedSet.class)) {
1314             elements = new TreeSet();
1315         else {
1316             elements = new HashSet();
1317         }
1318         return elements;
1319     }
1320 
1321     /**
1322      * Retrieves the name of a setter for the specified property name
1323      *
1324      @param propertyName The property name
1325      @return The setter equivalent
1326      */
1327     public static String getSetterName(String propertyName) {
1328         return PROPERTY_SET_PREFIX + propertyName.substring(01).toUpperCase() + propertyName.substring(1);
1329     }
1330 
1331     /**
1332      * Returns true if the name of the method specified and the number of arguments make it a javabean property
1333      *
1334      @param name True if its a Javabean property
1335      @param args The arguments
1336      @return True if it is a javabean property method
1337      */
1338     public static boolean isGetter(String name, Class[] args) {
1339         if (GriffonNameUtils.isBlank(name|| args == nullreturn false;
1340         if (args.length != 0return false;
1341 
1342         if (name.startsWith(PROPERTY_GET_PREFIX)) {
1343             name = name.substring(3);
1344             if (name.length() && Character.isUpperCase(name.charAt(0))) return true;
1345         else if (name.startsWith(PROPERTY_IS_PREFIX)) {
1346             name = name.substring(2);
1347             if (name.length() && Character.isUpperCase(name.charAt(0))) return true;
1348         }
1349         return false;
1350     }
1351 
1352     /**
1353      * Returns a property name equivalent for the given getter name or null if it is not a getter
1354      *
1355      @param getterName The getter name
1356      @return The property name equivalent
1357      */
1358     public static String getPropertyForGetter(String getterName) {
1359         if (GriffonNameUtils.isBlank(getterName)) return null;
1360 
1361         if (getterName.startsWith(PROPERTY_GET_PREFIX)) {
1362             String prop = getterName.substring(3);
1363             return convertPropertyName(prop);
1364         else if (getterName.startsWith(PROPERTY_IS_PREFIX)) {
1365             String prop = getterName.substring(2);
1366             return convertPropertyName(prop);
1367         }
1368         return null;
1369     }
1370 
1371     private static String convertPropertyName(String prop) {
1372         if (Character.isUpperCase(prop.charAt(0)) && Character.isUpperCase(prop.charAt(1))) {
1373             return prop;
1374         else if (Character.isDigit(prop.charAt(0))) {
1375             return prop;
1376         else {
1377             return Character.toLowerCase(prop.charAt(0)) + prop.substring(1);
1378         }
1379     }
1380 
1381     /**
1382      * Returns a property name equivalent for the given setter name or null if it is not a getter
1383      *
1384      @param setterName The setter name
1385      @return The property name equivalent
1386      */
1387     public static String getPropertyForSetter(String setterName) {
1388         if (GriffonNameUtils.isBlank(setterName)) return null;
1389 
1390         if (setterName.startsWith(PROPERTY_SET_PREFIX)) {
1391             String prop = setterName.substring(3);
1392             return convertPropertyName(prop);
1393         }
1394         return null;
1395     }
1396 
1397     public static boolean isSetter(String name, Class[] args) {
1398         if (GriffonNameUtils.isBlank(name|| args == nullreturn false;
1399 
1400         if (name.startsWith(PROPERTY_SET_PREFIX)) {
1401             if (args.length != 1return false;
1402             name = name.substring(3);
1403             if (name.length() && Character.isUpperCase(name.charAt(0))) return true;
1404         }
1405 
1406         return false;
1407     }
1408 
1409     public static MetaClass getExpandoMetaClass(Class<?> clazz) {
1410         MetaClassRegistry registry = GroovySystem.getMetaClassRegistry();
1411         isTrue(registry.getMetaClassCreationHandler() instanceof ExpandoMetaClassCreationHandle, "Griffon requires an instance of [ExpandoMetaClassCreationHandle] to be set in Groovy's MetaClassRegistry!");
1412         MetaClass mc = registry.getMetaClass(clazz);
1413         AdaptingMetaClass adapter = null;
1414         if (mc instanceof AdaptingMetaClass) {
1415             adapter = (AdaptingMetaClassmc;
1416             mc = ((AdaptingMetaClassmc).getAdaptee();
1417         }
1418 
1419         if (!(mc instanceof ExpandoMetaClass)) {
1420             // removes cached version
1421             registry.removeMetaClass(clazz);
1422             mc = registry.getMetaClass(clazz);
1423             if (adapter != null) {
1424                 adapter.setAdaptee(mc);
1425             }
1426         }
1427         isTrue(mc instanceof ExpandoMetaClass, "BUG! Method must return an instance of [ExpandoMetaClass]!");
1428         return mc;
1429     }
1430 
1431     /**
1432      * Returns true if the specified clazz parameter is either the same as, or is a superclass or superinterface
1433      * of, the specified type parameter. Converts primitive types to compatible class automatically.
1434      *
1435      @param clazz
1436      @param type
1437      @return True if the class is a taglib
1438      @see java.lang.Class#isAssignableFrom(Class)
1439      */
1440     public static boolean isAssignableOrConvertibleFrom(Class<?> clazz, Class<?> type) {
1441         if (type == null || clazz == null) {
1442             return false;
1443         else if (type.isPrimitive()) {
1444             // convert primitive type to compatible class 
1445             Class<?> primitiveClass = (Class<?>PRIMITIVE_TYPE_COMPATIBLE_CLASSES.get(type);
1446             if (primitiveClass == null) {
1447                 // no compatible class found for primitive type
1448                 return false;
1449             else {
1450                 return clazz.isAssignableFrom(primitiveClass);
1451             }
1452         else {
1453             return clazz.isAssignableFrom(type);
1454         }
1455     }
1456 
1457     /**
1458      * Retrieves a boolean value from a Map for the given key
1459      *
1460      @param key The key that references the boolean value
1461      @param map The map to look in
1462      @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
1463      */
1464     public static boolean getBooleanFromMap(String key, Map map) {
1465         if (map == nullreturn false;
1466         if (map.containsKey(key)) {
1467             Object o = map.get(key);
1468             if (o == nullreturn false;
1469             else if (instanceof Boolean) {
1470                 return ((Booleano).booleanValue();
1471             else {
1472                 return Boolean.valueOf(o.toString()).booleanValue();
1473             }
1474         }
1475         return false;
1476     }
1477 
1478     /**
1479      * Locates the name of a property for the given value on the target object using Groovy's meta APIs.
1480      * Note that this method uses the reference so the incorrect result could be returned for two properties
1481      * that refer to the same reference. Use with caution.
1482      *
1483      @param target The target
1484      @param obj    The property value
1485      @return The property name or null
1486      */
1487     public static String findPropertyNameForValue(Object target, Object obj) {
1488         MetaClass mc = GroovySystem.getMetaClassRegistry().getMetaClass(target.getClass());
1489         List<MetaProperty> metaProperties = mc.getProperties();
1490         for (MetaProperty metaProperty : metaProperties) {
1491             if (isAssignableOrConvertibleFrom(metaProperty.getType(), obj.getClass())) {
1492                 Object val = metaProperty.getProperty(target);
1493                 if (val != null && val.equals(obj))
1494                     return metaProperty.getName();
1495             }
1496         }
1497         return null;
1498     }
1499 
1500     /**
1501      * Returns whether the specified class is either within one of the specified packages or
1502      * within a subpackage of one of the packages
1503      *
1504      @param theClass    The class
1505      @param packageList The list of packages
1506      @return True if it is within the list of specified packages
1507      */
1508     public static boolean isClassBelowPackage(Class<?> theClass, List packageList) {
1509         String classPackage = theClass.getPackage().getName();
1510         for (Object packageName : packageList) {
1511             if (packageName != null) {
1512                 if (classPackage.startsWith(packageName.toString())) {
1513                     return true;
1514                 }
1515             }
1516         }
1517         return false;
1518     }
1519 
1520     // -- The following methods and properties were copied from commons-beanutils
1521 
1522     private static final Map<String, PropertyDescriptor[]> descriptorsCache = new LinkedHashMap<String, PropertyDescriptor[]>();
1523 
1524     /**
1525      <p>Retrieve the property descriptor for the specified property of the
1526      * specified bean, or return <code>null</code> if there is no such
1527      * descriptor.</p>
1528      * This method does not resolve index, nested nor mapped properties.<p>
1529      *
1530      @param bean Bean for which a property descriptor is requested
1531      @param name name of the property for which a property descriptor
1532      *             is requested
1533      @return the property descriptor or null if the bean does not have
1534      *         a property that matches the specified name.
1535      @throws IllegalAccessException    if the caller does not have
1536      *                                   access to the property accessor method
1537      @throws IllegalArgumentException  if <code>bean</code> or
1538      *                                   <code>name</code> is null
1539      @throws InvocationTargetException if the property accessor method
1540      *                                   throws an exception
1541      @throws NoSuchMethodException     if an accessor method for this
1542      *                                   property cannot be found
1543      */
1544     public static PropertyDescriptor getPropertyDescriptor(Object bean,
1545                                                            String name)
1546             throws IllegalAccessException, InvocationTargetException,
1547             NoSuchMethodException {
1548         if (bean == null) {
1549             throw new IllegalArgumentException("No bean specified");
1550         }
1551         if (name == null) {
1552             throw new IllegalArgumentException("No name specified for bean class '" +
1553                     bean.getClass() "'");
1554         }
1555 
1556         return getPropertyDescriptor(bean instanceof Class ? (Class<?>bean : bean.getClass(), name);
1557     }
1558 
1559     /**
1560      <p>Retrieve the property descriptor for the specified property of the
1561      * specified class, or return <code>null</code> if there is no such
1562      * descriptor.</p>
1563      * This method does not resolve index, nested nor mapped properties.<p>
1564      *
1565      @param clazz class for which a property descriptor is requested
1566      @param name  name of the property for which a property descriptor
1567      *              is requested
1568      @return the property descriptor or null if the bean does not have
1569      *         a property that matches the specified name.
1570      @throws IllegalAccessException    if the caller does not have
1571      *                                   access to the property accessor method
1572      @throws IllegalArgumentException  if <code>bean</code> or
1573      *                                   <code>name</code> is null
1574      @throws InvocationTargetException if the property accessor method
1575      *                                   throws an exception
1576      @throws NoSuchMethodException     if an accessor method for this
1577      *                                   property cannot be found
1578      */
1579     public static PropertyDescriptor getPropertyDescriptor(Class<?> clazz,
1580                                                            String name)
1581             throws IllegalAccessException, InvocationTargetException,
1582             NoSuchMethodException {
1583         if (clazz == null) {
1584             throw new IllegalArgumentException("No class specified");
1585         }
1586         if (name == null) {
1587             throw new IllegalArgumentException("No name specified for class '" + clazz + "'");
1588         }
1589 
1590         PropertyDescriptor[] descriptors = getPropertyDescriptors(clazz);
1591         if (descriptors != null) {
1592             for (int i = 0; i < descriptors.length; i++) {
1593                 if (name.equals(descriptors[i].getName())) {
1594                     return (descriptors[i]);
1595                 }
1596             }
1597         }
1598 
1599         return null;
1600     }
1601 
1602     /**
1603      <p>Retrieve the property descriptors for the specified class,
1604      * introspecting and caching them the first time a particular bean class
1605      * is encountered.</p>
1606      *
1607      @param beanClass Bean class for which property descriptors are requested
1608      @return the property descriptors
1609      @throws IllegalArgumentException if <code>beanClass</code> is null
1610      */
1611     public static PropertyDescriptor[] getPropertyDescriptors(Class<?> beanClass) {
1612         if (beanClass == null) {
1613             throw new IllegalArgumentException("No bean class specified");
1614         }
1615 
1616         // Look up any cached descriptors for this bean class
1617         PropertyDescriptor[] descriptors = null;
1618         descriptors = (PropertyDescriptor[]) descriptorsCache.get(beanClass.getName());
1619         if (descriptors != null) {
1620             return (descriptors);
1621         }
1622 
1623         // Introspect the bean and cache the generated descriptors
1624         BeanInfo beanInfo = null;
1625         try {
1626             beanInfo = Introspector.getBeanInfo(beanClass);
1627         catch (IntrospectionException e) {
1628             return (new PropertyDescriptor[0]);
1629         }
1630         descriptors = beanInfo.getPropertyDescriptors();
1631         if (descriptors == null) {
1632             descriptors = new PropertyDescriptor[0];
1633         }
1634 
1635         descriptorsCache.put(beanClass.getName(), descriptors);
1636         return (descriptors);
1637     }
1638 
1639     /**
1640      <p>Return an accessible property getter method for this property,
1641      * if there is one; otherwise return <code>null</code>.</p>
1642      *
1643      @param descriptor Property descriptor to return a getter for
1644      @return The read method
1645      */
1646     public static Method getReadMethod(PropertyDescriptor descriptor) {
1647         return (MethodUtils.getAccessibleMethod(descriptor.getReadMethod()));
1648     }
1649 
1650     /**
1651      <p>Return <code>true</code> if the specified property name identifies
1652      * a readable property on the specified bean; otherwise, return
1653      <code>false</code>.
1654      *
1655      @param bean Bean to be examined
1656      @param name Property name to be evaluated
1657      @return <code>true</code> if the property is readable,
1658      *         otherwise <code>false</code>
1659      @throws IllegalArgumentException if <code>bean</code>
1660      *                                  or <code>name</code> is <code>null</code>
1661      @since BeanUtils 1.6
1662      */
1663     public static boolean isReadable(Object bean, String name) {
1664         // Validate method parameters
1665         if (bean == null) {
1666             throw new IllegalArgumentException("No bean specified");
1667         }
1668         if (name == null) {
1669             throw new IllegalArgumentException("No name specified for bean class '" +
1670                     bean.getClass() "'");
1671         }
1672 
1673         try {
1674             PropertyDescriptor desc = getPropertyDescriptor(bean, name);
1675             if (desc != null) {
1676                 Method readMethod = getReadMethod(bean.getClass(), desc);
1677                 if (readMethod == null) {
1678                     readMethod = MethodUtils.getAccessibleMethod(bean.getClass(), readMethod);
1679                 }
1680                 return (readMethod != null);
1681             else {
1682                 return (false);
1683             }
1684         catch (IllegalAccessException e) {
1685             return (false);
1686         catch (InvocationTargetException e) {
1687             return (false);
1688         catch (NoSuchMethodException e) {
1689             return (false);
1690         }
1691     }
1692 
1693     /**
1694      * Return the value of the specified property of the specified bean,
1695      * no matter which property reference format is used, with no
1696      * type conversions.
1697      *
1698      @param bean Bean whose property is to be extracted
1699      @param name Possibly indexed and/or nested name of the property
1700      *             to be extracted
1701      @return the property value
1702      @throws IllegalAccessException    if the caller does not have
1703      *                                   access to the property accessor method
1704      @throws IllegalArgumentException  if <code>bean</code> or
1705      *                                   <code>name</code> is null
1706      @throws InvocationTargetException if the property accessor method
1707      *                                   throws an exception
1708      @throws NoSuchMethodException     if an accessor method for this
1709      *                                   property cannot be found
1710      */
1711     public static Object getProperty(Object bean, String name)
1712             throws IllegalAccessException, InvocationTargetException,
1713             NoSuchMethodException {
1714         if (bean == null) {
1715             throw new IllegalArgumentException("No bean specified");
1716         }
1717         if (name == null) {
1718             throw new IllegalArgumentException("No name specified for bean class '" +
1719                     bean.getClass() "'");
1720         }
1721 
1722         // Retrieve the property getter method for the specified property
1723         PropertyDescriptor descriptor = getPropertyDescriptor(bean, name);
1724         if (descriptor == null) {
1725             throw new NoSuchMethodException("Unknown property '" +
1726                     name + "' on class '" + bean.getClass() "'");
1727         }
1728         Method readMethod = getReadMethod(bean.getClass(), descriptor);
1729         if (readMethod == null) {
1730             throw new NoSuchMethodException("Property '" + name +
1731                     "' has no getter method in class '" + bean.getClass() "'");
1732         }
1733 
1734         // Call the property getter and return the value
1735         Object value = readMethod.invoke(bean, EMPTY_OBJECT_ARRAY);
1736         return (value);
1737     }
1738 
1739     /**
1740      <p>Return an accessible property getter method for this property,
1741      * if there is one; otherwise return <code>null</code>.</p>
1742      *
1743      @param clazz      The class of the read method will be invoked on
1744      @param descriptor Property descriptor to return a getter for
1745      @return The read method
1746      */
1747     public static Method getReadMethod(Class<?> clazz, PropertyDescriptor descriptor) {
1748         return (MethodUtils.getAccessibleMethod(clazz, descriptor.getReadMethod()));
1749     }
1750 
1751     // -- The following methods and properties were copied from commons-lang
1752 
1753     /**
1754      <p>Validate that the argument condition is <code>true</code>; otherwise
1755      * throwing an exception with the specified message. This method is useful when
1756      * validating according to an arbitrary boolean expression, such as validating a
1757      * primitive number or using your own custom validation expression.</p>
1758      <p/>
1759      <pre>
1760      * isTrue( (i > 0), "The value must be greater than zero");
1761      * isTrue( myObject.isOk(), "The object is not OK");
1762      </pre>
1763      *
1764      @param expression the boolean expression to check
1765      @param message    the exception message if invalid
1766      @throws IllegalArgumentException if expression is <code>false</code>
1767      */
1768     public static void isTrue(boolean expression, String message) {
1769         if (expression) {
1770             throw new IllegalArgumentException(message);
1771         }
1772     }
1773 
1774     public static Object invokeInstanceMethod(Object object, String methodName) {
1775         return invokeInstanceMethod(object, methodName, EMPTY_ARGS);
1776     }
1777 
1778     public static Object invokeInstanceMethod(Object object, String methodName, Object arg) {
1779         return invokeInstanceMethod(object, methodName, new Object[]{arg});
1780     }
1781 
1782     public static Object invokeInstanceMethod(Object object, String methodName, Object... args) {
1783         try {
1784             return invokeMethod(object, methodName, args);
1785         catch (NoSuchMethodException e) {
1786             throw new RuntimeException(e);
1787         catch (IllegalAccessException e) {
1788             throw new RuntimeException(e);
1789         catch (InvocationTargetException e) {
1790             Throwable cause = e.getTargetException();
1791             if (cause instanceof RuntimeException) {
1792                 throw (RuntimeExceptioncause;
1793             }
1794             throw new RuntimeException(e.getMessage(), cause);
1795         }
1796     }
1797 
1798     public static Object invokeExactInstanceMethod(Object object, String methodName) {
1799         return invokeExactInstanceMethod(object, methodName, EMPTY_ARGS);
1800     }
1801 
1802     public static Object invokeExactInstanceMethod(Object object, String methodName, Object arg) {
1803         return invokeExactInstanceMethod(object, methodName, new Object[]{arg});
1804     }
1805 
1806     public static Object invokeExactInstanceMethod(Object object, String methodName, Object... args) {
1807         try {
1808             return invokeExactMethod(object, methodName, args);
1809         catch (NoSuchMethodException e) {
1810             throw new RuntimeException(e);
1811         catch (IllegalAccessException e) {
1812             throw new RuntimeException(e);
1813         catch (InvocationTargetException e) {
1814             Throwable cause = e.getTargetException();
1815             if (cause instanceof RuntimeException) {
1816                 throw (RuntimeExceptioncause;
1817             }
1818             throw new RuntimeException(e.getMessage(), cause);
1819         }
1820     }
1821 
1822     public static Object invokeStaticMethod(Class type, String methodName) {
1823         return invokeStaticMethod(type, methodName, EMPTY_ARGS);
1824     }
1825 
1826     public static Object invokeStaticMethod(Class type, String methodName, Object arg) {
1827         return invokeStaticMethod(type, methodName, new Object[]{arg});
1828     }
1829 
1830     public static Object invokeStaticMethod(Class type, String methodName, Object... args) {
1831         try {
1832             return MethodUtils.invokeStaticMethod(type, methodName, args);
1833         catch (NoSuchMethodException e) {
1834             throw new RuntimeException(e);
1835         catch (IllegalAccessException e) {
1836             throw new RuntimeException(e);
1837         catch (InvocationTargetException e) {
1838             Throwable cause = e.getTargetException();
1839             if (cause instanceof RuntimeException) {
1840                 throw (RuntimeExceptioncause;
1841             }
1842             throw new RuntimeException(e.getMessage(), cause);
1843         }
1844     }
1845 
1846     public static Object invokeExactStaticMethod(Class type, String methodName) {
1847         return invokeExactStaticMethod(type, methodName, EMPTY_ARGS);
1848     }
1849 
1850     public static Object invokeExactStaticMethod(Class type, String methodName, Object arg) {
1851         return invokeExactStaticMethod(type, methodName, new Object[]{arg});
1852     }
1853 
1854     public static Object invokeExactStaticMethod(Class type, String methodName, Object... args) {
1855         try {
1856             return MethodUtils.invokeExactStaticMethod(type, methodName, args);
1857         catch (NoSuchMethodException e) {
1858             throw new RuntimeException(e);
1859         catch (IllegalAccessException e) {
1860             throw new RuntimeException(e);
1861         catch (InvocationTargetException e) {
1862             Throwable cause = e.getTargetException();
1863             if (cause instanceof RuntimeException) {
1864                 throw (RuntimeExceptioncause;
1865             }
1866             throw new RuntimeException(e.getMessage(), cause);
1867         }
1868     }
1869 
1870     private static final String EMPTY_STRING = "";
1871 
1872     /**
1873      <p>The package separator character: <code>'&#x2e;' == {@value}</code>.</p>
1874      */
1875     public static final char PACKAGE_SEPARATOR_CHAR = '.';
1876 
1877     /**
1878      <p>The package separator String: <code>"&#x2e;"</code>.</p>
1879      */
1880     public static final String PACKAGE_SEPARATOR = String.valueOf(PACKAGE_SEPARATOR_CHAR);
1881 
1882     /**
1883      <p>The inner class separator character: <code>'$' == {@value}</code>.</p>
1884      */
1885     public static final char INNER_CLASS_SEPARATOR_CHAR = '$';
1886 
1887     /**
1888      <p>The inner class separator String: <code>"$"</code>.</p>
1889      */
1890     public static final String INNER_CLASS_SEPARATOR = String.valueOf(INNER_CLASS_SEPARATOR_CHAR);
1891 
1892     /**
1893      * Maps a primitive class name to its corresponding abbreviation used in array class names.
1894      */
1895     private static final Map abbreviationMap = new HashMap();
1896 
1897     /**
1898      * Maps an abbreviation used in array class names to corresponding primitive class name.
1899      */
1900     private static final Map reverseAbbreviationMap = new HashMap();
1901 
1902     /**
1903      * Add primitive type abbreviation to maps of abbreviations.
1904      *
1905      @param primitive    Canonical name of primitive type
1906      @param abbreviation Corresponding abbreviation of primitive type
1907      */
1908     private static void addAbbreviation(String primitive, String abbreviation) {
1909         abbreviationMap.put(primitive, abbreviation);
1910         reverseAbbreviationMap.put(abbreviation, primitive);
1911     }
1912 
1913     /**
1914      * Feed abbreviation maps
1915      */
1916     static {
1917         addAbbreviation("int""I");
1918         addAbbreviation("boolean""Z");
1919         addAbbreviation("float""F");
1920         addAbbreviation("long""J");
1921         addAbbreviation("short""S");
1922         addAbbreviation("byte""B");
1923         addAbbreviation("double""D");
1924         addAbbreviation("char""C");
1925     }
1926 
1927     // ----------------------------------------------------------------------
1928 
1929     /**
1930      <p>Gets the class name minus the package name for an <code>Object</code>.</p>
1931      *
1932      @param object      the class to get the short name for, may be null
1933      @param valueIfNull the value to return if null
1934      @return the class name of the object without the package name, or the null value
1935      */
1936     public static String getShortClassName(Object object, String valueIfNull) {
1937         if (object == null) {
1938             return valueIfNull;
1939         }
1940         return getShortClassName(object.getClass());
1941     }
1942 
1943     /**
1944      <p>Gets the class name minus the package name from a <code>Class</code>.</p>
1945      *
1946      @param cls the class to get the short name for.
1947      @return the class name without the package name or an empty string
1948      */
1949     public static String getShortClassName(Class<?> cls) {
1950         if (cls == null) {
1951             return EMPTY_STRING;
1952         }
1953         return getShortClassName(cls.getName());
1954     }
1955 
1956     /**
1957      <p>Gets the class name minus the package name from a String.</p>
1958      <p/>
1959      <p>The string passed in is assumed to be a class name - it is not checked.</p>
1960      *
1961      @param className the className to get the short name for
1962      @return the class name of the class without the package name or an empty string
1963      */
1964     public static String getShortClassName(String className) {
1965         if (className == null) {
1966             return EMPTY_STRING;
1967         }
1968         if (className.length() == 0) {
1969             return EMPTY_STRING;
1970         }
1971 
1972         StringBuffer arrayPrefix = new StringBuffer();
1973 
1974         // Handle array encoding
1975         if (className.startsWith("[")) {
1976             while (className.charAt(0== '[') {
1977                 className = className.substring(1);
1978                 arrayPrefix.append("[]");
1979             }
1980             // Strip Object type encoding
1981             if (className.charAt(0== 'L' && className.charAt(className.length() 1== ';') {
1982                 className = className.substring(1, className.length() 1);
1983             }
1984         }
1985 
1986         if (reverseAbbreviationMap.containsKey(className)) {
1987             className = (StringreverseAbbreviationMap.get(className);
1988         }
1989 
1990         int lastDotIdx = className.lastIndexOf(PACKAGE_SEPARATOR_CHAR);
1991         int innerIdx = className.indexOf(
1992                 INNER_CLASS_SEPARATOR_CHAR, lastDotIdx == -: lastDotIdx + 1);
1993         String out = className.substring(lastDotIdx + 1);
1994         if (innerIdx != -1) {
1995             out = out.replace(INNER_CLASS_SEPARATOR_CHAR, PACKAGE_SEPARATOR_CHAR);
1996         }
1997         return out + arrayPrefix;
1998     }
1999 
2000     // Package name
2001     // ----------------------------------------------------------------------
2002 
2003     /**
2004      <p>Gets the package name of an <code>Object</code>.</p>
2005      *
2006      @param object      the class to get the package name for, may be null
2007      @param valueIfNull the value to return if null
2008      @return the package name of the object, or the null value
2009      */
2010     public static String getPackageName(Object object, String valueIfNull) {
2011         if (object == null) {
2012             return valueIfNull;
2013         }
2014         return getPackageName(object.getClass());
2015     }
2016 
2017     /**
2018      <p>Gets the package name of a <code>Class</code>.</p>
2019      *
2020      @param cls the class to get the package name for, may be <code>null</code>.
2021      @return the package name or an empty string
2022      */
2023     public static String getPackageName(Class<?> cls) {
2024         if (cls == null) {
2025             return EMPTY_STRING;
2026         }
2027         return getPackageName(cls.getName());
2028     }
2029 
2030     /**
2031      <p>Gets the package name from a <code>String</code>.</p>
2032      <p/>
2033      <p>The string passed in is assumed to be a class name - it is not checked.</p>
2034      <p>If the class is unpackaged, return an empty string.</p>
2035      *
2036      @param className the className to get the package name for, may be <code>null</code>
2037      @return the package name or an empty string
2038      */
2039     public static String getPackageName(String className) {
2040         if (className == null || className.length() == 0) {
2041             return EMPTY_STRING;
2042         }
2043 
2044         // Strip array encoding
2045         while (className.charAt(0== '[') {
2046             className = className.substring(1);
2047         }
2048         // Strip Object type encoding
2049         if (className.charAt(0== 'L' && className.charAt(className.length() 1== ';') {
2050             className = className.substring(1);
2051         }
2052 
2053         int i = className.lastIndexOf(PACKAGE_SEPARATOR_CHAR);
2054         if (i == -1) {
2055             return EMPTY_STRING;
2056         }
2057         return className.substring(0, i);
2058     }
2059 
2060     public static class MethodDescriptor implements Comparable {
2061         private final String methodName;
2062         private final String[] paramTypes;
2063         private final int hashCode;
2064         private final int modifiers;
2065 
2066         private static final String[] EMPTY_CLASS_PARAMETERS = new String[0];
2067 
2068         public static MethodDescriptor forMethod(Method method) {
2069             if (method == nullreturn null;
2070             return new MethodDescriptor(method.getName(), method.getParameterTypes(), method.getModifiers());
2071         }
2072 
2073         public static MethodDescriptor forMethod(MetaMethod method) {
2074             if (method == nullreturn null;
2075             CachedClass[] types = method.getParameterTypes();
2076             String[] parameterTypes = new String[types.length];
2077             for (int i = 0; i < types.length; i++) {
2078                 parameterTypes[i= types[i].getTheClass().getName();
2079             }
2080             return new MethodDescriptor(method.getName(), parameterTypes, method.getModifiers());
2081         }
2082 
2083         public MethodDescriptor(String methodName) {
2084             this(methodName, EMPTY_CLASS_PARAMETERS, Modifier.PUBLIC);
2085         }
2086 
2087         public MethodDescriptor(String methodName, int modifiers) {
2088             this(methodName, EMPTY_CLASS_PARAMETERS, modifiers);
2089         }
2090 
2091         public MethodDescriptor(String methodName, Class[] paramTypes) {
2092             this(methodName, paramTypes, Modifier.PUBLIC);
2093         }
2094 
2095         public MethodDescriptor(String methodName, String[] paramTypes) {
2096             this(methodName, paramTypes, Modifier.PUBLIC);
2097         }
2098 
2099         public MethodDescriptor(String methodName, Class[] paramTypes, int modifiers) {
2100             this.methodName = methodName;
2101             if (paramTypes == null) {
2102                 this.paramTypes = EMPTY_CLASS_PARAMETERS;
2103             else {
2104                 this.paramTypes = new String[paramTypes.length];
2105                 for (int i = 0; i < paramTypes.length; i++) {
2106                     this.paramTypes[i= paramTypes[i].getName();
2107                 }
2108             }
2109             this.modifiers = modifiers;
2110 
2111             this.hashCode = methodName.length() + modifiers;
2112         }
2113 
2114         public MethodDescriptor(String methodName, String[] paramTypes, int modifiers) {
2115             this.methodName = methodName;
2116             this.paramTypes = paramTypes == null ? EMPTY_CLASS_PARAMETERS : paramTypes;
2117             this.modifiers = modifiers;
2118 
2119             this.hashCode = methodName.length() + modifiers;
2120         }
2121 
2122         public String getName() {
2123             return methodName;
2124         }
2125 
2126         public String[] getParameterTypes() {
2127             return paramTypes;
2128         }
2129 
2130         public int getModifiers() {
2131             return modifiers;
2132         }
2133 
2134         public boolean equals(Object obj) {
2135             if (!(obj instanceof MethodDescriptor)) {
2136                 return false;
2137             }
2138             MethodDescriptor md = (MethodDescriptorobj;
2139 
2140             return methodName.equals(md.methodName&&
2141                     modifiers == md.modifiers &&
2142                     java.util.Arrays.equals(paramTypes, md.paramTypes);
2143         }
2144 
2145         public int hashCode() {
2146             return hashCode;
2147         }
2148 
2149         public String toString() {
2150             StringBuilder b = new StringBuilder();
2151             b.append(Modifier.toString(modifiers)).append(" ");
2152             b.append(methodName).append("(");
2153             for (int i = 0; i < paramTypes.length; i++) {
2154                 if (i != 0b.append(", ");
2155                 b.append(paramTypes[i]);
2156             }
2157             b.append(")");
2158             return b.toString();
2159         }
2160 
2161         public int compareTo(Object obj) {
2162             if (!(obj instanceof MethodDescriptor)) {
2163                 return -1;
2164             }
2165             MethodDescriptor md = (MethodDescriptorobj;
2166 
2167             int c = methodName.compareTo(md.methodName);
2168             if (c != 0return c;
2169             c = modifiers - md.modifiers;
2170             if (c != 0return c;
2171             c = paramTypes.length - md.paramTypes.length;
2172             if (c != 0return c;
2173             for (int i = 0; i < paramTypes.length; i++) {
2174                 c = paramTypes[i].compareTo(md.paramTypes[i]);
2175                 if (c != 0return c;
2176             }
2177 
2178             return 0;
2179         }
2180     }
2181 }