/*
* Copied from the Hibernate Validator project
* Original authors: Hardy Ferentschik, Gunnar Morling and Kevin Pollet.
*
* 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.optaplanner.core.impl.domain.common;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* Avoids the usage of Introspector to work on Android too.
*/
public final class ReflectionHelper {
private static final String PROPERTY_ACCESSOR_PREFIX_GET = "get";
private static final String PROPERTY_ACCESSOR_PREFIX_IS = "is";
private static final String[] PROPERTY_ACCESSOR_PREFIXES = {
PROPERTY_ACCESSOR_PREFIX_GET,
PROPERTY_ACCESSOR_PREFIX_IS
};
private static final String PROPERTY_MUTATOR_PREFIX = "set";
/**
* Returns the JavaBeans property name of the given member.
* @param member never null
* @return null if the member is neither a field nor a getter method according to the JavaBeans standard
*/
public static String getGetterPropertyName(Member member) {
if (member instanceof Field) {
return member.getName();
} else if (member instanceof Method) {
String methodName = member.getName();
for (String prefix : PROPERTY_ACCESSOR_PREFIXES) {
if (methodName.startsWith(prefix)) {
return decapitalizePropertyName(methodName.substring(prefix.length()));
}
}
}
return null;
}
private static String decapitalizePropertyName(String propertyName) {
if (propertyName.isEmpty() || startsWithSeveralUpperCaseLetters(propertyName)) {
return propertyName;
} else {
return propertyName.substring(0, 1).toLowerCase() + propertyName.substring(1);
}
}
private static boolean startsWithSeveralUpperCaseLetters(String propertyName) {
return propertyName.length() > 1 &&
Character.isUpperCase(propertyName.charAt(0)) &&
Character.isUpperCase(propertyName.charAt(1));
}
/**
* Checks whether the given method is a valid getter method according to the JavaBeans standard.
* @param method never null
* @return true if the given method is a getter method
*/
public static boolean isGetterMethod(Method method) {
if (method.getParameterTypes().length != 0) {
return false;
}
String methodName = method.getName();
if (methodName.startsWith(PROPERTY_ACCESSOR_PREFIX_GET) && method.getReturnType() != void.class) {
return true;
} else if (methodName.startsWith(PROPERTY_ACCESSOR_PREFIX_IS) && method.getReturnType() == boolean.class) {
return true;
}
return false;
}
/**
* @param containingClass never null
* @param propertyName never null
* @return true if that getter exists
*/
public static boolean hasGetterMethod(Class<?> containingClass, String propertyName) {
return getGetterMethod(containingClass, propertyName) != null;
}
/**
* @param containingClass never null
* @param propertyName never null
* @return sometimes null
*/
public static Method getGetterMethod(Class<?> containingClass, String propertyName) {
String getterName = PROPERTY_ACCESSOR_PREFIX_GET
+ (propertyName.isEmpty() ? "" : propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1));
try {
return containingClass.getMethod(getterName);
} catch (NoSuchMethodException e) {
// intentionally empty
}
String isserName = PROPERTY_ACCESSOR_PREFIX_IS
+ (propertyName.isEmpty() ? "" : propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1));
try {
Method method = containingClass.getMethod(isserName);
if (method.getReturnType() == boolean.class) {
return method;
}
} catch (NoSuchMethodException e) {
// intentionally empty
}
return null;
}
/**
* @param containingClass never null
* @param fieldName never null
* @return true if that field exists
*/
public static boolean hasField(Class<?> containingClass, String fieldName) {
return getField(containingClass, fieldName) != null;
}
/**
* @param containingClass never null
* @param fieldName never null
* @return sometimes null
*/
public static Field getField(Class<?> containingClass, String fieldName) {
try {
return containingClass.getField(fieldName);
} catch (NoSuchFieldException e) {
return null;
}
}
/**
* @param containingClass never null
* @param propertyType never null
* @param propertyName never null
* @return null if it doesn't exist
*/
public static Method getSetterMethod(Class<?> containingClass, Class<?> propertyType, String propertyName) {
String setterName = PROPERTY_MUTATOR_PREFIX
+ (propertyName.isEmpty() ? "" : propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1));
try {
return containingClass.getMethod(setterName, propertyType);
} catch (NoSuchMethodException e) {
return null;
}
}
/**
* @param containingClass never null
* @param propertyName never null
* @return null if it doesn't exist
*/
public static Method getSetterMethod(Class<?> containingClass, String propertyName) {
String setterName = PROPERTY_MUTATOR_PREFIX
+ (propertyName.isEmpty() ? "" : propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1));
Method[] methods = Arrays.stream(containingClass.getMethods())
.filter(method -> method.getName().equals(setterName))
.toArray(Method[]::new);
if (methods.length == 0) {
return null;
}
if (methods.length > 1) {
throw new IllegalStateException("The containingClass (" + containingClass
+ ") has multiple setter methods (" + Arrays.toString(methods)
+ ") with the propertyName (" + propertyName + ").");
}
return methods[0];
}
public static void assertGetterMethod(Method getterMethod, Class<? extends Annotation> annotationClass) {
if (getterMethod.getParameterTypes().length != 0) {
throw new IllegalStateException("The getterMethod (" + getterMethod + ") with a "
+ annotationClass.getSimpleName() + " annotation must not have any parameters ("
+ Arrays.toString(getterMethod.getParameterTypes()) + ").");
}
String methodName = getterMethod.getName();
if (methodName.startsWith(PROPERTY_ACCESSOR_PREFIX_GET)) {
if (getterMethod.getReturnType() == void.class) {
throw new IllegalStateException("The getterMethod (" + getterMethod + ") with a "
+ annotationClass.getSimpleName() + " annotation must have a non-void return type ("
+ getterMethod.getReturnType() + ").");
}
} else if (methodName.startsWith(PROPERTY_ACCESSOR_PREFIX_IS)) {
if (getterMethod.getReturnType() != boolean.class) {
throw new IllegalStateException("The getterMethod (" + getterMethod + ") with a "
+ annotationClass.getSimpleName() + " annotation must have a primitive boolean return type ("
+ getterMethod.getReturnType() + ") or use another prefix in its methodName ("
+ methodName + ").");
}
} else {
throw new IllegalStateException("The getterMethod (" + getterMethod + ") with a "
+ annotationClass.getSimpleName() + " annotation has a methodName ("
+ methodName + ") that does not start with a valid prefix ("
+ Arrays.toString(PROPERTY_ACCESSOR_PREFIXES) + ").");
}
}
public static void assertReadMethod(Method readMethod, Class<? extends Annotation> annotationClass) {
if (readMethod.getParameterTypes().length != 0) {
throw new IllegalStateException("The readMethod (" + readMethod + ") with a "
+ annotationClass.getSimpleName() + " annotation must not have any parameters ("
+ Arrays.toString(readMethod.getParameterTypes()) + ").");
}
if (readMethod.getReturnType() == void.class) {
throw new IllegalStateException("The readMethod (" + readMethod + ") with a "
+ annotationClass.getSimpleName() + " annotation must have a non-void return type ("
+ readMethod.getReturnType() + ").");
}
}
/**
* @param type never null
* @return true if it is a {@link Map}
*/
public static boolean isMap(Type type) {
if (type instanceof Class && Map.class.isAssignableFrom((Class<?>) type)) {
return true;
}
if (type instanceof ParameterizedType) {
return isMap(((ParameterizedType) type).getRawType());
}
if (type instanceof WildcardType) {
Type[] upperBounds = ((WildcardType) type).getUpperBounds();
return upperBounds.length != 0 && isMap(upperBounds[0]);
}
return false;
}
/**
* @param type never null
* @return true if it is a {@link List}
*/
public static boolean isList(Type type) {
if (type instanceof Class && List.class.isAssignableFrom((Class<?>) type)) {
return true;
}
if (type instanceof ParameterizedType) {
return isList(((ParameterizedType) type).getRawType());
}
if (type instanceof WildcardType) {
Type[] upperBounds = ((WildcardType) type).getUpperBounds();
return upperBounds.length != 0 && isList(upperBounds[0]);
}
return false;
}
private ReflectionHelper() {
}
}