/**
* $Id: ClassData.java 61 2009-09-25 11:14:16Z azeckoski $
* $URL: http://reflectutils.googlecode.com/svn/trunk/src/main/java/org/azeckoski/reflectutils/ClassData.java $
* ClassData.java - genericdao - Sep 4, 2008 6:19:39 PM - azeckoski
**************************************************************************
* Copyright (c) 2008 Aaron Zeckoski
* Licensed under the Apache License, Version 2.0
*
* A copy of the Apache License has been included in this
* distribution and is available at: http://www.apache.org/licenses/LICENSE-2.0.txt
*
* Aaron Zeckoski (azeckoski @ gmail.com) (aaronz @ vt.edu) (aaron @ caret.cam.ac.uk)
*/
package org.azeckoski.reflectutils;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* @param <T> the class type
* A class which retrieves and holds all the reflected class data related to a class
* (annotations, constructors, fields, methods), this is immutable<br/>
* WARNING: this is NOT cheap and the results need to be cached badly,
* you should get this from the {@link ClassDataCacher} rather than constructing it yourself<br/>
*/
public class ClassData<T> {
private final Class<T> type;
private final List<Annotation> annotations;
private final List<Constructor<T>> constructors;
private final List<Field> fields;
private final List<Method> methods;
private final List<Class<?>> interfaces;
private final List<Class<?>> superclasses;
private final List<T> enumConstants;
public ClassData(Class<T> type) {
if (type == null) {
throw new IllegalArgumentException("ClassData requires a valid class, type is null");
}
if (type.isPrimitive() || type.isArray()) {
throw new IllegalArgumentException("Invalid type to make reflection cache for ("+type.getName()+"), cannot reflect over primitives or arrays");
}
this.type = type;
T[] ecs = type.getEnumConstants();
if (ecs != null) {
enumConstants = new ArrayList<T>(ecs.length);
for (T t : ecs) {
enumConstants.add(t);
}
} else {
enumConstants = new ArrayList<T>(0);
}
annotations = new ArrayList<Annotation>(5);
getAllAnnotations(type, annotations);
constructors = new ArrayList<Constructor<T>>(5);
fields = new ArrayList<Field>(5);
methods = new ArrayList<Method>(5);
interfaces = new ArrayList<Class<?>>(5);
superclasses = new ArrayList<Class<?>>(5);
getAllThings(type, fields, methods, constructors, interfaces, superclasses);
// sort the fields,methods,constructors
Collections.sort(fields, new MemberComparator());
Collections.sort(methods, new MemberComparator());
Collections.sort(constructors, new MemberComparator());
// remove duplicates from the list of interfaces
ArrayUtils.removeDuplicates(interfaces);
}
/**
* @return only the public constructors in the class this data represents
*/
public List<Constructor<T>> getPublicConstructors() {
ArrayList<Constructor<T>> pubConstructors = new ArrayList<Constructor<T>>();
for (Constructor<T> constructor : constructors) {
if (Modifier.isPublic(constructor.getModifiers())) {
pubConstructors.add(constructor);
}
}
return pubConstructors;
}
/**
* @return only the public fields in the class this data represents
*/
public List<Field> getPublicFields() {
ArrayList<Field> pubFields = new ArrayList<Field>();
for (Field field : fields) {
if (Modifier.isPublic(field.getModifiers())) {
pubFields.add(field);
}
}
return pubFields;
}
/**
* @return only the public methods for the class this data represents
*/
public List<Method> getPublicMethods() {
ArrayList<Method> pubMethods = new ArrayList<Method>();
for (Method method : methods) {
if (Modifier.isPublic(method.getModifiers())) {
pubMethods.add(method);
}
}
return pubMethods;
}
protected void getAllAnnotations(final Class<?> type, final List<Annotation> list) {
// get only public annotations
Annotation[] annotations = type.getAnnotations();
for (Annotation annotation : annotations) {
if (annotation != null) {
list.add(annotation);
}
}
}
@SuppressWarnings("unchecked")
protected void getAllThings(final Class<?> type, final List<Field> fList, final List<Method> mList, final List<Constructor<T>> cList, final List<Class<?>> iList, final List<Class<?>> sList) {
// get the fields for the current class
Field[] fields = type.getDeclaredFields();
for (final Field field : fields) {
if (field != null) {
int modifiers = field.getModifiers();
if (Modifier.isPublic(modifiers)) {
fList.add(field);
} else {
try {
AccessController.doPrivileged(new PrivilegedAction<T>() {
public T run() {
field.setAccessible(true);
return null;
}
});
fList.add(field);
} catch (SecurityException e) {
// oh well, this does not get added then
}
}
}
}
Method[] methods = type.getDeclaredMethods();
for (final Method method : methods) {
if (method != null) {
int modifiers = method.getModifiers();
if (Modifier.isPublic(modifiers)) {
mList.add(method);
} else {
try {
AccessController.doPrivileged(new PrivilegedAction<T>() {
public T run() {
method.setAccessible(true);
return null;
}
});
mList.add(method);
} catch (SecurityException e) {
// oh well, this does not get added then
}
}
}
}
Constructor<?>[] constructors = type.getDeclaredConstructors();
for (final Constructor<?> constructor : constructors) {
// need to avoid the Object constructor
if (!Object.class.equals(type) && constructor != null) {
int modifiers = constructor.getModifiers();
if (Modifier.isPublic(modifiers)) {
cList.add((Constructor<T>)constructor);
} else {
try {
AccessController.doPrivileged(new PrivilegedAction<T>() {
public T run() {
constructor.setAccessible(true);
return null;
}
});
cList.add((Constructor<T>)constructor);
} catch (SecurityException e) {
// oh well, this does not get added then
}
}
}
}
// now we recursively go through the interfaces and super classes
Class<?>[] interfaces = type.getInterfaces();
for (Class<?> iface : interfaces) {
iList.add(iface); // add to the interfaces list
}
for (int i = 0; i < interfaces.length; i++) {
getAllThings(interfaces[i], fList, mList, cList, iList, sList);
}
Class<?> superClass = type.getSuperclass();
if (superClass != null) {
sList.add(superClass); // add to superclasses list
getAllThings(superClass, fList, mList, cList, iList, sList);
}
}
/**
* @return the type of the class this data represents
*/
public Class<T> getType() {
return type;
}
/**
* @return the annotations on the class this data represents
*/
public List<Annotation> getAnnotations() {
return annotations;
}
/**
* @return the constructors for the class this data represents
*/
public List<Constructor<T>> getConstructors() {
return constructors;
}
/**
* @return all fields in the class this data represents
*/
public List<Field> getFields() {
return fields;
}
/**
* @return all methods for the class this data represents
*/
public List<Method> getMethods() {
return methods;
}
/**
* @return all interfaces for the class this data represents
*/
public List<Class<?>> getInterfaces() {
return interfaces;
}
/**
* @return all superclasses (extends) for the class this data represents
*/
public List<Class<?>> getSuperclasses() {
return superclasses;
}
/**
* @return the list of all enum constants (these are the actual enum statics for this enum) OR empty if this is not an enum
*/
public List<T> getEnumConstants() {
return enumConstants;
}
/**
* Sorts the members by visibility and name order
*/
public static final class MemberComparator implements Comparator<Member>, Serializable {
public static final long serialVersionUID = 1l;
public int compare(Member o1, Member o2) {
String c1 = getModifierPrefix(o1.getModifiers()) + o1.getName();
String c2 = getModifierPrefix(o2.getModifiers()) + o2.getName();
return c1.compareTo(c2);
}
}
public static final String getModifierPrefix(int modifier) {
String prefix = "0public-";
if (Modifier.isProtected(modifier)) {
prefix = "1protected-";
} else if (Modifier.isPrivate(modifier)) {
prefix = "2private-";
}
return prefix;
}
}