package net.rrm.ehour.backup.service.restore.structure;
import com.google.common.collect.Maps;
import org.hibernate.annotations.NotFound;
import org.hibernate.annotations.NotFoundAction;
import javax.persistence.*;
import java.lang.reflect.Field;
import java.util.Map;
/**
* Build the FieldMap for a domain object, a map with the field names of the domain object mapped to FieldDefinitions
*/
public abstract class FieldMapFactory {
private static final Map<Class<?>, FieldMap> FIELD_MAP_CACHE = Maps.newHashMap();
/**
* Iterate over the domain object and extract any JPA annotated fields
* @param clazz
* @param <T>
* @return a Map with lowercase annotated field names from domain object and matching field definitions
*/
public static <T> FieldMap buildFieldMapForEntity(Class<T> clazz) {
if (FIELD_MAP_CACHE.containsKey(clazz)) {
return FIELD_MAP_CACHE.get(clazz);
}
FieldMap fieldMap = new FieldMap();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
if (field.isAnnotationPresent(Column.class)) {
column(fieldMap, field);
} else if (field.isAnnotationPresent(JoinColumn.class)) {
manyToOne(fieldMap, field);
} else if (field.isAnnotationPresent(ManyToMany.class) && field.isAnnotationPresent(JoinTable.class)) {
manyToMany(fieldMap, field);
} else {
embeddable(fieldMap, field);
}
}
fieldMap.afterFieldsSet();
// this code is executed serially, no multithreading to parse XML
// so no worries about synchronization
FIELD_MAP_CACHE.put(clazz, fieldMap);
return fieldMap;
}
private static void embeddable(FieldMap fieldMap, Field field) {
Class<?> fieldType = field.getType();
// do we need to go a level deeper with composite primary keys marked as Embeddable?
if (fieldType.isAnnotationPresent(Embeddable.class)) {
fieldMap.put(field.getName(), new FieldDefinition(field));
FieldMap embeddableFieldMap = buildFieldMapForEntity(fieldType);
for (FieldDefinition fieldDefinition : embeddableFieldMap.fieldDefinitions()) {
fieldDefinition.setProcessor(new FieldProcessorEmbeddableImpl());
}
fieldMap.merge(embeddableFieldMap);
}
}
private static void column(FieldMap fieldMap, Field field) {
Column column = field.getAnnotation(Column.class);
String columnName = column.name();
fieldMap.put(columnName.toLowerCase(), new FieldDefinition(field));
}
private static void manyToOne(FieldMap fieldMap, Field field) {
JoinColumn column = field.getAnnotation(JoinColumn.class);
String columnName = column.name();
FieldDefinition definition;
if (field.isAnnotationPresent(NotFound.class)) {
NotFound notFoundAnnotation = field.getAnnotation(NotFound.class);
definition = notFoundAnnotation.action() == NotFoundAction.IGNORE ? new IgnorableFieldDefinition(field) : new FieldDefinition(field);
} else {
definition = new FieldDefinition(field);
}
fieldMap.put(columnName.toLowerCase(), definition);
}
private static void manyToMany(FieldMap fieldMap, Field field) {
JoinTable joinTable = field.getAnnotation(JoinTable.class);
String columnName = joinTable.name();
FieldDefinition definition = new FieldDefinitionManyToMany(field);
fieldMap.put(columnName.toLowerCase(), definition);
}
}