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