package org.chinasb.framework.core.base.search.jpa;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
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.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
import javax.persistence.Embeddable;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Transient;
import org.chinasb.framework.core.base.search.Metadata;
public class JPAAnnotationMetadata implements Metadata {
Class<?> klass;
private JPAAnnotationMetadata(Class<?> klass) {
this.klass = klass;
}
static Map<Class<?>, JPAAnnotationMetadata> metadataCache = new HashMap<Class<?>, JPAAnnotationMetadata>();
public static <T> Metadata getMetadata(Type type) {
Class<?> klass = null;
Type[] typeArguments = null;
if (type instanceof ParameterizedType) {
typeArguments = ((ParameterizedType) type).getActualTypeArguments();
type = ((ParameterizedType) type).getRawType();
}
if (type instanceof Class) {
klass = (Class<?>) type;
} else {
throw new IllegalArgumentException("weirdness");
}
if (Collection.class.isAssignableFrom(klass)) {
if (typeArguments == null || typeArguments.length != 1) {
throw new IllegalArgumentException("weirdness again.");
}
if (!(typeArguments[0] instanceof Class)) {
throw new IllegalArgumentException("weirdness a third time.");
}
return new JPAAnnotationCollectionMetadata((Class<?>) typeArguments[0], klass);
} else if (klass.isArray()) {
return new JPAAnnotationCollectionMetadata(klass.getComponentType(), klass);
}
JPAAnnotationMetadata md = metadataCache.get(klass);
if (md == null) {
md = new JPAAnnotationMetadata(klass);
metadataCache.put(klass, md);
}
return md;
}
public Class<?> getCollectionClass() {
return null;
}
public String getEntityName() {
Entity annotation = klass.getAnnotation(Entity.class);
if (annotation == null) {
throw new UnsupportedOperationException("Cannot get Entity Name of non-entity type.");
} else {
if (annotation.name() != null && !annotation.name().isEmpty()) {
return annotation.name();
} else {
return klass.getSimpleName();
}
}
}
public String getIdProperty() {
for (Property prop : getProps().values()) {
if (prop.hasAnnotation(Id.class) || prop.hasAnnotation(EmbeddedId.class)) {
return prop.name;
}
}
return null;
}
public Metadata getIdType() {
String idProp = getIdProperty();
if (idProp != null) {
return getPropertyType(idProp);
}
return null;
}
public Serializable getIdValue(Object object) {
String idProp = getIdProperty();
if (idProp != null) {
return (Serializable) getPropertyValue(object, idProp);
}
return null;
}
public Class<?> getJavaClass() {
return klass;
}
public String[] getProperties() {
String[] array = new String[getProps().size()];
int i = 0;
for (String prop : getProps().keySet()) {
array[i++] = prop;
}
return array;
}
public Metadata getPropertyType(String property) {
Property prop = getProps().get(property);
if (prop == null)
return null;
return getMetadata(prop.getType());
}
public Object getPropertyValue(Object object, String property) {
Property prop = getProps().get(property);
if (prop == null)
return null;
return prop.getValue(object);
}
public boolean isCollection() {
return false;
}
public boolean isEmeddable() {
return null != klass.getAnnotation(Embeddable.class);
}
public boolean isEntity() {
return null != klass.getAnnotation(Entity.class);
}
public boolean isNumeric() {
return Number.class.isAssignableFrom(klass);
}
public boolean isString() {
return String.class.equals(klass);
}
Map<String, Property> props;
public synchronized Map<String, Property> getProps() {
if (props != null)
return props;
props = new TreeMap<String, Property>();
if (!isEntity() && !isEmeddable())
return props; //Will have no persistable properties.
for (Field field : klass.getFields()) {
if (null != field.getDeclaringClass().getAnnotation(Entity.class)
|| null != field.getDeclaringClass().getAnnotation(Embeddable.class)) {
props.put(field.getName(), new Property(field));
}
}
for (Method method : klass.getMethods()) {
String[] name = getterName(method);
if (name != null) {
if (null != method.getDeclaringClass().getAnnotation(Entity.class)
|| null != method.getDeclaringClass().getAnnotation(Embeddable.class)) {
Property property = props.get(name[0]);
if (property == null) {
property = new Property(name[0]);
props.put(name[0], property);
}
property.getter = method;
// if there is a private field with the same name and type,
// add that too.
// it might have annotations
if (property.field == null) {
try {
property.field = klass.getDeclaredField(name[0]);
if (!property.field.getGenericType().equals(property.getter.getGenericReturnType()))
property.field = null;
} catch (SecurityException e) {
} catch (NoSuchFieldException e) {
// if not found just don't add
}
}
// if there is a setter, add that too
try {
property.setter = klass.getMethod("set" + name[1], method.getReturnType());
} catch (SecurityException e) {
} catch (NoSuchMethodException e) {
// if not found just don't add
}
}
}
}
// filter out
for (Iterator<Map.Entry<String, Property>> iterator = props.entrySet().iterator(); iterator.hasNext();) {
Property property = iterator.next().getValue();
if (property.hasAnnotation(Transient.class))
iterator.remove();
}
return props;
}
public static String[] getterName(Method method) {
if (method.getParameterTypes().length != 0 || method.getReturnType() == null)
return null;
String name = method.getName();
if (name.length() > 3 && name.startsWith("get")) {
name = name.substring(3);
} else if (name.length() > 2
&& (method.getReturnType().equals(Boolean.class) || method.getReturnType().equals(boolean.class))
&& name.startsWith("is")) {
name = name.substring(2);
} else {
return null;
}
if (Character.isUpperCase(name.charAt(0))) {
return new String[] { name.substring(0, 1).toLowerCase() + name.substring(1), name };
} else {
return null;
}
}
private static class Property {
public Property(String name) {
this.name = name;
}
public Property(Field field) {
name = field.getName();
this.field = field;
};
public Type getType() {
if (getter != null) {
return getter.getGenericReturnType();
} else if (field != null) {
return field.getGenericType();
} else if (setter != null) {
return setter.getGenericParameterTypes()[0];
} else {
return null;
}
}
public Object getValue(Object o) {
try {
if (getter != null) {
return getter.invoke(o);
} else if (field != null) {
return field.get(o);
}
} catch (IllegalArgumentException e) {
throw new RuntimeException("Unexpected error getting value of property");
} catch (IllegalAccessException e) {
throw new RuntimeException("Unexpected error getting value of property");
} catch (InvocationTargetException e) {
throw new RuntimeException("Unexpected error getting value of property");
}
return null;
}
public <T extends Annotation> boolean hasAnnotation(Class<T> annotationClass) {
return getAnnotation(annotationClass) != null;
}
public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
T val = null;
if (getter != null)
val = getter.getAnnotation(annotationClass);
if (val == null && field != null)
val = field.getAnnotation(annotationClass);
if (val == null && setter != null)
val = setter.getAnnotation(annotationClass);
return val;
}
String name;
Field field;
Method getter;
Method setter;
}
}