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