package nl.elastique.poetry.reflection; import com.j256.ormlite.dao.ForeignCollection; import com.j256.ormlite.field.DatabaseField; import com.j256.ormlite.table.DatabaseTable; import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import nl.elastique.poetry.annotations.Nullable; /** * A set of reflection utilities for OrmLite to process and retrieve fields and annotations. */ // TODO: most of these calls should be cached as is done in AnnotationRetriever/FieldRetriever public class OrmliteReflection { // Reference: http://sourceforge.net/p/ormlite/code/HEAD/tree/ormlite-core/trunk/src/main/java/com/j256/ormlite/field/FieldType.java private static final String sForeignIdFieldSuffix = "_id"; /** * Get the SQLite table name for an OrmLite model. * * @param annotationRetriever the annotation retriever that caches the annotations * @param modelClass an OrmLite model class annotated with {@link DatabaseTable} * @return the SQLite table name */ public static String getTableName(AnnotationRetriever annotationRetriever, Class<?> modelClass) { DatabaseTable table_annotation = annotationRetriever.getAnnotation(modelClass, DatabaseTable.class); if (table_annotation == null) { throw new RuntimeException("DatabaseTable annotation not found for " + modelClass.getName()); } return getTableName(modelClass, table_annotation); } /** * Get the SQLite table name for an OrmLite model. * * @param modelClass an OrmLite model class annotated with {@link DatabaseTable} * @param tableAnnotation the annotation to process * @return the SQLite table name */ public static String getTableName(Class<?> modelClass, DatabaseTable tableAnnotation) { return !tableAnnotation.tableName().isEmpty() ? tableAnnotation.tableName() : modelClass.getSimpleName(); } /** * Get SQLite column name for a given Field. * * @param annotationRetriever the annotation retriever that caches the annotations * @param field the model's field * @return the SQLite column name */ public static String getFieldName(AnnotationRetriever annotationRetriever, Field field) { DatabaseField database_field = annotationRetriever.getAnnotation(field, DatabaseField.class); if (database_field == null) { throw new RuntimeException("DatabaseField annotation not found in " + field.getDeclaringClass().getName() + " for " + field.getName()); } return getFieldName(field, database_field); } /** * Get SQLite column name for a given Field. * * @param field the model's field * @param databaseField the DatabaseField annotation for the specified Field * @return the SQLite column name */ public static String getFieldName(Field field, DatabaseField databaseField) { if (!databaseField.columnName().isEmpty()) { return databaseField.columnName(); } else if (OrmliteReflection.isForeign(databaseField)) { return field.getName() + sForeignIdFieldSuffix; } else { return field.getName(); } } /** * Check if the provided DatabaseField is a foreign field. * * @param databaseField the annotation to check * @return true if foreign() is true, foreignAutoRefresh() is true or foreignColumnName() is set to a non-empty string */ public static boolean isForeign(DatabaseField databaseField) { return databaseField.foreign() || databaseField.foreignAutoRefresh() || !databaseField.foreignColumnName().isEmpty(); } /** * Check if the DatabaseField is an ID field. * * @param databaseField the annotation to check * @return true if id() or generatedId() are true */ public static boolean isId(DatabaseField databaseField) { return databaseField.id() || databaseField.generatedId(); } /** * Retrieves the generic type argument: the type that is held by the specified ForeignCollection Field * * @param field a {@link Field} that holds the type {@link com.j256.ormlite.dao.ForeignCollection} * @throws RuntimeException when the Field is not a ForeignCollection * @return the class */ public static Class<?> getForeignCollectionParameterType(Field field) { if (!field.getType().equals(ForeignCollection.class)) { throw new RuntimeException(field.getDeclaringClass().getName() + " declares the field \"" + field.getName() + "\" which is not a ForeignCollection but is annotated by ForeignCollectionField"); } Type type = field.getGenericType(); ParameterizedType parameterized_type = (ParameterizedType)type; Type child_type = parameterized_type.getActualTypeArguments()[0]; return (Class<?>)child_type; // TODO: check conversion? } /** * Find a Field with a DatabaseField annotation that defines it as being an id column. * * @param annotationRetriever the annotation retriever that caches the annotations * @param modelClass the class to find the ID field in * @return the Field or null */ public static @Nullable Field findIdField(AnnotationRetriever annotationRetriever, Class<?> modelClass) { for (Field field : modelClass.getDeclaredFields()) { DatabaseField database_field = annotationRetriever.getAnnotation(field, DatabaseField.class); if (database_field == null) { continue; } if (database_field.generatedId() || database_field.id()) { return field; } } if (modelClass.getSuperclass() != null) { // Recursively check superclass return findIdField(annotationRetriever, modelClass.getSuperclass()); } else { return null; } } /** * Find a Field with a DatabaseField annotation that defines it as foreign. * * @param annotationRetriever the annotation retriever that caches the annotations * @param parentClass the class to search for the Field * @param findClass the field class to search for * @return a Field or null */ public static @Nullable Field findForeignField(AnnotationRetriever annotationRetriever, Class<?> parentClass, Class<?> findClass) { for (Field field : parentClass.getDeclaredFields()) { DatabaseField database_field = annotationRetriever.getAnnotation(field, DatabaseField.class); if (database_field != null && isForeign(database_field) && findClass.isAssignableFrom(field.getType())) { return field; } } if (parentClass.getSuperclass() != null) { // Recursively check superclass return findForeignField(annotationRetriever, parentClass.getSuperclass(), findClass); } else { return null; } } }