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