Skip to content
Table.java 10.5 KiB
Newer Older
/* 
 * _____________________________________________________________________________
 * 
 * INAF - OATS National Institute for Astrophysics - Astronomical Observatory of
 * Trieste INAF - IA2 Italian Center for Astronomical Archives
 * _____________________________________________________________________________
 * 
 * Copyright (C) 2016 Istituto Nazionale di Astrofisica
 * 
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License Version 3 as published by the
 * Free Software Foundation.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 51
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */
package it.inaf.ia2.tsm;

import it.inaf.ia2.tsm.datalayer.DBBroker;
import it.inaf.ia2.tsm.model.TableModel;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.TreeMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Represents a Table entity belonging to a {@link TapSchema}.
 *
 * @author Sonia Zorba {@literal <zorba at oats.inaf.it>}
 */
public class Table extends ChildEntity<Schema> implements EntitiesContainer<Column> {

    public final static String SCHEMA_NAME_KEY = "schema_name";
    public final static String TABLE_NAME_KEY = "table_name";
    public final static String TABLE_TYPE_KEY = "table_type";
    public final static String DBNAME = "dbname";

    private static final long serialVersionUID = 8265331530960896871L;
    private static final Logger LOG = LoggerFactory.getLogger(Table.class);

    private Map<String, Map<String, Object>> columnsMetadata;
    private Map<String, Column> columns;

    private String simpleName;
    private Schema parentSchema;

    // WARNING: this is different from tableModel inherited field.
    private TableModel tableTableModel;

    /**
     * Only for serialization.
     */
    /**
     * If this table is one of the TAP_SCHEMA tables or it is the ObsCore table
     * this method returns the related {@link TableModel}. This method is used
     * inside the constructor to set the {@link #tableTableModel} field.
     * <strong>WARNING</strong>: this has a different meaning compared to the
     * inherited {@link #getTableModel()} method.
     */
    private TableModel getTableTableModel() {
        if (tapSchema.getName().equals(parentSchema.getName())) {
            return tapSchema.getTapSchemaModel().getTable(simpleName);
        if (tapSchema.isHasObscore() && parentSchema.getName().equals(TapSchema.STANDARD_IVOA_SCHEMA_NAME)
                && simpleName.equals("obscore")) {
            return tapSchema.getIvoaSchemaModel().getTable(simpleName);
    /**
     * Special behavior for retro-compatibility with oldest TAP_SCHEMA versions
     * in which the indexed column was represented using 1 and 0 instead boolean
     * values.
     */
    private void fixIndexedMetadataValues() {
        boolean useIntForBool = tapSchema.getTapSchemaModel()
                .getTable(TapSchema.COLUMNS_TABLE)
                .get(Column.INDEXED_KEY).getJavaType() == Integer.class;
        if (useIntForBool) {
            for (Map<String, Object> cm : columnsMetadata.values()) {
                boolean indexed = (boolean) cm.get(Column.INDEXED_KEY);
                cm.put(Column.INDEXED_KEY, indexed ? 1 : 0);
            }
        }
    }

    /**
     * Default constructor.
     *
     * @see TapSchemaEntity
     * @param tapSchema the {@code TapSchema} owning this table.
     * @param schema the {@code Schema} owning this table.
     * @param tableSimpleName the name of the table without its schema name.
     * @throws SQLException if there are problems while retrieving columns
     * metadata.
     */
    protected Table(TapSchema tapSchema, Schema schema, String tableSimpleName) throws SQLException {
        super(tapSchema, tapSchema.getTableModel(TapSchema.TABLES_TABLE), schema.getTableMetadata(tableSimpleName));

        parentSchema = schema;
        this.simpleName = tableSimpleName;

        columns = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);

        DBBroker broker = tapSchema.getDBBroker(schema.getName());
        tableTableModel = getTableTableModel();
        columnsMetadata = broker.getAllColumnsMetadata(schema.getRealSchemaName(), tableSimpleName, tableTableModel, tapSchema.getDataTypeMode());
        fixIndexedMetadataValues();
        for (Map.Entry<String, Map<String, Object>> entry : columnsMetadata.entrySet()) {
            // Adding table names to columns metadata
            entry.getValue().put(Column.TABLE_NAME_KEY, schema.getName() + "." + tableSimpleName);
            // Initializing columns map
            columns.put(entry.getKey(), null);
        }

        setStatus(Status.LOADED);
    }

    /**
     * Returns only the table name (without the schema name).
     */
    @Override
    public String getName() {
        return simpleName;
    }

    /**
     * Returns the complete table name: {@code schema_name.table_name}.
     */
    public String getCompleteName() {
        return getValue(TABLE_NAME_KEY, String.class);
    }

    private boolean isMandatory(String columnName) {
        if (tableTableModel != null) {
            return tableTableModel.get(columnName).isMandatory();
        }
        return false;
    }

     * {@inheritDoc}
     */
    @Override
    public Column addChild(String columnName) {
        String tableCompleteName = getCompleteName();

        LOG.debug("Adding column {} into table {}", columnName, tableCompleteName);

        if (!columns.containsKey(columnName)) {
            return null;
        } else {
            Column column = columns.get(columnName);
            if (column == null) {
                column = new Column(tapSchema, this, columnName, isMandatory(columnName));
                columns.put(columnName, column);
                column.setStatus(Status.ADDED_NOT_PERSISTED);
            } else {
                switch (column.getStatus()) {
                    case REMOVED_NOT_PERSISTED: // undo removal
                        column.setStatus(Status.ADDED_NOT_PERSISTED);
                        break;
                    case TO_REMOVE: // undo removal
                        column.setStatus(Status.ADDED_PERSISTED);
                        break;
                    default:
                        throw new IllegalArgumentException("Cannot add column " + columnName + " in table " + tableCompleteName + ". Invalid column status: " + column.getStatus());
                }
            }

            tapSchema.checkKeys();

            return column;
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void removeChild(String columnName) {
        String tableCompleteName = getCompleteName();

        LOG.debug("Removing column {} from table {}", columnName, tableCompleteName);

        if (!columns.containsKey(columnName)) {
            throw new IllegalArgumentException("Column " + columnName + " doesn't exists in table " + tableCompleteName);
        }

        Column column = columns.get(columnName);

        switch (column.getStatus()) {
            case ADDED_NOT_PERSISTED:
                column.setStatus(Status.REMOVED_NOT_PERSISTED);
                break;
            case ADDED_PERSISTED:
                column.setStatus(Status.TO_REMOVE);
                break;
            default:
                throw new IllegalArgumentException("Cannot remove column " + columnName + " in table " + tableCompleteName + ". Invalid column status: " + column.getStatus());
        }

        tapSchema.checkKeys();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Column getChild(String childName, Status... statuses) {
        return TSMUtil.getChild(columns, childName, statuses);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public List<Column> getChildren(Status... statuses) {
        return TSMUtil.getChildrenByStatus(columns.values(), statuses);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public List<String> getAddableChildrenNames() {
        return TSMUtil.getAddableChildrenNames(columns);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean isAddable(String childName) {
        return columns.containsKey(childName);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public List<Column> getAddedChildren() {
        return getChildren(Status.ADDED_PERSISTED, Status.ADDED_NOT_PERSISTED);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public List<Column> getAddedOrRemovedChildren() {
        return getChildren(Status.ADDED_PERSISTED, Status.ADDED_NOT_PERSISTED, Status.TO_REMOVE, Status.REMOVED_NOT_PERSISTED);
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 29 * hash + Objects.hashCode(this.getCompleteName());
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Table other = (Table) obj;
        return Objects.equals(this.getCompleteName(), other.getCompleteName());
    }

    protected void afterUpdate() {
        for (Column column : columns.values()) {
            if (column.getStatus() == Status.ADDED_NOT_PERSISTED) {
                column.setStatus(Status.ADDED_PERSISTED);
            } else if (column.getStatus() != Status.ADDED_PERSISTED) {
                column.setStatus(Status.LOADED);
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Schema getParent() {
        return parentSchema;
    }

     * Returns the metadata about a column having this table as parent. Metadata
     * are column properties the value of which can be retrieved from the
     * database metadata (or information_schema).
     *
     * @param columnName the name of the column of which retrieve the metadata.
     * @return a {@code Map} the keys of which are property names and the values
     * of which are property values retrieved from database metadata.
     */
    public Map<String, Object> getColumnMetadata(String columnName) {
        return columnsMetadata.get(columnName);
    }
}