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 TAPLibrary. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright 2014-2016 - Astronomisches Rechen Institut (ARI)
*/
/**
* <p>
* Describe a full column type as it is described in the IVOA document of TAP.
* Thus, this object contains 2 attributes: <code>type</code> (or datatype) and <code>length</code> (or size).
* </p>
*
* <p>The length/size may be not defined ; in this case, its value is set to {@link #NO_LENGTH} or is negative or null.</p>
*
* <p>All datatypes declared in the IVOA recommendation document of TAP are listed in an enumeration type: {@link DBDatatype}.
* It is used to set the attribute type/datatype of this class.</p>
*
* @author Grégory Mantelet (ARI)
* @version 1.4 (07/2016)
* @since 1.3
*/
public class DBType {
/**
* List of all datatypes declared in the IVOA recommendation of TAP (in the section UPLOAD).
*
* @author Grégory Mantelet (ARI)
* @version 1.4 (07/2016)
* @since 1.3
*/
public static enum DBDatatype{
SMALLINT, INTEGER, BIGINT, REAL, DOUBLE, BINARY, VARBINARY, CHAR, VARCHAR, BLOB, CLOB, TIMESTAMP, POINT, REGION,
/** Type to use when the precise datatype is unknown.
* @since 1.4 */
UNKNOWN,
/** <p>Type to use when the type is known as numeric but there is no precise datatype
* (e.g. double, float, integer, ...).</p>
* <p>It is particularly used when creating a {@link DefaultDBColumn} from an ADQL function
* or operation while listing resulting columns of a sub-query.</p>
* <p>This type is similar to {@link #UNKNOWN}.</p>
* @since 1.4 */
UNKNOWN_NUMERIC;
/** String to return when {@link #toString()} is called.
* @since 1.4*/
private String strExp = this.name();
@Override
public String toString(){
return strExp;
}
/**
* <p>This function lets define the name of the type as provided
* <b>ONLY FOR {@link #UNKNOWN} and {@link #UNKNOWN_NUMERIC} {@link DBDatatype}s</b>.</p>
*
* <p><i><b>Important:</b>
* If this {@link DBDatatype} is not {@link #UNKNOWN} or {@link #UNKNOWN_NUMERIC} this function has no effect.
* But if the given name is NULL or empty, no custom type will be set ; instead the default value (i.e. name of
* the unknown enum item) will be returned.
* </i></p>
*
* @param typeName User type name.
*
* @since 1.4
*/
public void setCustomType(final String typeName){
if ((this == UNKNOWN || this == UNKNOWN_NUMERIC)){
if (typeName != null && typeName.trim().length() > 0)
strExp = "?" + typeName.trim() + "?";
else
strExp = this.name();
}
}
}
/** Special value in case no length/size is specified. */
public static final int NO_LENGTH = -1;
/** Datatype of a column. */
public final DBDatatype type;
/** The length parameter (only few datatypes need this parameter: char, varchar, binary and varbinary). */
public final int length;
/**
* Build a TAP column type by specifying a datatype.
*
* @param datatype Column datatype.
*/
public DBType(final DBDatatype datatype){
this(datatype, NO_LENGTH);
}
/**
* Build a TAP column type by specifying a datatype and a length (needed only for datatypes like char, varchar, binary and varbinary).
*
* @param datatype Column datatype.
* @param length Length of the column value (needed only for datatypes like char, varchar, binary and varbinary).
*/
public DBType(final DBDatatype datatype, final int length){
if (datatype == null)
throw new NullPointerException("Missing TAP column datatype !");
this.type = datatype;
this.length = length;
}
/**
* <p>Tells whether this type is a numeric.</p>
*
* <p><i>Concerned types:
* {@link DBDatatype#SMALLINT SMALLINT}, {@link DBDatatype#INTEGER INTEGER}, {@link DBDatatype#BIGINT BIGINT},
* {@link DBDatatype#REAL REAL}, {@link DBDatatype#DOUBLE DOUBLE}, {@link DBDatatype#BINARY BINARY},
* {@link DBDatatype#VARBINARY VARBINARY} and {@link DBDatatype#BLOB BLOB}.
* </i></p>
*
* <p><i><b>Important note</b>:
* Since {@link DBDatatype#UNKNOWN UNKNOWN} is an unresolved type, it can potentially be anything.
* But, in order to avoid incorrect operation while expecting a numeric although the type is unknown
* and is in fact not really a numeric, this function will return <code>false</code> if the type is
* {@link DBDatatype#UNKNOWN UNKNOWN} <b>BUT</b> <code>true</code> if
* {@link DBDatatype#UNKNOWN_NUMERIC UNKNOWN_NUMERIC}.
* </i></p>
*
* @return <code>true</code> if this type is a numeric, <code>false</code> otherwise.
*/
public boolean isNumeric(){
switch(type){
case SMALLINT:
case INTEGER:
case BIGINT:
case REAL:
case DOUBLE:
/* Note: binaries are also included here because they can also be considered as Numeric,
* but not for JOINs. */
case BINARY:
case VARBINARY:
case BLOB:
case UNKNOWN_NUMERIC:
return true;
default:
return false;
}
}
/**
* <p>Tells whether this type is a list of bytes.</p>
*
* <p><i>Concerned types:
* {@link DBDatatype#BINARY BINARY}, {@link DBDatatype#VARBINARY VARBINARY} and {@link DBDatatype#BLOB BLOB}.
* </i></p>
*
* <p><i><b>Important note</b>:
* Since {@link DBDatatype#UNKNOWN UNKNOWN} is an unresolved type, it can potentially be anything.
* But, in order to avoid incorrect operation while expecting a binary although the type is unknown
* and is in fact not really a binary, this function will return <code>false</code> if the type is
* {@link DBDatatype#UNKNOWN UNKNOWN} or {@link DBDatatype#UNKNOWN_NUMERIC UNKNOWN_NUMERIC}.
* </i></p>
*
* @return <code>true</code> if this type is a binary, <code>false</code> otherwise.
*/
public boolean isBinary(){
switch(type){
case BINARY:
case VARBINARY:
case BLOB:
return true;
default:
return false;
}
}
/**
* <p>Tells whether this type is about characters.</p>
*
* <p><i>Concerned types:
* {@link DBDatatype#CHAR CHAR}, {@link DBDatatype#VARCHAR VARCHAR}, {@link DBDatatype#CLOB CLOB}
* and {@link DBDatatype#TIMESTAMP TIMESTAMP}.
* </i></p>
*
* <p><i><b>Important note</b>:
* Since {@link DBDatatype#UNKNOWN UNKNOWN} is an unresolved type, it can potentially be anything.
* But, in order to avoid incorrect operation while expecting a string although the type is unknown
* and is in fact not really a string, this function will return <code>false</code> if the type is
* {@link DBDatatype#UNKNOWN UNKNOWN} or {@link DBDatatype#UNKNOWN_NUMERIC UNKNOWN_NUMERIC}
* </i></p>
*
* @return <code>true</code> if this type is a string, <code>false</code> otherwise.
*/
public boolean isString(){
switch(type){
case CHAR:
case VARCHAR:
case CLOB:
case TIMESTAMP:
return true;
default:
return false;
}
}
/**
* <p>Tells whether this type is a geometrical region.</p>
*
* <p><i>Concerned types:
* {@link DBDatatype#POINT POINT} and {@link DBDatatype#REGION REGION}.
* </i></p>
*
* <p><i><b>Important note</b>:
* Since {@link DBDatatype#UNKNOWN UNKNOWN} is an unresolved type, it can potentially be anything.
* But, in order to avoid incorrect operation while expecting a geometry although the type is unknown
* and is in fact not really a geometry, this function will return <code>false</code> if the type is
* {@link DBDatatype#UNKNOWN UNKNOWN} or {@link DBDatatype#UNKNOWN_NUMERIC UNKNOWN_NUMERIC}.
* </i></p>
*
* @return <code>true</code> if this type is a geometry, <code>false</code> otherwise.
*/
public boolean isGeometry(){
return (type == DBDatatype.POINT || type == DBDatatype.REGION);
}
/**
* <p>Tell whether this type has been resolved or not.</p>
*
* <p><i>Concerned types:
* {@link DBDatatype#UNKNOWN UNKNOWN} and {@link DBDatatype#UNKNOWN_NUMERIC UNKNOWN_NUMERIC}.
* </i></p>
*
* @return <code>true</code> if this type has NOT been resolved, <code>false</code> otherwise.
*
* @since 1.4
*/
public boolean isUnknown(){
return type == DBDatatype.UNKNOWN || type == DBDatatype.UNKNOWN_NUMERIC;
}
/**
* <p>Tell whether this {@link DBType} is compatible with the given one.</p>
*
* <p>
* Two {@link DBType}s are said compatible if they are both binary, numeric, geometric or string.
* If one of the two types is {@link DBDatatype#UNKNOWN unknown} or {@link DBDatatype#UNKNOWN_NUMERIC unknown_numeric},
* this function will consider them as compatible and will return <code>true</code>.
* </p>
*
* @param t The type to compare to.
*
* @return <code>true</code> if this type is compatible with the given one, <code>false</code> otherwise.
*/
public boolean isCompatible(final DBType t){
if (t == null)
return false;
else if (isUnknown() || t.isUnknown())
return true;
else if (isBinary() == t.isBinary())
return (type == DBDatatype.BLOB && t.type == DBDatatype.BLOB) || (type != DBDatatype.BLOB && t.type != DBDatatype.BLOB);
else if (isNumeric() == t.isNumeric())
return true;
else if (isGeometry() == t.isGeometry())
return (type == t.type);
else if (isString())
return (type == DBDatatype.CLOB && t.type == DBDatatype.CLOB) || (type != DBDatatype.CLOB && t.type != DBDatatype.CLOB);
else
return (type == t.type);
}
@Override
public String toString(){
if (length > 0)
return type + "(" + length + ")";
else
return type.toString();
}
}