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