package org.springframework.data.simpledb.reflection;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Transient;
import org.springframework.data.mapping.model.MappingException;
import org.springframework.data.simpledb.annotation.Attributes;
import org.springframework.stereotype.Component;
@Component
public final class MetadataParser {
private static final Logger LOGGER = LoggerFactory.getLogger(MetadataParser.class);
public static final String FIELD_NAME_DEFAULT_ID = "id";
private MetadataParser() {
// Utility class
}
public static String getItemName(Object object) {
Field idField = getIdField(object);
if(idField != null) {
try {
idField.setAccessible(true);
return (String) idField.get(object);
} catch(IllegalAccessException e) {
throw new MappingException("Could not read simpleDb id field", e);
}
}
return null;
}
public static Field getIdField(Object object) {
return getIdField(object.getClass());
}
public static Field getIdField(Class<?> clazz) {
Field idField = null;
for(Field f : ReflectionUtils.getDeclaredFieldsInHierarchy(clazz)) {
// named id or annotated with Id
if(f.getName().equals(FIELD_NAME_DEFAULT_ID) || f.getAnnotation(Id.class) != null) {
if(idField != null) {
throw new MappingException("Multiple id fields detected for class " + clazz.getName());
}
idField = f;
}
}
return idField;
}
@SuppressWarnings("unchecked")
public static Map<String, String> getAttributes(Object object) {
Class<?> clazz = object.getClass();
for(Field f : clazz.getDeclaredFields()) {
Attributes attributes = f.getAnnotation(Attributes.class);
if(attributes != null) {
try {
f.setAccessible(true);
return (Map<String, String>) f.get(object);
} catch(IllegalAccessException e) {
LOGGER.error("Could not read simpleDb attributes", e);
}
}
}
return null;
}
public static Field getAttributesField(Object object) {
Class<?> clazz = object.getClass();
for(Field f : clazz.getDeclaredFields()) {
// annotated with Attributes
Attributes attributes = f.getAnnotation(Attributes.class);
if(attributes != null) {
return f;
}
}
return null;
}
public static List<Field> getSupportedFields(Class<?> clazz) {
List<Field> supportedFields = new ArrayList<Field>();
for(Field field : ReflectionUtils.getDeclaredFieldsInHierarchy(clazz)) {
if(isSerializableFieldForObject(clazz, field)) {
supportedFields.add(field);
}
}
return supportedFields;
}
private static boolean isSerializableFieldForObject(Class<?> clazz, Field field) {
boolean isSerializable = !hasUnsupportedAnnotations(field);
isSerializable = isSerializable && !isTransientField(field);
isSerializable = isSerializable && !(Modifier.isStatic(field.getModifiers()) ||
Modifier.isFinal(field.getModifiers()));
isSerializable = isSerializable && (ReflectionUtils.isPersistentField(field) ||
ReflectionUtils.hasDeclaredGetterAndSetter(field, clazz));
return isSerializable;
}
private static boolean hasUnsupportedAnnotations(Field field) {
return (field.getAnnotation(Attributes.class) != null);
}
private static boolean isTransientField(Field field) {
return field.isAnnotationPresent(Transient.class);
}
public static List<Field> getNestedDomainFields(Object object) {
final List<Field> fieldList = new ArrayList<Field>();
for(Field field : object.getClass().getDeclaredFields()) {
if(isNestedDomainField(field)) {
fieldList.add(field);
}
}
return fieldList;
}
public static boolean isNestedDomainField(Field field) {
return FieldTypeIdentifier.isOfType(field, FieldType.NESTED_ENTITY);
}
public static void validateReferenceAnnotation(Field nestedField) {
if(FieldType.REFERENCE_ENTITY.isOfType(nestedField) && getIdField(nestedField.getType()) != null) {
return;
}
// TODO: check spring alliance - is converted to spring + make package for Reference Tests
throw new IllegalStateException("Field @Reference " + nestedField.getName()
+ " should contain an @Id and should be a bean");
}
}