/** * 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; } } }