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