From b74c06febb338fa650b0517a30a15ef180dd368a Mon Sep 17 00:00:00 2001
From: gmantele <gmantele@ari.uni-heidelberg.de>
Date: Fri, 27 Jun 2014 16:29:52 +0200
Subject: [PATCH] [TAP] Remove TAPTypes and create a new class to represent a
 TAP column type: TAPType. + Improve the conversion between VOTable type and
 TAP type by embedding it in TAPType. + Modify TAPColumn in order to integrate
 TAPType and that its VotType is just a conversion of a TAPType. {This commit
 is not compilable.}

---
 src/tap/metadata/TAPColumn.java   |  97 ++------
 src/tap/metadata/TAPMetadata.java |  10 +-
 src/tap/metadata/TAPTable.java    |  23 +-
 src/tap/metadata/TAPType.java     | 257 +++++++++++++++++++++
 src/tap/metadata/TAPTypes.java    | 359 ------------------------------
 src/tap/metadata/VotType.java     | 112 ++++++++--
 6 files changed, 380 insertions(+), 478 deletions(-)
 create mode 100644 src/tap/metadata/TAPType.java
 delete mode 100644 src/tap/metadata/TAPTypes.java

diff --git a/src/tap/metadata/TAPColumn.java b/src/tap/metadata/TAPColumn.java
index 94b69da..2fca36d 100644
--- a/src/tap/metadata/TAPColumn.java
+++ b/src/tap/metadata/TAPColumn.java
@@ -22,6 +22,7 @@ package tap.metadata;
 import java.util.ArrayList;
 import java.util.Iterator;
 
+import tap.metadata.TAPType.TAPDatatype;
 import adql.db.DBColumn;
 import adql.db.DBTable;
 
@@ -41,11 +42,9 @@ public class TAPColumn implements DBColumn {
 
 	private String utype = null;
 
-	private String datatype = null;
+	private TAPType datatype = new TAPType(TAPDatatype.VARCHAR);
 
-	private int size = TAPTypes.NO_SIZE;
-
-	private VotType votType = null;
+	private VotType votType = datatype.toVotType();
 
 	private boolean principal = false;
 
@@ -67,21 +66,25 @@ public class TAPColumn implements DBColumn {
 		dbName = adqlName;
 		lstTargets = new ArrayList<TAPForeignKey>(1);
 		lstSources = new ArrayList<TAPForeignKey>(1);
-		setDefaultType();
 	}
 
-	public TAPColumn(String columnName, String description){
+	public TAPColumn(String columnName, TAPType type){
 		this(columnName);
+		setDatatype(type);
+	}
+
+	public TAPColumn(String columnName, TAPType type, String description){
+		this(columnName, type);
 		this.description = description;
 	}
 
-	public TAPColumn(String columnName, String description, String unit){
-		this(columnName, description);
+	public TAPColumn(String columnName, TAPType type, String description, String unit){
+		this(columnName, type, description);
 		this.unit = unit;
 	}
 
-	public TAPColumn(String columnName, String description, String unit, String ucd, String utype){
-		this(columnName, description, unit);
+	public TAPColumn(String columnName, TAPType type, String description, String unit, String ucd, String utype){
+		this(columnName, type, description, unit);
 		this.ucd = ucd;
 		this.utype = utype;
 	}
@@ -111,6 +114,7 @@ public class TAPColumn implements DBColumn {
 	/**
 	 * @return The table.
 	 */
+	@Override
 	public final DBTable getTable(){
 		return table;
 	}
@@ -181,79 +185,25 @@ public class TAPColumn implements DBColumn {
 	/**
 	 * @return The datatype.
 	 */
-	public final String getDatatype(){
+	public final TAPType getDatatype(){
 		return datatype;
 	}
 
 	/**
-	 * @return Array size (>0 or 2 special values: {@link TAPTypes#NO_SIZE} and {@link TAPTypes#STAR_SIZE}).
-	 */
-	public final int getArraySize(){
-		return size;
-	}
-
-	/**
-	 * <p>Sets the DB datatype, the size and uses these information to set the corresponding VOTable type.</p>
-	 * <b>Important:</b>
-	 * <ul>
-	 * 	<li>If the given datatype is not known according to {@link TAPTypes#getDBType(String)}, the datatype of this column is set to its default value (see {@link #setDefaultType()}),</li>
-	 * 	<li>The VOTable type is set automatically thanks to {@link TAPTypes#getVotType(String, int)}.</li>
-	 * </ul>
-	 * 
-	 * @param datatype 	The datatype to set.
-	 * @param size		Array size (>0 or 2 special values: {@link TAPTypes#NO_SIZE} and {@link TAPTypes#STAR_SIZE}).
-	 * 
-	 * @see TAPTypes#getDBType(VotType)
-	 * @see TAPTypes#getVotType(String, int)
-	 * @see #setDefaultType()
+	 * @param type	The new column datatype.
 	 */
-	public final void setDatatype(String datatype, int size){
-		this.datatype = TAPTypes.getDBType(datatype);
-		this.size = (size <= 0 && size != TAPTypes.STAR_SIZE) ? TAPTypes.NO_SIZE : size;
-
-		if (this.datatype == null)
-			setDefaultType();
-		else
-			this.votType = TAPTypes.getVotType(this.datatype, this.size);
+	public final void setDatatype(final TAPType type){
+		datatype = type;
+		votType = datatype.toVotType();
 	}
 
 	/**
-	 * @return The VOTable type to use.
+	 * @return The votType.
 	 */
 	public final VotType getVotType(){
 		return votType;
 	}
 
-	/**
-	 * <p>Sets the VOTable type and uses it to set the DB datatype and its size.</p>
-	 * <b>Important:</b>
-	 * <ul>
-	 * 	<li>If the given VOTable type is not known according to {@link TAPTypes#getDBType(VotType)}, the DB datatype of this column and its size are set to the default value (see {@link #setDefaultType()}).</li>
-	 * </ul>
-	 * 
-	 * @param type	A full VOTable type (that's to say: <code>datatype</code>, <code>arraysize</code> and <code>xtype</code>).
-	 * 
-	 * @see TAPTypes#getDBType(VotType)
-	 * @see #setDefaultType()
-	 */
-	public final void setVotType(final VotType type){
-		this.votType = type;
-		this.datatype = TAPTypes.getDBType(type);
-		this.size = type.arraysize;
-
-		if (this.datatype == null)
-			setDefaultType();
-	}
-
-	/**
-	 * Sets the default DB datatype (VARCHAR) and its corresponding VOTable type (char , *).
-	 */
-	protected final void setDefaultType(){
-		datatype = TAPTypes.VARCHAR;
-		size = TAPTypes.STAR_SIZE;
-		votType = TAPTypes.getVotType(datatype, size);
-	}
-
 	/**
 	 * @return The principal.
 	 */
@@ -346,12 +296,12 @@ public class TAPColumn implements DBColumn {
 		lstSources.clear();
 	}
 
+	@Override
 	public DBColumn copy(final String dbName, final String adqlName, final DBTable dbTable){
-		TAPColumn copy = new TAPColumn((adqlName == null) ? this.adqlName : adqlName, description, unit, ucd, utype);
+		TAPColumn copy = new TAPColumn((adqlName == null) ? this.adqlName : adqlName, datatype, description, unit, ucd, utype);
 		copy.setDBName((dbName == null) ? this.dbName : dbName);
 		copy.setTable(dbTable);
 
-		copy.setDatatype(datatype, size);
 		copy.setIndexed(indexed);
 		copy.setPrincipal(principal);
 		copy.setStd(std);
@@ -361,10 +311,9 @@ public class TAPColumn implements DBColumn {
 	}
 
 	public DBColumn copy(){
-		TAPColumn copy = new TAPColumn(adqlName, description, unit, ucd, utype);
+		TAPColumn copy = new TAPColumn(adqlName, datatype, description, unit, ucd, utype);
 		copy.setDBName(dbName);
 		copy.setTable(table);
-		copy.setDatatype(datatype, size);
 		copy.setIndexed(indexed);
 		copy.setPrincipal(principal);
 		copy.setStd(std);
diff --git a/src/tap/metadata/TAPMetadata.java b/src/tap/metadata/TAPMetadata.java
index e3c36c7..fde3103 100644
--- a/src/tap/metadata/TAPMetadata.java
+++ b/src/tap/metadata/TAPMetadata.java
@@ -21,7 +21,6 @@ package tap.metadata;
 
 import java.io.IOException;
 import java.io.PrintWriter;
-
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Iterator;
@@ -30,14 +29,13 @@ import java.util.NoSuchElementException;
 
 import javax.servlet.ServletConfig;
 import javax.servlet.ServletException;
-
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-import adql.db.DBTable;
 import tap.resource.Capabilities;
 import tap.resource.TAPResource;
 import tap.resource.VOSIResource;
+import adql.db.DBTable;
 
 public class TAPMetadata implements Iterable<TAPSchema>, VOSIResource, TAPResource {
 
@@ -314,13 +312,13 @@ public class TAPMetadata implements Iterable<TAPSchema>, VOSIResource, TAPResour
 		if (c.getDatatype() != null){
 			writer.print(prefix);
 			writer.print("<dataType xsi:type=\"vod:TAPType\"");
-			if (c.getArraySize() >= 0){
+			if (c.getDatatype().length > 0){
 				writer.print(" size=\"");
-				writer.print(c.getArraySize());
+				writer.print(c.getDatatype().length);
 				writer.print("\"");
 			}
 			writer.print('>');
-			writer.print(c.getDatatype().toUpperCase());
+			writer.print(c.getDatatype().type.toString().toUpperCase());
 			writer.println("</dataType>");
 		}
 
diff --git a/src/tap/metadata/TAPTable.java b/src/tap/metadata/TAPTable.java
index 92fd803..5486377 100644
--- a/src/tap/metadata/TAPTable.java
+++ b/src/tap/metadata/TAPTable.java
@@ -199,34 +199,20 @@ public class TAPTable implements DBTable {
 		return c;
 	}
 
-	public TAPColumn addColumn(String columnName, String description, String unit, String ucd, String utype){
+	public TAPColumn addColumn(String columnName, TAPType datatype, String description, String unit, String ucd, String utype){
 		if (columnName == null)
 			return null;
 
-		TAPColumn c = new TAPColumn(columnName, description, unit, ucd, utype);
+		TAPColumn c = new TAPColumn(columnName, datatype, description, unit, ucd, utype);
 		addColumn(c);
 		return c;
 	}
 
-	public TAPColumn addColumn(String columnName, String description, String unit, String ucd, String utype, String datatype, int size, boolean principal, boolean indexed, boolean std){
+	public TAPColumn addColumn(String columnName, TAPType datatype, String description, String unit, String ucd, String utype, boolean principal, boolean indexed, boolean std){
 		if (columnName == null)
 			return null;
 
-		TAPColumn c = new TAPColumn(columnName, description, unit, ucd, utype);
-		c.setDatatype(datatype, size);
-		c.setPrincipal(principal);
-		c.setIndexed(indexed);
-		c.setStd(std);
-		addColumn(c);
-		return c;
-	}
-
-	public TAPColumn addColumn(String columnName, String description, String unit, String ucd, String utype, VotType votType, boolean principal, boolean indexed, boolean std){
-		if (columnName == null)
-			return null;
-
-		TAPColumn c = new TAPColumn(columnName, description, unit, ucd, utype);
-		c.setVotType(votType);
+		TAPColumn c = new TAPColumn(columnName, datatype, description, unit, ucd, utype);
 		c.setPrincipal(principal);
 		c.setIndexed(indexed);
 		c.setStd(std);
@@ -482,6 +468,7 @@ public class TAPTable implements DBTable {
 		}
 	}
 
+	@Override
 	public DBTable copy(final String dbName, final String adqlName){
 		TAPTable copy = new TAPTable((adqlName == null) ? this.adqlName : adqlName);
 		copy.setDBName((dbName == null) ? this.dbName : dbName);
diff --git a/src/tap/metadata/TAPType.java b/src/tap/metadata/TAPType.java
new file mode 100644
index 0000000..a36c1c3
--- /dev/null
+++ b/src/tap/metadata/TAPType.java
@@ -0,0 +1,257 @@
+package tap.metadata;
+
+/*
+ * This file is part of TAPLibrary.
+ * 
+ * TAPLibrary 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.
+ * 
+ * TAPLibrary 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 - Astronomishes Rechen Institute (ARI)
+ */
+
+import tap.metadata.VotType.VotDatatype;
+
+/**
+ * 
+ * <p>
+ * 	Describe a full TAP column type as it is described in the IVOA document.
+ * 	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 TAPDatatype}.
+ * It is used to set the attribute type/datatype of this class.</p>
+ *  
+ * @author Gr&eacute;gory Mantelet (ARI) - gmantele@ari.uni-heidelberg.de
+ * @version 2.0 (06/2014)
+ * @since 2.0
+ */
+public class TAPType {
+
+	/**
+	 * List of all datatypes declared in the IVOA recommendation of TAP (in the section UPLOAD).
+	 * 
+	 * @author Gr&eacute;gory Mantelet (ARI) - gmantele@ari.uni-heidelberg.de
+	 * @version 2.0 (06/2014)
+	 * @since 2.0
+	 */
+	public static enum TAPDatatype{
+		SMALLINT, INTEGER, BIGINT, REAL, DOUBLE, BINARY, VARBINARY, CHAR, VARCHAR, BLOB, CLOB, TIMESTAMP, POINT, REGION;
+	}
+
+	/** Special value in case no length/size is specified. */
+	public static final int NO_LENGTH = -1;
+
+	/** Datatype of a column. */
+	public final TAPDatatype 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 TAPType(final TAPDatatype 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 TAPType(final TAPDatatype datatype, final int length){
+		if (datatype == null)
+			throw new NullPointerException("Missing TAP column datatype !");
+		this.type = datatype;
+		this.length = length;
+	}
+
+	/**
+	 * Convert this TAP column type into a VOTable field type.
+	 * 
+	 * @return	The corresponding VOTable field type.
+	 * 
+	 * @see #convertIntoVotType(TAPType)
+	 */
+	public VotType toVotType(){
+		return convertIntoVotType(this);
+	}
+
+	@Override
+	public String toString(){
+		if (length > 0)
+			return type + "(" + length + ")";
+		else
+			return type.toString();
+	}
+
+	/**
+	 * Convert the given TAP column type into a VOTable field type.
+	 * 
+	 * @param taptype	The TAP column type to convert.
+	 * 
+	 * @return	The corresponding VOTable field type.
+	 */
+	public static VotType convertIntoVotType(final TAPType taptype){
+		VotType vot = new VotType(VotDatatype.CHAR, VotType.NO_SIZE, false);
+
+		switch(taptype.type){
+			case SMALLINT:
+				vot = new VotType(VotDatatype.SHORT, 1, false);
+				break;
+
+			case INTEGER:
+				vot = new VotType(VotDatatype.INT, 1, false);
+				break;
+
+			case BIGINT:
+				vot = new VotType(VotDatatype.LONG, 1, false);
+				break;
+
+			case REAL:
+				vot = new VotType(VotDatatype.FLOAT, 1, false);
+				break;
+
+			case DOUBLE:
+				vot = new VotType(VotDatatype.DOUBLE, 1, false);
+				break;
+
+			case CHAR:
+				vot = new VotType(VotDatatype.CHAR, (taptype.length > 0 ? taptype.length : 1), false);
+				break;
+
+			case BINARY:
+				vot = new VotType(VotDatatype.UNSIGNED_BYTE, (taptype.length > 0 ? taptype.length : VotType.NO_SIZE), false);
+				break;
+
+			case VARBINARY:
+				vot = new VotType(VotDatatype.UNSIGNED_BYTE, (taptype.length > 0 ? taptype.length : VotType.NO_SIZE), (taptype.length > 0));
+				break;
+
+			case BLOB:
+				vot = new VotType(VotDatatype.UNSIGNED_BYTE, VotType.NO_SIZE, true, VotType.XTYPE_BLOB);
+				break;
+
+			case CLOB:
+				vot = new VotType(VotDatatype.CHAR, VotType.NO_SIZE, true, VotType.XTYPE_CLOB);
+				break;
+
+			case TIMESTAMP:
+				vot = new VotType(VotDatatype.CHAR, VotType.NO_SIZE, true, VotType.XTYPE_TIMESTAMP);
+				break;
+
+			case POINT:
+				vot = new VotType(VotDatatype.CHAR, VotType.NO_SIZE, true, VotType.XTYPE_POINT);
+				break;
+
+			case REGION:
+				vot = new VotType(VotDatatype.CHAR, VotType.NO_SIZE, true, VotType.XTYPE_REGION);
+				break;
+
+			case VARCHAR:
+			default:
+				vot = new VotType(VotDatatype.CHAR, (taptype.length > 0 ? taptype.length : VotType.NO_SIZE), (taptype.length > 0), null);
+				break;
+		}
+
+		return vot;
+	}
+
+	/**
+	 * Convert the given VOTable field type into a TAP column type.
+	 * 
+	 * @param vottype	The VOTable field type to convert.
+	 * 
+	 * @return	The corresponding TAP column type.
+	 */
+	public static TAPType convertFromVotType(final VotType vottype){
+		if (vottype == null)
+			return new TAPType(TAPDatatype.VARCHAR);
+
+		switch(vottype.datatype){
+			case SHORT:
+			case BOOLEAN:
+				if ((vottype.arraysize <= 1 || vottype.arraysize == VotType.NO_SIZE) && !vottype.unlimitedArraysize)
+					return new TAPType(TAPDatatype.SMALLINT);
+				else
+					return new TAPType(TAPDatatype.VARBINARY);
+
+			case INT:
+				if ((vottype.arraysize <= 1 || vottype.arraysize == VotType.NO_SIZE) && !vottype.unlimitedArraysize)
+					return new TAPType(TAPDatatype.INTEGER);
+				else
+					return new TAPType(TAPDatatype.VARBINARY);
+
+			case LONG:
+				if ((vottype.arraysize <= 1 || vottype.arraysize == VotType.NO_SIZE) && !vottype.unlimitedArraysize)
+					return new TAPType(TAPDatatype.BIGINT);
+				else
+					return new TAPType(TAPDatatype.VARBINARY);
+
+			case FLOAT:
+				if ((vottype.arraysize <= 1 || vottype.arraysize == VotType.NO_SIZE) && !vottype.unlimitedArraysize)
+					return new TAPType(TAPDatatype.REAL);
+				else
+					return new TAPType(TAPDatatype.VARBINARY);
+
+			case DOUBLE:
+				if ((vottype.arraysize <= 1 || vottype.arraysize == VotType.NO_SIZE) && !vottype.unlimitedArraysize)
+					return new TAPType(TAPDatatype.DOUBLE);
+				else
+					return new TAPType(TAPDatatype.VARBINARY);
+
+			case UNSIGNED_BYTE:
+				if (vottype.arraysize > 0){
+					if (vottype.unlimitedArraysize)
+						return new TAPType(TAPDatatype.VARBINARY, vottype.arraysize);
+					else
+						return new TAPType(TAPDatatype.BINARY, vottype.arraysize);
+				}else
+					return new TAPType(TAPDatatype.VARBINARY);
+
+			case CHAR:
+			default:
+				TAPType taptype = null;
+				if (vottype.xtype != null && vottype.xtype.trim().length() > 0){
+					if (vottype.xtype.equalsIgnoreCase(VotType.XTYPE_BLOB))
+						taptype = new TAPType(TAPDatatype.BLOB);
+					else if (vottype.xtype.equalsIgnoreCase(VotType.XTYPE_CLOB))
+						taptype = new TAPType(TAPDatatype.CLOB);
+					else if (vottype.xtype.equalsIgnoreCase(VotType.XTYPE_TIMESTAMP))
+						taptype = new TAPType(TAPDatatype.TIMESTAMP);
+					else if (vottype.xtype.equalsIgnoreCase(VotType.XTYPE_POINT))
+						taptype = new TAPType(TAPDatatype.POINT);
+					else if (vottype.xtype.equalsIgnoreCase(VotType.XTYPE_REGION))
+						taptype = new TAPType(TAPDatatype.REGION);
+				}
+				if (taptype == null){
+					if (vottype.unlimitedArraysize)
+						taptype = new TAPType(TAPDatatype.VARCHAR, (vottype.arraysize > 0) ? vottype.arraysize : NO_LENGTH);
+					else{
+						if (vottype.arraysize <= 0 || vottype.arraysize == VotType.NO_SIZE)
+							taptype = new TAPType(TAPDatatype.VARCHAR);
+						else if (vottype.arraysize == 1)
+							taptype = new TAPType(TAPDatatype.CHAR, 1);
+						else
+							taptype = new TAPType(TAPDatatype.CHAR, vottype.arraysize);
+					}
+				}
+				return taptype;
+		}
+	}
+}
diff --git a/src/tap/metadata/TAPTypes.java b/src/tap/metadata/TAPTypes.java
deleted file mode 100644
index 423b854..0000000
--- a/src/tap/metadata/TAPTypes.java
+++ /dev/null
@@ -1,359 +0,0 @@
-package tap.metadata;
-
-/*
- * This file is part of TAPLibrary.
- * 
- * TAPLibrary 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.
- * 
- * TAPLibrary 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 2012 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
- */
-
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Map.Entry;
-
-/**
- * <p>
- * 	Gathers all types used by a TAP service and described in the IVOA document for TAP.
- * 	This class lets "translating" a DB type into a VOTable field type and vice-versa.
- * 	You can also add some DB type aliases, that's to say other other names for the existing DB types:
- * 	smallint, integer, bigint, real, double, binary, varbinary, char, varchar, blob, clob, timestamp, point, region.
- * 	For instance: TEXT &lt;-&gt; VARCHAR.
- * </p>
- * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 11/2011
- * 
- * @see VotType
- */
-public final class TAPTypes {
-
-	private static final Map<String,VotType> dbTypes;
-	private static final Map<String,String> dbTypeAliases;
-	private static final Map<VotType,String> votTypes;
-
-	public static final String SMALLINT = "SMALLINT";
-	public static final String INTEGER = "INTEGER";
-	public static final String BIGINT = "BIGINT";
-	public static final String REAL = "REAL";
-	public static final String DOUBLE = "DOUBLE";
-	public static final String BINARY = "BINARY";
-	public static final String VARBINARY = "VARBINARY";
-	public static final String CHAR = "CHAR";
-	public static final String VARCHAR = "VARCHAR";
-	public static final String BLOB = "BLOB";
-	public static final String CLOB = "CLOB";
-	public static final String TIMESTAMP = "TIMESTAMP";
-	public static final String POINT = "POINT";
-	public static final String REGION = "REGION";
-
-	/** No array size. */
-	public static final int NO_SIZE = -1;
-
-	/** Means '*' (i.e. char(*)). */
-	public static final int STAR_SIZE = -12345;
-
-	static{
-		dbTypes = new HashMap<String,VotType>(14);
-		votTypes = new HashMap<VotType,String>(7);
-
-		VotType type = new VotType("short", 1, null);
-		dbTypes.put(SMALLINT, type);
-		votTypes.put(type, SMALLINT);
-
-		type = new VotType("int", 1, null);
-		dbTypes.put(INTEGER, type);
-		votTypes.put(type, INTEGER);
-
-		type = new VotType("long", 1, null);
-		dbTypes.put(BIGINT, type);
-		votTypes.put(type, BIGINT);
-
-		type = new VotType("float", 1, null);
-		dbTypes.put(REAL, type);
-		votTypes.put(type, REAL);
-
-		type = new VotType("double", 1, null);
-		dbTypes.put(DOUBLE, type);
-		votTypes.put(type, DOUBLE);
-
-		dbTypes.put(BINARY, new VotType("unsignedByte", 1, null));
-
-		type = new VotType("unsignedByte", STAR_SIZE, null);
-		dbTypes.put(VARBINARY, type);
-		votTypes.put(type, VARBINARY);
-
-		dbTypes.put(CHAR, new VotType("char", 1, null));
-
-		type = new VotType("char", STAR_SIZE, null);
-		dbTypes.put(VARCHAR, type);
-		votTypes.put(type, VARCHAR);
-
-		type = new VotType("unsignedByte", STAR_SIZE, "adql:BLOB");
-		dbTypes.put(BLOB, type);
-		votTypes.put(type, BLOB);
-
-		type = new VotType("char", STAR_SIZE, "adql:CLOB");
-		dbTypes.put(CLOB, type);
-		votTypes.put(type, CLOB);
-
-		type = new VotType("char", STAR_SIZE, "adql:TIMESTAMP");
-		dbTypes.put(TIMESTAMP, type);
-		votTypes.put(type, TIMESTAMP);
-
-		type = new VotType("char", STAR_SIZE, "adql:POINT");
-		dbTypes.put(POINT, type);
-		votTypes.put(type, POINT);
-
-		type = new VotType("char", STAR_SIZE, "adql:REGION");
-		dbTypes.put(REGION, type);
-		votTypes.put(type, REGION);
-
-		dbTypeAliases = new HashMap<String,String>(8);
-		// PostgreSQL data types:
-		dbTypeAliases.put("INT2", SMALLINT);
-		dbTypeAliases.put("INT", INTEGER);
-		dbTypeAliases.put("INT4", INTEGER);
-		dbTypeAliases.put("INT8", BIGINT);
-		dbTypeAliases.put("FLOAT4", REAL);
-		dbTypeAliases.put("FLOAT8", DOUBLE);
-		dbTypeAliases.put("TEXT", VARCHAR);
-		dbTypeAliases.put("SPOINT", POINT);
-	}
-
-	/**
-	 * Gets all DB types.
-	 * @return	An iterator on DB type name.
-	 */
-	public static final Iterator<String> getDBTypes(){
-		return dbTypes.keySet().iterator();
-	}
-
-	/**
-	 * Gets all DB type aliases.
-	 * @return	An iterator on Entry&lt;String,String&gt; whose the key is the alias and the value is its corresponding DB type.
-	 */
-	public static final Iterator<Entry<String,String>> getDBTypeAliases(){
-		return dbTypeAliases.entrySet().iterator();
-	}
-
-	/**
-	 * Gets all VOTable types.
-	 * @return	An iterator on {@link VotType}.
-	 */
-	public static final Iterator<VotType> getVotTypes(){
-		return votTypes.keySet().iterator();
-	}
-
-	/**
-	 * <p>Gets the VOTable type corresponding to the given DB type (or a DB type alias).</p>
-	 * <b>Important:</b>
-	 * <ul>
-	 * 	<li>Spaces before and after the DB type are automatically removed,</li>
-	 * 	<li>The DB type is automatically formatted in UPPER-CASE,</li>
-	 * 	<li>Nothing is done if the given DB type is <code>null</code> or empty.</li>
-	 * </ul>
-	 * 
-	 * @param dbType	A DB type (ex: SMALLINT, INTEGER, VARCHAR, POINT, ...)
-	 * 
-	 * @return	The corresponding VOTable type or <code>null</code> if not found.
-	 */
-	public static final VotType getVotType(String dbType){
-		if (dbType == null)
-			return null;
-
-		// Normalize the type name (upper case and with no leading and trailing spaces):
-		dbType = dbType.trim().toUpperCase();
-		if (dbType.length() == 0)
-			return null;
-
-		// Search the corresponding VOTable type:
-		VotType votType = dbTypes.get(dbType);
-		// If no match, try again considering the given type as an alias:
-		if (votType == null)
-			votType = dbTypes.get(dbTypeAliases.get(dbType));
-
-		return votType;
-	}
-
-	/**
-	 * <p>Gets the VOTable type (with the given arraysize) corresponding to the given DB type (or a DB type alias).</p>
-	 * <b>Important:</b>
-	 * <ul>
-	 * 	<li>Spaces before and after the DB type are automatically removed,</li>
-	 * 	<li>The DB type is automatically formatted in UPPER-CASE,</li>
-	 * 	<li>Nothing is done if the given DB type is <code>null</code> or empty,</li>
-	 * 	<li>The given arraysize is used only if the found VOTable type is not special (that's to say: <code>xtype</code> is <code>null</code>).</li>
-	 * </ul>
-	 * 
-	 * @param dbType	A DB type (ex: SMALLINT, INTEGER, VARCHAR, POINT, ...)
-	 * @param arraysize	Arraysize to set in the found VOTable type.
-	 * 
-	 * @return	The corresponding VOTable type or <code>null</code> if not found.
-	 */
-	public static final VotType getVotType(String dbType, int arraysize){
-		VotType votType = getVotType(dbType);
-
-		// If there is a match, set the arraysize:
-		if (votType != null && votType.xtype == null && arraysize > 0)
-			votType = new VotType(votType.datatype, arraysize, null);
-
-		return votType;
-	}
-
-	/**
-	 * 
-	 * <p>Gets the DB type corresponding to the given DB type alias.</p>
-	 * <b>Important:</b>
-	 * <ul>
-	 * 	<li>Spaces before and after the DB type are automatically removed,</li>
-	 * 	<li>The DB type is automatically formatted in UPPER-CASE,</li>
-	 * 	<li>If the given DB type is not alias but directly a DB type, it is immediately return.</li>
-	 * </ul>
-	 * 
-	 * @param dbTypeAlias	A DB type alias.
-	 * 
-	 * @return		The corresponding DB type or <code>null</code> if not found.
-	 */
-	public static final String getDBType(String dbTypeAlias){
-		if (dbTypeAlias == null)
-			return null;
-
-		// Normalize the type name:
-		dbTypeAlias = dbTypeAlias.trim().toUpperCase();
-		if (dbTypeAlias.length() == 0)
-			return null;
-
-		// Get the corresponding DB type:
-		if (dbTypes.containsKey(dbTypeAlias))
-			return dbTypeAlias;
-		else
-			return dbTypeAliases.get(dbTypeAlias);
-	}
-
-	/**
-	 * 
-	 * <p>Gets the DB type corresponding to the given VOTable field type.</p>
-	 * <b>Important:</b>
-	 * <ul>
-	 * 	<li>The research is made only on the following fields: <code>datatype</code> and <code>xtype</code>,</li>
-	 * 	<li>Case <b>insensitive</b> research.</li>
-	 * </ul>
-	 * 
-	 * @param type	A VOTable type.
-	 * 
-	 * @return		The corresponding DB type or <code>null</code> if not found.
-	 */
-	public static final String getDBType(final VotType type){
-		if (type == null)
-			return null;
-		return votTypes.get(type);
-	}
-
-	/**
-	 * <p>Adds, replaces or removes a DB type alias.</p>
-	 * <b>Important:</b>
-	 * <ul>
-	 * 	<li>Spaces before and after the DB type are automatically removed,</li>
-	 * 	<li>The DB type is automatically formatted in UPPER-CASE,</li>
-	 * 	<li>The same "normalizations" are done on the given alias (so the case sensitivity is ignored),</li>
-	 * 	<li>Nothing is done if the given alias is <code>null</code> or empty,</li>
-	 * 	<li>If the given DB type is <code>null</code>, the given alias is removed,</li>
-	 * 	<li>Nothing is done if the given DB type (!= null) does not match with a known DB type.</li>
-	 * </ul>
-	 * 
-	 * @param alias		A DB type alias (ex: spoint)
-	 * @param dbType	A DB type (ex: POINT).
-	 * 
-	 * @return	<code>true</code> if the association has been updated, <code>false</code> otherwise.
-	 */
-	public static final boolean putDBTypeAlias(String alias, String dbType){
-		if (alias == null)
-			return false;
-
-		// Normalize the given alias:
-		alias = alias.trim().toUpperCase();
-		if (alias.length() == 0)
-			return false;
-
-		// Check the existence of the given DB type:
-		if (dbType != null){
-			dbType = dbType.trim().toUpperCase();
-			if (dbType.length() == 0)
-				return false;
-			else if (!dbTypes.containsKey(dbType))
-				return false;
-		}
-
-		// Update the map of aliases:
-		if (dbType == null)
-			dbTypeAliases.remove(alias);
-		else
-			dbTypeAliases.put(alias, dbType);
-
-		return true;
-	}
-
-	/** SELF TEST */
-	public final static void main(final String[] args) throws Exception{
-		System.out.println("***** DB TYPES *****");
-		Iterator<String> itDB = TAPTypes.getDBTypes();
-		while(itDB.hasNext())
-			System.out.println("\t- " + itDB.next());
-
-		System.out.println("\n***** DB TYPE ALIASES *****");
-		Iterator<Entry<String,String>> itAliases = TAPTypes.getDBTypeAliases();
-		while(itAliases.hasNext()){
-			Entry<String,String> e = itAliases.next();
-			System.out.println("\t- " + e.getKey() + " = " + e.getValue());
-		}
-
-		System.out.println("\n***** VOTABLE TYPES *****");
-		Iterator<VotType> itVot = TAPTypes.getVotTypes();
-		while(itVot.hasNext())
-			System.out.println("\t- " + itVot.next());
-
-		byte[] buffer = new byte[1024];
-		int nbRead = 0;
-		String type = null;
-
-		System.out.print("\nDB Type ? ");
-		nbRead = System.in.read(buffer);
-		type = new String(buffer, 0, nbRead);
-		System.out.println(TAPTypes.getVotType(type));
-
-		int arraysize = 1;
-		String xtype = null;
-		VotType votType = null;
-		System.out.print("\nVOTable datatype ? ");
-		nbRead = System.in.read(buffer);
-		type = (new String(buffer, 0, nbRead)).trim();
-		System.out.print("VOTable arraysize ? ");
-		nbRead = System.in.read(buffer);
-		try{
-			arraysize = Integer.parseInt((new String(buffer, 0, nbRead)).trim());
-		}catch(NumberFormatException nfe){
-			arraysize = STAR_SIZE;
-		}
-		System.out.print("VOTable xtype ? ");
-		nbRead = System.in.read(buffer);
-		xtype = (new String(buffer, 0, nbRead)).trim();
-		if (xtype != null && xtype.length() == 0)
-			xtype = null;
-		votType = new VotType(type, arraysize, xtype);
-		System.out.println(TAPTypes.getDBType(votType));
-	}
-
-}
diff --git a/src/tap/metadata/VotType.java b/src/tap/metadata/VotType.java
index 27daede..c72db5c 100644
--- a/src/tap/metadata/VotType.java
+++ b/src/tap/metadata/VotType.java
@@ -16,7 +16,8 @@ package tap.metadata;
  * 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 2012 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomishes Rechen Institute (ARI)
  */
 
 import cds.savot.writer.SavotWriter;
@@ -29,25 +30,88 @@ import cds.savot.writer.SavotWriter;
  * 	<li><code>xtype</code>.</li>
  * </ul>
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 11/2011
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 06/2014
  */
 public final class VotType {
-	public final String datatype;
+	/**
+	 * All possible values for a VOTable datatype (i.e. boolean, short, char, ...).
+	 * 
+	 * @author Gr&eacute;gory Mantelet (ARI) - gmantele@ari.uni-heidelberg.de
+	 * @version 2.0 (06/2014)
+	 * @since 2.0
+	 */
+	public static enum VotDatatype{
+		BOOLEAN("boolean"), SHORT("short"), INT("int"), LONG("long"), FLOAT("float"), DOUBLE("double"), CHAR("char"), UNSIGNED_BYTE("unsignedByte");
+
+		private final String strExpr;
+
+		private VotDatatype(final String str){
+			strExpr = (str == null || str.trim().length() == 0) ? name() : str;
+		}
+
+		@Override
+		public String toString(){
+			return strExpr;
+		}
+	}
+
+	/** Special VOTable type (XType) for TAP/DB type BLOB.
+	 * @since 2.0*/
+	public final static String XTYPE_BLOB = "adql:BLOB";
+	/** Special VOTable type (XType) for TAP/DB type CLOB.
+	 * @since 2.0 */
+	public final static String XTYPE_CLOB = "adql:CLOB";
+	/** Special VOTable type (XType) for TAP/DB type TIMESTAMP.
+	 * @since 2.0 */
+	public final static String XTYPE_TIMESTAMP = "adql:TIMESTAMP";
+	/** Special VOTable type (XType) for TAP/DB type POINT.
+	 * @since 2.0 */
+	public final static String XTYPE_POINT = "adql:POINT";
+	/** Special VOTable type (XType) for TAP/DB type REGION.
+	 * @since 2.0 */
+	public final static String XTYPE_REGION = "adql:REGION";
+
+	/** No array size.
+	 * @since 2.0 */
+	public static final int NO_SIZE = -1;
+
+	/** VOTable datatype
+	 * @since 2.0 */
+	public final VotDatatype datatype;
 	/** A negative or null value means "*" (that's to say: an undetermined arraysize). */
-	public int arraysize;
+	public final int arraysize;
+	/** If true, it means either "n*" (where n is the arraysize when > 0) or "*".
+	 * @since 2.0*/
+	public final boolean unlimitedArraysize;
+	/** Special type specification (i.e. POINT, TIMESTAMP, ...). */
 	public final String xtype;
 
 	/**
-	 * @param datatype		A datatype (ex: char, int, long, ...). <b>Null value forbidden</b>
-	 * @param arraysize		A non-null positive integer. (any value &le; 0 will be considered as an undetermined arraysize).
+	 * Build a VOTable field type.
+	 * 
+	 * @param datatype		A datatype. <b>Null value forbidden</b>
+	 * @param arraysize		A non-null positive integer. (any value &le; 0 will be considered as an undetermined arraysize, that's to say {@link #NO_SIZE}).
+	 * @param unlimitedSize	Indicate whether a * must be appended at the end of the arraysize attribute (so in these 2 cases: "n*" or "*").
+	 */
+	public VotType(final VotDatatype datatype, final int arraysize, final boolean unlimitedSize){
+		this(datatype, arraysize, unlimitedSize, null);
+	}
+
+	/**
+	 * Build a VOTable field type.
+	 * 
+	 * @param datatype		A datatype. <b>Null value forbidden</b>
+	 * @param arraysize		A non-null positive integer. (any value &le; 0 will be considered as an undetermined arraysize, that's to say {@link #NO_SIZE}).
+	 * @param unlimitedSize	Indicate whether a * must be appended at the end of the arraysize attribute (so in these 2 cases: "n*" or "*").
 	 * @param xtype			A special type (ex: adql:POINT, adql:TIMESTAMP, ...). Null value allowed.
 	 */
-	public VotType(final String datatype, final int arraysize, final String xtype){
+	public VotType(final VotDatatype datatype, final int arraysize, final boolean unlimitedSize, final String xtype){
 		if (datatype == null)
-			throw new NullPointerException("Null VOTable datatype !");
+			throw new NullPointerException("Missing VOTable datatype !");
 		this.datatype = datatype;
-		this.arraysize = arraysize;
+		this.arraysize = (arraysize > 0) ? arraysize : NO_SIZE;
+		this.unlimitedArraysize = unlimitedSize;
 		this.xtype = xtype;
 	}
 
@@ -56,13 +120,7 @@ public final class VotType {
 		if (obj == null)
 			return false;
 		try{
-			VotType vot = (VotType)obj;
-			if (datatype.equalsIgnoreCase(vot.datatype)){
-				if (xtype == null)
-					return (vot.xtype == null);
-				else
-					return xtype.equalsIgnoreCase(vot.xtype);
-			}
+			return toString().equals(obj);
 		}catch(ClassCastException cce){
 			;
 		}
@@ -71,7 +129,7 @@ public final class VotType {
 
 	@Override
 	public int hashCode(){
-		return datatype.toLowerCase().hashCode();
+		return datatype.toString().hashCode();
 	}
 
 	@Override
@@ -79,10 +137,13 @@ public final class VotType {
 		StringBuffer str = new StringBuffer("datatype=\"");
 		str.append(datatype).append('"');
 
-		if (arraysize == TAPTypes.STAR_SIZE)
+		if (arraysize > 0){
+			str.append(" arraysize=\"").append(SavotWriter.encodeAttribute("" + arraysize));
+			if (unlimitedArraysize)
+				str.append("*");
+			str.append('"');
+		}else if (unlimitedArraysize)
 			str.append(" arraysize=\"*\"");
-		else if (arraysize != TAPTypes.NO_SIZE && arraysize > 0)
-			str.append(" arraysize=\"").append(SavotWriter.encodeAttribute("" + arraysize)).append('"');
 
 		if (xtype != null)
 			str.append(" xtype=\"").append(SavotWriter.encodeAttribute(xtype)).append('"');
@@ -90,4 +151,13 @@ public final class VotType {
 		return str.toString();
 	}
 
+	/**
+	 * Convert this VOTable type definition into a TAPColumn type.
+	 * 
+	 * @return	The corresponding {@link TAPType}.
+	 */
+	public TAPType toTAPType(){
+		return TAPType.convertFromVotType(this);
+	}
+
 }
-- 
GitLab