Package net.canarymod.database

Source Code of net.canarymod.database.DataAccess

package net.canarymod.database;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;

import net.canarymod.Canary;
import net.canarymod.ToolBox;
import net.canarymod.database.exceptions.DatabaseAccessException;
import net.canarymod.database.exceptions.DatabaseTableInconsistencyException;
import net.canarymod.database.exceptions.DatabaseWriteException;

/**
* Handle the layout and creation of tables
*
* @author Chris (damagefilter)
*/
public abstract class DataAccess {

    protected String tableName;
    private boolean isInconsistent = false;
    private boolean isLoaded = false;
    private boolean hasData = false;

    /**
     * Construct a new DataAccess object that represents a table
     * in the database with the given name. This AccessObject is empty after it has been created.
     * You need to get data from Database and load it. CanaryMod will do this for you.
     * For this simply call Canary.db().load(yourDataAccess, String[]lookupFields, Object[]whereData);
     * This will fill your AccessObject.
     *
     * @param tableName
     *         The table name
     */
    public DataAccess(String tableName) {
        this.tableName = tableName;
        createTable();
    }

    public DataAccess(String tableName, String tableSuffix) {
        if (tableSuffix != null) {
            this.tableName = tableName + "_" + tableSuffix;
        }
        else {
            this.tableName = tableName;
        }
        createTable();
    }

    /**
     * Load a Data set into this DataAccess object
     *
     * @param dataSet
     *         the data set to be loaded
     *
     * @throws DatabaseAccessException
     */
    public final void load(HashMap<String, Object> dataSet) throws DatabaseAccessException {
        try {
            applyDataSet(dataSet);
        }
        catch (IllegalAccessException e) {
            throw new DatabaseAccessException(e.getMessage());
        }
        catch (IllegalArgumentException e) {
            throw new DatabaseAccessException(e.getMessage());
        }
        if (dataSet.size() > 0) {
            hasData = true;
        }
        isLoaded = true;
    }

    /**
     * Creates a HashMap containing all relevant fields for the database, which will then
     * be saved into the database along with their values
     *
     * @return HashMap that maps the Column meta data to the data present in database.
     *
     * @throws DatabaseTableInconsistencyException
     *
     */
    public final HashMap<Column, Object> toDatabaseEntryList() throws DatabaseTableInconsistencyException {
        List<Field> fields = Arrays.asList(ToolBox.safeArrayMerge(getClass().getFields(), getClass().getDeclaredFields(), new Field[1]));
        HashMap<Column, Object> fieldMap = new HashMap<Column, Object>();

        for (Field field : fields) {
            Column colInfo = field.getAnnotation(Column.class);

            if (colInfo == null) {
                // Not what we're looking for
                continue;
            }
            if (fieldMap.containsKey(colInfo)) {
                // Seriously ...
                isInconsistent = true;
                throw new DatabaseTableInconsistencyException("Found duplicate column field: " + colInfo.columnName());
            }
            try {
                fieldMap.put(colInfo, field.get(this));
            }
            catch (IllegalArgumentException e) {
                Canary.logStacktrace(e.getMessage(), e);
            }
            catch (IllegalAccessException e) {
                isInconsistent = true;
                throw new DatabaseTableInconsistencyException("Could not access an annotated column field: " + field.getName());
            }
        }
        return fieldMap;
    }

    public final void applyDataSet(HashMap<String, Object> dataSet) throws DatabaseAccessException, IllegalArgumentException, IllegalAccessException {
        Field[] fields = ToolBox.safeArrayMerge(getClass().getFields(), getClass().getDeclaredFields(), new Field[1]);
        int columnFields = 0;

        for (Field field : fields) {
            Column col = field.getAnnotation(Column.class);

            if (col == null) {
                continue;
            }
            if (!dataSet.containsKey(col.columnName())) {
                isInconsistent = true;
                throw new DatabaseAccessException("Cannot apply data to " + getClass().getSimpleName() + ". Column name mismatches! (" + col.columnName() + " does not exist) - " + dataSet.keySet().toString());
            }
            field.set(this, dataSet.get(col.columnName()));
            columnFields++;
        }
        // If the columnFields is not the size of the dataSet,
        // There is either excess data or data that has not been put in the AccessObject.
        // This causes inconsistency and therefore must throw an exception
        if (columnFields != dataSet.size()) {
            isInconsistent = true;
            throw new DatabaseAccessException("Supplied Data set cannot be applied to this DataAccess(" + getClass().getSimpleName() + "). Column count mismatches!");
        }
    }

    /**
     * Gets the table layout. That is: all column annotations in this class that make up the table
     *
     * @return a HashSet containing all Columns as defined in this {@link DataAccess} object
     *
     * @throws DatabaseTableInconsistencyException
     *
     */
    public final HashSet<Column> getTableLayout() throws DatabaseTableInconsistencyException {
        Field[] fields = ToolBox.safeArrayMerge(getClass().getFields(), getClass().getDeclaredFields(), new Field[1]);
        HashSet<Column> layout = new HashSet<Column>(fields.length);

        for (Field field : fields) {
            if (field == null) {
                throw new DatabaseTableInconsistencyException("A field of " + getClass().getSimpleName() + " is not initialized, check your DataAccess!");
            }
            Column colInfo = field.getAnnotation(Column.class);

            if (colInfo == null) {
                // Not what we're looking for
                continue;
            }
            if (layout.contains(colInfo)) {
                // Dude!
                isInconsistent = true;
                throw new DatabaseTableInconsistencyException("Found duplicate column field: " + colInfo.columnName());
            }
            layout.add(colInfo);
        }
        return layout;
    }

    /**
     * This shall return the name of the Table this DataAccess belongs to
     *
     * @return the table name
     */
    public final String getName() {
        return tableName;
    }

    /**
     * Returns true if this DataAccess object has been marked as inconsistent.
     * Inconsistent DataAccess objects will not be saved into the database.
     * This is probably rarely going to be true but it's an extra security measure
     * to keep the data safe and consistent
     *
     * @return true if this object is inconsistent to the database schema, false otherwise
     */
    public final boolean isInconsistent() {
        return isInconsistent;
    }

    /**
     * Check if this DataAccess has been loaded properly
     *
     * @return true if a load has been attempted for this {@link DataAccess}
     */
    public final boolean isLoaded() {
        return isLoaded;
    }

    /**
     * Check if there is data in this DataAccess object.
     * This will also return false if there was an exception while
     * the data has been loaded
     *
     * @return true if this {@link DataAccess} contains data
     */
    public final boolean hasData() {
        return hasData;
    }

    /**
     * Checks if this {@link DataAccess} has a Column with the given name.
     *
     * @param name
     *         the name to check for
     *
     * @return true if DataAccess has this column, false otherwise
     */
    public final boolean hasColumn(String name) {
        try {
            for (Column col : getTableLayout()) {
                if (col.columnName().equals(name)) {
                    return true;
                }
            }
            return false;
        }
        catch (DatabaseTableInconsistencyException e) {
            Canary.logSevere("Could not finish column name lookup in database for " + tableName, e);
            return false;
        }
    }

    /** Makes sure the database file for this DataAccess exists before anything starts to use it */
    private void createTable() {
        try {
            Database.get().updateSchema(this);
        }
        catch (DatabaseWriteException e) {
            Canary.logStacktrace(e.getMessage(), e);
        }
    }

    /**
     * Converts this DataAccess object into a string representation.<br>
     * Format: Table : tableName { [`columnName`,'fieldName'] }
     *
     * @return string representation
     */
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        Map<Column, Object> columns = null;
        try {
            columns = this.toDatabaseEntryList();
        }
        catch (DatabaseTableInconsistencyException dtie) {

        }
        if (columns != null) {
            for (Column column : columns.keySet()) {
                sb.append("[`").append(column.columnName()).append("`, '").append(columns.get(column)).append("'] ");
            }
        }
        return "Table : " + this.tableName + " { " + sb.toString() + "}";
    }

    /**
     * Returns an empty instance of this {@link DataAccess} object
     *
     * @return instance
     */
    public abstract DataAccess getInstance();
}
TOP

Related Classes of net.canarymod.database.DataAccess

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.