/* * Copyright (c) 2010-2016. Axon Framework * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.axonframework.common; import java.lang.reflect.*; import java.security.AccessController; import java.util.*; /** * Utility class for working with Java Reflection API. * * @author Allard Buijze * @since 0.7 */ public abstract class ReflectionUtils { /** * A map of Primitive types to their respective wrapper types. */ private static final Map<Class<?>, Class<?>> primitiveWrapperTypeMap = new HashMap<>(8); static { primitiveWrapperTypeMap.put(boolean.class, Boolean.class); primitiveWrapperTypeMap.put(byte.class, Byte.class); primitiveWrapperTypeMap.put(char.class, Character.class); primitiveWrapperTypeMap.put(double.class, Double.class); primitiveWrapperTypeMap.put(float.class, Float.class); primitiveWrapperTypeMap.put(int.class, Integer.class); primitiveWrapperTypeMap.put(long.class, Long.class); primitiveWrapperTypeMap.put(short.class, Short.class); } private ReflectionUtils() { // utility class } /** * Returns the value of the given {@code field} in the given {@code object}. If necessary, the field is * made accessible, assuming the security manager allows it. * * @param field The field containing the value * @param object The object to retrieve the field's value from * @return the value of the {@code field} in the {@code object} * @throws IllegalStateException if the field is not accessible and the security manager doesn't allow it to be * made accessible */ @SuppressWarnings("unchecked") public static <R> R getFieldValue(Field field, Object object) { ensureAccessible(field); try { return (R) field.get(object); } catch (IllegalArgumentException | IllegalAccessException ex) { throw new IllegalStateException("Unable to access field for getting.", ex); } } /** * Set the {@code field} of {@code object} to a certain {@code value}. If necessary, the field is made accessible, * assuming the security manager allows it. * * @param field The field to set {@code value} on * @param object The object to set the {@code value} on {@code field} * @param value The value to set on {@code field} * @param <T> The type of the {@code value} */ public static <T> void setFieldValue(Field field, Object object, T value) { ensureAccessible(field); try { field.set(object, value); } catch (IllegalAccessException ex) { throw new IllegalStateException("Unable to access field for setting.", ex); } } /** * Returns the class on which the method with name "{@code getter}" and parameters of type * {@code parameterTypes} is declared. The given {@code instanceClass} is the instance on which the * method cn be called. If the method is not available on the given {@code instanceClass}, {@code null} * is returned. * * @param instanceClass The class on which to look for the method * @param methodName The name of the method * @param parameterTypes The parameter types of the method * @return The class on which the method is declared, or {@code null} if not found */ public static Class<?> declaringClass(Class<?> instanceClass, String methodName, Class<?>... parameterTypes) { try { return instanceClass.getMethod(methodName, parameterTypes).getDeclaringClass(); } catch (NoSuchMethodException e) { return null; } } /** * Indicates whether the given class implements a customized equals method. This methods returns true if the * declaring type of the equals method is not {@code Object}. * * @param type The type to inspect * @return {@code true} if the given type overrides the equals method, otherwise {@code false} */ public static boolean hasEqualsMethod(Class<?> type) { return !Object.class.equals(declaringClass(type, "equals", Object.class)); } /** * Indicates whether the two given objects are <em>not the same</em>, override an equals method that indicates * they are <em>not equal</em>, or implements {@link Comparable} which indicates the two are not equal. If this * method cannot safely indicate two objects are not equal, it returns * {@code false}. * * @param value One of the values to compare * @param otherValue other value to compare * @return {@code true} if these objects explicitly indicate they are not equal, {@code false} otherwise. */ @SuppressWarnings("unchecked") public static boolean explicitlyUnequal(Object value, Object otherValue) { if (value == otherValue) { // NOSONAR (The == comparison is intended here) return false; } else if (value == null || otherValue == null) { return true; } else if (value instanceof Comparable) { return ((Comparable) value).compareTo(otherValue) != 0; } else if (hasEqualsMethod(value.getClass())) { return !value.equals(otherValue); } return false; } /** * Makes the given {@code member} accessible via reflection if it is not the case already. * * @param member The member (field, method, constructor, etc) to make accessible * @param <T> The type of member to make accessible * @return the given {@code member}, for easier method chaining * @throws IllegalStateException if the member is not accessible and the security manager doesn't allow it to be * made accessible */ public static <T extends AccessibleObject> T ensureAccessible(T member) { if (!isAccessible(member)) { AccessController.doPrivileged(new MemberAccessibilityCallback(member)); } return member; } /** * Indicates whether the given {@code member} is accessible. It does so by checking whether the member is * non-final and public, or made accessible via reflection. * * @param member The member (field, method, constructor, etc) to check for accessibility * @return {@code true} if the member is accessible, otherwise {@code false}. */ public static boolean isAccessible(AccessibleObject member) { return member.isAccessible() || (Member.class.isInstance(member) && isNonFinalPublicMember((Member) member)); } /** * Checks whether the given {@code member} is public and non-final. These members do no need to be set * accessible using reflection. * * @param member The member to check * @return {@code true} if the member is public and non-final, otherwise {@code false}. * @see #isAccessible(java.lang.reflect.AccessibleObject) * @see #ensureAccessible(java.lang.reflect.AccessibleObject) */ public static boolean isNonFinalPublicMember(Member member) { return (Modifier.isPublic(member.getModifiers()) && Modifier.isPublic(member.getDeclaringClass().getModifiers()) && !Modifier.isFinal(member.getModifiers())); } /** * Returns an {@link Iterable} of all the fields declared on the given class and its super classes. The iterator * will always return fields declared in a subtype before returning fields declared in a super type. * * @param clazz The class to return fields for * @return an {@code Iterable} providing access to all declared fields in the class hierarchy */ public static Iterable<Field> fieldsOf(Class<?> clazz) { List<Field> fields = new LinkedList<>(); Class<?> currentClazz = clazz; do { fields.addAll(Arrays.asList(currentClazz.getDeclaredFields())); currentClazz = currentClazz.getSuperclass(); } while (currentClazz != null); return Collections.unmodifiableList(fields); } /** * Returns an {@link Iterable} of all the methods declared on the given class and its super classes. The iterator * will always return methods declared in a subtype before returning methods declared in a super type. * * @param clazz The class to return methods for * @return an {@code Iterable} providing access to all declared methods in the class hierarchy */ public static Iterable<Method> methodsOf(Class<?> clazz) { List<Method> methods = new LinkedList<>(); Class<?> currentClazz = clazz; do { methods.addAll(Arrays.asList(currentClazz.getDeclaredMethods())); addMethodsOnDeclaredInterfaces(currentClazz, methods); currentClazz = currentClazz.getSuperclass(); } while (currentClazz != null); return Collections.unmodifiableList(methods); } /** * Returns the boxed wrapper type for the given {@code primitiveType}. * * @param primitiveType The primitive type to return boxed wrapper type for * @return the boxed wrapper type for the given {@code primitiveType} * @throws IllegalArgumentException will be thrown instead of returning null if no wrapper class was found. */ public static Class<?> resolvePrimitiveWrapperType(Class<?> primitiveType) { Assert.notNull(primitiveType, () -> "primitiveType may not be null"); Assert.isTrue(primitiveType.isPrimitive(), () -> "primitiveType is not actually primitive: " + primitiveType); Class<?> primitiveWrapperType = primitiveWrapperTypeMap.get(primitiveType); Assert.notNull(primitiveWrapperType, () -> "no wrapper found for primitiveType: " + primitiveType); return primitiveWrapperType; } private static void addMethodsOnDeclaredInterfaces(Class<?> currentClazz, List<Method> methods) { for (Class iface : currentClazz.getInterfaces()) { methods.addAll(Arrays.asList(iface.getDeclaredMethods())); addMethodsOnDeclaredInterfaces(iface, methods); } } /** * Indicates whether the given field has the "transient" modifier * * @param field the field to inspect * @return {@code true} if the field is marked transient, otherwise {@code false * } */ public static boolean isTransient(Field field) { return Modifier.isTransient(field.getModifiers()); } /** * Resolve a generic type parameter from a field declaration * * @param field The field to find generic parameters for * @param genericTypeIndex The index of the type * @return an optional that contains the resolved type, if found */ public static Optional<Class<?>> resolveGenericType(Field field, int genericTypeIndex) { final Type genericType = field.getGenericType(); if (genericType == null || !(genericType instanceof ParameterizedType) || ((ParameterizedType) genericType).getActualTypeArguments().length <= genericTypeIndex) { return Optional.empty(); } return Optional.of((Class<?>) ((ParameterizedType) genericType).getActualTypeArguments()[genericTypeIndex]); } }