package adql.db;
/*
* 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 2014-2015 - Astronomisches Rechen Institut (ARI)
*/
import java.util.ArrayList;
import java.util.Iterator;
import adql.db.exception.UnresolvedJoinException;
import adql.query.ADQLQuery;
/**
* This is a special column which exists only after a NATURAL JOIN or a JOIN ... USING between two tables.
* It lets unify several columns of the joined tables in a single column.
*
* Thus, the writer of an ADQL query can use the column name without table prefix (since after the join there will be only one)
* or with a prefix table of the joined tables. The list of all covered tables is stored in this object and can be extended
* in case of several JOINs.
*
* @author Grégory Mantelet (ARI) - gmantele@ari.uni-heidelberg.de
* @version 1.3 (05/2015)
* @since 1.2
*/
public class DBCommonColumn implements DBColumn {
protected DBColumn generalColumnDesc;
protected ArrayList<DBTable> lstCoveredTables = new ArrayList<DBTable>();
/**
* Create a column which merges both of the given columns.
*
* This special {@link DBColumn} implementation is not associated with one table,
* and can be listed in a {@link DBTable} ONLY IF the latter is the result of a sub-query (see {@link ADQLQuery#getResultingColumns()}).
*
* A column resulting from a tables join is common only to the joined tables. That's why a list of all tables covered
* by this column is created or update at each merge. It can be accessed thanks to {@link #getCoveredTables()}.
*
* Note: In the case one or both of the columns to join are {@link DBCommonColumn}, the list of their covered tables are also merged.
*
* @param leftCol Column of the left join table. May be a {@link DBCommonColumn}.
* @param rightCol Column of the right join table. May be a {@link DBCommonColumn}.
*
* @throws UnresolvedJoinException If the type of the two given columns are not roughly (just testing numeric, string or geometry) compatible.
*/
public DBCommonColumn(final DBColumn leftCol, final DBColumn rightCol) throws UnresolvedJoinException{
// Test whether type of both columns are compatible:
if (leftCol.getDatatype() != null && rightCol.getDatatype() != null && !leftCol.getDatatype().isCompatible(rightCol.getDatatype()))
throw new UnresolvedJoinException("JOIN impossible: incompatible column types when trying to join the columns " + leftCol.getADQLName() + " (" + leftCol.getDatatype() + ") and " + rightCol.getADQLName() + " (" + rightCol.getDatatype() + ")!");
// LEFT COLUMN:
if (leftCol instanceof DBCommonColumn){
// set the general column description:
generalColumnDesc = ((DBCommonColumn)leftCol).generalColumnDesc;
// add all covered tables of the left common column:
Iterator<DBTable> it = ((DBCommonColumn)leftCol).getCoveredTables();
while(it.hasNext())
addCoveredTable(it.next());
}else{
// set the general column description:
generalColumnDesc = leftCol.copy(leftCol.getDBName(), leftCol.getADQLName(), null);
// add the table to cover:
addCoveredTable(leftCol.getTable());
}
// RIGHT COLUMN:
if (rightCol instanceof DBCommonColumn){
// add all covered tables of the left common column:
Iterator<DBTable> it = ((DBCommonColumn)rightCol).getCoveredTables();
while(it.hasNext())
addCoveredTable(it.next());
}else{
// add the table to cover:
addCoveredTable(rightCol.getTable());
}
}
/**
* Constructor by copy.
* It returns a copy of this instance of {@link DBCommonColumn}.
*
* Note: The list of covered tables is NOT deeply copied!
*
* @param toCopy The {@link DBCommonColumn} to copy.
* @param dbName The new DB name of this {@link DBCommonColumn}.
* @param adqlName The new ADQL name of this {@link DBCommonColumn}.
*/
@SuppressWarnings("unchecked")
public DBCommonColumn(final DBCommonColumn toCopy, final String dbName, final String adqlName){
generalColumnDesc = toCopy.generalColumnDesc.copy(dbName, adqlName, null);
lstCoveredTables = (ArrayList<DBTable>)toCopy.lstCoveredTables.clone();
}
@Override
public final String getADQLName(){
return generalColumnDesc.getADQLName();
}
@Override
public final String getDBName(){
return generalColumnDesc.getDBName();
}
@Override
public final DBType getDatatype(){
return generalColumnDesc.getDatatype();
}
@Override
public final DBTable getTable(){
return null;
}
/**
* Get an iterator over the list of all tables covered by this common column.
*
* @return Iterator over all covered tables.
*/
public final Iterator<DBTable> getCoveredTables(){
return lstCoveredTables.iterator();
}
/**
* Add a table that this common column must cover from now.
*
* Warning: no unicity check is never done !
*
* @param table Table to add in the covered tables list.
*/
protected void addCoveredTable(final DBTable table){
if (table != null)
lstCoveredTables.add(table);
}
/**
* WARNING: This copy function does not make a real copy of this DBCommonColumn !
* It returns a modified copy of the general column description it contains.
*
* Note: To make a real copy of this DBCommonColumn use the Constructor by copy {@link #DBCommonColumn(DBCommonColumn, String, String)}.
*
* @param dbName Its new DB name.
* @param adqlName Its new ADQL name.
* @param dbTable Its new DBTable
*
* @return A modified copy of the general column description this common column represents.
*
* @see adql.db.DBColumn#copy(java.lang.String, java.lang.String, adql.db.DBTable)
*/
@Override
public DBColumn copy(final String dbName, final String adqlName, final DBTable dbTable){
return generalColumnDesc.copy(dbName, adqlName, dbTable);
}
}