Newer
Older
simple-database / src / nl / astraeus / database / FieldMetaData.java
package nl.astraeus.database;

import nl.astraeus.database.annotations.*;
import nl.astraeus.template.EscapeMode;
import nl.astraeus.template.SimpleTemplate;

import java.lang.reflect.Field;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.HashMap;
import java.util.Map;

/**
 * Date: 11/14/13
 * Time: 8:59 PM
 */
public class FieldMetaData {

    private Field field;
    private String fieldName;

    private Class<?> javaType;
    private Integer sqlType;

    private ColumnInfo  columnInfo;

    private Length length;
    private Default defaultValue;

    private boolean primaryKey = false;

    private static Map<Class<?>, SimpleTemplate> ddlMapping;
    private static Map<Class<?>, Integer> sqlTypeMapping;

//    java.lang.Boolean BOOLEAN
//    java.lang.Byte TINYINT
//    java.lang.BigDecinal DECIMAL(${precision}, ${scale})

    // todo: get these definitions from some configuration file
    static {
        ddlMapping = new HashMap<>();
        sqlTypeMapping = new HashMap<>();

        ddlMapping.put(String.class, new SimpleTemplate("${", "}", EscapeMode.NONE, "VARCHAR(${length})"));
        ddlMapping.put(Long.class, new SimpleTemplate("${", "}", EscapeMode.NONE, "BIGINT"));
        ddlMapping.put(long.class, new SimpleTemplate("${", "}", EscapeMode.NONE, "BIGINT"));
        ddlMapping.put(Integer.class, new SimpleTemplate("${", "}", EscapeMode.NONE, "INT"));
        ddlMapping.put(int.class, new SimpleTemplate("${", "}", EscapeMode.NONE, "INT"));
        ddlMapping.put(Short.class, new SimpleTemplate("${", "}", EscapeMode.NONE, "SMALLINT"));
        ddlMapping.put(short.class, new SimpleTemplate("${", "}", EscapeMode.NONE, "SMALLINT"));
        ddlMapping.put(Double.class, new SimpleTemplate("${", "}", EscapeMode.NONE, "DECIMAL(${precision}, ${scale})"));
        ddlMapping.put(double.class, new SimpleTemplate("${", "}", EscapeMode.NONE, "DECIMAL(${precision}, ${scale})"));

        sqlTypeMapping.put(String.class, Types.VARCHAR);
        sqlTypeMapping.put(Long.class, Types.BIGINT);
        sqlTypeMapping.put(long.class, Types.BIGINT);
        sqlTypeMapping.put(Integer.class, Types.INTEGER);
        sqlTypeMapping.put(int.class, Types.INTEGER);
        sqlTypeMapping.put(Short.class, Types.SMALLINT);
        sqlTypeMapping.put(short.class, Types.SMALLINT);
        sqlTypeMapping.put(Double.class, Types.DECIMAL);
        sqlTypeMapping.put(double.class, Types.DECIMAL);
    }

    public FieldMetaData(Field field) {
        this.field = field;
        this.field.setAccessible(true);

        fieldName = field.getName();
        String columnName = fieldName;
        javaType = field.getType();

        length = field.getAnnotation(Length.class);
        defaultValue = field.getAnnotation(Default.class);
        Column column = field.getAnnotation(Column.class);
        Id id = field.getAnnotation(Id.class);

        if (column != null) {
            columnName = column.name();
        }

        if (id != null) {
            primaryKey = true;
            field.setAccessible(true);
        }

        Map<String, Object> model = new HashMap<>();

        if (length == null) {
            model.put("length", 255);
            model.put("precision", 10);
            model.put("scale", 2);
        } else {
            model.put("length", length.value());
            model.put("precision", length.precision());
            model.put("scale", length.scale());
        }

        SimpleTemplate template = ddlMapping.get(javaType);
        sqlType = sqlTypeMapping.get(javaType);

        String type = "BIGINT"; // default to id ref

        if (template != null) {
            type = template.render(model);
        } else {
            Collection collection = field.getAnnotation(Collection.class);

            if (collection != null) {
                Class<?> collectionClass = collection.value();

                // BLOB
                type = "BLOB";
                sqlType = Types.BLOB;
            } else {
                Serialized serialized = field.getAnnotation(Serialized.class);

                if (serialized != null) {
                    // BLOB
                    type = "BLOB";
                    sqlType = Types.BLOB;
                } else if (javaType.getAnnotation(Table.class) != null) {
                    // oneToone
                    type = "BIGINT";
                    sqlType = Types.BIGINT;
                } else {
                    throw new IllegalStateException("Type "+field.getType().getSimpleName()+" of field "+field.getDeclaringClass().getSimpleName()+"."+field.getName()+" is not supported!");
                }
            }
        }

        columnInfo = new ColumnInfo(columnName, type);
    }

    public boolean isPrimaryKey() {
        return primaryKey;
    }

    public Field getField() {
        return field;
    }

    public String getFieldName() {
        return fieldName;
    }

    public Class getJavaType() {
        return javaType;
    }

    public ColumnInfo getColumnInfo() {
        return columnInfo;
    }

    public Length getLength() {
        return length;
    }

    public Default getDefaultValue() {
        return defaultValue;
    }

    public Object get(Object obj) {
        try {
            return field.get(obj);
        } catch (IllegalAccessException e) {
            throw new IllegalStateException(e);
        }
    }

    public void set(Object obj, Object value) {
        try {
            field.set(obj, value);
        } catch (IllegalAccessException e) {
            throw new IllegalStateException(e);
        }
    }

    public void set(PreparedStatement statement, int index, Object obj) throws SQLException {
        Object value = get(obj);

        if (value == null) {
            statement.setNull(index, sqlType);
        } else {
            switch(sqlType) {
                case Types.VARCHAR:
                    statement.setString(index, (String)value);
                    break;
                case Types.BIGINT:
                    statement.setLong(index, (Long) value);
                    break;
                case Types.INTEGER:
                    statement.setInt(index, (Integer) value);
                    break;
                case Types.SMALLINT:
                    statement.setShort(index, (Short) value);
                    break;
                case Types.DECIMAL:
                    statement.setDouble(index, (Double) value);
                    break;
                case Types.BLOB:
                    //statement.setBlob(index, (Double) value);
                    break;
            }
        }
    }

    public void set(ResultSet rs, int index, Object obj) throws SQLException {
        switch(sqlType) {
            case Types.VARCHAR:
                set(obj, rs.getString(index));
                break;
            case Types.BIGINT:
                set(obj, rs.getLong(index));
                break;
            case Types.INTEGER:
                set(obj, rs.getInt(index));
                break;
            case Types.SMALLINT:
                set(obj, rs.getShort(index));
                break;
            case Types.DECIMAL:
                set(obj, rs.getDouble(index));
                break;
            case Types.BLOB:
                //set(obj, rs.getString(index));
                //statement.setBlob(index, (Double) value);
                break;
        }

    }
}