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