/* Reflections.java
Purpose:
Description:
History:
Mar 20, 2012 Created by pao
Copyright (C) 2011 Potix Corporation. All Rights Reserved.
*/
// ported from zk 6.0.0
// original package: org.zkoss.zk.ui.select
package org.zkoss.zats.common.select.impl;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
/**
* A collection of reflection utilities.
* @since 6.0.0
* @author simonpai
*/
public class Reflections {
// field scanner //
/**
* Traverse through all fields with a certain annotation in a class and
* it super classes. Subclass first. If a field is re-declared in subclass,
* it will be skipped at the superclass.
*/
public static <A extends Annotation> void forFields(Class<?> clazz,
Class<A> annotationClass, FieldRunner<A> runner){
final Set<String> handled = new HashSet<String>();
for(Class<?> c = clazz; c != null; c = c.getSuperclass()) {
for(Field f : c.getDeclaredFields()){
A anno = f.getAnnotation(annotationClass);
if(anno == null) continue;
// skip if handled in subclass
if(handled.contains(f.getName())) continue;
f.setAccessible(true);
runner.onField(c, f, anno);
handled.add(f.getName());
}
}
}
// method scanner //
/**
* Traverse through all methods with a certain annotation in a class,
* including ones inherited from its super class.
*/
public static <A extends Annotation> void forMethods(Class<?> clazz,
Class<A> annotationClass, MethodRunner<A> runner){
for(Method m : clazz.getMethods()){
A anno = m.getAnnotation(annotationClass);
if(anno == null) continue;
m.setAccessible(true);
runner.onMethod(clazz, m, anno);
}
}
// field information //
/**
* Return true if field is of Collection type, and an object of type clazz
* can be added into the Collection.
*/
public static boolean isAppendableToCollection(Type type, Object object){
if(!Collection.class.isAssignableFrom(getClass(type)))
return false;
if(type instanceof ParameterizedType)
return getClass(((ParameterizedType) type)
.getActualTypeArguments()[0]).isAssignableFrom(
object.getClass());
return type instanceof Class<?>;
}
// method information //
/**
* Return true if the given arguments can be passed as the arguments of
* the method.
*/
public static boolean isPassableToMethod(Method method, Object ... arguments){
Type[] types = method.getGenericParameterTypes();
if(types.length != arguments.length) return false;
for(int i = 0; i < types.length; i++)
if(!getClass(types[i]).isAssignableFrom(arguments[i].getClass()))
return false;
return true;
}
// field operation //
/**
* Set a value to a field of the object.
*/
public static void setFieldValue(Object bean, Field field, Object value){
try {
field.set(bean, value);
} catch (IllegalArgumentException e) {
throw new IllegalStateException(
"IllegalStateException @ setFieldValue");
} catch (IllegalAccessException e) {
throw new IllegalStateException(
"IllegalAccessException @ setFieldValue");
}
}
/**
* Get field value.
*/
public static Object getFieldValue(Object bean, Field field) {
try {
return field.get(bean);
} catch (IllegalArgumentException e) {
throw new IllegalStateException("IllegalStateException @ getFieldValue");
} catch (IllegalAccessException e) {
throw new IllegalStateException("IllegalStateException @ getFieldValue");
}
}
/**
* Add the item to a collection field of an object.
*/
@SuppressWarnings("unchecked")
public static void addToCollectionField(Object owner, Field field,
Object item){
if(!isAppendableToCollection(field.getGenericType(), item))
throw new IllegalArgumentException(
item + " is not appendable to " + field);
try {
field.setAccessible(true);
Collection<Object> c = (Collection<Object>) field.get(owner);
if(c == null) throw new IllegalArgumentException(
field + " has not been initiated.");
c.add(item);
} catch (IllegalAccessException e) {
throw new IllegalStateException(e);
}
}
/**
* Invoke a method reflexively.
*/
public static void invokeMethod(Method method, Object bean,
Object ... arguments){
if(!isPassableToMethod(method, arguments))
throw new IllegalArgumentException(
"Arguments does not match method signature: " + method);
method.setAccessible(true);
try {
method.invoke(bean, arguments);
} catch (IllegalAccessException e) {
throw new IllegalStateException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
}
// interface //
public interface FieldRunner<A extends Annotation> {
public void onField(Class<?> clazz, Field field, A annotation);
}
public interface MethodRunner<A extends Annotation> {
public void onMethod(Class<?> clazz, Method method, A annotation);
}
/**
* Return the Class representing the given Type.
*/
public static Class<?> getClass(Type type) {
if (type instanceof Class<?>) return (Class<?>) type;
else if (type instanceof ParameterizedType)
return getClass(((ParameterizedType) type).getRawType());
else if (type instanceof GenericArrayType) {
Class<?> c =
getClass(((GenericArrayType) type).getGenericComponentType());
if (c != null) return Array.newInstance(c, 0).getClass();
}
return null;
}
}