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 != 0) return 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 == null) return 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 == null) return 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 == null) return 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 == null) return 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 == null) return 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 != null) return 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 == null) return 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(0, 1).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 == null) return false;
1340 if (args.length != 0) return false;
1341
1342 if (name.startsWith(PROPERTY_GET_PREFIX)) {
1343 name = name.substring(3);
1344 if (name.length() > 0 && Character.isUpperCase(name.charAt(0))) return true;
1345 } else if (name.startsWith(PROPERTY_IS_PREFIX)) {
1346 name = name.substring(2);
1347 if (name.length() > 0 && 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 == null) return false;
1399
1400 if (name.startsWith(PROPERTY_SET_PREFIX)) {
1401 if (args.length != 1) return false;
1402 name = name.substring(3);
1403 if (name.length() > 0 && 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 = (AdaptingMetaClass) mc;
1416 mc = ((AdaptingMetaClass) mc).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 == null) return false;
1466 if (map.containsKey(key)) {
1467 Object o = map.get(key);
1468 if (o == null) return false;
1469 else if (o instanceof Boolean) {
1470 return ((Boolean) o).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 (RuntimeException) cause;
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 (RuntimeException) cause;
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 (RuntimeException) cause;
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 (RuntimeException) cause;
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>'.' == {@value}</code>.</p>
1874 */
1875 public static final char PACKAGE_SEPARATOR_CHAR = '.';
1876
1877 /**
1878 * <p>The package separator String: <code>"."</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 = (String) reverseAbbreviationMap.get(className);
1988 }
1989
1990 int lastDotIdx = className.lastIndexOf(PACKAGE_SEPARATOR_CHAR);
1991 int innerIdx = className.indexOf(
1992 INNER_CLASS_SEPARATOR_CHAR, lastDotIdx == -1 ? 0 : 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 == null) return null;
2070 return new MethodDescriptor(method.getName(), method.getParameterTypes(), method.getModifiers());
2071 }
2072
2073 public static MethodDescriptor forMethod(MetaMethod method) {
2074 if (method == null) return 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 = (MethodDescriptor) obj;
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 != 0) b.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 = (MethodDescriptor) obj;
2166
2167 int c = methodName.compareTo(md.methodName);
2168 if (c != 0) return c;
2169 c = modifiers - md.modifiers;
2170 if (c != 0) return c;
2171 c = paramTypes.length - md.paramTypes.length;
2172 if (c != 0) return c;
2173 for (int i = 0; i < paramTypes.length; i++) {
2174 c = paramTypes[i].compareTo(md.paramTypes[i]);
2175 if (c != 0) return c;
2176 }
2177
2178 return 0;
2179 }
2180 }
2181 }
|