package adql.query.operand; /* * This file is part of ADQLLibrary. * * ADQLLibrary is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * ADQLLibrary 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with ADQLLibrary. If not, see <http://www.gnu.org/licenses/>. * * Copyright 2012-2016 - UDS/Centre de DonnĂ©es astronomiques de Strasbourg (CDS), * Astronomisches Rechen Institut (ARI) */ import adql.db.DBColumn; import adql.query.ADQLIterator; import adql.query.ADQLObject; import adql.query.IdentifierField; import adql.query.NullADQLIterator; import adql.query.TextPosition; import adql.query.from.ADQLTable; /** * Represents the complete (literal) reference to a column ({schema(s)}.{table}.{column}). * * @author Grégory Mantelet (CDS;ARI) * @version 1.4 (03/2016) */ public class ADQLColumn implements ADQLOperand, UnknownType { /** Position in the original ADQL query string. */ private TextPosition position = null; /** The name of the catalog which contains this column. */ private String catalog = null; /** The name of the schema which contains this column. */ private String schema = null; /** The name of the table which contains this column. */ private String table = null; /** Column name (NEVER <i>null</i> but ""). */ private String column; /** Lets specify the case sensitivity of the catalog, schema, table and column parts. */ private byte caseSensitivity = 0; /** The corresponding column in the "database". By default, this field is automatically filled by {@link adql.db.DBChecker}. */ private DBColumn dbLink = null; /** The {@link ADQLTable} which is supposed to contain this column. By default, this field is automatically filled by {@link adql.db.DBChecker}. */ private ADQLTable adqlTable = null; /** Type expected by the parser. * @since 1.3 */ private char expectedType = '?'; /* ************ */ /* CONSTRUCTORS */ /* ************ */ /** * Builds a Column with the complete reference to a column ({schema(s)}.{table}.{column}). * * @param columnRef The complete reference to a column. * * @see ADQLColumn#setColumn(String) */ public ADQLColumn(String columnRef){ setColumn(columnRef); } /** * Builds a column with the given column name and the given table name. * * @param tableName Name of the table. * @param columnName Name of the column. * * @see ADQLColumn#setTableName(String) * @see ADQLColumn#setColumnName(String) */ public ADQLColumn(String tableName, String columnName){ setTableName(tableName); setColumnName(columnName); } /** * Builds a column with the given column name, table name and schema name. * * @param schema Name of the schema. * @param table Name of the table. * @param column Name of the column. * * @see #ADQLColumn(String, String) * @see #setSchemaName(String) */ public ADQLColumn(String schema, String table, String column){ this(table, column); setSchemaName(schema); } /** * Builds a column with the given column name, table name, schema name and catalog name. * * @param catalog Name of the catalog. * @param schema Name of the schema. * @param table Name of the table. * @param column Name of the column. * * @see #ADQLColumn(String, String) * @see #setSchemaName(String) */ public ADQLColumn(String catalog, String schema, String table, String column){ this(schema, table, column); setCatalogName(catalog); } /** * Builds a Column by copying the given one. * * @param toCopy The Column to copy. */ public ADQLColumn(ADQLColumn toCopy){ column = toCopy.column; table = toCopy.table; } /** * Lets normalizing any catalog/schema/table name or alias. * If the name is surrounded by double-quotes, they are removed * and the corresponding field will be declared as case sensitive. * * @param name Name to normalize. * * @return The normalized name. */ protected String normalizeName(String name, IdentifierField field){ if (name == null) return null; StringBuffer n = new StringBuffer(name); n.trimToSize(); if (n.length() == 0) return null; else{ if (n.length() > 1 && n.charAt(0) == '\"' && n.charAt(n.length() - 1) == '\"'){ n.deleteCharAt(0); n.deleteCharAt(n.length() - 1); n.trimToSize(); if (n.length() == 0) return null; else setCaseSensitive(field, true); } } return n.toString(); } /* ***************** */ /* GETTERS & SETTERS */ /* ***************** */ @Override public final TextPosition getPosition(){ return position; } /** * Sets the position at which this {@link ADQLColumn} has been found in the original ADQL query string. * * @param pos Position of this {@link ADQLColumn}. */ public void setPosition(final TextPosition pos){ position = pos; } /** * Gets the name of the catalog which contains this column. * * @return Catalog name. */ public final String getCatalogName(){ return catalog; } /** * Sets the name of the catalog which contains this column. * * @param catalog New name of the catalog. */ public final void setCatalogName(String catalog){ final String temp = normalizeName(catalog, IdentifierField.CATALOG); if ((this.catalog == null && temp != null) || (this.catalog != null && !this.catalog.equalsIgnoreCase(temp))) dbLink = null; this.catalog = temp; } /** * Gets the name of the schema which contains this column. * * @return Schema name. */ public final String getSchemaName(){ return schema; } /** * Sets the name of the schema which contains this column. * * @param schema New name of the schema. */ public final void setSchemaName(String schema){ final String temp = normalizeName(schema, IdentifierField.SCHEMA); if ((this.schema == null && temp != null) || (this.schema != null && !this.schema.equalsIgnoreCase(temp))) dbLink = null; this.schema = temp; } /** * Gets the name of the table which contains this column. * * @return Table name. */ public final String getTableName(){ return table; } /** * Sets the name of the table which contains this column. * * @param tableName New name of the table. */ public final void setTableName(String tableName){ final String temp = normalizeName(tableName, IdentifierField.TABLE); if ((this.table == null && temp != null) || (this.table != null && !this.table.equalsIgnoreCase(temp))) dbLink = null; this.table = temp; } /** * Gets the name of this column. * * @return Its column name. */ public final String getColumnName(){ return column; } /** * Gets the full name of this column (catalogName . schemaName . tableName . columnName) * by respecting the case sensitivity of each field (if case sensitive, double-quotes will surround the concerned fields name). * * @return Its full name. * * @see #getFullColumnPrefix() */ public final String getFullColumnName(){ if (column == null) return ""; StringBuffer name = getFullColumnPrefix(); if (name.length() > 0) name.append('.'); // COLUMN: if (isCaseSensitive(IdentifierField.COLUMN)) name.append('\"').append(column).append('\"'); else name.append(column); return name.toString(); } /** * Gets the full column prefix (catalogName . schemaName . tableName) * by respecting the case sensitivity of each field (if case sensitive, double-quotes will surround the concerned fields name). * * @return Its full prefix. */ public final StringBuffer getFullColumnPrefix(){ StringBuffer name = new StringBuffer(); // CATALOG: if (catalog != null){ if (isCaseSensitive(IdentifierField.CATALOG)) name.append('\"').append(catalog).append("\"."); else name.append(catalog).append('.'); } // SCHEMA: if (schema != null){ if (isCaseSensitive(IdentifierField.SCHEMA)) name.append('\"').append(schema).append("\"."); else name.append(schema).append('.'); } // TABLE: if (table != null){ if (isCaseSensitive(IdentifierField.TABLE)) name.append('\"').append(table).append("\""); else name.append(table); } return name; } /** * Changes the name of the column ({column} in {schema(s)}.{table}.{column}). * * @param columnName The new column name. */ public final void setColumnName(String columnName){ final String temp = normalizeName(columnName, IdentifierField.COLUMN); if ((this.column == null && temp != null) || (this.column != null && !this.column.equalsIgnoreCase(temp))) dbLink = null; this.column = temp; } /** * Updates the whole Column according to the given column reference ({catalog}.{schema}.{table}.{column}). * * @param columnRef The complete column reference ({catalog}.{schema}.{table}.{column}). */ public final void setColumn(String columnRef){ String[] parts = (columnRef == null) ? null : columnRef.split("\\."); if (parts != null && parts.length > 4) return; else{ int i = (parts == null) ? -1 : (parts.length - 1); setColumnName((i < 0) ? null : parts[i--]); setTableName((i < 0) ? null : parts[i--]); setSchemaName((i < 0) ? null : parts[i--]); setCatalogName((i < 0) ? null : parts[i]); } } /** * Indicates whether the specified field (catalog, schema, table or column) is case sensitive or not. * * @param field A field (catalog, schema, table or column). * * @return <i>true</i> if the specified field is case sensitive, <i>false</i> otherwise. * * @see IdentifierField * @see IdentifierField#isCaseSensitive(byte) */ public final boolean isCaseSensitive(IdentifierField field){ return field.isCaseSensitive(caseSensitivity); } /** * Sets the case sensitivity of the specified field (catalog, schema, table, column). * * @param field The field for which the case sensitivity must be updated. * * @param sensitive <i>true</i> if the specified field must be case sensitive, <i>false</i> otherwise. * * @see IdentifierField * @see IdentifierField#setCaseSensitive(byte, boolean) */ public final void setCaseSensitive(IdentifierField field, boolean sensitive){ caseSensitivity = field.setCaseSensitive(caseSensitivity, sensitive); } /** * Indicates whether all fields (catalog, schema, table and column) are case sensitive. * * @return <i>true</i> if all fields are case sensitive, <i>false</i> otherwise. * * @see IdentifierField#isFullCaseSensitive(byte) */ public final boolean isCaseSensitive(){ return IdentifierField.isFullCaseSensitive(caseSensitivity); } /** * Sets the case sensitivity of all fields (catalog, schema, table and column). * * @param sensitive <i>true</i> if all fields must be case sensitive, <i>false</i> otherwise. * * @see IdentifierField#getFullCaseSensitive(boolean) */ public final void setCaseSensitive(boolean sensitive){ caseSensitivity = IdentifierField.getFullCaseSensitive(sensitive); } /** * Gets the whole case sensitivity of this ADQL column. * * @return Its new case sensitivity (one bit per fields). * * @see IdentifierField */ public final byte getCaseSensitive(){ return caseSensitivity; } /** * Sets the whole case sensitivity of this ADQL column. * * @param sensitivity Its new case sensitivity (one bit per fields). * * @see IdentifierField */ public final void setCaseSensitive(final byte sensitivity){ caseSensitivity = sensitivity; } /** * Gets the corresponding {@link DBColumn}. * * @return The corresponding {@link DBColumn}. */ public final DBColumn getDBLink(){ return dbLink; } /** * <p>Sets the {@link DBColumn} corresponding to this {@link ADQLColumn}.</p> * * <p>By default, this field is automatically filled by {@link adql.db.DBChecker}.</p> * * @param dbLink Its corresponding {@link DBColumn}. */ public final void setDBLink(DBColumn dbLink){ this.dbLink = dbLink; } /** * Gets the {@link ADQLTable} from which this column is supposed to come. * * @return Its source table. */ public final ADQLTable getAdqlTable(){ return adqlTable; } /** * <p>Sets the {@link ADQLTable} from which this column is supposed to come.</p> * * <p>By default, this field is automatically filled by {@link adql.db.DBChecker} when {@link adql.db.DBChecker#check(adql.query.ADQLQuery)} is called.</p> * * @param adqlTable Its source table. */ public final void setAdqlTable(ADQLTable adqlTable){ this.adqlTable = adqlTable; } /* ***************** */ /* INHERITED METHODS */ /* ***************** */ @Override public char getExpectedType(){ return expectedType; } @Override public void setExpectedType(final char c){ expectedType = c; } @Override public boolean isNumeric(){ return (dbLink == null || dbLink.getDatatype() == null || dbLink.getDatatype().isNumeric() || dbLink.getDatatype().isUnknown()); } @Override public boolean isString(){ return (dbLink == null || dbLink.getDatatype() == null || dbLink.getDatatype().isString() || (dbLink.getDatatype().isUnknown() && !dbLink.getDatatype().isNumeric())); } @Override public boolean isGeometry(){ return (dbLink == null || dbLink.getDatatype() == null || dbLink.getDatatype().isGeometry() || (dbLink.getDatatype().isUnknown() && !dbLink.getDatatype().isNumeric())); } @Override public ADQLObject getCopy() throws Exception{ return new ADQLColumn(this); } @Override public String getName(){ return getColumnName(); } @Override public ADQLIterator adqlIterator(){ return new NullADQLIterator(); } @Override public String toADQL(){ return getFullColumnName(); } @Override public String toString(){ return toADQL(); } }