/**
* Copyright 2011 The Apache Software Foundation
*
* 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.
*
* @author Felipe Oliveira (http://mashup.fm)
*
*/
package org.tynamo.model.elasticsearch.util;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tynamo.model.elasticsearch.mapping.MappingUtil;
/**
* The Class ReflectionUtil.
*/
public abstract class ReflectionUtil {
public static final Logger logger = LoggerFactory.getLogger(ReflectionUtil.class);
/** The Constant annotationFieldsCache. */
private static final ConcurrentMap<ClassAnnotationHolder, AnnotationFieldsHolder> annotationFieldsCache = new ConcurrentHashMap<ClassAnnotationHolder, AnnotationFieldsHolder>();
/** The Constant classFieldsCache. */
private static final ConcurrentMap<String, List<Field>> classFieldsCache = new ConcurrentHashMap<String, List<Field>>();
/** Constructor cache */
private static final ConcurrentMap<Class<?>, Constructor<?>> classConstructorCache = new ConcurrentHashMap<Class<?>, Constructor<?>>();
/**
* Instantiates a new reflection util.
*/
private ReflectionUtil() {
// private
}
public static void clearCache() {
annotationFieldsCache.clear();
classFieldsCache.clear();
classConstructorCache.clear();
}
/**
* Gets the all fields.
*
* @param originalClass
* the original class
* @return the all fields
*/
public static List<Field> getAllFields(final Class<?> originalClass) {
Class<?> clazz = originalClass;
// class name is the key
final String className = clazz.getCanonicalName();
// return from cache
if (classFieldsCache.containsKey(className)) {
List<Field> fields = classFieldsCache.get(className);
return fields;
}
// Init Counter
int count = 0;
// Init List
final List<Field> fields = new ArrayList<Field>();
// Get all the fields including superclasses
while (clazz != null) {
fields.addAll(Arrays.asList(clazz.getDeclaredFields()));
clazz = clazz.getSuperclass();
count++;
}
// Check Count
if (count > 10) {
logger.warn("Too many iterations on ReflectionUtil.getAllFields() - class: "
+ originalClass);
}
// make the list unmodifiable
List<Field> unmodifiableFields = Collections.<Field> unmodifiableList(fields);
// put into cache
classFieldsCache.put(className, unmodifiableFields);
// Return List
return unmodifiableFields;
}
/**
* Gets the field value.
*
* @param object
* the object
* @param field
* the field
* @return the field value
*/
public static Object getFieldValue(Object object, Field field) {
try {
field.setAccessible(true);
return field.get(object);
} catch (Exception e) {
logger.warn(ExceptionUtils.getStackTrace(e));
// throw new RuntimeException( e );
}
return null;
}
/**
* Gets the field value.
*
* @param object
* the object
* @param fieldName
* the field name
* @return the field value
*/
public static Object getFieldValue(Object object, String fieldName) {
try {
Class<?> clazz = object.getClass();
// Get all the fields including superclasses
while (clazz != null) {
for (Field field : clazz.getDeclaredFields()) {
if (field.getName().equals(fieldName)) {
field.setAccessible(true);
return field.get(object);
}
}
clazz = clazz.getSuperclass();
}
} catch (Exception e) {
logger.warn(ExceptionUtils.getStackTrace(e));
// throw new RuntimeException( e );
}
return null;
}
/**
* Gets the all field names with type.
*
* @param originalClass
* the original class
* @param type
* the type
* @return the all field names with type
*/
public static List<String> getAllFieldNamesWithType(final Class<?> originalClass, Class<?> type) {
Class<?> clazz = originalClass;
// Init List
List<String> fieldNames = new ArrayList<String>();
// Init Counter
int count = 0;
// Get all the fields including superclasses
while (clazz != null) {
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (type.isAssignableFrom(field.getType())) {
fieldNames.add(field.getName());
}
}
clazz = clazz.getSuperclass();
count++;
}
// Check Count
if (count > 10) {
logger.warn("Too many iterations on ReflectionUtil.getFieldNamesWithType() - class: "
+ originalClass + " - " + type);
}
// Return List
return fieldNames;
}
/**
* Checks for annotation.
*
* @param field
* the field
* @param clazz
* the clazz
* @return true, if successful
*/
public static boolean hasAnnotation(Field field, Class<? extends Annotation> clazz) {
return field.isAnnotationPresent(clazz);
}
/**
* New instance.
*
* @param className
* the class name
* @return the object
*/
public static Object newInstance(String className) {
Class<?> clazz;
try {
clazz = Class.forName(className);
return newInstance(clazz);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
/**
* New instance.
*
* @param <T>
* the generic type
* @param clazz
* the clazz
* @return the t
*/
public static <T> T newInstance(Class<T> clazz) {
Constructor<T> ctor = null;
if (classConstructorCache.containsKey(clazz)) {
ctor = (Constructor<T>) classConstructorCache.get(clazz);
} else {
// Try public ctor first
try {
ctor = clazz.getConstructor();
} catch (Exception e) {
// Swallow, no public ctor
}
// Next, try non-public ctor
try {
ctor = clazz.getDeclaredConstructor();
ctor.setAccessible(true);
} catch (Exception e) {
// Swallow, no non-public ctor
}
classConstructorCache.put(clazz, ctor);
}
if (ctor != null) {
try {
return ctor.newInstance();
} catch (Exception e) {
throw new RuntimeException("Cannot instantiate " + clazz, e);
}
} else {
throw new RuntimeException("No default constructor for " + clazz);
}
}
private static AnnotationFieldsHolder getAnnotationHolder(Class<?> mainClass,
Class<? extends Annotation> annotationClass) {
final ClassAnnotationHolder holder = new ClassAnnotationHolder(mainClass, annotationClass);
// get from cache, if present
if (annotationFieldsCache.containsKey(holder)) {
AnnotationFieldsHolder fieldHolder = annotationFieldsCache.get(holder);
return fieldHolder;
}
// Init List
List<Field> fieldsWithAnnotation = new ArrayList<Field>();
List<Field> fields = ReflectionUtil.getAllFields(mainClass);
// Do Work
for (Field field : fields) {
if (field.isAnnotationPresent(annotationClass)) {
fieldsWithAnnotation.add(field);
}
}
final AnnotationFieldsHolder fieldHolder = new AnnotationFieldsHolder(fieldsWithAnnotation);
// put into cache
annotationFieldsCache.put(holder, fieldHolder);
// Return List
return fieldHolder;
}
/**
* Gets the fields with annotation.
*
* @param mainClass
* the main class
* @param annotationClass
* the annotation class
* @return the fields with annotation
*/
public static List<Field> getFieldsWithAnnotation(Class<?> mainClass,
Class<? extends Annotation> annotationClass) {
final AnnotationFieldsHolder holder = getAnnotationHolder(mainClass, annotationClass);
return holder.getFields();
}
/**
* Gets the field names with annotation.
*
* @param mainClass
* the main class
* @param annotationClass
* the annotation class
* @return the field names with annotation
*/
public static List<String> getFieldNamesWithAnnotation(Class<?> mainClass,
Class<? extends Annotation> annotationClass) {
final AnnotationFieldsHolder holder = getAnnotationHolder(mainClass, annotationClass);
return holder.getFieldNames();
}
/**
* Gets the all field names without annotation.
*
* @param clazz
* the clazz
* @param annotationClass
* the annotation class
* @return the all field names without annotation
*/
public static List<String> getAllFieldNamesWithoutAnnotation(Class<?> clazz,
Class<? extends Annotation> annotationClass) {
String className = clazz.getName() + ".";
List<String> fieldNames = new ArrayList<String>();
for (Field field : getAllFields(clazz)) {
if (!field.isAnnotationPresent(annotationClass)) {
fieldNames.add(className + field.getName());
}
}
return fieldNames;
}
/**
* Gets the all fields without annotation.
*
* @param clazz
* the clazz
* @param annotationClass
* the annotation class
* @return the all fields without annotation
*/
public static List<Field> getAllFieldsWithoutAnnotation(Class<?> clazz,
Class<? extends Annotation> annotationClass) {
List<Field> fieldsWithoutAnnotation = new ArrayList<Field>();
for (Field field : getAllFields(clazz)) {
if (!field.isAnnotationPresent(annotationClass)) {
fieldsWithoutAnnotation.add(field);
}
}
return fieldsWithoutAnnotation;
}
/**
* Sets the field value.
*
* @param object
* the object
* @param fieldName
* the field name
* @param value
* the value
*/
public static void setFieldValue(Object object, String fieldName, Object value) {
Field field = getField(object, fieldName);
setFieldValue(object, field, value);
}
/**
* Sets the field value.
*
* @param object
* the object
* @param field
* the field
* @param value
* the value
*/
private static void setFieldValue(Object object, Field field, Object value) {
// make accessible
field.setAccessible(true);
Type type = field.getType();
Class<?> fieldClass = (Class<?>) type;
try {
value = MappingUtil.convertValue(value, fieldClass);
field.set(object, value);
} catch (IllegalArgumentException e) {
logger.error(ExceptionUtils.getStackTrace(e));
} catch (IllegalAccessException e) {
logger.error(ExceptionUtils.getStackTrace(e));
}
}
/**
* Gets the field.
*
* @param object
* the object
* @param fieldName
* the field name
* @return the field
*/
private static Field getField(final Object object, String fieldName) {
Object obj = object;
// make sure object is not null
if (obj == null) {
return null;
}
Class<?> clazz = obj.getClass();
try {
while (clazz != null) {
for (Field f : clazz.getDeclaredFields()) {
if (f.getName().equalsIgnoreCase(fieldName)) {
return f;
}
}
clazz = clazz.getSuperclass();
}
} catch (SecurityException e) {
logger.error(ExceptionUtils.getStackTrace(e));
}
return null;
}
/**
* Gets the parent class by type.
*
* @param object
* the object
* @param clazz
* the clazz
* @return the parent class by type
*/
public static Class<?> getParentClassByType(Object object, Class<?> clazz) {
Class<?> clz = object.getClass();
while (clz != null) {
if (clz.equals(clazz)) {
return clz;
}
clz = clz.getSuperclass();
}
return null;
}
/**
* Checks if is abstract.
*
* @param clazz
* the clazz
* @return true, if is abstract
*/
public static boolean isAbstract(Class<?> clazz) {
int modifiers = clazz.getModifiers();
return (modifiers & Modifier.ABSTRACT) > 0;
}
/**
* Checks if is interface.
*
* @param clazz
* the clazz
* @return true, if is interface
*/
public static boolean isInterface(Class<?> clazz) {
int modifiers = clazz.getModifiers();
return (modifiers & Modifier.INTERFACE) > 0;
}
/**
* Checks if is concrete.
*
* @param clazz
* the clazz
* @return true, if is concrete
*/
public static boolean isConcrete(Class<?> clazz) {
return !(isInterface(clazz) || isAbstract(clazz));
}
/**
* The Class ClassAnnotationHolder.
*/
private static class ClassAnnotationHolder {
/** The main class. */
private final Class<?> mainClass;
/** The annotation class. */
private final Class<?> annotationClass;
/**
* Instantiates a new class annotation holder.
*
* @param mainClass
* the main class
* @param annotationClass
* the annotation class
*/
public ClassAnnotationHolder(Class<?> mainClass, Class<?> annotationClass) {
super();
this.mainClass = mainClass;
this.annotationClass = annotationClass;
}
/**
* Gets the main class.
*
* @return the main class
*/
@SuppressWarnings("unused")
public Class<?> getMainClass() {
return mainClass;
}
/**
* Gets the annotation class.
*
* @return the annotation class
*/
@SuppressWarnings("unused")
public Class<?> getAnnotationClass() {
return annotationClass;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((annotationClass == null) ? 0 : annotationClass.hashCode());
result = prime * result + ((mainClass == null) ? 0 : mainClass.hashCode());
return result;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (this.getClass() != obj.getClass()) {
return false;
}
final ClassAnnotationHolder other = (ClassAnnotationHolder) obj;
if (annotationClass == null) {
if (other.annotationClass != null) {
return false;
}
} else if (!annotationClass.equals(other.annotationClass)) {
return false;
}
if (mainClass == null) {
if (other.mainClass != null) {
return false;
}
} else if (!mainClass.equals(other.mainClass)) {
return false;
}
return true;
}
}
/**
* The Class AnnotationFieldsHolder.
*/
private static class AnnotationFieldsHolder {
/** The fields. */
private final List<Field> fields;
/** The field names. */
private List<String> fieldNames;
/**
* Instantiates a new annotation fields holder.
*
* @param fields
* the fields
*/
public AnnotationFieldsHolder(List<Field> fields) {
super();
this.fields = Collections.<Field> unmodifiableList(fields);
initFieldNames();
}
/**
* Gets the fields.
*
* @return the fields
*/
public List<Field> getFields() {
return fields;
}
/**
* Gets the field names.
*
* @return the field names
*/
public List<String> getFieldNames() {
return fieldNames;
}
/**
* Inits the field names.
*/
private void initFieldNames() {
List<String> names = new ArrayList<String>();
for (Field field : fields) {
names.add(field.getName());
}
// make this an unmodifiableList
fieldNames = Collections.<String> unmodifiableList(names);
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((fieldNames == null) ? 0 : fieldNames.hashCode());
result = prime * result + ((fields == null) ? 0 : fields.hashCode());
return result;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (this.getClass() != obj.getClass()) {
return false;
}
final AnnotationFieldsHolder other = (AnnotationFieldsHolder) obj;
if (fieldNames == null) {
if (other.fieldNames != null) {
return false;
}
} else if (!fieldNames.equals(other.fieldNames)) {
return false;
}
if (fields == null) {
if (other.fields != null) {
return false;
}
} else if (!fields.equals(other.fields)) {
return false;
}
return true;
}
}
}