0001 /*
0002 * SPDX-License-Identifier: Apache-2.0
0003 *
0004 * Copyright 2008-2017 the original author or authors.
0005 *
0006 * Licensed under the Apache License, Version 2.0 (the "License");
0007 * you may not use this file except in compliance with the License.
0008 * You may obtain a copy of the License at
0009 *
0010 * http://www.apache.org/licenses/LICENSE-2.0
0011 *
0012 * Unless required by applicable law or agreed to in writing, software
0013 * distributed under the License is distributed on an "AS IS" BASIS,
0014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0015 * See the License for the specific language governing permissions and
0016 * limitations under the License.
0017 */
0018 package griffon.util;
0019
0020 import griffon.core.Observable;
0021 import griffon.core.Vetoable;
0022 import griffon.core.artifact.GriffonArtifact;
0023 import griffon.core.artifact.GriffonMvcArtifact;
0024 import griffon.core.event.EventPublisher;
0025 import griffon.core.i18n.MessageSource;
0026 import griffon.core.mvc.MVCHandler;
0027 import griffon.core.resources.ResourceHandler;
0028 import griffon.core.resources.ResourceResolver;
0029 import griffon.core.threading.ThreadingHandler;
0030 import griffon.exceptions.BeanInstantiationException;
0031 import griffon.exceptions.FieldException;
0032 import griffon.exceptions.InstanceMethodInvocationException;
0033 import griffon.exceptions.PropertyException;
0034 import griffon.exceptions.StaticMethodInvocationException;
0035
0036 import javax.annotation.Nonnull;
0037 import javax.annotation.Nullable;
0038 import java.beans.BeanInfo;
0039 import java.beans.IntrospectionException;
0040 import java.beans.Introspector;
0041 import java.beans.PropertyDescriptor;
0042 import java.lang.annotation.Annotation;
0043 import java.lang.reflect.Field;
0044 import java.lang.reflect.InvocationTargetException;
0045 import java.lang.reflect.Method;
0046 import java.lang.reflect.Modifier;
0047 import java.security.AccessController;
0048 import java.security.PrivilegedAction;
0049 import java.util.ArrayList;
0050 import java.util.Arrays;
0051 import java.util.Collection;
0052 import java.util.HashMap;
0053 import java.util.HashSet;
0054 import java.util.LinkedHashMap;
0055 import java.util.LinkedHashSet;
0056 import java.util.List;
0057 import java.util.Map;
0058 import java.util.Set;
0059 import java.util.SortedSet;
0060 import java.util.TreeSet;
0061 import java.util.regex.Pattern;
0062
0063 import static griffon.util.GriffonNameUtils.requireNonBlank;
0064 import static griffon.util.MethodUtils.invokeExactMethod;
0065 import static griffon.util.MethodUtils.invokeMethod;
0066 import static java.util.Objects.requireNonNull;
0067
0068 /**
0069 * Class containing utility methods for dealing with Griffon class artifacts.<p>
0070 * Contains utility methods copied from commons-lang and commons-beanutils in order
0071 * to reduce dependencies on external libraries.<p>
0072 * <p>
0073 * <b>Contains code copied from commons-beanutils and commons-langs</b>
0074 *
0075 * @author Graeme Rocher (Grails 0.1)
0076 */
0077 public class GriffonClassUtils {
0078 public static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0];
0079 public static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
0080 public static final Object[] EMPTY_ARGS = EMPTY_OBJECT_ARRAY;
0081
0082 private static final String PROPERTY_GET_PREFIX = "get";
0083 private static final String PROPERTY_IS_PREFIX = "is";
0084 private static final String PROPERTY_SET_PREFIX = "set";
0085 private static final String ON_SHUTDOWN_METHOD_NAME = "onShutdown";
0086 public static final Map<Class<?>, Class<?>> PRIMITIVE_TYPE_COMPATIBLE_CLASSES = new LinkedHashMap<>();
0087 public static final Map<String, String> PRIMITIVE_TYPE_COMPATIBLE_TYPES = new LinkedHashMap<>();
0088
0089 private static final Pattern EVENT_HANDLER_PATTERN = Pattern.compile("^on[A-Z][\\w]*$");
0090 private static final Pattern CONTRIBUTION_PATTERN = Pattern.compile("^with[A-Z][a-z0-9_]*[\\w]*$");
0091 private static final Pattern GETTER_PATTERN_1 = Pattern.compile("^get[A-Z][\\w]*$");
0092 private static final Pattern GETTER_PATTERN_2 = Pattern.compile("^is[A-Z][\\w]*$");
0093 private static final Pattern SETTER_PATTERN = Pattern.compile("^set[A-Z][\\w]*$");
0094 private static final Set<MethodDescriptor> BASIC_METHODS = new TreeSet<>();
0095 private static final Set<MethodDescriptor> ARTIFACT_METHODS = new TreeSet<>();
0096 private static final Set<MethodDescriptor> MVC_METHODS = new TreeSet<>();
0097 private static final Set<MethodDescriptor> THREADING_METHODS = new TreeSet<>();
0098 private static final Set<MethodDescriptor> EVENT_PUBLISHER_METHODS = new TreeSet<>();
0099 private static final Set<MethodDescriptor> OBSERVABLE_METHODS = new TreeSet<>();
0100 private static final Set<MethodDescriptor> RESOURCE_HANDLER_METHODS = new TreeSet<>();
0101 private static final Set<MethodDescriptor> MESSAGE_SOURCE_METHODS = new TreeSet<>();
0102 private static final Set<MethodDescriptor> RESOURCE_RESOLVER_METHODS = new TreeSet<>();
0103 private static final String ERROR_TYPE_NULL = "Argument 'type' must not be null";
0104 private static final String ERROR_METHOD_NAME_BLANK = "Argument 'methodName' must not be blank";
0105 private static final String ERROR_OBJECT_NULL = "Argument 'object' must not be null";
0106 private static final String ERROR_CLAZZ_NULL = "Argument 'clazz' must not be null";
0107 private static final String ERROR_DESCRIPTOR_NULL = "Argument 'descriptor' must not be null";
0108 private static final String ERROR_BEAN_NULL = "Argument 'bean' must not be null";
0109 private static final String ERROR_NAME_BLANK = "Argument 'name' must not be blank";
0110 private static final String ERROR_PROPERTIES_NULL = "Argument 'properties' must not be null";
0111 private static final String ERROR_FIELDS_NULL = "Argument 'fields' must not be null";
0112 private static final String ERROR_PROPERTY_NAME_BLANK = "Argument 'propertyName' must not be blank";
0113 private static final String ERROR_METHOD_NULL = "Argument 'method' must not be null";
0114 private static final String MESSAGE = "message";
0115
0116 /**
0117 * Just add two entries to the class compatibility map
0118 *
0119 * @param left
0120 * @param right
0121 */
0122 private static void registerPrimitiveClassPair(Class<?> left, Class<?> right) {
0123 PRIMITIVE_TYPE_COMPATIBLE_CLASSES.put(left, right);
0124 PRIMITIVE_TYPE_COMPATIBLE_CLASSES.put(right, left);
0125 PRIMITIVE_TYPE_COMPATIBLE_TYPES.put(left.getName(), right.getName());
0126 PRIMITIVE_TYPE_COMPATIBLE_TYPES.put(right.getName(), left.getName());
0127 }
0128
0129 static {
0130 registerPrimitiveClassPair(Boolean.class, boolean.class);
0131 registerPrimitiveClassPair(Integer.class, int.class);
0132 registerPrimitiveClassPair(Short.class, short.class);
0133 registerPrimitiveClassPair(Byte.class, byte.class);
0134 registerPrimitiveClassPair(Character.class, char.class);
0135 registerPrimitiveClassPair(Long.class, long.class);
0136 registerPrimitiveClassPair(Float.class, float.class);
0137 registerPrimitiveClassPair(Double.class, double.class);
0138
0139 for (Method method : Object.class.getMethods()) {
0140 MethodDescriptor md = MethodDescriptor.forMethod(method);
0141 if (!BASIC_METHODS.contains(md)) {
0142 BASIC_METHODS.add(md);
0143 }
0144 }
0145
0146 try {
0147 Class<?> groovyObjectClass = GriffonClassUtils.class.getClassLoader().loadClass("groovy.lang.GroovyObject");
0148 for (Method method : groovyObjectClass.getMethods()) {
0149 MethodDescriptor md = MethodDescriptor.forMethod(method, true);
0150 if (!BASIC_METHODS.contains(md)) {
0151 BASIC_METHODS.add(md);
0152 }
0153 }
0154 } catch (ClassNotFoundException cnfe) {
0155 // ignore
0156 }
0157
0158 try {
0159 Class<?> groovyObjectClass = GriffonClassUtils.class.getClassLoader().loadClass("groovy.lang.GroovyObjectSupport");
0160 for (Method method : groovyObjectClass.getMethods()) {
0161 MethodDescriptor md = MethodDescriptor.forMethod(method);
0162 if (!BASIC_METHODS.contains(md)) {
0163 BASIC_METHODS.add(md);
0164 }
0165 }
0166 } catch (ClassNotFoundException cnfe) {
0167 // ignore
0168 }
0169
0170 for (Method method : GriffonArtifact.class.getMethods()) {
0171 MethodDescriptor md = MethodDescriptor.forMethod(method, true);
0172 if (!ARTIFACT_METHODS.contains(md)) {
0173 ARTIFACT_METHODS.add(md);
0174 }
0175 }
0176
0177 // MVC_METHODS.add(new MethodDescriptor("getMvcGroup"));
0178 for (Method method : MVCHandler.class.getMethods()) {
0179 MethodDescriptor md = MethodDescriptor.forMethod(method, true);
0180 if (!MVC_METHODS.contains(md)) {
0181 MVC_METHODS.add(md);
0182 }
0183 }
0184 for (Method method : GriffonMvcArtifact.class.getMethods()) {
0185 MethodDescriptor md = MethodDescriptor.forMethod(method, true);
0186 if (!MVC_METHODS.contains(md)) {
0187 MVC_METHODS.add(md);
0188 }
0189 }
0190
0191 // GriffonView
0192 MVC_METHODS.add(new MethodDescriptor("initUI"));
0193 // GriffonController
0194 MVC_METHODS.add(new MethodDescriptor("invokeAction", new Class<?>[]{String.class, Object[].class}));
0195 MVC_METHODS.add(new MethodDescriptor("invokeAction", new Class<?>[]{String.class, Object[].class}, Modifier.PUBLIC | Modifier.TRANSIENT));
0196
0197 for (Method method : ThreadingHandler.class.getMethods()) {
0198 MethodDescriptor md = MethodDescriptor.forMethod(method, true);
0199 if (!THREADING_METHODS.contains(md)) {
0200 THREADING_METHODS.add(md);
0201 }
0202 }
0203 // Special case due to the usage of varargs
0204 //THREADING_METHODS.add(new MethodDescriptor("runFuture", new Class<?>[]{Object[].class}));
0205
0206 for (Method method : EventPublisher.class.getMethods()) {
0207 MethodDescriptor md = MethodDescriptor.forMethod(method, true);
0208 if (!EVENT_PUBLISHER_METHODS.contains(md)) {
0209 EVENT_PUBLISHER_METHODS.add(md);
0210 }
0211 }
0212
0213 for (Method method : Observable.class.getMethods()) {
0214 MethodDescriptor md = MethodDescriptor.forMethod(method, true);
0215 if (!OBSERVABLE_METHODS.contains(md)) {
0216 OBSERVABLE_METHODS.add(md);
0217 }
0218 }
0219 for (Method method : Vetoable.class.getMethods()) {
0220 MethodDescriptor md = MethodDescriptor.forMethod(method, true);
0221 if (!OBSERVABLE_METHODS.contains(md)) {
0222 OBSERVABLE_METHODS.add(md);
0223 }
0224 }
0225
0226 for (Method method : ResourceHandler.class.getMethods()) {
0227 MethodDescriptor md = MethodDescriptor.forMethod(method, true);
0228 if (!RESOURCE_HANDLER_METHODS.contains(md)) {
0229 RESOURCE_HANDLER_METHODS.add(md);
0230 }
0231 }
0232
0233 for (Method method : MessageSource.class.getMethods()) {
0234 MethodDescriptor md = MethodDescriptor.forMethod(method, true);
0235 if (!MESSAGE_SOURCE_METHODS.contains(md)) {
0236 MESSAGE_SOURCE_METHODS.add(md);
0237 }
0238 }
0239
0240 for (Method method : ResourceResolver.class.getMethods()) {
0241 MethodDescriptor md = MethodDescriptor.forMethod(method, true);
0242 if (!RESOURCE_RESOLVER_METHODS.contains(md)) {
0243 RESOURCE_RESOLVER_METHODS.add(md);
0244 }
0245 }
0246 }
0247
0248 private GriffonClassUtils() {
0249 // prevent instantiation
0250 }
0251
0252 /**
0253 * Checks that the specified condition is met. This method is designed
0254 * primarily for doing parameter validation in methods and constructors,
0255 * as demonstrated below:
0256 * <blockquote><pre>
0257 * public Foo(int[] array) {
0258 * GriffonClassUtils.requireState(array.length > 0);
0259 * }
0260 * </pre></blockquote>
0261 *
0262 * @param condition the condition to check
0263 *
0264 * @throws IllegalStateException if {@code condition} evaluates to false
0265 */
0266 public static void requireState(boolean condition) {
0267 if (!condition) {
0268 throw new IllegalStateException();
0269 }
0270 }
0271
0272 /**
0273 * Checks that the specified condition is met and throws a customized
0274 * {@link IllegalStateException} if it is. This method is designed primarily
0275 * for doing parameter validation in methods and constructors with multiple
0276 * parameters, as demonstrated below:
0277 * <blockquote><pre>
0278 * public Foo(int[] array) {
0279 * GriffonClassUtils.requireState(array.length > 0, "array must not be empty");
0280 * }
0281 * </pre></blockquote>
0282 *
0283 * @param condition the condition to check
0284 * @param message detail message to be used in the event that a {@code
0285 * IllegalStateException} is thrown
0286 *
0287 * @throws IllegalStateException if {@code condition} evaluates to false
0288 */
0289 public static void requireState(boolean condition, String message) {
0290 if (!condition) {
0291 throw new IllegalStateException(message);
0292 }
0293 }
0294
0295 /**
0296 * Checks that the specified array is not empty, throwing a
0297 * {@link IllegalStateException} if it is.
0298 *
0299 * @param array the array to check
0300 *
0301 * @throws NullPointerException if {@code array} is null
0302 * @throws IllegalStateException if {@code array} is empty
0303 */
0304 public static byte[] requireNonEmpty(@Nonnull byte[] array) {
0305 requireNonNull(array);
0306 requireState(array.length != 0);
0307 return array;
0308 }
0309
0310 /**
0311 * Checks that the specified array is not empty, throwing a customized
0312 * {@link IllegalStateException} if it is.
0313 *
0314 * @param array the array to check
0315 * @param message detail message to be used in the event that a {@code
0316 * IllegalStateException} is thrown
0317 *
0318 * @throws NullPointerException if {@code array} is null
0319 * @throws IllegalArgumentException if {@code message} is {@code blank}
0320 * @throws IllegalStateException if {@code array} is empty
0321 */
0322 public static byte[] requireNonEmpty(@Nonnull byte[] array, @Nonnull String message) {
0323 requireNonNull(array);
0324 requireState(array.length != 0, requireNonBlank(message, MESSAGE));
0325 return array;
0326 }
0327
0328 /**
0329 * Checks that the specified array is not empty, throwing a
0330 * {@link IllegalStateException} if it is.
0331 *
0332 * @param array the array to check
0333 *
0334 * @throws NullPointerException if {@code array} is null
0335 * @throws IllegalStateException if {@code array} is empty
0336 */
0337 public static short[] requireNonEmpty(@Nonnull short[] array) {
0338 requireNonNull(array);
0339 requireState(array.length != 0);
0340 return array;
0341 }
0342
0343 /**
0344 * Checks that the specified array is not empty, throwing a customized
0345 * {@link IllegalStateException} if it is.
0346 *
0347 * @param array the array to check
0348 * @param message detail message to be used in the event that a {@code
0349 * IllegalStateException} is thrown
0350 *
0351 * @throws NullPointerException if {@code array} is null
0352 * @throws IllegalArgumentException if {@code message} is {@code blank}
0353 * @throws IllegalStateException if {@code array} is empty
0354 */
0355 public static short[] requireNonEmpty(@Nonnull short[] array, @Nonnull String message) {
0356 requireNonNull(array);
0357 requireState(array.length != 0, requireNonBlank(message, MESSAGE));
0358 return array;
0359 }
0360
0361 /**
0362 * Checks that the specified array is not empty, throwing a
0363 * {@link IllegalStateException} if it is.
0364 *
0365 * @param array the array to check
0366 *
0367 * @throws NullPointerException if {@code array} is null
0368 * @throws IllegalStateException if {@code array} is empty
0369 */
0370 public static int[] requireNonEmpty(@Nonnull int[] array) {
0371 requireNonNull(array);
0372 requireState(array.length != 0);
0373 return array;
0374 }
0375
0376 /**
0377 * Checks that the specified array is not empty, throwing a customized
0378 * {@link IllegalStateException} if it is.
0379 *
0380 * @param array the array to check
0381 * @param message detail message to be used in the event that a {@code
0382 * IllegalStateException} is thrown
0383 *
0384 * @throws NullPointerException if {@code array} is null
0385 * @throws IllegalArgumentException if {@code message} is {@code blank}
0386 * @throws IllegalStateException if {@code array} is empty
0387 */
0388 public static int[] requireNonEmpty(@Nonnull int[] array, @Nonnull String message) {
0389 requireNonNull(array);
0390 requireState(array.length != 0, requireNonBlank(message, MESSAGE));
0391 return array;
0392 }
0393
0394 /**
0395 * Checks that the specified array is not empty, throwing a
0396 * {@link IllegalStateException} if it is.
0397 *
0398 * @param array the array to check
0399 *
0400 * @throws NullPointerException if {@code array} is null
0401 * @throws IllegalStateException if {@code array} is empty
0402 */
0403 public static long[] requireNonEmpty(@Nonnull long[] array) {
0404 requireNonNull(array);
0405 requireState(array.length != 0);
0406 return array;
0407 }
0408
0409 /**
0410 * Checks that the specified array is not empty, throwing a customized
0411 * {@link IllegalStateException} if it is.
0412 *
0413 * @param array the array to check
0414 * @param message detail message to be used in the event that a {@code
0415 * IllegalStateException} is thrown
0416 *
0417 * @throws NullPointerException if {@code array} is null
0418 * @throws IllegalArgumentException if {@code message} is {@code blank}
0419 * @throws IllegalStateException if {@code array} is empty
0420 */
0421 public static long[] requireNonEmpty(@Nonnull long[] array, @Nonnull String message) {
0422 requireNonNull(array);
0423 requireState(array.length != 0, requireNonBlank(message, MESSAGE));
0424 return array;
0425 }
0426
0427 /**
0428 * Checks that the specified array is not empty, throwing a
0429 * {@link IllegalStateException} if it is.
0430 *
0431 * @param array the array to check
0432 *
0433 * @throws NullPointerException if {@code array} is null
0434 * @throws IllegalStateException if {@code array} is empty
0435 */
0436 public static float[] requireNonEmpty(@Nonnull float[] array) {
0437 requireNonNull(array);
0438 requireState(array.length != 0);
0439 return array;
0440 }
0441
0442 /**
0443 * Checks that the specified array is not empty, throwing a customized
0444 * {@link IllegalStateException} if it is.
0445 *
0446 * @param array the array to check
0447 * @param message detail message to be used in the event that a {@code
0448 * IllegalStateException} is thrown
0449 *
0450 * @throws NullPointerException if {@code array} is null
0451 * @throws IllegalArgumentException if {@code message} is {@code blank}
0452 * @throws IllegalStateException if {@code array} is empty
0453 */
0454 public static float[] requireNonEmpty(@Nonnull float[] array, @Nonnull String message) {
0455 requireNonNull(array);
0456 requireState(array.length != 0, requireNonBlank(message, MESSAGE));
0457 return array;
0458 }
0459
0460 /**
0461 * Checks that the specified array is not empty, throwing a
0462 * {@link IllegalStateException} if it is.
0463 *
0464 * @param array the array to check
0465 *
0466 * @throws NullPointerException if {@code array} is null
0467 * @throws IllegalStateException if {@code array} is empty
0468 */
0469 public static double[] requireNonEmpty(@Nonnull double[] array) {
0470 requireNonNull(array);
0471 requireState(array.length != 0);
0472 return array;
0473 }
0474
0475 /**
0476 * Checks that the specified array is not empty, throwing a customized
0477 * {@link IllegalStateException} if it is.
0478 *
0479 * @param array the array to check
0480 * @param message detail message to be used in the event that a {@code
0481 * IllegalStateException} is thrown
0482 *
0483 * @throws NullPointerException if {@code array} is null
0484 * @throws IllegalArgumentException if {@code message} is {@code blank}
0485 * @throws IllegalStateException if {@code array} is empty
0486 */
0487 public static double[] requireNonEmpty(@Nonnull double[] array, @Nonnull String message) {
0488 requireNonNull(array);
0489 requireState(array.length != 0, requireNonBlank(message, MESSAGE));
0490 return array;
0491 }
0492
0493 /**
0494 * Checks that the specified array is not empty, throwing a
0495 * {@link IllegalStateException} if it is.
0496 *
0497 * @param array the array to check
0498 *
0499 * @throws NullPointerException if {@code array} is null
0500 * @throws IllegalStateException if {@code array} is empty
0501 */
0502 public static char[] requireNonEmpty(@Nonnull char[] array) {
0503 requireNonNull(array);
0504 requireState(array.length != 0);
0505 return array;
0506 }
0507
0508 /**
0509 * Checks that the specified array is not empty, throwing a customized
0510 * {@link IllegalStateException} if it is.
0511 *
0512 * @param array the array to check
0513 * @param message detail message to be used in the event that a {@code
0514 * IllegalStateException} is thrown
0515 *
0516 * @throws NullPointerException if {@code array} is null
0517 * @throws IllegalArgumentException if {@code message} is {@code blank}
0518 * @throws IllegalStateException if {@code array} is empty
0519 */
0520 public static char[] requireNonEmpty(@Nonnull char[] array, @Nonnull String message) {
0521 requireNonNull(array);
0522 requireState(array.length != 0, requireNonBlank(message, MESSAGE));
0523 return array;
0524 }
0525
0526 /**
0527 * Checks that the specified array is not empty, throwing a
0528 * {@link IllegalStateException} if it is.
0529 *
0530 * @param array the array to check
0531 *
0532 * @throws NullPointerException if {@code array} is null
0533 * @throws IllegalStateException if {@code array} is empty
0534 */
0535 public static boolean[] requireNonEmpty(@Nonnull boolean[] array) {
0536 requireNonNull(array);
0537 requireState(array.length != 0);
0538 return array;
0539 }
0540
0541 /**
0542 * Checks that the specified array is not empty, throwing a customized
0543 * {@link IllegalStateException} if it is.
0544 *
0545 * @param array the array to check
0546 * @param message detail message to be used in the event that a {@code
0547 * IllegalStateException} is thrown
0548 *
0549 * @throws NullPointerException if {@code array} is null
0550 * @throws IllegalArgumentException if {@code message} is {@code blank}
0551 * @throws IllegalStateException if {@code array} is empty
0552 */
0553 public static boolean[] requireNonEmpty(@Nonnull boolean[] array, @Nonnull String message) {
0554 requireNonNull(array);
0555 requireState(array.length != 0, requireNonBlank(message, MESSAGE));
0556 return array;
0557 }
0558
0559 /**
0560 * Checks that the specified array is not empty, throwing a
0561 * {@link IllegalStateException} if it is.
0562 *
0563 * @param array the array to check
0564 *
0565 * @throws NullPointerException if {@code array} is null
0566 * @throws IllegalStateException if {@code array} is empty
0567 */
0568 public static <E> E[] requireNonEmpty(@Nonnull E[] array) {
0569 requireNonNull(array);
0570 requireState(array.length != 0);
0571 return array;
0572 }
0573
0574 /**
0575 * Checks that the specified array is not empty, throwing a customized
0576 * {@link IllegalStateException} if it is.
0577 *
0578 * @param array the array to check
0579 * @param message detail message to be used in the event that a {@code
0580 * IllegalStateException} is thrown
0581 *
0582 * @throws NullPointerException if {@code array} is null
0583 * @throws IllegalArgumentException if {@code message} is {@code blank}
0584 * @throws IllegalStateException if {@code array} is empty
0585 */
0586 public static <E> E[] requireNonEmpty(@Nonnull E[] array, @Nonnull String message) {
0587 requireNonNull(array);
0588 requireState(array.length != 0, requireNonBlank(message, MESSAGE));
0589 return array;
0590 }
0591
0592 /**
0593 * Checks that the specified collection is not empty, throwing a
0594 * {@link IllegalStateException} if it is.
0595 *
0596 * @param collection the collection to check
0597 *
0598 * @throws NullPointerException if {@code collection} is null
0599 * @throws IllegalStateException if {@code collection} is empty
0600 */
0601 public static Collection<?> requireNonEmpty(@Nonnull Collection<?> collection) {
0602 requireNonNull(collection);
0603 requireState(!collection.isEmpty());
0604 return collection;
0605 }
0606
0607 /**
0608 * Checks that the specified collection is not empty, throwing a customized
0609 * {@link IllegalStateException} if it is.
0610 *
0611 * @param collection the collection to check
0612 * @param message detail message to be used in the event that a {@code
0613 * IllegalStateException} is thrown
0614 *
0615 * @throws NullPointerException if {@code collection} is null
0616 * @throws IllegalArgumentException if {@code message} is {@code blank}
0617 * @throws IllegalStateException if {@code collection} is empty
0618 */
0619 public static Collection<?> requireNonEmpty(@Nonnull Collection<?> collection, @Nonnull String message) {
0620 requireNonNull(collection);
0621 requireState(!collection.isEmpty(), requireNonBlank(message, MESSAGE));
0622 return collection;
0623 }
0624
0625 /**
0626 * Checks that the specified map is not empty, throwing a
0627 * {@link IllegalStateException} if it is.
0628 *
0629 * @param map the map to check
0630 *
0631 * @throws NullPointerException if {@code map} is null
0632 * @throws IllegalStateException if {@code map} is empty
0633 */
0634 public static Map<?, ?> requireNonEmpty(@Nonnull Map<?, ?> map) {
0635 requireNonNull(map);
0636 requireState(!map.isEmpty());
0637 return map;
0638 }
0639
0640 /**
0641 * Checks that the specified map is not empty, throwing a customized
0642 * {@link IllegalStateException} if it is.
0643 *
0644 * @param map the map to check
0645 * @param message detail message to be used in the event that a {@code
0646 * IllegalStateException} is thrown
0647 *
0648 * @throws NullPointerException if {@code map} is null
0649 * @throws IllegalArgumentException if {@code message} is {@code blank}
0650 * @throws IllegalStateException if {@code map} is empty
0651 */
0652 public static Map<?, ?> requireNonEmpty(@Nonnull Map<?, ?> map, @Nonnull String message) {
0653 requireNonNull(map);
0654 requireState(!map.isEmpty(), requireNonBlank(message, MESSAGE));
0655 return map;
0656 }
0657
0658 /**
0659 * Finds out if the given string represents the name of an
0660 * event handler by matching against the following pattern:
0661 * "^on[A-Z][\\w]*$"<p>
0662 * <p>
0663 * <pre>
0664 * isEventHandler("onBootstrapEnd") = true
0665 * isEventHandler("mvcGroupInit") = false
0666 * isEventHandler("online") = false
0667 * </pre>
0668 *
0669 * @param name the name of a possible event handler
0670 *
0671 * @return true if the name matches the given event handler
0672 * pattern, false otherwise.
0673 */
0674 public static boolean isEventHandler(@Nonnull String name) {
0675 requireNonBlank(name, ERROR_NAME_BLANK);
0676 return EVENT_HANDLER_PATTERN.matcher(name).matches() &&
0677 !ON_SHUTDOWN_METHOD_NAME.equals(name);
0678 }
0679
0680 /**
0681 * Finds out if the given Method represents an event handler
0682 * by matching its name against the following pattern:
0683 * "^on[A-Z][\\w]*$"<p>
0684 * <pre>
0685 * // assuming getMethod() returns an appropriate Method reference
0686 * isEventHandler(getMethod("onBootstrapEnd")) = true
0687 * isEventHandler(getMethod("mvcGroupInit")) = false
0688 * isEventHandler(getMethod("online")) = false
0689 * </pre>
0690 *
0691 * @param method a Method reference
0692 *
0693 * @return true if the method name matches the given event handler
0694 * pattern, false otherwise.
0695 */
0696 public static boolean isEventHandler(@Nonnull Method method) {
0697 return isEventHandler(method, false);
0698 }
0699
0700 /**
0701 * Finds out if the given Method represents an event handler
0702 * by matching its name against the following pattern:
0703 * "^on[A-Z][\\w]*$"<p>
0704 * <pre>
0705 * // assuming getMethod() returns an appropriate Method reference
0706 * isEventHandler(getMethod("onBootstrapEnd")) = true
0707 * isEventHandler(getMethod("mvcGroupInit")) = false
0708 * isEventHandler(getMethod("online")) = false
0709 * </pre>
0710 *
0711 * @param method a Method reference
0712 *
0713 * @return true if the method name matches the given event handler
0714 * pattern, false otherwise.
0715 */
0716 public static boolean isEventHandler(@Nonnull Method method, boolean removeAbstractModifier) {
0717 requireNonNull(method, ERROR_METHOD_NULL);
0718 return isEventHandler(MethodDescriptor.forMethod(method, removeAbstractModifier));
0719 }
0720
0721 /**
0722 * Finds out if the given Method represents an event handler
0723 * by matching its name against the following pattern:
0724 * "^on[A-Z][\\w]*$"<p>
0725 * <pre>
0726 * // assuming getMethod() returns an appropriate MethodDescriptor reference
0727 * isEventHandler(getMethod("onBootstrapEnd")) = true
0728 * isEventHandler(getMethod("mvcGroupInit")) = false
0729 * isEventHandler(getMethod("online")) = false
0730 * </pre>
0731 *
0732 * @param method a MethodDescriptor reference
0733 *
0734 * @return true if the method name matches the given event handler
0735 * pattern, false otherwise.
0736 */
0737 public static boolean isEventHandler(@Nonnull MethodDescriptor method) {
0738 requireNonNull(method, ERROR_METHOD_NULL);
0739 return isInstanceMethod(method) &&
0740 isEventHandler(method.getName());
0741 }
0742
0743 /**
0744 * Finds out if the given {@code Method} belongs either to the
0745 * {@code Object} class or the {@code GroovyObject} class.<p>
0746 *
0747 * @param method a Method reference
0748 *
0749 * @return true if the method belongs to {@code Object} or
0750 * {@code GroovyObject}, false otherwise.
0751 */
0752 public static boolean isBasicMethod(@Nonnull Method method) {
0753 return isBasicMethod(method, false);
0754 }
0755
0756 /**
0757 * Finds out if the given {@code Method} belongs either to the
0758 * {@code Object} class or the {@code GroovyObject} class.<p>
0759 *
0760 * @param method a Method reference
0761 *
0762 * @return true if the method belongs to {@code Object} or
0763 * {@code GroovyObject}, false otherwise.
0764 */
0765 public static boolean isBasicMethod(@Nonnull Method method, boolean removeAbstractModifier) {
0766 requireNonNull(method, ERROR_METHOD_NULL);
0767 return isBasicMethod(MethodDescriptor.forMethod(method, removeAbstractModifier));
0768 }
0769
0770 /**
0771 * Finds out if the given {@code MethodDescriptor} belongs either to the
0772 * {@code Object} class or the {@code GroovyObject} class.<p>
0773 *
0774 * @param method a MethodDescriptor reference
0775 *
0776 * @return true if the method belongs to {@code Object} or
0777 * {@code GroovyObject}, false otherwise.
0778 */
0779 public static boolean isBasicMethod(@Nonnull MethodDescriptor method) {
0780 requireNonNull(method, ERROR_METHOD_NULL);
0781 return isInstanceMethod(method) && BASIC_METHODS.contains(method);
0782 }
0783
0784 /**
0785 * Finds out if the given string represents the name of a
0786 * contribution method by matching against the following pattern:
0787 * "^with[A-Z][a-z0-9_]*[\w]*$"<p>
0788 * <p>
0789 * <pre>
0790 * isContributionMethod("withRest") = true
0791 * isContributionMethod("withMVCGroup") = false
0792 * isContributionMethod("without") = false
0793 * </pre>
0794 *
0795 * @param name the name of a possible contribution method
0796 *
0797 * @return true if the name matches the given contribution method
0798 * pattern, false otherwise.
0799 */
0800 public static boolean isContributionMethod(@Nonnull String name) {
0801 requireNonBlank(name, ERROR_NAME_BLANK);
0802 return CONTRIBUTION_PATTERN.matcher(name).matches();
0803 }
0804
0805 /**
0806 * Finds out if the given Method represents a contribution method
0807 * by matching its name against the following pattern:
0808 * "^with[A-Z][a-z0-9_]*[\w]*$"<p>
0809 * <pre>
0810 * // assuming getMethod() returns an appropriate Method reference
0811 * isContributionMethod(getMethod("withRest")) = true
0812 * isContributionMethod(getMethod("withMVCGroup")) = false
0813 * isContributionMethod(getMethod("without")) = false
0814 * </pre>
0815 *
0816 * @param method a Method reference
0817 *
0818 * @return true if the method name matches the given contribution method
0819 * pattern, false otherwise.
0820 */
0821 public static boolean isContributionMethod(@Nonnull Method method) {
0822 return isContributionMethod(method, false);
0823 }
0824
0825 /**
0826 * Finds out if the given Method represents a contribution method
0827 * by matching its name against the following pattern:
0828 * "^with[A-Z][a-z0-9_]*[\w]*$"<p>
0829 * <pre>
0830 * // assuming getMethod() returns an appropriate Method reference
0831 * isContributionMethod(getMethod("withRest")) = true
0832 * isContributionMethod(getMethod("withMVCGroup")) = false
0833 * isContributionMethod(getMethod("without")) = false
0834 * </pre>
0835 *
0836 * @param method a Method reference
0837 *
0838 * @return true if the method name matches the given contribution method
0839 * pattern, false otherwise.
0840 */
0841 public static boolean isContributionMethod(@Nonnull Method method, boolean removeAbstractModifier) {
0842 requireNonNull(method, ERROR_METHOD_NULL);
0843 return isContributionMethod(MethodDescriptor.forMethod(method, removeAbstractModifier));
0844 }
0845
0846 /**
0847 * Finds out if the given Method represents a contribution method
0848 * by matching its name against the following pattern:
0849 * "^with[A-Z][a-z0-9_]*[\w]*$"<p>
0850 * <pre>
0851 * // assuming getMethod() returns an appropriate MethodDescriptor reference
0852 * isContributionMethod(getMethod("withRest")) = true
0853 * isContributionMethod(getMethod("withMVCGroup")) = false
0854 * isContributionMethod(getMethod("without")) = false
0855 * </pre>
0856 *
0857 * @param method a MethodDescriptor reference
0858 *
0859 * @return true if the method name matches the given contribution method
0860 * pattern, false otherwise.
0861 */
0862 public static boolean isContributionMethod(@Nonnull MethodDescriptor method) {
0863 requireNonNull(method, ERROR_METHOD_NULL);
0864 return isInstanceMethod(method) &&
0865 CONTRIBUTION_PATTERN.matcher(method.getName()).matches();
0866 }
0867
0868 /**
0869 * Finds out if the given {@code Method} was injected by the Groovy
0870 * compiler.<p>
0871 * Performs a basic checks against the method's name, returning true
0872 * if the name starts with either "super$" or "this$".
0873 *
0874 * @param method a Method reference
0875 *
0876 * @return true if the method matches the given criteria, false otherwise.
0877 */
0878 public static boolean isGroovyInjectedMethod(@Nonnull Method method) {
0879 return isGroovyInjectedMethod(method, false);
0880 }
0881
0882 /**
0883 * Finds out if the given {@code Method} was injected by the Groovy
0884 * compiler.<p>
0885 * Performs a basic checks against the method's name, returning true
0886 * if the name starts with either "super$" or "this$".
0887 *
0888 * @param method a Method reference
0889 *
0890 * @return true if the method matches the given criteria, false otherwise.
0891 */
0892 public static boolean isGroovyInjectedMethod(@Nonnull Method method, boolean removeAbstractModifier) {
0893 requireNonNull(method, ERROR_METHOD_NULL);
0894 return isGroovyInjectedMethod(MethodDescriptor.forMethod(method, removeAbstractModifier));
0895 }
0896
0897 /**
0898 * Finds out if the given {@code MethodDescriptor} was injected by the Groovy
0899 * compiler.<p>
0900 * Performs a basic checks against the method's name, returning true
0901 * if the name starts with either "super$" or "this$".
0902 *
0903 * @param method a MethodDescriptor reference
0904 *
0905 * @return true if the method matches the given criteria, false otherwise.
0906 */
0907 public static boolean isGroovyInjectedMethod(@Nonnull MethodDescriptor method) {
0908 requireNonNull(method, ERROR_METHOD_NULL);
0909 return isInstanceMethod(method) &&
0910 (method.getName().startsWith("super$") || method.getName().startsWith("this$"));
0911 }
0912
0913 /**
0914 * Finds out if the given {@code Method} is a getter method.
0915 * <p>
0916 * <pre>
0917 * // assuming getMethod() returns an appropriate Method reference
0918 * isGetterMethod(getMethod("getFoo")) = true
0919 * isGetterMethod(getMethod("getfoo") ) = false
0920 * isGetterMethod(getMethod("mvcGroupInit")) = false
0921 * isGetterMethod(getMethod("isFoo")) = true
0922 * isGetterMethod(getMethod("island")) = false
0923 * </pre>
0924 *
0925 * @param method a Method reference
0926 *
0927 * @return true if the method is a getter, false otherwise.
0928 */
0929 public static boolean isGetterMethod(@Nonnull Method method) {
0930 return isGetterMethod(method, false);
0931 }
0932
0933 /**
0934 * Finds out if the given {@code Method} is a getter method.
0935 * <p>
0936 * <pre>
0937 * // assuming getMethod() returns an appropriate Method reference
0938 * isGetterMethod(getMethod("getFoo")) = true
0939 * isGetterMethod(getMethod("getfoo") ) = false
0940 * isGetterMethod(getMethod("mvcGroupInit")) = false
0941 * isGetterMethod(getMethod("isFoo")) = true
0942 * isGetterMethod(getMethod("island")) = false
0943 * </pre>
0944 *
0945 * @param method a Method reference
0946 *
0947 * @return true if the method is a getter, false otherwise.
0948 */
0949 public static boolean isGetterMethod(@Nonnull Method method, boolean removeAbstractModifier) {
0950 requireNonNull(method, ERROR_METHOD_NULL);
0951 return isGetterMethod(MethodDescriptor.forMethod(method, removeAbstractModifier));
0952 }
0953
0954 /**
0955 * Finds out if the given {@code MetaMethod} is a getter method.
0956 * <p>
0957 * <pre>
0958 * // assuming getMethod() returns an appropriate MethodDescriptor reference
0959 * isGetterMethod(getMethod("getFoo")) = true
0960 * isGetterMethod(getMethod("getfoo") ) = false
0961 * isGetterMethod(getMethod("mvcGroupInit")) = false
0962 * isGetterMethod(getMethod("isFoo")) = true
0963 * isGetterMethod(getMethod("island")) = false
0964 * </pre>
0965 *
0966 * @param method a MethodDescriptor reference
0967 *
0968 * @return true if the method is a getter, false otherwise.
0969 */
0970 public static boolean isGetterMethod(@Nonnull MethodDescriptor method) {
0971 requireNonNull(method, ERROR_METHOD_NULL);
0972 return isInstanceMethod(method) &&
0973 (GETTER_PATTERN_1.matcher(method.getName()).matches() || GETTER_PATTERN_2.matcher(method.getName()).matches());
0974 }
0975
0976 /**
0977 * Finds out if the given {@code Method} is a setter method.
0978 * <p>
0979 * <pre>
0980 * // assuming getMethod() returns an appropriate Method reference
0981 * isGetterMethod(getMethod("setFoo")) = true
0982 * isGetterMethod(getMethod("setfoo")) = false
0983 * isGetterMethod(getMethod("mvcGroupInit")) = false
0984 * </pre>
0985 *
0986 * @param method a Method reference
0987 *
0988 * @return true if the method is a setter, false otherwise.
0989 */
0990 public static boolean isSetterMethod(@Nonnull Method method) {
0991 return isSetterMethod(method, false);
0992 }
0993
0994 /**
0995 * Finds out if the given {@code Method} is a setter method.
0996 * <p>
0997 * <pre>
0998 * // assuming getMethod() returns an appropriate Method reference
0999 * isGetterMethod(getMethod("setFoo")) = true
1000 * isGetterMethod(getMethod("setfoo")) = false
1001 * isGetterMethod(getMethod("mvcGroupInit")) = false
1002 * </pre>
1003 *
1004 * @param method a Method reference
1005 *
1006 * @return true if the method is a setter, false otherwise.
1007 */
1008 public static boolean isSetterMethod(@Nonnull Method method, boolean removeAbstractModifier) {
1009 requireNonNull(method, ERROR_METHOD_NULL);
1010 return isSetterMethod(MethodDescriptor.forMethod(method, removeAbstractModifier));
1011 }
1012
1013 /**
1014 * Finds out if the given {@code MethodDescriptor} is a setter method.
1015 * <p>
1016 * <pre>
1017 * // assuming getMethod() returns an appropriate MethodDescriptor reference
1018 * isGetterMethod(getMethod("setFoo")) = true
1019 * isGetterMethod(getMethod("setfoo")) = false
1020 * isGetterMethod(getMethod("mvcGroupInit")) = false
1021 * </pre>
1022 *
1023 * @param method a MethodDescriptor reference
1024 *
1025 * @return true if the method is a setter, false otherwise.
1026 */
1027 public static boolean isSetterMethod(@Nonnull MethodDescriptor method) {
1028 requireNonNull(method, ERROR_METHOD_NULL);
1029 return isInstanceMethod(method) && SETTER_PATTERN.matcher(method.getName()).matches();
1030 }
1031
1032 /**
1033 * Finds out if the given {@code Method} belongs to the set of
1034 * predefined Artifact methods by convention.
1035 * <p>
1036 * <pre>
1037 * // assuming getMethod() returns an appropriate Method reference
1038 * isArtifactMethod(getMethod("newInstance")) = true
1039 * isArtifactMethod(getMethod("griffonDestroy")) = false
1040 * isArtifactMethod(getMethod("foo")) = false
1041 * </pre>
1042 *
1043 * @param method a Method reference
1044 *
1045 * @return true if the method is an Artifact method, false otherwise.
1046 */
1047 public static boolean isArtifactMethod(@Nonnull Method method) {
1048 return isArtifactMethod(method, false);
1049 }
1050
1051 /**
1052 * Finds out if the given {@code Method} belongs to the set of
1053 * predefined Artifact methods by convention.
1054 * <p>
1055 * <pre>
1056 * // assuming getMethod() returns an appropriate Method reference
1057 * isArtifactMethod(getMethod("newInstance")) = true
1058 * isArtifactMethod(getMethod("griffonDestroy")) = false
1059 * isArtifactMethod(getMethod("foo")) = false
1060 * </pre>
1061 *
1062 * @param method a Method reference
1063 *
1064 * @return true if the method is an Artifact method, false otherwise.
1065 */
1066 public static boolean isArtifactMethod(@Nonnull Method method, boolean removeAbstractModifier) {
1067 requireNonNull(method, ERROR_METHOD_NULL);
1068 return isArtifactMethod(MethodDescriptor.forMethod(method, removeAbstractModifier));
1069 }
1070
1071 /**
1072 * Finds out if the given {@code MethodDescriptor} belongs to the set of
1073 * predefined Artifact methods by convention.
1074 * <p>
1075 * <pre>
1076 * // assuming getMethod() returns an appropriate MethodDescriptor reference
1077 * isArtifactMethod(getMethod("newInstance")) = true
1078 * isArtifactMethod(getMethod("griffonDestroy")) = false
1079 * isArtifactMethod(getMethod("foo")) = false
1080 * </pre>
1081 *
1082 * @param method a MethodDescriptor reference
1083 *
1084 * @return true if the method is an Artifact method, false otherwise.
1085 */
1086 public static boolean isArtifactMethod(@Nonnull MethodDescriptor method) {
1087 requireNonNull(method, ERROR_METHOD_NULL);
1088 return isInstanceMethod(method) &&
1089 ARTIFACT_METHODS.contains(method);
1090 }
1091
1092 /**
1093 * Finds out if the given {@code Method} belongs to the set of
1094 * predefined MVC methods by convention.
1095 * <p>
1096 * <pre>
1097 * // assuming getMethod() returns an appropriate Method reference
1098 * isMvcMethod(getMethod("mvcGroupInit")) = true
1099 * isMvcMethod(getMethod("mvcGroupDestroy")) = true
1100 * isMvcMethod(getMethod("foo")) = false
1101 * </pre>
1102 *
1103 * @param method a Method reference
1104 *
1105 * @return true if the method is an MVC method, false otherwise.
1106 */
1107 public static boolean isMvcMethod(@Nonnull Method method) {
1108 return isMvcMethod(method, false);
1109 }
1110
1111 /**
1112 * Finds out if the given {@code Method} belongs to the set of
1113 * predefined MVC methods by convention.
1114 * <p>
1115 * <pre>
1116 * // assuming getMethod() returns an appropriate Method reference
1117 * isMvcMethod(getMethod("mvcGroupInit")) = true
1118 * isMvcMethod(getMethod("mvcGroupDestroy")) = true
1119 * isMvcMethod(getMethod("foo")) = false
1120 * </pre>
1121 *
1122 * @param method a Method reference
1123 *
1124 * @return true if the method is an MVC method, false otherwise.
1125 */
1126 public static boolean isMvcMethod(@Nonnull Method method, boolean removeAbstractModifier) {
1127 requireNonNull(method, ERROR_METHOD_NULL);
1128 return isMvcMethod(MethodDescriptor.forMethod(method, removeAbstractModifier));
1129 }
1130
1131 /**
1132 * Finds out if the given {@code MethodDescriptor} belongs to the set of
1133 * predefined MVC methods by convention.
1134 * <p>
1135 * <pre>
1136 * // assuming getMethod() returns an appropriate MethodDescriptor reference
1137 * isMvcMethod(getMethod("mvcGroupInit")) = true
1138 * isMvcMethod(getMethod("mvcGroupDestroy")) = true
1139 * isMvcMethod(getMethod("foo")) = false
1140 * </pre>
1141 *
1142 * @param method a MethodDescriptor reference
1143 *
1144 * @return true if the method is an MVC method, false otherwise.
1145 */
1146 public static boolean isMvcMethod(@Nonnull MethodDescriptor method) {
1147 requireNonNull(method, ERROR_METHOD_NULL);
1148 return isInstanceMethod(method) &&
1149 MVC_METHODS.contains(method);
1150 }
1151
1152 /**
1153 * Finds out if the given {@code Method} belongs to the set of
1154 * predefined threading methods by convention.
1155 * <p>
1156 * <pre>
1157 * // assuming getMethod() returns an appropriate Method reference
1158 * isThreadingMethod(getMethod("execOutsideUI")) = true
1159 * isThreadingMethod(getMethod("doLater")) = true
1160 * isThreadingMethod(getMethod("foo")) = false
1161 * </pre>
1162 *
1163 * @param method a Method reference
1164 *
1165 * @return true if the method is a threading method, false otherwise.
1166 */
1167 public static boolean isThreadingMethod(@Nonnull Method method) {
1168 return isThreadingMethod(method, false);
1169 }
1170
1171 /**
1172 * Finds out if the given {@code Method} belongs to the set of
1173 * predefined threading methods by convention.
1174 * <p>
1175 * <pre>
1176 * // assuming getMethod() returns an appropriate Method reference
1177 * isThreadingMethod(getMethod("execOutsideUI")) = true
1178 * isThreadingMethod(getMethod("doLater")) = true
1179 * isThreadingMethod(getMethod("foo")) = false
1180 * </pre>
1181 *
1182 * @param method a Method reference
1183 *
1184 * @return true if the method is a threading method, false otherwise.
1185 */
1186 public static boolean isThreadingMethod(@Nonnull Method method, boolean removeAbstractModifier) {
1187 requireNonNull(method, ERROR_METHOD_NULL);
1188 return isThreadingMethod(MethodDescriptor.forMethod(method, removeAbstractModifier));
1189 }
1190
1191 /**
1192 * Finds out if the given {@code MethodDescriptor} belongs to the set of
1193 * predefined threading methods by convention.
1194 * <p>
1195 * <pre>
1196 * // assuming getMethod() returns an appropriate MethodDescriptor reference
1197 * isThreadingMethod(getMethod("execOutsideUI")) = true
1198 * isThreadingMethod(getMethod("doLater")) = true
1199 * isThreadingMethod(getMethod("foo")) = false
1200 * </pre>
1201 *
1202 * @param method a MethodDescriptor reference
1203 *
1204 * @return true if the method is a threading method, false otherwise.
1205 */
1206 public static boolean isThreadingMethod(@Nonnull MethodDescriptor method) {
1207 requireNonNull(method, ERROR_METHOD_NULL);
1208 return isInstanceMethod(method) &&
1209 THREADING_METHODS.contains(method);
1210 }
1211
1212 /**
1213 * Finds out if the given {@code Method} belongs to the set of
1214 * predefined event publisher methods by convention.
1215 * <p>
1216 * <pre>
1217 * // assuming getMethod() returns an appropriate Method reference
1218 * isEventPublisherMethod(getMethod("addEventPublisher")) = true
1219 * isEventPublisherMethod(getMethod("publishEvent")) = true
1220 * isEventPublisherMethod(getMethod("foo")) = false
1221 * </pre>
1222 *
1223 * @param method a Method reference
1224 *
1225 * @return true if the method is an @EventPublisher method, false otherwise.
1226 */
1227 public static boolean isEventPublisherMethod(@Nonnull Method method) {
1228 return isEventPublisherMethod(method, false);
1229 }
1230
1231 /**
1232 * Finds out if the given {@code Method} belongs to the set of
1233 * predefined event publisher methods by convention.
1234 * <p>
1235 * <pre>
1236 * // assuming getMethod() returns an appropriate Method reference
1237 * isEventPublisherMethod(getMethod("addEventPublisher")) = true
1238 * isEventPublisherMethod(getMethod("publishEvent")) = true
1239 * isEventPublisherMethod(getMethod("foo")) = false
1240 * </pre>
1241 *
1242 * @param method a Method reference
1243 *
1244 * @return true if the method is an @EventPublisher method, false otherwise.
1245 */
1246 public static boolean isEventPublisherMethod(@Nonnull Method method, boolean removeAbstractModifier) {
1247 requireNonNull(method, ERROR_METHOD_NULL);
1248 return isEventPublisherMethod(MethodDescriptor.forMethod(method, removeAbstractModifier));
1249 }
1250
1251 /**
1252 * Finds out if the given {@code MethodDescriptor} belongs to the set of
1253 * predefined event publisher methods by convention.
1254 * <p>
1255 * <pre>
1256 * // assuming getMethod() returns an appropriate MethodDescriptor reference
1257 * isEventPublisherMethod(getMethod("addEventPublisher")) = true
1258 * isEventPublisherMethod(getMethod("publishEvent")) = true
1259 * isEventPublisherMethod(getMethod("foo")) = false
1260 * </pre>
1261 *
1262 * @param method a MethodDescriptor reference
1263 *
1264 * @return true if the method is an @EventPublisher method, false otherwise.
1265 */
1266 public static boolean isEventPublisherMethod(@Nonnull MethodDescriptor method) {
1267 requireNonNull(method, ERROR_METHOD_NULL);
1268 return isInstanceMethod(method) &&
1269 EVENT_PUBLISHER_METHODS.contains(method);
1270 }
1271
1272 /**
1273 * Finds out if the given {@code Method} belongs to the set of
1274 * predefined observable methods by convention.
1275 * <p>
1276 * <pre>
1277 * // assuming getMethod() returns an appropriate Method reference
1278 * isObservableMethod(getMethod("addPropertyChangeListener")) = true
1279 * isObservableMethod(getMethod("getPropertyChangeListeners")) = true
1280 * isObservableMethod(getMethod("foo")) = false
1281 * </pre>
1282 *
1283 * @param method a Method reference
1284 *
1285 * @return true if the method is an Observable method, false otherwise.
1286 */
1287 public static boolean isObservableMethod(@Nonnull Method method) {
1288 return isObservableMethod(method, false);
1289 }
1290
1291 /**
1292 * Finds out if the given {@code Method} belongs to the set of
1293 * predefined observable methods by convention.
1294 * <p>
1295 * <pre>
1296 * // assuming getMethod() returns an appropriate Method reference
1297 * isObservableMethod(getMethod("addPropertyChangeListener")) = true
1298 * isObservableMethod(getMethod("getPropertyChangeListeners")) = true
1299 * isObservableMethod(getMethod("foo")) = false
1300 * </pre>
1301 *
1302 * @param method a Method reference
1303 *
1304 * @return true if the method is an Observable method, false otherwise.
1305 */
1306 public static boolean isObservableMethod(@Nonnull Method method, boolean removeAbstractModifier) {
1307 requireNonNull(method, ERROR_METHOD_NULL);
1308 return isObservableMethod(MethodDescriptor.forMethod(method, removeAbstractModifier));
1309 }
1310
1311 /**
1312 * Finds out if the given {@code MethodDescriptor} belongs to the set of
1313 * predefined observable methods by convention.
1314 * <p>
1315 * <pre>
1316 * // assuming getMethod() returns an appropriate MethodDescriptor reference
1317 * isObservableMethod(getMethod("addPropertyChangeListener")) = true
1318 * isObservableMethod(getMethod("getPropertyChangeListeners")) = true
1319 * isObservableMethod(getMethod("foo")) = false
1320 * </pre>
1321 *
1322 * @param method a MethodDescriptor reference
1323 *
1324 * @return true if the method is an Observable method, false otherwise.
1325 */
1326 public static boolean isObservableMethod(@Nonnull MethodDescriptor method) {
1327 requireNonNull(method, ERROR_METHOD_NULL);
1328 return isInstanceMethod(method) &&
1329 OBSERVABLE_METHODS.contains(method);
1330 }
1331
1332 /**
1333 * Finds out if the given {@code Method} belongs to the set of
1334 * predefined resources methods by convention.
1335 * <p>
1336 * <pre>
1337 * // assuming getMethod() returns an appropriate Method reference
1338 * isResourceHandlerMethod(getMethod("getResourceAsURL")) = true
1339 * isResourceHandlerMethod(getMethod("getResourceAsStream")) = true
1340 * isResourceHandlerMethod(getMethod("foo")) = false
1341 * </pre>
1342 *
1343 * @param method a Method reference
1344 *
1345 * @return true if the method is an ResourceHandler method, false otherwise.
1346 */
1347 public static boolean isResourceHandlerMethod(@Nonnull Method method) {
1348 return isResourceHandlerMethod(method, false);
1349 }
1350
1351 /**
1352 * Finds out if the given {@code Method} belongs to the set of
1353 * predefined resources methods by convention.
1354 * <p>
1355 * <pre>
1356 * // assuming getMethod() returns an appropriate Method reference
1357 * isResourceHandlerMethod(getMethod("getResourceAsURL")) = true
1358 * isResourceHandlerMethod(getMethod("getResourceAsStream")) = true
1359 * isResourceHandlerMethod(getMethod("foo")) = false
1360 * </pre>
1361 *
1362 * @param method a Method reference
1363 *
1364 * @return true if the method is an ResourceHandler method, false otherwise.
1365 */
1366 public static boolean isResourceHandlerMethod(@Nonnull Method method, boolean removeAbstractModifier) {
1367 requireNonNull(method, ERROR_METHOD_NULL);
1368 return isResourceHandlerMethod(MethodDescriptor.forMethod(method, removeAbstractModifier));
1369 }
1370
1371 /**
1372 * Finds out if the given {@code MethodDescriptor} belongs to the set of
1373 * predefined resources methods by convention.
1374 * <p>
1375 * <pre>
1376 * // assuming getMethod() returns an appropriate MethodDescriptor reference
1377 * isResourceHandlerMethod(getMethod("getResourceAsURL")) = true
1378 * isResourceHandlerMethod(getMethod("getResourceAsStream")) = true
1379 * isResourceHandlerMethod(getMethod("foo")) = false
1380 * </pre>
1381 *
1382 * @param method a MethodDescriptor reference
1383 *
1384 * @return true if the method is an ResourceHandler method, false otherwise.
1385 */
1386 public static boolean isResourceHandlerMethod(@Nonnull MethodDescriptor method) {
1387 requireNonNull(method, ERROR_METHOD_NULL);
1388 return isInstanceMethod(method) &&
1389 RESOURCE_HANDLER_METHODS.contains(method);
1390 }
1391
1392 /**
1393 * Finds out if the given {@code Method} belongs to the set of
1394 * predefined message source methods by convention.
1395 * <p>
1396 * <pre>
1397 * // assuming getMethod() returns an appropriate Method reference
1398 * isMessageSourceMethod(getMethod("getMessage")) = true
1399 * isMessageSourceMethod(getMethod("foo")) = false
1400 * </pre>
1401 *
1402 * @param method a Method reference
1403 *
1404 * @return true if the method is an MessageSource method, false otherwise.
1405 */
1406 public static boolean isMessageSourceMethod(@Nonnull Method method) {
1407 return isMessageSourceMethod(method, false);
1408 }
1409
1410 /**
1411 * Finds out if the given {@code Method} belongs to the set of
1412 * predefined message source methods by convention.
1413 * <p>
1414 * <pre>
1415 * // assuming getMethod() returns an appropriate Method reference
1416 * isMessageSourceMethod(getMethod("getMessage")) = true
1417 * isMessageSourceMethod(getMethod("foo")) = false
1418 * </pre>
1419 *
1420 * @param method a Method reference
1421 *
1422 * @return true if the method is an MessageSource method, false otherwise.
1423 */
1424 public static boolean isMessageSourceMethod(@Nonnull Method method, boolean removeAbstractModifier) {
1425 requireNonNull(method, ERROR_METHOD_NULL);
1426 return isMessageSourceMethod(MethodDescriptor.forMethod(method, removeAbstractModifier));
1427 }
1428
1429 /**
1430 * Finds out if the given {@code MethodDescriptor} belongs to the set of
1431 * predefined message source methods by convention.
1432 * <p>
1433 * <pre>
1434 * // assuming getMethod() returns an appropriate MethodDescriptor reference
1435 * isMessageSourceMethod(getMethod("getMessage")) = true
1436 * isMessageSourceMethod(getMethod("foo")) = false
1437 * </pre>
1438 *
1439 * @param method a MethodDescriptor reference
1440 *
1441 * @return true if the method is an MessageSource method, false otherwise.
1442 */
1443 public static boolean isMessageSourceMethod(@Nonnull MethodDescriptor method) {
1444 requireNonNull(method, ERROR_METHOD_NULL);
1445 return isInstanceMethod(method) &&
1446 MESSAGE_SOURCE_METHODS.contains(method);
1447 }
1448
1449 /**
1450 * Finds out if the given {@code Method} belongs to the set of
1451 * predefined resource resolver methods by convention.
1452 * <p>
1453 * <pre>
1454 * // assuming getMethod() returns an appropriate Method reference
1455 * isResourceResolverMethod(getMethod("resolveResource")) = true
1456 * isResourceResolverMethod(getMethod("foo")) = false
1457 * </pre>
1458 *
1459 * @param method a Method reference
1460 *
1461 * @return true if the method is an ResourceResolver method, false otherwise.
1462 */
1463 public static boolean isResourceResolverMethod(@Nonnull Method method) {
1464 return isResourceResolverMethod(method, false);
1465 }
1466
1467 /**
1468 * Finds out if the given {@code Method} belongs to the set of
1469 * predefined resource resolver methods by convention.
1470 * <p>
1471 * <pre>
1472 * // assuming getMethod() returns an appropriate Method reference
1473 * isResourceResolverMethod(getMethod("resolveResource")) = true
1474 * isResourceResolverMethod(getMethod("foo")) = false
1475 * </pre>
1476 *
1477 * @param method a Method reference
1478 *
1479 * @return true if the method is an ResourceResolver method, false otherwise.
1480 */
1481 public static boolean isResourceResolverMethod(@Nonnull Method method, boolean removeAbstractModifier) {
1482 requireNonNull(method, ERROR_METHOD_NULL);
1483 return isResourceResolverMethod(MethodDescriptor.forMethod(method, removeAbstractModifier));
1484 }
1485
1486 /**
1487 * Finds out if the given {@code MethodDescriptor} belongs to the set of
1488 * predefined resource resolver methods by convention.
1489 * <p>
1490 * <pre>
1491 * // assuming getMethod() returns an appropriate MethodDescriptor reference
1492 * isResourceResolverMethod(getMethod("resolveResource")) = true
1493 * isResourceResolverMethod(getMethod("foo")) = false
1494 * </pre>
1495 *
1496 * @param method a MethodDescriptor reference
1497 *
1498 * @return true if the method is an ResourceResolver method, false otherwise.
1499 */
1500 public static boolean isResourceResolverMethod(@Nonnull MethodDescriptor method) {
1501 requireNonNull(method, ERROR_METHOD_NULL);
1502 return isInstanceMethod(method) &&
1503 RESOURCE_RESOLVER_METHODS.contains(method);
1504 }
1505
1506 /**
1507 * Finds out if the given {@code Method} is an instance method, i.e,
1508 * it is public and non-static.
1509 *
1510 * @param method a Method reference
1511 *
1512 * @return true if the method is an instance method, false otherwise.
1513 */
1514 public static boolean isInstanceMethod(@Nonnull Method method) {
1515 return isInstanceMethod(method, false);
1516 }
1517
1518 /**
1519 * Finds out if the given {@code Method} is an instance method, i.e,
1520 * it is public and non-static.
1521 *
1522 * @param method a Method reference
1523 *
1524 * @return true if the method is an instance method, false otherwise.
1525 */
1526 public static boolean isInstanceMethod(@Nonnull Method method, boolean removeAbstractModifier) {
1527 requireNonNull(method, ERROR_METHOD_NULL);
1528 return isInstanceMethod(MethodDescriptor.forMethod(method, removeAbstractModifier));
1529 }
1530
1531 /**
1532 * Finds out if the given {@code MethodDescriptor} is an instance method, i.e,
1533 * it is public and non-static.
1534 *
1535 * @param method a MethodDescriptor reference
1536 *
1537 * @return true if the method is an instance method, false otherwise.
1538 */
1539 public static boolean isInstanceMethod(@Nonnull MethodDescriptor method) {
1540 requireNonNull(method, ERROR_METHOD_NULL);
1541 int modifiers = method.getModifiers();
1542 return Modifier.isPublic(modifiers) &&
1543 !Modifier.isAbstract(modifiers) &&
1544 !Modifier.isStatic(modifiers);
1545 }
1546
1547 /**
1548 * Finds out if the given {@code Method} matches the following criteria:<ul>
1549 * <li>isInstanceMethod(method)</li>
1550 * <li>! isBasicMethod(method)</li>
1551 * <li>! isGroovyInjectedMethod(method)</li>
1552 * <li>! isThreadingMethod(method)</li>
1553 * <li>! isArtifactMethod(method)</li>
1554 * <li>! isMvcMethod(method)</li>
1555 * <li>! isServiceMethod(method)</li>
1556 * <li>! isEventPublisherMethod(method)</li>
1557 * <li>! isObservableMethod(method)</li>
1558 * <li>! isResourceHandlerMethod(method)</li>
1559 * <li>! isGetterMethod(method)</li>
1560 * <li>! isSetterMethod(method)</li>
1561 * <li>! isContributionMethod(method)</li>
1562 * </ul>
1563 *
1564 * @param method a Method reference
1565 *
1566 * @return true if the method matches the given criteria, false otherwise.
1567 */
1568 public static boolean isPlainMethod(@Nonnull Method method) {
1569 return isPlainMethod(method, false);
1570 }
1571
1572 /**
1573 * Finds out if the given {@code Method} matches the following criteria:<ul>
1574 * <li>isInstanceMethod(method)</li>
1575 * <li>! isBasicMethod(method)</li>
1576 * <li>! isGroovyInjectedMethod(method)</li>
1577 * <li>! isThreadingMethod(method)</li>
1578 * <li>! isArtifactMethod(method)</li>
1579 * <li>! isMvcMethod(method)</li>
1580 * <li>! isServiceMethod(method)</li>
1581 * <li>! isEventPublisherMethod(method)</li>
1582 * <li>! isObservableMethod(method)</li>
1583 * <li>! isResourceHandlerMethod(method)</li>
1584 * <li>! isGetterMethod(method)</li>
1585 * <li>! isSetterMethod(method)</li>
1586 * <li>! isContributionMethod(method)</li>
1587 * </ul>
1588 *
1589 * @param method a Method reference
1590 *
1591 * @return true if the method matches the given criteria, false otherwise.
1592 */
1593 public static boolean isPlainMethod(@Nonnull Method method, boolean removeAbstractModifier) {
1594 requireNonNull(method, ERROR_METHOD_NULL);
1595 return isPlainMethod(MethodDescriptor.forMethod(method, removeAbstractModifier));
1596 }
1597
1598 /**
1599 * Finds out if the given {@code MethodDescriptor} matches the following criteria:<ul>
1600 * <li>isInstanceMethod(method)</li>
1601 * <li>! isBasicMethod(method)</li>
1602 * <li>! isGroovyInjectedMethod(method)</li>
1603 * <li>! isThreadingMethod(method)</li>
1604 * <li>! isArtifactMethod(method)</li>
1605 * <li>! isMvcMethod(method)</li>
1606 * <li>! isServiceMethod(method)</li>
1607 * <li>! isEventPublisherMethod(method)</li>
1608 * <li>! isObservableMethod(method)</li>
1609 * <li>! isResourceHandlerMethod(method)</li>
1610 * <li>! isGetterMethod(method)</li>
1611 * <li>! isSetterMethod(method)</li>
1612 * <li>! isContributionMethod(method)</li>
1613 * </ul>
1614 *
1615 * @param method a MethodDescriptor reference
1616 *
1617 * @return true if the method matches the given criteria, false otherwise.
1618 */
1619 public static boolean isPlainMethod(@Nonnull MethodDescriptor method) {
1620 requireNonNull(method, ERROR_METHOD_NULL);
1621 return isInstanceMethod(method) &&
1622 !isBasicMethod(method) &&
1623 !isGroovyInjectedMethod(method) &&
1624 !isThreadingMethod(method) &&
1625 !isArtifactMethod(method) &&
1626 !isMvcMethod(method) &&
1627 !isEventPublisherMethod(method) &&
1628 !isObservableMethod(method) &&
1629 !isResourceHandlerMethod(method) &&
1630 !isGetterMethod(method) &&
1631 !isSetterMethod(method) &&
1632 !isContributionMethod(method);
1633 }
1634
1635 /**
1636 * Returns true if the specified property in the specified class is of the specified type
1637 *
1638 * @param clazz The class which contains the property
1639 * @param propertyName The property name
1640 * @param type The type to check
1641 *
1642 * @return A boolean value
1643 */
1644 public static boolean isPropertyOfType(Class<?> clazz, String propertyName, Class<?> type) {
1645 try {
1646 Class<?> propType = getPropertyType(clazz, propertyName);
1647 return propType != null && propType.equals(type);
1648 } catch (Exception e) {
1649 return false;
1650 }
1651 }
1652
1653 /**
1654 * Instantiates a Class, wrapping any exceptions in a RuntimeException.
1655 *
1656 * @param clazz target Class for which an object will be instantiated
1657 *
1658 * @return the newly instantiated object.
1659 *
1660 * @throws BeanInstantiationException if an error occurs when creating the object
1661 */
1662 @Nonnull
1663 public static Object instantiateClass(@Nonnull Class<?> clazz) {
1664 requireNonNull(clazz, ERROR_CLAZZ_NULL);
1665 try {
1666 return clazz.newInstance();
1667 } catch (Exception e) {
1668 throw new BeanInstantiationException("Could not create an instance of " + clazz, e);
1669 }
1670 }
1671
1672 @Nonnull
1673 public static Object instantiate(@Nonnull Class<?> clazz, @Nullable Object[] args) {
1674 requireNonNull(clazz, ERROR_CLAZZ_NULL);
1675 try {
1676 if (args == null) {
1677 args = EMPTY_OBJECT_ARRAY;
1678 }
1679 int arguments = args.length;
1680 Class<?>[] parameterTypes = new Class<?>[arguments];
1681 for (int i = 0; i < arguments; i++) {
1682 parameterTypes[i] = args[i].getClass();
1683 }
1684 return clazz.getDeclaredConstructor(parameterTypes).newInstance(args);
1685 } catch (Exception e) {
1686 throw new BeanInstantiationException("Could not create an instance of " + clazz, e);
1687 }
1688 }
1689
1690 /**
1691 * Returns the value of the specified property and type from an instance of the specified Griffon class
1692 *
1693 * @param clazz The name of the class which contains the property
1694 * @param propertyName The property name
1695 * @param propertyType The property type
1696 *
1697 * @return The value of the property or null if none exists
1698 */
1699 @Nullable
1700 public static Object getPropertyValueOfNewInstance(@Nullable Class<?> clazz, @Nullable String propertyName, Class<?> propertyType) {
1701 // validate
1702 if (clazz == null || GriffonNameUtils.isBlank(propertyName)) {
1703 return null;
1704 }
1705
1706 Object instance;
1707 try {
1708 instance = instantiateClass(clazz);
1709 } catch (BeanInstantiationException e) {
1710 return null;
1711 }
1712
1713 return getPropertyOrStaticPropertyOrFieldValue(instance, propertyName);
1714 }
1715
1716 /**
1717 * Returns the value of the specified property and type from an instance of the specified Griffon class
1718 *
1719 * @param clazz The name of the class which contains the property
1720 * @param propertyName The property name
1721 *
1722 * @return The value of the property or null if none exists
1723 */
1724 public static Object getPropertyValueOfNewInstance(Class<?> clazz, String propertyName) {
1725 // validate
1726 if (clazz == null || GriffonNameUtils.isBlank(propertyName)) {
1727 return null;
1728 }
1729
1730 Object instance;
1731 try {
1732 instance = instantiateClass(clazz);
1733 } catch (BeanInstantiationException e) {
1734 return null;
1735 }
1736
1737 return getPropertyOrStaticPropertyOrFieldValue(instance, propertyName);
1738 }
1739
1740 /**
1741 * Retrieves a PropertyDescriptor for the specified instance and property value
1742 *
1743 * @param instance The instance
1744 * @param propertyValue The value of the property
1745 *
1746 * @return The PropertyDescriptor
1747 */
1748 public static PropertyDescriptor getPropertyDescriptorForValue(Object instance, Object propertyValue) {
1749 if (instance == null || propertyValue == null) { return null; }
1750
1751 PropertyDescriptor[] descriptors = getPropertyDescriptors(instance.getClass());
1752
1753 for (PropertyDescriptor pd : descriptors) {
1754 if (isAssignableOrConvertibleFrom(pd.getPropertyType(), propertyValue.getClass())) {
1755 Object value;
1756 try {
1757 value = getReadMethod(pd).invoke(instance, (Object[]) null);
1758 } catch (Exception e) {
1759 throw new RuntimeException("Problem calling readMethod of " + pd, e);
1760 }
1761 if (propertyValue.equals(value)) { return pd; }
1762 }
1763 }
1764 return null;
1765 }
1766
1767 /**
1768 * Returns the type of the given property contained within the specified class
1769 *
1770 * @param clazz The class which contains the property
1771 * @param propertyName The name of the property
1772 *
1773 * @return The property type or null if none exists
1774 */
1775 @Nullable
1776 public static Class<?> getPropertyType(@Nullable Class<?> clazz, @Nullable String propertyName) {
1777 if (clazz == null || GriffonNameUtils.isBlank(propertyName)) {
1778 return null;
1779 }
1780
1781 try {
1782 PropertyDescriptor desc = getPropertyDescriptor(clazz, propertyName);
1783 if (desc != null) {
1784 return desc.getPropertyType();
1785 } else {
1786 return null;
1787 }
1788 } catch (Exception e) {
1789 // if there are any errors in instantiating just return null for the moment
1790 return null;
1791 }
1792 }
1793
1794 /**
1795 * Retrieves all the properties of the given class for the given type
1796 *
1797 * @param clazz The class to retrieve the properties from
1798 * @param propertyType The type of the properties you wish to retrieve
1799 *
1800 * @return An array of PropertyDescriptor instances
1801 */
1802 @Nonnull
1803 public static PropertyDescriptor[] getPropertiesOfType(@Nullable Class<?> clazz, @Nullable Class<?> propertyType) {
1804 if (clazz == null || propertyType == null) {
1805 return new PropertyDescriptor[0];
1806 }
1807
1808 Set<PropertyDescriptor> properties = new HashSet<>();
1809 try {
1810 PropertyDescriptor[] descriptors = getPropertyDescriptors(clazz);
1811
1812 for (PropertyDescriptor descriptor : descriptors) {
1813 Class<?> currentPropertyType = descriptor.getPropertyType();
1814 if (isTypeInstanceOfPropertyType(propertyType, currentPropertyType)) {
1815 properties.add(descriptor);
1816 }
1817 }
1818 } catch (Exception e) {
1819 // if there are any errors in instantiating just return null for the moment
1820 return new PropertyDescriptor[0];
1821 }
1822 return properties.toArray(new PropertyDescriptor[properties.size()]);
1823 }
1824
1825 private static boolean isTypeInstanceOfPropertyType(Class<?> type, Class<?> propertyType) {
1826 return propertyType.isAssignableFrom(type) && !propertyType.equals(Object.class);
1827 }
1828
1829 /**
1830 * Retrieves all the properties of the given class which are assignable to the given type
1831 *
1832 * @param clazz The class to retrieve the properties from
1833 * @param propertySuperType The type of the properties you wish to retrieve
1834 *
1835 * @return An array of PropertyDescriptor instances
1836 */
1837 public static PropertyDescriptor[] getPropertiesAssignableToType(Class<?> clazz, Class<?> propertySuperType) {
1838 if (clazz == null || propertySuperType == null) { return new PropertyDescriptor[0]; }
1839
1840 Set<PropertyDescriptor> properties = new HashSet<>();
1841 try {
1842 PropertyDescriptor[] descriptors = getPropertyDescriptors(clazz);
1843
1844 for (PropertyDescriptor descriptor : descriptors) {
1845 if (propertySuperType.isAssignableFrom(descriptor.getPropertyType())) {
1846 properties.add(descriptor);
1847 }
1848 }
1849 } catch (Exception e) {
1850 return new PropertyDescriptor[0];
1851 }
1852 return properties.toArray(new PropertyDescriptor[properties.size()]);
1853 }
1854
1855 /**
1856 * Retrieves a property of the given class of the specified name and type
1857 *
1858 * @param clazz The class to retrieve the property from
1859 * @param propertyName The name of the property
1860 * @param propertyType The type of the property
1861 *
1862 * @return A PropertyDescriptor instance or null if none exists
1863 */
1864 public static PropertyDescriptor getProperty(Class<?> clazz, String propertyName, Class<?> propertyType) {
1865 if (clazz == null || propertyName == null || propertyType == null) { return null; }
1866
1867 try {
1868 PropertyDescriptor pd = getPropertyDescriptor(clazz, propertyName);
1869 if (pd != null && pd.getPropertyType().equals(propertyType)) {
1870 return pd;
1871 } else {
1872 return null;
1873 }
1874 } catch (Exception e) {
1875 // if there are any errors in instantiating just return null for the moment
1876 return null;
1877 }
1878 }
1879
1880 /**
1881 * Convenience method for converting a collection to an Object[]
1882 *
1883 * @param c The collection
1884 *
1885 * @return An object array
1886 */
1887 public static Object[] collectionToObjectArray(Collection<?> c) {
1888 if (c == null) { return EMPTY_OBJECT_ARRAY; }
1889 return c.toArray(new Object[c.size()]);
1890 }
1891
1892 /**
1893 * Detect if left and right types are matching types. In particular,
1894 * test if one is a primitive type and the other is the corresponding
1895 * Java wrapper type. Primitive and wrapper classes may be passed to
1896 * either arguments.
1897 *
1898 * @param leftType
1899 * @param rightType
1900 *
1901 * @return true if one of the classes is a native type and the other the object representation
1902 * of the same native type
1903 */
1904 public static boolean isMatchBetweenPrimitiveAndWrapperTypes(@Nonnull Class<?> leftType, @Nonnull Class<?> rightType) {
1905 requireNonNull(leftType, "Left type is null!");
1906 requireNonNull(rightType, "Right type is null!");
1907 return isMatchBetweenPrimitiveAndWrapperTypes(leftType.getName(), rightType.getName());
1908 }
1909
1910 /**
1911 * Detect if left and right types are matching types. In particular,
1912 * test if one is a primitive type and the other is the corresponding
1913 * Java wrapper type. Primitive and wrapper classes may be passed to
1914 * either arguments.
1915 *
1916 * @param leftType
1917 * @param rightType
1918 *
1919 * @return true if one of the classes is a native type and the other the object representation
1920 * of the same native type
1921 */
1922 public static boolean isMatchBetweenPrimitiveAndWrapperTypes(@Nonnull String leftType, @Nonnull String rightType) {
1923 requireNonBlank(leftType, "Left type is null!");
1924 requireNonBlank(rightType, "Right type is null!");
1925 String r = PRIMITIVE_TYPE_COMPATIBLE_TYPES.get(leftType);
1926 return r != null && r.equals(rightType);
1927 }
1928
1929 @Nullable
1930 @SuppressWarnings("ConstantConditions")
1931 private static Method findDeclaredMethod(@Nonnull Class<?> clazz, @Nonnull String methodName, Class[] parameterTypes) {
1932 requireNonNull(clazz, ERROR_CLAZZ_NULL);
1933 requireNonBlank(methodName, ERROR_METHOD_NAME_BLANK);
1934 while (clazz != null) {
1935 try {
1936 Method method = clazz.getDeclaredMethod(methodName, parameterTypes);
1937 if (method != null) { return method; }
1938 } catch (NoSuchMethodException | SecurityException e) {
1939 // skip
1940 }
1941 clazz = clazz.getSuperclass();
1942 }
1943
1944 return null;
1945 }
1946
1947 /**
1948 * <p>Work out if the specified property is readable and static. Java introspection does not
1949 * recognize this concept of static properties but Groovy does. We also consider public static fields
1950 * as static properties with no getters/setters</p>
1951 *
1952 * @param clazz The class to check for static property
1953 * @param propertyName The property name
1954 *
1955 * @return true if the property with name propertyName has a static getter method
1956 */
1957 public static boolean isStaticProperty(@Nonnull Class<?> clazz, @Nonnull String propertyName) {
1958 requireNonNull(clazz, ERROR_CLAZZ_NULL);
1959 requireNonBlank(propertyName, ERROR_PROPERTY_NAME_BLANK);
1960 Method getter = findDeclaredMethod(clazz, getGetterName(propertyName), null);
1961 if (getter != null) {
1962 return isPublicStatic(getter);
1963 } else {
1964 try {
1965 Field f = clazz.getDeclaredField(propertyName);
1966 if (f != null) {
1967 return isPublicStatic(f);
1968 }
1969 } catch (NoSuchFieldException ignore) {
1970 //ignore
1971 }
1972 }
1973
1974 return false;
1975 }
1976
1977 /**
1978 * Determine whether the method is declared public static
1979 *
1980 * @param m the method to be tested
1981 *
1982 * @return True if the method is declared public static
1983 */
1984 public static boolean isPublicStatic(@Nonnull Method m) {
1985 requireNonNull(m, "Argument 'method' must not be null");
1986 final int modifiers = m.getModifiers();
1987 return Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers);
1988 }
1989
1990 /**
1991 * Determine whether the field is declared public static
1992 *
1993 * @param f the field to be tested
1994 *
1995 * @return True if the field is declared public static
1996 */
1997 public static boolean isPublicStatic(@Nonnull Field f) {
1998 requireNonNull(f, "Argument 'field' must not be null");
1999 final int modifiers = f.getModifiers();
2000 return Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers);
2001 }
2002
2003 /**
2004 * Calculate the name for a getter method to retrieve the specified property
2005 *
2006 * @param propertyName the name of the property
2007 *
2008 * @return The name for the getter method for this property, if it were to exist, i.e. getConstraints
2009 */
2010 @Nonnull
2011 public static String getGetterName(@Nonnull String propertyName) {
2012 requireNonBlank(propertyName, ERROR_PROPERTY_NAME_BLANK);
2013 return PROPERTY_GET_PREFIX + Character.toUpperCase(propertyName.charAt(0))
2014 + propertyName.substring(1);
2015 }
2016
2017 /**
2018 * <p>Get a static property value, which has a public static getter or is just a public static field.</p>
2019 *
2020 * @param clazz The class to check for static property
2021 * @param name The property name
2022 *
2023 * @return The value if there is one, or null if unset OR there is no such property
2024 */
2025 @Nullable
2026 public static Object getStaticPropertyValue(@Nonnull Class<?> clazz, @Nonnull String name) {
2027 requireNonNull(clazz, ERROR_CLAZZ_NULL);
2028 requireNonBlank(name, ERROR_NAME_BLANK);
2029 Method getter = findDeclaredMethod(clazz, getGetterName(name), null);
2030 try {
2031 if (getter != null) {
2032 return getter.invoke(null, (Object[]) null);
2033 } else {
2034 Field f = clazz.getDeclaredField(name);
2035 if (f != null) {
2036 return f.get(null);
2037 }
2038 }
2039 } catch (Exception ignore) {
2040 //ignore
2041 }
2042 return null;
2043 }
2044
2045 /**
2046 * <p>Looks for a property of the reference instance with a given name.</p>
2047 * <p>If found its value is returned. We follow the Java bean conventions with augmentation for groovy support
2048 * and static fields/properties. We will therefore match, in this order:
2049 * </p>
2050 * <ol>
2051 * <li>Standard public bean property (with getter or just public field, using normal introspection)
2052 * <li>Public static property with getter method
2053 * <li>Public static field
2054 * </ol>
2055 *
2056 * @return property value or null if no property found
2057 */
2058 @Nullable
2059 public static Object getPropertyOrStaticPropertyOrFieldValue(@Nonnull Object obj, @Nonnull String name) {
2060 requireNonNull(obj, ERROR_OBJECT_NULL);
2061 requireNonBlank(name, ERROR_NAME_BLANK);
2062 if (isReadable(obj, name)) {
2063 try {
2064 return getProperty(obj, name);
2065 } catch (Exception e) {
2066 throw new PropertyException(obj, name);
2067 }
2068 } else {
2069 // Look for public fields
2070 if (isPublicField(obj, name)) {
2071 return getFieldValue(obj, name);
2072 }
2073
2074 // Look for statics
2075 Class<?> clazz = obj.getClass();
2076 if (isStaticProperty(clazz, name)) {
2077 return getStaticPropertyValue(clazz, name);
2078 } else {
2079 return null;
2080 }
2081 }
2082 }
2083
2084 /**
2085 * Get the value of a declared field on an object
2086 *
2087 * @param obj the instance that owns the field
2088 * @param name the name of the file to lookup
2089 *
2090 * @return The object value or null if there is no such field or access problems
2091 */
2092 @Nullable
2093 public static Object getFieldValue(@Nonnull Object obj, @Nonnull String name) {
2094 requireNonNull(obj, ERROR_OBJECT_NULL);
2095 requireNonBlank(name, ERROR_NAME_BLANK);
2096
2097 Class<?> clazz = obj.getClass();
2098 Class<?> c = clazz;
2099 while (c != null && !c.equals(Object.class)) {
2100 Field field = null;
2101 boolean wasAccessible = false;
2102 try {
2103 field = c.getDeclaredField(name);
2104 wasAccessible = field.isAccessible();
2105 field.setAccessible(true);
2106 return field.get(obj);
2107 } catch (Exception e) {
2108 // ignore
2109 } finally {
2110 if (field != null) {
2111 field.setAccessible(wasAccessible);
2112 }
2113 }
2114 c = c.getSuperclass();
2115 }
2116
2117 return null;
2118 }
2119
2120 /**
2121 * Get the a declared field on an object
2122 *
2123 * @param obj the instance that owns the field
2124 * @param name the name of the file to lookup
2125 *
2126 * @return The field or null if there is no such field or access problems
2127 */
2128 @Nullable
2129 public static Field getField(@Nonnull Object obj, @Nonnull String name) {
2130 requireNonNull(obj, ERROR_OBJECT_NULL);
2131 requireNonBlank(name, ERROR_NAME_BLANK);
2132 return getField(obj.getClass(), name);
2133 }
2134
2135 /**
2136 * Get the a declared field on a class
2137 *
2138 * @param clazz the clazz that owns the field
2139 * @param name the name of the file to lookup
2140 *
2141 * @return The field or null if there is no such field or access problems
2142 */
2143 @Nullable
2144 public static Field getField(@Nonnull Class<?> clazz, @Nonnull String name) {
2145 requireNonNull(clazz, ERROR_CLAZZ_NULL);
2146 requireNonBlank(name, ERROR_NAME_BLANK);
2147
2148 Class<?> c = clazz;
2149 while (c != null && !c.equals(Object.class)) {
2150 try {
2151 return c.getDeclaredField(name);
2152 } catch (Exception e) {
2153 // ignore
2154 }
2155 c = c.getSuperclass();
2156 }
2157 return null;
2158 }
2159
2160 /**
2161 * Returns an array of {@code Field} objects reflecting all the fields
2162 * declared by the class and its hierarchy, represented by this
2163 * {@code Class} object. This includes public, protected, default
2164 * (package) access, and private fields, but excludes inherited fields.
2165 * <p>
2166 * <p> The elements in the returned array are not sorted and are not in any
2167 * particular order.
2168 *
2169 * @param clazz the clazz that will be queried.
2170 *
2171 * @return the array of {@code Field} objects representing all the
2172 * declared fields of this class and its hierarchy
2173 */
2174 public static Field[] getAllDeclaredFields(@Nonnull Class<?> clazz) {
2175 requireNonNull(clazz, ERROR_CLAZZ_NULL);
2176
2177 List<Field> fields = new ArrayList<>();
2178
2179 Class<?> c = clazz;
2180 while (c != null && !c.equals(Object.class)) {
2181 Field[] declaredFields = c.getDeclaredFields();
2182 if (declaredFields != null && declaredFields.length > 0) {
2183 fields.addAll(Arrays.asList(declaredFields));
2184 }
2185 c = c.getSuperclass();
2186 }
2187
2188 return fields.toArray(new Field[fields.size()]);
2189 }
2190
2191 /**
2192 * Work out if the specified object has a public field with the name supplied.
2193 *
2194 * @param obj the instance that owns the field
2195 * @param name the name of the file to lookup
2196 *
2197 * @return True if a public field with the name exists
2198 */
2199 public static boolean isPublicField(@Nonnull Object obj, @Nonnull String name) {
2200 requireNonNull(obj, ERROR_OBJECT_NULL);
2201 requireNonBlank(name, ERROR_NAME_BLANK);
2202 Class<?> clazz = obj.getClass();
2203 Field f;
2204 try {
2205 f = clazz.getDeclaredField(name);
2206 return Modifier.isPublic(f.getModifiers());
2207 } catch (NoSuchFieldException e) {
2208 return false;
2209 }
2210 }
2211
2212 /**
2213 * Checks whether the specified property is inherited from a super class
2214 *
2215 * @param clz The class to check
2216 * @param propertyName The property name
2217 *
2218 * @return True if the property is inherited
2219 */
2220 public static boolean isPropertyInherited(@Nullable Class<?> clz, @Nonnull String propertyName) {
2221 if (clz == null) { return false; }
2222 requireNonBlank(propertyName, ERROR_PROPERTY_NAME_BLANK);
2223 Class<?> superClass = clz.getSuperclass();
2224
2225 PropertyDescriptor pd;
2226 try {
2227 pd = getPropertyDescriptor(superClass, propertyName);
2228 } catch (Exception e) {
2229 throw new PropertyException(superClass, propertyName, e);
2230 }
2231 return pd != null && pd.getReadMethod() != null;
2232 }
2233
2234 /**
2235 * Creates a concrete collection for the supplied interface
2236 *
2237 * @param interfaceType The interface
2238 *
2239 * @return ArrayList for List, TreeSet for SortedSet, HashSet for Set etc.
2240 */
2241 @Nonnull
2242 public static Collection<?> createConcreteCollection(@Nonnull Class<?> interfaceType) {
2243 requireNonNull(interfaceType, ERROR_TYPE_NULL);
2244 Collection<?> elements;
2245 if (interfaceType.equals(List.class)) {
2246 elements = new ArrayList<>();
2247 } else if (interfaceType.equals(SortedSet.class)) {
2248 elements = new TreeSet<>();
2249 } else {
2250 elements = new LinkedHashSet<>();
2251 }
2252 return elements;
2253 }
2254
2255 /**
2256 * Retrieves the name of a setter for the specified property name
2257 *
2258 * @param propertyName The property name
2259 *
2260 * @return The setter equivalent
2261 */
2262 @Nonnull
2263 public static String getSetterName(@Nonnull String propertyName) {
2264 requireNonBlank(propertyName, ERROR_PROPERTY_NAME_BLANK);
2265 return PROPERTY_SET_PREFIX + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
2266 }
2267
2268 /**
2269 * Returns true if the name of the method specified and the number of arguments make it a javabean property
2270 *
2271 * @param name True if its a Javabean property
2272 * @param args The arguments
2273 *
2274 * @return True if it is a javabean property method
2275 */
2276 @SuppressWarnings("ConstantConditions")
2277 public static boolean isGetter(@Nullable String name, @Nullable Class[] args) {
2278 if (GriffonNameUtils.isBlank(name) || args == null) { return false; }
2279 if (args.length != 0) { return false; }
2280
2281 if (name.startsWith(PROPERTY_GET_PREFIX)) {
2282 name = name.substring(3);
2283 if (name.length() > 0 && Character.isUpperCase(name.charAt(0))) { return true; }
2284 } else if (name.startsWith(PROPERTY_IS_PREFIX)) {
2285 name = name.substring(2);
2286 if (name.length() > 0 && Character.isUpperCase(name.charAt(0))) { return true; }
2287 }
2288 return false;
2289 }
2290
2291 /**
2292 * Returns a property name equivalent for the given getter name or null if it is not a getter
2293 *
2294 * @param getterName The getter name
2295 *
2296 * @return The property name equivalent
2297 */
2298 @Nullable
2299 @SuppressWarnings("ConstantConditions")
2300 public static String getPropertyForGetter(@Nullable String getterName) {
2301 if (GriffonNameUtils.isBlank(getterName)) { return null; }
2302
2303 if (getterName.startsWith(PROPERTY_GET_PREFIX)) {
2304 String prop = getterName.substring(3);
2305 return convertPropertyName(prop);
2306 } else if (getterName.startsWith(PROPERTY_IS_PREFIX)) {
2307 String prop = getterName.substring(2);
2308 return convertPropertyName(prop);
2309 }
2310 return null;
2311 }
2312
2313 @Nonnull
2314 private static String convertPropertyName(@Nonnull String prop) {
2315 if (Character.isUpperCase(prop.charAt(0)) && Character.isUpperCase(prop.charAt(1))) {
2316 return prop;
2317 } else if (Character.isDigit(prop.charAt(0))) {
2318 return prop;
2319 } else {
2320 return Character.toLowerCase(prop.charAt(0)) + prop.substring(1);
2321 }
2322 }
2323
2324 /**
2325 * Returns a property name equivalent for the given setter name or null if it is not a getter
2326 *
2327 * @param setterName The setter name
2328 *
2329 * @return The property name equivalent
2330 */
2331 @Nullable
2332 @SuppressWarnings("ConstantConditions")
2333 public static String getPropertyForSetter(@Nullable String setterName) {
2334 if (GriffonNameUtils.isBlank(setterName)) { return null; }
2335
2336 if (setterName.startsWith(PROPERTY_SET_PREFIX)) {
2337 String prop = setterName.substring(3);
2338 return convertPropertyName(prop);
2339 }
2340 return null;
2341 }
2342
2343 @SuppressWarnings("ConstantConditions")
2344 public static boolean isSetter(@Nullable String name, @Nullable Class[] args) {
2345 if (GriffonNameUtils.isBlank(name) || args == null) { return false; }
2346
2347 if (name.startsWith(PROPERTY_SET_PREFIX)) {
2348 if (args.length != 1) { return false; }
2349 name = name.substring(3);
2350 if (name.length() > 0 && Character.isUpperCase(name.charAt(0))) { return true; }
2351 }
2352
2353 return false;
2354 }
2355
2356 /**
2357 * Returns true if the specified clazz parameter is either the same as, or is a superclass or super interface
2358 * of, the specified type parameter. Converts primitive types to compatible class automatically.
2359 *
2360 * @param clazz
2361 * @param type
2362 *
2363 * @return True if the class is a taglib
2364 *
2365 * @see java.lang.Class#isAssignableFrom(Class)
2366 */
2367 public static boolean isAssignableOrConvertibleFrom(@Nullable Class<?> clazz, @Nullable Class<?> type) {
2368 if (type == null || clazz == null) {
2369 return false;
2370 } else if (type.isPrimitive()) {
2371 // convert primitive type to compatible class
2372 Class<?> primitiveClass = PRIMITIVE_TYPE_COMPATIBLE_CLASSES.get(type);
2373 return primitiveClass != null && clazz.isAssignableFrom(primitiveClass);
2374 } else {
2375 return clazz.isAssignableFrom(type);
2376 }
2377 }
2378
2379 /**
2380 * Retrieves a boolean value from a Map for the given key
2381 *
2382 * @param key The key that references the boolean value
2383 * @param map The map to look in
2384 *
2385 * @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
2386 */
2387 public static boolean getBooleanFromMap(@Nullable String key, @Nullable Map<String, Object> map) {
2388 if (map == null) { return false; }
2389 if (map.containsKey(key)) {
2390 Object o = map.get(key);
2391 if (o == null) { return false; } else if (o instanceof Boolean) {
2392 return (Boolean) o;
2393 } else {
2394 return Boolean.valueOf(o.toString());
2395 }
2396 }
2397 return false;
2398 }
2399
2400 /**
2401 * Returns whether the specified class is either within one of the specified packages or
2402 * within a subpackage of one of the packages
2403 *
2404 * @param clazz The class
2405 * @param packageList The list of packages
2406 *
2407 * @return True if it is within the list of specified packages
2408 */
2409 public static boolean isClassBelowPackage(@Nonnull Class<?> clazz, @Nonnull List<?> packageList) {
2410 requireNonNull(clazz, ERROR_CLAZZ_NULL);
2411 requireNonNull(packageList, "Argument 'packageList' must not be null");
2412 String classPackage = clazz.getPackage().getName();
2413 for (Object packageName : packageList) {
2414 if (packageName != null && classPackage.startsWith(packageName.toString())) {
2415 return true;
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 }
|