package org.jboss.resteasy.links.impl;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.persistence.Id;
import javax.xml.bind.annotation.XmlID;
import org.jboss.resteasy.links.ParentResource;
import org.jboss.resteasy.links.ResourceID;
import org.jboss.resteasy.links.ResourceIDs;
public class BeanUtils {
private static final Class<Annotation>[] IdAnnotationList = new Class[]{ResourceID.class, XmlID.class, Id.class};
public static List<Object> findIDs(Object entity){
Class<? extends Object> klass = entity.getClass();
ResourceIDs resourceIDs = findTypeAnnotation(klass, ResourceIDs.class);
if(resourceIDs != null){
// return those properties
String[] names = resourceIDs.value();
List<Object> values = new ArrayList<Object>();
for (String name : names) {
try {
values.add(getPropertyValue(entity, entity.getClass(), name));
} catch (NotFoundException e) {
throw new RuntimeException("Failed to find bean property "+name);
}
}
return values;
}
for(Class<Annotation> idAnnotationClass : IdAnnotationList){
try {
return Collections.singletonList(findAnnotatedProperty(entity, klass, idAnnotationClass));
} catch (NotFoundException e) {
// ignore
}
}
// we got nothing
return Collections.emptyList();
}
private static Object getPropertyValue(Object entity,
Class<?> klass, String name) throws NotFoundException {
// easiest is a public property:
try {
return readPropertyMethods(entity, klass, name, false);
} catch (NotFoundException e) {
// ignore
}
// not found, try private properties
do{
try {
return readPropertyMethods(entity, klass, name, true);
} catch (NotFoundException e) {
// ignore
}
// try the field
try {
Field f = klass.getDeclaredField(name);
return readField(f, entity);
} catch (SecurityException e) {
// there's one but it's not accessible?
throw new RuntimeException("Failed to read property "+name, e);
} catch (NoSuchFieldException e) {
// ignore
}
// go up
klass = klass.getSuperclass();
}while(klass != null);
// we got nothing
throw new NotFoundException();
}
private static Object readPropertyMethods(Object entity, Class<?> klass,
String propertyName, boolean b) throws NotFoundException {
try {
return readPropertyMethod(entity, klass, "is"+capitalise(propertyName), false);
} catch (NotFoundException e) {
// ignore
}
// let this one throw
return readPropertyMethod(entity, klass, "get"+capitalise(propertyName), false);
}
private static Object readPropertyMethod(Object entity, Class<?> klass,
String methodName, boolean declared) throws NotFoundException {
try{
Method getter = declared ? klass.getDeclaredMethod(methodName) : klass.getMethod(methodName);
return readMethod(getter, entity);
} catch (SecurityException e) {
// there's one but it's not accessible?
throw new RuntimeException("Failed to read property "+methodName, e);
} catch (NoSuchMethodException e) {
throw new NotFoundException();
}
}
private static String capitalise(String name) {
return name.substring(0, 1).toUpperCase() + name.substring(1);
}
private static <T extends Annotation> T findTypeAnnotation(Class<?> klass,
Class<T> annotationClass) {
do{
if(klass.isAnnotationPresent(annotationClass))
return klass.getAnnotation(annotationClass);
klass = klass.getSuperclass();
}while(klass != null);
return null;
}
public static Object findParentResource(Object entity) {
try {
return findAnnotatedProperty(entity, entity.getClass(), ParentResource.class);
} catch (NotFoundException e) {
return null;
}
}
private static Object findAnnotatedProperty(Object entity, Class<?> type, Class<? extends Annotation> annotation) throws NotFoundException {
for(Field f : type.getDeclaredFields()){
if(f.isAnnotationPresent(annotation)){
return readField(f, entity);
}
}
for(Method m : type.getDeclaredMethods()){
if(m.isAnnotationPresent(annotation) && isBeanAccessor(m)){
return readMethod(m, entity);
}
}
if(type.getSuperclass() != null)
return findAnnotatedProperty(entity, type.getSuperclass(), annotation);
throw new NotFoundException();
}
private static Object readMethod(Method m, Object entity) {
// read that property
m.setAccessible(true);
try {
return m.invoke(entity);
} catch (Exception e) {
throw new RuntimeException("Failed to read property from method "+m.getName(), e);
}finally{
m.setAccessible(false);
}
}
private static Object readField(Field f, Object entity) {
// read that field
f.setAccessible(true);
try {
return f.get(entity);
} catch (Exception e) {
throw new RuntimeException("Failed to read field "+f.getName(), e);
}finally{
f.setAccessible(false);
}
}
private static boolean isBeanAccessor(Method m) {
String name = m.getName();
return (name.startsWith("get") || name.startsWith("is")) && m.getParameterTypes().length == 0;
}
}