From 496e769c4ce85f3ce3305ece6e618317d3bd7a92 Mon Sep 17 00:00:00 2001
From: gmantele <gmantele@ari.uni-heidelberg.de>
Date: Tue, 28 Oct 2014 19:25:02 +0100
Subject: [PATCH] [ADQL,TAP] Add STC-S and UDFs support in the ADQL parser.
 Now, it is possible to provide a list of allowed UDFs, regions and coordinate
 systems. The ServiceConnection of TAP is now able to provide these lists and
 to propagate them to the ADQLExecutor. UDFs and allowed regions are now
 listed automatically in the /capabilities resource of TAP. The type
 'geometry' is now fully supported in ADQL. That's why the new function
 'isGeometry()' has been added to all ADQLOperand extensions. Now the
 DBChecker is also able to check roughly types of columns and UDFs (unknown
 when parsing syntactically a query). The syntax of STC-S regions (expressed
 in the REGION function) are now checked by DBChecker. However, for the
 moment, geometries are not serialized in STC-S in the output....but it should
 be possible in some way in the next commit(s).

---
 src/adql/db/DBChecker.java                    | 1214 ++-
 src/adql/db/DBColumn.java                     |   20 +-
 src/adql/db/DBCommonColumn.java               |   18 +-
 .../TAPType.java => adql/db/DBType.java}      |  103 +-
 src/adql/db/DefaultDBColumn.java              |   95 +-
 src/adql/db/FunctionDef.java                  |  476 ++
 src/adql/db/STCS.java                         | 1685 +++++
 src/adql/db/SearchColumnList.java             |    2 +-
 src/adql/db/exception/UnresolvedFunction.java |  124 +
 .../UnresolvedIdentifiersException.java       |   11 +-
 src/adql/db/exception/UnresolvedJoin.java     |    2 +-
 src/adql/parser/ADQLParser.java               | 6687 ++++++++---------
 src/adql/parser/ADQLParserConstants.java      |  505 +-
 src/adql/parser/ADQLParserTokenManager.java   | 3515 ++++-----
 src/adql/parser/ADQLQueryFactory.java         |   63 +-
 src/adql/parser/adqlGrammar.jj                |  116 +-
 src/adql/query/ADQLList.java                  |   27 +-
 src/adql/query/ADQLQuery.java                 |    2 +-
 src/adql/query/from/ADQLJoin.java             |    2 +-
 src/adql/query/from/FromContent.java          |    2 +-
 src/adql/query/operand/ADQLColumn.java        |   39 +-
 src/adql/query/operand/ADQLOperand.java       |   28 +-
 src/adql/query/operand/Concatenation.java     |   14 +-
 src/adql/query/operand/NegativeOperand.java   |   25 +-
 src/adql/query/operand/NumericConstant.java   |   21 +-
 src/adql/query/operand/Operation.java         |   25 +-
 src/adql/query/operand/StringConstant.java    |   22 +-
 src/adql/query/operand/UnknownType.java       |   52 +
 src/adql/query/operand/WrappedOperand.java    |   22 +-
 .../query/operand/function/DefaultUDF.java    |   56 +-
 .../query/operand/function/MathFunction.java  |    7 +-
 .../query/operand/function/SQLFunction.java   |   17 +-
 .../operand/function/UserDefinedFunction.java |   25 +-
 .../function/geometry/AreaFunction.java       |   20 +-
 .../function/geometry/BoxFunction.java        |   18 +-
 .../function/geometry/CentroidFunction.java   |   19 +-
 .../function/geometry/CircleFunction.java     |   17 +-
 .../function/geometry/ContainsFunction.java   |   19 +-
 .../function/geometry/DistanceFunction.java   |   19 +-
 .../function/geometry/ExtractCoord.java       |   19 +-
 .../function/geometry/ExtractCoordSys.java    |   19 +-
 .../function/geometry/GeometryFunction.java   |   34 +-
 .../function/geometry/IntersectsFunction.java |   19 +-
 .../function/geometry/PointFunction.java      |   19 +-
 .../function/geometry/PolygonFunction.java    |   20 +-
 .../function/geometry/RegionFunction.java     |    7 +-
 src/tap/ADQLExecutor.java                     |    2 -
 src/tap/AbstractTAPFactory.java               |   11 +-
 src/tap/ServiceConnection.java                |  440 +-
 src/tap/data/LimitedTableIterator.java        |    4 +-
 src/tap/data/ResultSetTableIterator.java      |   64 +-
 src/tap/data/TableIterator.java               |    4 +-
 src/tap/data/VOTableIterator.java             |    6 +-
 src/tap/db/JDBCConnection.java                |   24 +-
 src/tap/error/DefaultTAPErrorWriter.java      |    2 +-
 src/tap/formatter/JSONFormat.java             |    8 +-
 src/tap/formatter/VOTableFormat.java          |   14 +-
 src/tap/metadata/TAPColumn.java               |   31 +-
 src/tap/metadata/TAPMetadata.java             |   57 +-
 src/tap/metadata/TAPTable.java                |    9 +-
 src/tap/metadata/VotType.java                 |   63 +-
 src/tap/parameters/TAPParameters.java         |   10 +-
 src/tap/resource/TAP.java                     |   35 +-
 .../service/error/DefaultUWSErrorWriter.java  |    2 +-
 test/adql/SearchColumnListTest.java           |   24 +-
 test/adql/db/TestDBChecker.java               |  609 ++
 test/adql/db/TestFunctionDef.java             |  312 +
 test/adql/db/TestSTCS.java                    |  536 ++
 test/adql/parser/TestADQLParser.java          |   43 +
 test/tap/db/JDBCConnectionTest.java           |   12 +-
 test/tap/formatter/JSONFormatTest.java        |   23 +-
 test/tap/formatter/SVFormatTest.java          |   23 +-
 test/tap/formatter/TextFormatTest.java        |   23 +-
 test/tap/formatter/VOTableFormatTest.java     |   23 +-
 test/uws/service/UWSUrlTest.java              |   91 +-
 75 files changed, 11853 insertions(+), 5923 deletions(-)
 rename src/{tap/metadata/TAPType.java => adql/db/DBType.java} (51%)
 create mode 100644 src/adql/db/FunctionDef.java
 create mode 100644 src/adql/db/STCS.java
 create mode 100644 src/adql/db/exception/UnresolvedFunction.java
 create mode 100644 src/adql/query/operand/UnknownType.java
 create mode 100644 test/adql/db/TestDBChecker.java
 create mode 100644 test/adql/db/TestFunctionDef.java
 create mode 100644 test/adql/db/TestSTCS.java
 create mode 100644 test/adql/parser/TestADQLParser.java

diff --git a/src/adql/db/DBChecker.java b/src/adql/db/DBChecker.java
index 7dcbc62..834d45a 100644
--- a/src/adql/db/DBChecker.java
+++ b/src/adql/db/DBChecker.java
@@ -17,16 +17,22 @@ package adql.db;
  * along with ADQLLibrary.  If not, see <http://www.gnu.org/licenses/>.
  * 
  * Copyright 2011,2013-2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
- *                            Astronomishes Rechen Institut (ARI)
+ *                            Astronomisches Rechen Institut (ARI)
  */
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.Map;
 import java.util.Stack;
 
+import adql.db.STCS.CoordSys;
+import adql.db.STCS.Region;
+import adql.db.STCS.RegionType;
 import adql.db.exception.UnresolvedColumnException;
+import adql.db.exception.UnresolvedFunction;
 import adql.db.exception.UnresolvedIdentifiersException;
 import adql.db.exception.UnresolvedTableException;
 import adql.parser.ParseException;
@@ -42,14 +48,37 @@ import adql.query.SelectItem;
 import adql.query.from.ADQLTable;
 import adql.query.from.FromContent;
 import adql.query.operand.ADQLColumn;
+import adql.query.operand.ADQLOperand;
+import adql.query.operand.StringConstant;
+import adql.query.operand.UnknownType;
+import adql.query.operand.function.ADQLFunction;
+import adql.query.operand.function.DefaultUDF;
+import adql.query.operand.function.UserDefinedFunction;
+import adql.query.operand.function.geometry.BoxFunction;
+import adql.query.operand.function.geometry.CircleFunction;
+import adql.query.operand.function.geometry.GeometryFunction;
+import adql.query.operand.function.geometry.PointFunction;
+import adql.query.operand.function.geometry.PolygonFunction;
+import adql.query.operand.function.geometry.RegionFunction;
 import adql.search.ISearchHandler;
 import adql.search.SearchColumnHandler;
 import adql.search.SimpleSearchHandler;
 
 /**
+ * This {@link QueryChecker} implementation is able to do the following verifications on an ADQL query:
+ * <ol>
+ * 	<li>Check the existence of all table and column references found in a query</li>
+ * 	<li>Resolve all unknown functions as supported User Defined Functions (UDFs)</li>
+ * 	<li>Check whether all used geometrical functions are supported</li>
+ * 	<li>Check whether all used coordinate systems are supported</li>
+ * 	<li>Check that types of columns and UDFs match with their context</li>
+ * </ol>
+ * 
+ * <h3>Check tables and columns</h3>
  * <p>
- * 	Checks the existence of tables and columns, but also adds database metadata
- * 	on {@link ADQLTable} and {@link ADQLColumn} instances when they are resolved.
+ * 	In addition to check the existence of tables and columns referenced in the query,
+ * 	this checked will also attach database metadata on these references ({@link ADQLTable}
+ * 	and {@link ADQLColumn} instances when they are resolved.
  * </p>
  * 
  * <p>These information are:</p>
@@ -59,35 +88,239 @@ import adql.search.SimpleSearchHandler;
  * </ul>
  * 
  * <p><i><u>Note:</u>
- * 	Knowing DB metadata of {@link ADQLTable} and {@link ADQLColumn} is particularly useful for the translation of the ADQL query to SQL, because the ADQL name of columns and tables
- * 	can be replaced in SQL by their DB name, if different. This mapping is done automatically by {@link adql.translator.PostgreSQLTranslator}.
+ * 	Knowing DB metadata of {@link ADQLTable} and {@link ADQLColumn} is particularly useful for the translation of the ADQL query to SQL,
+ * 	because the ADQL name of columns and tables can be replaced in SQL by their DB name, if different. This mapping is done automatically
+ * 	by {@link adql.translator.JDBCTranslator}.
  * </i></p>
  * 
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 1.3 (08/2014)
+ * @version 1.3 (10/2014)
  */
 public class DBChecker implements QueryChecker {
 
 	/** List of all available tables ({@link DBTable}). */
 	protected SearchTableList lstTables;
 
+	/** <p>List of all allowed geometrical functions (i.e. CONTAINS, REGION, POINT, COORD2, ...).</p>
+	 * <p>
+	 * 	If this list is NULL, all geometrical functions are allowed.
+	 * 	However, if not, all items of this list must be the only allowed geometrical functions.
+	 * 	So, if the list is empty, no such function is allowed.
+	 * </p>
+	 * @since 1.3 */
+	protected String[] allowedGeo = null;
+
+	/** <p>List of all allowed coordinate systems.</p>
+	 * <p>
+	 * 	Each item of this list must be of the form: "{frame} {refpos} {flavor}".
+	 * 	Each of these 3 items can be either of value, a list of values expressed with the syntax "({value1}|{value2}|...)"
+	 * 	or a '*' to mean all possible values.
+	 * </p>
+	 * <p><i>Note: since a default value (corresponding to the empty string - '') should always be possible for each part of a coordinate system,
+	 * the checker will always add the default value (UNKNOWNFRAME, UNKNOWNREFPOS or SPHERICAL2) into the given list of possible values for each coord. sys. part.</i></p>
+	 * <p>
+	 * 	If this list is NULL, all coordinates systems are allowed.
+	 * 	However, if not, all items of this list must be the only allowed coordinate systems.
+	 * 	So, if the list is empty, none is allowed.
+	 * </p>
+	 * @since 1.3 */
+	protected String[] allowedCoordSys = null;
+
+	/** <p>A regular expression built using the list of allowed coordinate systems.
+	 * With this regex, it is possible to known whether a coordinate system expression is allowed or not.</p>
+	 * <p>If NULL, all coordinate systems are allowed.</p>
+	 * @since 1.3 */
+	protected String coordSysRegExp = null;
+
+	/** <p>List of all allowed User Defined Functions (UDFs).</p>
+	 * <p>
+	 * 	If this list is NULL, any encountered UDF will be allowed.
+	 * 	However, if not, all items of this list must be the only allowed UDFs.
+	 * 	So, if the list is empty, no UDF is allowed.
+	 * </p>
+	 * @since 1.3 */
+	protected FunctionDef[] allowedUdfs = null;
+
 	/* ************ */
 	/* CONSTRUCTORS */
 	/* ************ */
 	/**
-	 * Builds a {@link DBChecker} with an empty list of tables.
+	 * <p>Builds a {@link DBChecker} with an empty list of tables.</p>
+	 * 
+	 * <p>Verifications done by this object after creation:</p>
+	 * <ul>
+	 * 	<li>Existence of tables and columns:            <b>NO <i>(even unknown or fake tables and columns are allowed)</i></b></li>
+	 * 	<li>Existence of User Defined Functions (UDFs): <b>NO <i>(any "unknown" function is allowed)</i></b></li>
+	 * 	<li>Support of geometrical functions:           <b>NO <i>(all valid geometrical functions are allowed)</i></b></li>
+	 * 	<li>Support of coordinate systems:              <b>NO <i>(all valid coordinate systems are allowed)</i></b></li>
+	 * </ul>
 	 */
 	public DBChecker(){
-		lstTables = new SearchTableList();
+		this(null, null);
 	}
 
 	/**
-	 * Builds a {@link DBChecker} with the given list of tables.
+	 * <p>Builds a {@link DBChecker} with the given list of known tables.</p>
+	 * 
+	 * <p>Verifications done by this object after creation:</p>
+	 * <ul>
+	 * 	<li>Existence of tables and columns:            <b>OK</b></li>
+	 * 	<li>Existence of User Defined Functions (UDFs): <b>NO <i>(any "unknown" function is allowed)</i></b></li>
+	 * 	<li>Support of geometrical functions:           <b>NO <i>(all valid geometrical functions are allowed)</i></b></li>
+	 * 	<li>Support of coordinate systems:              <b>NO <i>(all valid coordinate systems are allowed)</i></b></li>
+	 * </ul>
 	 * 
 	 * @param tables	List of all available tables.
 	 */
 	public DBChecker(final Collection<? extends DBTable> tables){
+		this(tables, null);
+	}
+
+	/**
+	 * <p>Builds a {@link DBChecker} with the given list of known tables and with a restricted list of user defined functions.</p>
+	 * 
+	 * <p>Verifications done by this object after creation:</p>
+	 * <ul>
+	 * 	<li>Existence of tables and columns:            <b>OK</b></li>
+	 * 	<li>Existence of User Defined Functions (UDFs): <b>OK</b></li>
+	 * 	<li>Support of geometrical functions:           <b>NO <i>(all valid geometrical functions are allowed)</i></b></li>
+	 * 	<li>Support of coordinate systems:              <b>NO <i>(all valid coordinate systems are allowed)</i></b></li>
+	 * </ul>
+	 * 
+	 * @param tables		List of all available tables.
+	 * @param allowedUdfs	List of all allowed user defined functions.
+	 *                   	If NULL, no verification will be done (and so, all UDFs are allowed).
+	 *                   	If empty list, no "unknown" (or UDF) is allowed.
+	 *                   	<i>Note: match with items of this list are done case insensitively.</i>
+	 * 
+	 * @since 1.3
+	 */
+	public DBChecker(final Collection<? extends DBTable> tables, final Collection<? extends FunctionDef> allowedUdfs){
+		// Sort and store the given tables:
 		setTables(tables);
+
+		Object[] tmp;
+		int cnt;
+
+		// Store all allowed UDFs in a sorted array:
+		if (allowedUdfs != null){
+			// Remove all NULL and empty strings:
+			tmp = new FunctionDef[allowedUdfs.size()];
+			cnt = 0;
+			for(FunctionDef udf : allowedUdfs){
+				if (udf != null && udf.name.trim().length() > 0)
+					tmp[cnt++] = udf;
+			}
+			// make a copy of the array:
+			this.allowedUdfs = Arrays.copyOf(tmp, cnt, FunctionDef[].class);
+			tmp = null;
+			// sort the values:
+			Arrays.sort(this.allowedUdfs);
+		}
+	}
+
+	/**
+	 * <p>Builds a {@link DBChecker} with the given list of known tables and with a restricted list of user defined functions.</p>
+	 * 
+	 * <p>Verifications done by this object after creation:</p>
+	 * <ul>
+	 * 	<li>Existence of tables and columns:            <b>OK</b></li>
+	 * 	<li>Existence of User Defined Functions (UDFs): <b>NO <i>(any "unknown" function is allowed)</i></b></li>
+	 * 	<li>Support of geometrical functions:           <b>OK</b></li>
+	 * 	<li>Support of coordinate systems:              <b>OK</b></li>
+	 * </ul>
+	 * 
+	 * @param tables			List of all available tables.
+	 * @param allowedGeoFcts	List of all allowed geometrical functions (i.e. CONTAINS, POINT, UNION, CIRCLE, COORD1).
+	 *                      	If NULL, no verification will be done (and so, all geometries are allowed).
+	 *                      	If empty list, no geometry function is allowed.
+	 *                      	<i>Note: match with items of this list are done case insensitively.</i>
+	 * @param allowedCoordSys	List of all allowed coordinate system patterns. The syntax of a such pattern is the following:
+	 *                       	"{frame} {refpos} {flavor}" ; on the contrary to a coordinate system expression, here no part is optional.
+	 *                       	Each part of this pattern can be one the possible values (case insensitive), a list of possible values
+	 *                       	expressed with the syntax "({value1}|{value2}|...)", or a '*' for any valid value.
+	 *                       	For instance: "ICRS (GEOCENTER|heliocenter) *".
+	 *                       	If the given list is NULL, no verification will be done (and so, all coordinate systems are allowed).
+	 *                       	If it is empty, no coordinate system is allowed (except the default values - generally expressed by an empty string: '').
+	 * 
+	 * @since 1.3
+	 */
+	public DBChecker(final Collection<? extends DBTable> tables, final Collection<String> allowedGeoFcts, final Collection<String> allowedCoordSys) throws ParseException{
+		this(tables, null, allowedGeoFcts, allowedCoordSys);
+	}
+
+	/**
+	 * <p>Builds a {@link DBChecker}.</p>
+	 * 
+	 * <p>Verifications done by this object after creation:</p>
+	 * <ul>
+	 * 	<li>Existence of tables and columns:            <b>OK</b></li>
+	 * 	<li>Existence of User Defined Functions (UDFs): <b>OK</b></li>
+	 * 	<li>Support of geometrical functions:           <b>OK</b></li>
+	 * 	<li>Support of coordinate systems:              <b>OK</b></li>
+	 * </ul>
+	 * 
+	 * @param tables			List of all available tables.
+	 * @param allowedUdfs		List of all allowed user defined functions.
+	 *                   		If NULL, no verification will be done (and so, all UDFs are allowed).
+	 *                   		If empty list, no "unknown" (or UDF) is allowed.
+	 *                   		<i>Note: match with items of this list are done case insensitively.</i>
+	 * @param allowedGeoFcts	List of all allowed geometrical functions (i.e. CONTAINS, POINT, UNION, CIRCLE, COORD1).
+	 *                      	If NULL, no verification will be done (and so, all geometries are allowed).
+	 *                      	If empty list, no geometry function is allowed.
+	 *                      	<i>Note: match with items of this list are done case insensitively.</i>
+	 * @param allowedCoordSys	List of all allowed coordinate system patterns. The syntax of a such pattern is the following:
+	 *                       	"{frame} {refpos} {flavor}" ; on the contrary to a coordinate system expression, here no part is optional.
+	 *                       	Each part of this pattern can be one the possible values (case insensitive), a list of possible values
+	 *                       	expressed with the syntax "({value1}|{value2}|...)", or a '*' for any valid value.
+	 *                       	For instance: "ICRS (GEOCENTER|heliocenter) *".
+	 *                       	If the given list is NULL, no verification will be done (and so, all coordinate systems are allowed).
+	 *                       	If it is empty, no coordinate system is allowed (except the default values - generally expressed by an empty string: '').
+	 * 
+	 * @since 1.3
+	 */
+	public DBChecker(final Collection<? extends DBTable> tables, final Collection<? extends FunctionDef> allowedUdfs, final Collection<String> allowedGeoFcts, final Collection<String> allowedCoordSys) throws ParseException{
+		// Set the list of available tables + Set the list of all known UDFs:
+		this(tables, allowedUdfs);
+
+		// Set the list of allowed geometrical functions:
+		allowedGeo = specialSort(allowedGeoFcts);
+
+		// Set the list of allowed coordinate systems:
+		this.allowedCoordSys = specialSort(allowedCoordSys);
+		coordSysRegExp = STCS.buildCoordSysRegExp(this.allowedCoordSys);
+	}
+
+	/**
+	 * Transform the given collection of string elements in a sorted array.
+	 * Only non-NULL and non-empty strings are kept.
+	 * 
+	 * @param items	Items to copy and sort.
+	 * 
+	 * @return	A sorted array containing all - except NULL and empty strings - items of the given collection.
+	 * 
+	 * @since 1.3
+	 */
+	protected final static String[] specialSort(final Collection<String> items){
+		// Nothing to do if the array is NULL:
+		if (items == null)
+			return null;
+
+		// Keep only valid items (not NULL and not empty string):
+		String[] tmp = new String[items.size()];
+		int cnt = 0;
+		for(String item : items){
+			if (item != null && item.trim().length() > 0)
+				tmp[cnt++] = item;
+		}
+
+		// Make an adjusted array copy:
+		String[] copy = Arrays.copyOf(tmp, cnt);
+
+		// Sort the values:
+		Arrays.sort(copy);
+
+		return copy;
 	}
 
 	/* ****** */
@@ -98,7 +331,7 @@ public class DBChecker implements QueryChecker {
 	 * 
 	 * <p><i><u>Note:</u>
 	 * 	Only if the given collection is NOT an instance of {@link SearchTableList},
-	 * 	the collection will be copied inside a new {@link SearchTableList}.
+	 * 	the collection will be copied inside a new {@link SearchTableList}, otherwise it is used as provided.
 	 * </i></p>
 	 * 
 	 * @param tables	List of {@link DBTable}s.
@@ -130,63 +363,136 @@ public class DBChecker implements QueryChecker {
 	 * @see #check(ADQLQuery, Stack)
 	 */
 	@Override
-	public void check(final ADQLQuery query) throws ParseException{
+	public final void check(final ADQLQuery query) throws ParseException{
 		check(query, null);
 	}
 
 	/**
-	 * Followed algorithm:
-	 * <pre>
-	 * Map&lt;DBTable,ADQLTable&gt; mapTables;
-	 * 
-	 * For each ADQLTable t
-	 * 	if (t.isSubQuery())
-	 * 		dbTable = generateDBTable(t.getSubQuery, t.getAlias());
-	 * 	else
-	 * 		dbTable = resolveTable(t);
-	 * 	t.setDBLink(dbTable);
-	 * 	dbTables.put(t, dbTable);
-	 * End
-	 * 
-	 * For each SelectAllColumns c
-	 * 	table = c.getAdqlTable();
-	 * 	if (table != null){
-	 * 		dbTable = resolveTable(table);
-	 * 		if (dbTable == null)
-	 * 			dbTable = query.getFrom().getTablesByAlias(table.getTableName(), table.isCaseSensitive(IdentifierField.TABLE));
-	 *		if (dbTable == null)
-	 *			throw new UnresolvedTableException(table);
-	 * 		table.setDBLink(dbTable);
-	 * 	}
-	 * End
-	 * 
-	 * SearchColumnList list = query.getFrom().getDBColumns();
-	 * 
-	 * For each ADQLColumn c
-	 * 	dbColumn = resolveColumn(c, list);
-	 * 	c.setDBLink(dbColumn);
-	 * 	c.setAdqlTable(mapTables.get(dbColumn.getTable()));
-	 * End
-	 * 
-	 * For each ColumnReference colRef
-	 *	checkColumnReference(colRef, query.getSelect(), list);
-	 * End
-	 * </pre>
+	 * <p>Process several (semantic) verifications in the given ADQL query.</p>
+	 * 
+	 * <p>Main verifications done in this function:</p>
+	 * <ol>
+	 * 	<li>Existence of DB items (tables and columns)</li>
+	 * 	<li>Semantic verification of sub-queries</li>
+	 * 	<li>Support of every encountered User Defined Functions (UDFs - functions unknown by the syntactic parser)</li>
+	 * 	<li>Support of every encountered geometries (functions, coordinate systems and STC-S expressions)</li>
+	 * 	<li>Consistency of types still unknown (because the syntactic parser could not yet resolve them)</li>
+	 * </ol>
 	 * 
 	 * @param query			The query to check.
-	 * @param fathersList	List of all columns available in the father query.
+	 * @param fathersList	List of all columns available in the father queries and that should be accessed in sub-queries.
+	 *                   	Each item of this stack is a list of columns available in each father-level query.
+	 *                   	<i>Note: this parameter is NULL if this function is called with the root/father query as parameter.</i>
 	 * 
-	 * @throws UnresolvedIdentifiersException	An {@link UnresolvedIdentifiersException} if some tables or columns can not be resolved.
+	 * @throws UnresolvedIdentifiersException	An {@link UnresolvedIdentifiersException} if one or several of the above listed tests have detected
+	 *                                       	some semantic errors (i.e. unresolved table, columns, function).
 	 * 
 	 * @since 1.2
 	 * 
-	 * @see #resolveTable(ADQLTable)
-	 * @see #generateDBTable(ADQLQuery, String)
-	 * @see #resolveColumn(ADQLColumn, SearchColumnList, Stack)
-	 * @see #checkColumnReference(ColumnReference, ClauseSelect, SearchColumnList)
+	 * @see #checkDBItems(ADQLQuery, Stack, UnresolvedIdentifiersException)
+	 * @see #checkSubQueries(ADQLQuery, Stack, SearchColumnList, UnresolvedIdentifiersException)
+	 * @see #checkUDFs(ADQLQuery, UnresolvedIdentifiersException)
+	 * @see #checkGeometries(ADQLQuery, UnresolvedIdentifiersException)
+	 * @see #checkTypes(ADQLQuery, UnresolvedIdentifiersException)
 	 */
-	protected void check(final ADQLQuery query, Stack<SearchColumnList> fathersList) throws UnresolvedIdentifiersException{
+	protected void check(final ADQLQuery query, final Stack<SearchColumnList> fathersList) throws UnresolvedIdentifiersException{
 		UnresolvedIdentifiersException errors = new UnresolvedIdentifiersException();
+
+		// A. Check DB items (tables and columns):
+		SearchColumnList availableColumns = checkDBItems(query, fathersList, errors);
+
+		// B. Check UDFs:
+		if (allowedUdfs != null)
+			checkUDFs(query, errors);
+
+		// C. Check geometries:
+		checkGeometries(query, errors);
+
+		// D. Check types:
+		checkTypes(query, errors);
+
+		// E. Check sub-queries:
+		checkSubQueries(query, fathersList, availableColumns, errors);
+
+		// Throw all errors, if any:
+		if (errors.getNbErrors() > 0)
+			throw errors;
+	}
+
+	/* ************************************************ */
+	/* CHECKING METHODS FOR DB ITEMS (TABLES & COLUMNS) */
+	/* ************************************************ */
+
+	/**
+	 * <p>Check DB items (tables and columns) used in the given ADQL query.</p>
+	 * 
+	 * <p>Operations done in this function:</p>
+	 * <ol>
+	 * 	<li>Resolve all found tables</li>
+	 * 	<li>Get the whole list of all available columns <i>Note: this list is returned by this function.</i></li>
+	 * 	<li>Resolve all found columns</li>
+	 * </ol>
+	 * 
+	 * @param query			Query in which the existence of DB items must be checked.
+	 * @param fathersList	List of all columns available in the father queries and that should be accessed in sub-queries.
+	 *                   	Each item of this stack is a list of columns available in each father-level query.
+	 *                   	<i>Note: this parameter is NULL if this function is called with the root/father query as parameter.</i>
+	 * @param errors		List of errors to complete in this function each time an unknown table or column is encountered.
+	 * 
+	 * @return	List of all columns available in the given query.
+	 * 
+	 * @see #resolveTables(ADQLQuery, Stack, UnresolvedIdentifiersException)
+	 * @see FromContent#getDBColumns()
+	 * @see #resolveColumns(ADQLQuery, Stack, Map, SearchColumnList, UnresolvedIdentifiersException)
+	 * 
+	 * @since 1.3
+	 */
+	protected SearchColumnList checkDBItems(final ADQLQuery query, final Stack<SearchColumnList> fathersList, final UnresolvedIdentifiersException errors){
+		// a. Resolve all tables:
+		Map<DBTable,ADQLTable> mapTables = resolveTables(query, fathersList, errors);
+
+		// b. Get the list of all columns made available in the clause FROM:
+		SearchColumnList availableColumns;
+		try{
+			availableColumns = query.getFrom().getDBColumns();
+		}catch(ParseException pe){
+			errors.addException(pe);
+			availableColumns = new SearchColumnList();
+		}
+
+		// c. Resolve all columns:
+		resolveColumns(query, fathersList, mapTables, availableColumns, errors);
+
+		return availableColumns;
+	}
+
+	/**
+	 * <p>Search all table references inside the given query, resolve them against the available tables, and if there is only one match,
+	 * attach the matching metadata to them.</p>
+	 * 
+	 * <b>Management of sub-query tables</b>
+	 * <p>
+	 * 	If a table is not a DB table reference but a sub-query, this latter is first checked (using {@link #check(ADQLQuery, Stack)} ;
+	 * 	but the father list must not contain tables of the given query, because on the same level) and then corresponding table metadata
+	 * 	are generated (using {@link #generateDBTable(ADQLQuery, String)}) and attached to it.
+	 * </p>
+	 * 
+	 * <b>Management of "{table}.*" in the SELECT clause</b>
+	 * <p>
+	 * 	For each of this SELECT item, this function tries to resolve the table name. If only one match is found, the corresponding ADQL table object
+	 * 	is got from the list of resolved tables and attached to this SELECT item (thus, the joker item will also have the good metadata,
+	 * 	particularly if the referenced table is a sub-query).
+	 * </p>
+	 * 
+	 * @param query			Query in which the existence of tables must be checked.
+	 * @param fathersList	List of all columns available in the father queries and that should be accessed in sub-queries.
+	 *                      Each item of this stack is a list of columns available in each father-level query.
+	 *                   	<i>Note: this parameter is NULL if this function is called with the root/father query as parameter.</i>
+	 * @param errors		List of errors to complete in this function each time an unknown table or column is encountered.
+	 * 
+	 * @return	An associative map of all the resolved tables.
+	 */
+	protected Map<DBTable,ADQLTable> resolveTables(final ADQLQuery query, final Stack<SearchColumnList> fathersList, final UnresolvedIdentifiersException errors){
 		HashMap<DBTable,ADQLTable> mapTables = new HashMap<DBTable,ADQLTable>();
 		ISearchHandler sHandler;
 
@@ -200,7 +506,7 @@ public class DBChecker implements QueryChecker {
 				// resolve the table:
 				DBTable dbTable = null;
 				if (table.isSubQuery()){
-					// check the subquery tables:
+					// check the sub-query tables:
 					check(table.getSubQuery(), fathersList);
 					// generate its DBTable:
 					dbTable = generateDBTable(table.getSubQuery(), table.getAlias());
@@ -220,9 +526,9 @@ public class DBChecker implements QueryChecker {
 
 		// Attach table information on wildcards with the syntax "{tableName}.*" of the SELECT clause:
 		/* Note: no need to check the table name among the father tables, because there is
-		 *       no interest to select a father column in a subquery
-		 *       (which can return only one column ; besides, no aggregate is not allowed
-		 *       in subqueries).*/
+		 *       no interest to select a father column in a sub-query
+		 *       (which can return only one column ; besides, no aggregate is allowed
+		 *       in sub-queries).*/
 		sHandler = new SearchWildCardHandler();
 		sHandler.search(query.getSelect());
 		for(ADQLObject result : sHandler){
@@ -230,31 +536,77 @@ public class DBChecker implements QueryChecker {
 				SelectAllColumns wildcard = (SelectAllColumns)result;
 				ADQLTable table = wildcard.getAdqlTable();
 				DBTable dbTable = null;
-				// First, try to resolve the table by table alias:
+
+				// first, try to resolve the table by table alias:
 				if (table.getTableName() != null && table.getSchemaName() == null){
 					ArrayList<ADQLTable> tables = query.getFrom().getTablesByAlias(table.getTableName(), table.isCaseSensitive(IdentifierField.TABLE));
 					if (tables.size() == 1)
 						dbTable = tables.get(0).getDBLink();
 				}
-				// Then try to resolve the table reference by table name:
+
+				// then try to resolve the table reference by table name:
 				if (dbTable == null)
 					dbTable = resolveTable(table);
 
-				//			table.setDBLink(dbTable);
+				// set the corresponding tables among the list of resolved tables:
 				wildcard.setAdqlTable(mapTables.get(dbTable));
 			}catch(ParseException pe){
 				errors.addException(pe);
 			}
 		}
 
-		// Get the list of all columns made available in the clause FROM:
-		SearchColumnList list;
-		try{
-			list = query.getFrom().getDBColumns();
-		}catch(ParseException pe){
-			errors.addException(pe);
-			list = new SearchColumnList();
-		}
+		return mapTables;
+	}
+
+	/**
+	 * Resolve the given table, that's to say search for the corresponding {@link DBTable}.
+	 * 
+	 * @param table	The table to resolve.
+	 * 
+	 * @return		The corresponding {@link DBTable} if found, <i>null</i> otherwise.
+	 * 
+	 * @throws ParseException	An {@link UnresolvedTableException} if the given table can't be resolved.
+	 */
+	protected DBTable resolveTable(final ADQLTable table) throws ParseException{
+		ArrayList<DBTable> tables = lstTables.search(table);
+
+		// good if only one table has been found:
+		if (tables.size() == 1)
+			return tables.get(0);
+		// but if more than one: ambiguous table name !
+		else if (tables.size() > 1)
+			throw new UnresolvedTableException(table, tables.get(0).getADQLSchemaName() + "." + tables.get(0).getADQLName(), tables.get(1).getADQLSchemaName() + "." + tables.get(1).getADQLName());
+		// otherwise (no match): unknown table !
+		else
+			throw new UnresolvedTableException(table);
+	}
+
+	/**
+	 * <p>Search all column references inside the given query, resolve them thanks to the given tables' metadata,
+	 * and if there is only one match, attach the matching metadata to them.</p>
+	 * 
+	 * <b>Management of selected columns' references</b>
+	 * <p>
+	 * 	A column reference is not only a direct reference to a table column using a column name.
+	 * 	It can also be a reference to an item of the SELECT clause (which will then call a "selected column").
+	 * 	That kind of reference can be either an index (an unsigned integer starting from 1 to N, where N is the
+	 * 	number selected columns), or the name/alias of the column.
+	 * </p>
+	 * <p>
+	 * 	These references are also checked, in a second step, in this function. Thus, column metadata are
+	 * 	also attached to them, as common columns.
+	 * </p>
+	 * 
+	 * @param query			Query in which the existence of tables must be checked.
+	 * @param fathersList	List of all columns available in the father queries and that should be accessed in sub-queries.
+	 *                      Each item of this stack is a list of columns available in each father-level query.
+	 *                   	<i>Note: this parameter is NULL if this function is called with the root/father query as parameter.</i>
+	 * @param mapTables		List of all resolved tables.
+	 * @param list			List of column metadata to complete in this function each time a column reference is resolved.
+	 * @param errors		List of errors to complete in this function each time an unknown table or column is encountered.
+	 */
+	protected void resolveColumns(final ADQLQuery query, final Stack<SearchColumnList> fathersList, final Map<DBTable,ADQLTable> mapTables, final SearchColumnList list, final UnresolvedIdentifiersException errors){
+		ISearchHandler sHandler;
 
 		// Check the existence of all columns:
 		sHandler = new SearchColumnHandler();
@@ -272,7 +624,7 @@ public class DBChecker implements QueryChecker {
 			}
 		}
 
-		// Check the correctness of all column references:
+		// Check the correctness of all column references (= references to selected columns):
 		/* Note: no need to provide the father tables when resolving column references,
 		 *       because no father column can be used in ORDER BY and/or GROUP BY. */
 		sHandler = new SearchColReferenceHandler();
@@ -291,70 +643,22 @@ public class DBChecker implements QueryChecker {
 				errors.addException(pe);
 			}
 		}
-
-		// Check subqueries outside the clause FROM:
-		sHandler = new SearchSubQueryHandler();
-		sHandler.search(query);
-		if (sHandler.getNbMatch() > 0){
-
-			// Push the list of columns in the father columns stack:
-			if (fathersList == null)
-				fathersList = new Stack<SearchColumnList>();
-			fathersList.push(list);
-
-			// Check each found subquery (except the first one because it is the current query):
-			for(ADQLObject result : sHandler){
-				try{
-					check((ADQLQuery)result, fathersList);
-				}catch(UnresolvedIdentifiersException uie){
-					Iterator<ParseException> itPe = uie.getErrors();
-					while(itPe.hasNext())
-						errors.addException(itPe.next());
-				}
-			}
-
-			// Pop the list of columns from the father columns stack:
-			fathersList.pop();
-
-		}
-
-		// Throw all errors if any:
-		if (errors.getNbErrors() > 0)
-			throw errors;
 	}
 
 	/**
-	 * Resolves the given table, that's to say searches for the corresponding {@link DBTable}.
-	 * 
-	 * @param table	The table to resolve.
+	 * <p>Resolve the given column, that's to say search for the corresponding {@link DBColumn}.</p>
 	 * 
-	 * @return		The corresponding {@link DBTable} if found, <i>null</i> otherwise.
-	 * 
-	 * @throws ParseException	An {@link UnresolvedTableException} if the given table can't be resolved.
-	 */
-	protected DBTable resolveTable(final ADQLTable table) throws ParseException{
-		ArrayList<DBTable> tables = lstTables.search(table);
-
-		// good if only one table has been found:
-		if (tables.size() == 1)
-			return tables.get(0);
-		// but if more than one: ambiguous table name !
-		else if (tables.size() > 1)
-			throw new UnresolvedTableException(table, tables.get(0).getADQLSchemaName() + "." + tables.get(0).getADQLName(), tables.get(1).getADQLSchemaName() + "." + tables.get(1).getADQLName());
-		// otherwise (no match): unknown table !
-		else
-			throw new UnresolvedTableException(table);
-	}
-
-	/**
-	 * <p>Resolves the given column, that's to say searches for the corresponding {@link DBColumn}.</p>
-	 * <p>The third parameter is used only if this function is called inside a subquery. In this case,
-	 * column is tried to be resolved with the first list (dbColumns). If no match is found,
-	 * the resolution is tried with the father columns list (fatherColumns).</p>
+	 * <p>
+	 * 	The third parameter is used only if this function is called inside a sub-query. In this case,
+	 * 	the column is tried to be resolved with the first list (dbColumns). If no match is found,
+	 * 	the resolution is tried with the father columns list (fathersList).
+	 * </p>
 	 * 
 	 * @param column		The column to resolve.
 	 * @param dbColumns		List of all available {@link DBColumn}s.
-	 * @param fathersList	List of all columns available in the father query ; a list for each father-level.
+	 * @param fathersList	List of all columns available in the father queries and that should be accessed in sub-queries.
+	 *                      Each item of this stack is a list of columns available in each father-level query.
+	 *                   	<i>Note: this parameter is NULL if this function is called with the root/father query as parameter.</i>
 	 * 
 	 * @return 				The corresponding {@link DBColumn} if found. Otherwise an exception is thrown.
 	 * 
@@ -386,14 +690,14 @@ public class DBChecker implements QueryChecker {
 	}
 
 	/**
-	 * Checks whether the given column reference corresponds to a selected item (column or an expression with an alias)
+	 * Check whether the given column reference corresponds to a selected item (column or an expression with an alias)
 	 * or to an existing column.
 	 * 
-	 * @param colRef			The column reference which must be checked.
-	 * @param select			The SELECT clause of the ADQL query.
-	 * @param dbColumns			The list of all available {@link DBColumn}s.
+	 * @param colRef		The column reference which must be checked.
+	 * @param select		The SELECT clause of the ADQL query.
+	 * @param dbColumns		The list of all available columns.
 	 * 
-	 * @return 					The corresponding {@link DBColumn} if this reference is actually the name of a column, <i>null</i> otherwise.
+	 * @return 		The corresponding {@link DBColumn} if this reference is actually the name of a column, <i>null</i> otherwise.
 	 * 
 	 * @throws ParseException	An {@link UnresolvedColumnException} if the given column can't be resolved
 	 * 							or an {@link UnresolvedTableException} if its table reference can't be resolved.
@@ -431,19 +735,16 @@ public class DBChecker implements QueryChecker {
 		}
 	}
 
-	/* ************************************* */
-	/* DBTABLE & DBCOLUMN GENERATION METHODS */
-	/* ************************************* */
 	/**
-	 * Generates a {@link DBTable} corresponding to the given sub-query with the given table name.
-	 * This {@link DBTable} which contains all {@link DBColumn} returned by {@link ADQLQuery#getResultingColumns()}.
+	 * Generate a {@link DBTable} corresponding to the given sub-query with the given table name.
+	 * This {@link DBTable} will contain all {@link DBColumn} returned by {@link ADQLQuery#getResultingColumns()}.
 	 * 
 	 * @param subQuery	Sub-query in which the specified table must be searched.
 	 * @param tableName	Name of the table to search.
 	 * 
-	 * @return			The corresponding {@link DBTable} if the table has been found in the given sub-query, <i>null</i> otherwise.
+	 * @return	The corresponding {@link DBTable} if the table has been found in the given sub-query, <i>null</i> otherwise.
 	 * 
-	 * @throws ParseException	Can be used to explain why the table has not been found.
+	 * @throws ParseException	Can be used to explain why the table has not been found. <i>Note: not used by default.</i>
 	 */
 	public static DBTable generateDBTable(final ADQLQuery subQuery, final String tableName) throws ParseException{
 		DefaultDBTable dbTable = new DefaultDBTable(tableName);
@@ -455,6 +756,469 @@ public class DBChecker implements QueryChecker {
 		return dbTable;
 	}
 
+	/* ************************* */
+	/* CHECKING METHODS FOR UDFs */
+	/* ************************* */
+
+	/**
+	 * <p>Search all UDFs (User Defined Functions) inside the given query, and then
+	 * check their signature against the list of allowed UDFs.</p>
+	 * 
+	 * <p><i>Note:
+	 * 	When more than one allowed function match, the function is considered as correct
+	 * 	and no error is added.
+	 * 	However, in case of multiple matches, the return type of matching functions could
+	 * 	be different and in this case, there would be an error while checking later
+	 * 	the types. In such case, throwing an error could make sense, but the user would
+	 * 	then need to cast some parameters to help the parser identifying the right function.
+	 * 	But the type-casting ability is not yet possible in ADQL.
+	 * </i></p>
+	 * 
+	 * @param query		Query in which UDFs must be checked.
+	 * @param errors	List of errors to complete in this function each time a UDF does not match to any of the allowed UDFs.
+	 * 
+	 * @since 1.3
+	 */
+	protected void checkUDFs(final ADQLQuery query, final UnresolvedIdentifiersException errors){
+		// Search all UDFs:
+		ISearchHandler sHandler = new SearchUDFHandler();
+		sHandler.search(query);
+
+		// If no UDF are allowed, throw immediately an error:
+		if (allowedUdfs.length == 0){
+			for(ADQLObject result : sHandler)
+				errors.addException(new UnresolvedFunction((UserDefinedFunction)result));
+		}
+		// Otherwise, try to resolve all of them:
+		else{
+			ArrayList<UserDefinedFunction> toResolveLater = new ArrayList<UserDefinedFunction>();
+			UserDefinedFunction udf;
+			int match;
+			BinarySearch<FunctionDef,UserDefinedFunction> binSearch = new BinarySearch<FunctionDef,UserDefinedFunction>(){
+				@Override
+				protected int compare(UserDefinedFunction searchItem, FunctionDef arrayItem){
+					return arrayItem.compareTo(searchItem) * -1;
+				}
+			};
+
+			// Try to resolve all the found UDFs:
+			/* Note: at this stage, it can happen that UDFs can not be yet resolved because the building of
+			 *       their signature depends of other UDFs. That's why, these special cases should be kept
+			 *       for a later resolution try. */
+			for(ADQLObject result : sHandler){
+				udf = (UserDefinedFunction)result;
+				// search for a match:
+				match = binSearch.search(udf, allowedUdfs);
+				// if no match...
+				if (match < 0){
+					// ...if the type of all parameters is resolved, add an error (no match is possible):
+					if (isAllParamTypesResolved(udf))
+						errors.addException(new UnresolvedFunction(udf));	// TODO Add the ADQLOperand position!
+					// ...otherwise, try to resolved it later (when other UDFs will be mostly resolved):
+					else
+						toResolveLater.add(udf);
+				}
+				// if there is a match, metadata may be attached (particularly if the function is built automatically by the syntactic parser):
+				else if (udf instanceof DefaultUDF)
+					((DefaultUDF)udf).setDefinition(allowedUdfs[match]);
+			}
+
+			// Try to resolve UDFs whose some parameter types are depending of other UDFs:
+			for(int i = 0; i < toResolveLater.size(); i++){
+				udf = toResolveLater.get(i);
+				// search for a match:
+				match = binSearch.search(udf, allowedUdfs);
+				// if no match, add an error:
+				if (match < 0)
+					errors.addException(new UnresolvedFunction(udf));	// TODO Add the ADQLOperand position!
+				// otherwise, metadata may be attached (particularly if the function is built automatically by the syntactic parser):
+				else if (udf instanceof DefaultUDF)
+					((DefaultUDF)udf).setDefinition(allowedUdfs[match]);
+			}
+		}
+	}
+
+	/**
+	 * <p>Tell whether the type of all parameters of the given ADQL function
+	 * is resolved.</p>
+	 * 
+	 * <p>A parameter type may not be resolved for 2 main reasons:</p>
+	 * <ul>
+	 * 	<li>the parameter is a <b>column</b>, but this column has not been successfully resolved. Thus its type is still unknown.</li>
+	 * 	<li>the parameter is a <b>UDF</b>, but this UDF has not been already resolved. Thus, as for the column, its return type is still unknown.
+	 * 		But it could be known later if the UDF is resolved later ; a second try should be done afterwards.</li>
+	 * </ul>
+	 * 
+	 * @param fct	ADQL function whose the parameters' type should be checked.
+	 * 
+	 * @return	<i>true</i> if the type of all parameters is known, <i>false</i> otherwise.
+	 * 
+	 * @since 1.3
+	 */
+	protected final boolean isAllParamTypesResolved(final ADQLFunction fct){
+		for(ADQLOperand op : fct.getParameters()){
+			if (op.isNumeric() == op.isString())
+				return false;
+		}
+		return true;
+	}
+
+	/* ************************************************************************************************* */
+	/* METHODS CHECKING THE GEOMETRIES (geometrical functions, coordinate systems and STC-S expressions) */
+	/* ************************************************************************************************* */
+
+	/**
+	 * <p>Check all geometries.</p>
+	 * 
+	 * <p>Operations done in this function:</p>
+	 * <ol>
+	 * 	<li>Check that all geometrical functions are supported</li>
+	 * 	<li>Check that all explicit (string constant) coordinate system definitions are supported</i></li>
+	 * 	<li>Check all STC-S expressions (only in {@link RegionFunction} for the moment) and
+	 * 	    Apply the 2 previous checks on them</li>
+	 * </ol>
+	 * 
+	 * @param query		Query in which geometries must be checked.
+	 * @param errors	List of errors to complete in this function each time a geometry item is not supported.
+	 * 
+	 * @see #resolveGeometryFunctions(ADQLQuery, BinarySearch, UnresolvedIdentifiersException)
+	 * @see #resolveCoordinateSystems(ADQLQuery, UnresolvedIdentifiersException)
+	 * @see #resolveSTCSExpressions(ADQLQuery, BinarySearch, UnresolvedIdentifiersException)
+	 * 
+	 * @since 1.3
+	 */
+	protected void checkGeometries(final ADQLQuery query, final UnresolvedIdentifiersException errors){
+		BinarySearch<String,String> binSearch = new BinarySearch<String,String>(){
+			@Override
+			protected int compare(String searchItem, String arrayItem){
+				return searchItem.compareToIgnoreCase(arrayItem);
+			}
+		};
+
+		// a. Ensure that all used geometry functions are allowed:
+		if (allowedGeo != null)
+			resolveGeometryFunctions(query, binSearch, errors);
+
+		// b. Check whether the coordinate systems are allowed:
+		if (allowedCoordSys != null)
+			resolveCoordinateSystems(query, errors);
+
+		// c. Check all STC-S expressions (in RegionFunctions only) + the used coordinate systems (if StringConstant only):
+		if (allowedGeo == null || (allowedGeo.length > 0 && binSearch.search("REGION", allowedGeo) >= 0))
+			resolveSTCSExpressions(query, binSearch, errors);
+	}
+
+	/**
+	 * Search for all geometrical functions and check whether they are allowed.
+	 * 
+	 * @param query		Query in which geometrical functions must be checked.
+	 * @param errors	List of errors to complete in this function each time a geometrical function is not supported.
+	 * 
+	 * @see #checkGeometryFunction(String, ADQLFunction, BinarySearch, UnresolvedIdentifiersException)
+	 * 
+	 * @since 1.3
+	 */
+	protected void resolveGeometryFunctions(final ADQLQuery query, final BinarySearch<String,String> binSearch, final UnresolvedIdentifiersException errors){
+		ISearchHandler sHandler = new SearchGeometryHandler();
+		sHandler.search(query);
+
+		String fctName;
+		for(ADQLObject result : sHandler){
+			fctName = result.getName();
+			checkGeometryFunction(fctName, (ADQLFunction)result, binSearch, errors);
+		}
+	}
+
+	/**
+	 * <p>Check whether the specified geometrical function is allowed by this implementation.</p>
+	 * 
+	 * <p><i>Note:
+	 * 	If the list of allowed geometrical functions is empty, this function will always add an errors to the given list.
+	 * 	Indeed, it means that no geometrical function is allowed and so that the specified function is automatically not supported.
+	 * </i></p>
+	 * 
+	 * @param fctName		Name of the geometrical function to test.
+	 * @param fct			The function instance being or containing the geometrical function to check. <i>Note: this function can be the function to test or a function embedding the function under test (i.e. RegionFunction).
+	 * @param binSearch		The object to use in order to search a function name inside the list of allowed functions.
+	 *                 		It is able to perform a binary search inside a sorted array of String objects. The interest of
+	 *                 		this object is its compare function which must be overridden and tells how to compare the item
+	 *                 		to search and the items of the array (basically, a non-case-sensitive comparison between 2 strings).
+	 * @param errors		List of errors to complete in this function each time a geometrical function is not supported.
+	 * 
+	 * @since 1.3
+	 */
+	protected void checkGeometryFunction(final String fctName, final ADQLFunction fct, final BinarySearch<String,String> binSearch, final UnresolvedIdentifiersException errors){
+		int match = -1;
+		if (allowedGeo.length != 0)
+			match = binSearch.search(fctName, allowedGeo);
+		if (match < 0)
+			errors.addException(new UnresolvedFunction("The geometrical function \"" + fctName + "\" is not available in this implementation!", fct));
+	}
+
+	/**
+	 * <p>Search all explicit coordinate system declarations, check their syntax and whether they are allowed by this implementation.</p>
+	 * 
+	 * <p><i>Note:
+	 * 	"explicit" means here that all {@link StringConstant} instances. Only coordinate systems expressed as string can
+	 * 	be parsed and so checked. So if a coordinate system is specified by a column, no check can be done at this stage...
+	 * 	it will be possible to perform such test only at the execution.
+	 * </i></p>
+	 * 
+	 * @param query		Query in which coordinate systems must be checked.
+	 * @param errors	List of errors to complete in this function each time a coordinate system has a wrong syntax or is not supported.
+	 * 
+	 * @see #checkCoordinateSystem(StringConstant, UnresolvedIdentifiersException)
+	 * 
+	 * @since 1.3
+	 */
+	protected void resolveCoordinateSystems(final ADQLQuery query, final UnresolvedIdentifiersException errors){
+		ISearchHandler sHandler = new SearchCoordSysHandler();
+		sHandler.search(query);
+		for(ADQLObject result : sHandler)
+			checkCoordinateSystem((StringConstant)result, errors);
+	}
+
+	/**
+	 * Parse and then check the coordinate system contained in the given {@link StringConstant} instance.
+	 * 
+	 * @param adqlCoordSys	The {@link StringConstant} object containing the coordinate system to check.
+	 * @param errors		List of errors to complete in this function each time a coordinate system has a wrong syntax or is not supported.
+	 * 
+	 * @see STCS#parseCoordSys(String)
+	 * @see #checkCoordinateSystem(CoordSys, ADQLOperand, UnresolvedIdentifiersException)
+	 * 
+	 * @since 1.3
+	 */
+	protected void checkCoordinateSystem(final StringConstant adqlCoordSys, final UnresolvedIdentifiersException errors){
+		String coordSysStr = adqlCoordSys.getValue();
+		try{
+			checkCoordinateSystem(STCS.parseCoordSys(coordSysStr), adqlCoordSys, errors);
+		}catch(ParseException pe){
+			errors.addException(new ParseException(pe.getMessage())); // TODO Missing object position!
+		}
+	}
+
+	/**
+	 * Check whether the given coordinate system is allowed by this implementation. 
+	 * 
+	 * @param coordSys	Coordinate system to test.
+	 * @param operand	The operand representing or containing the coordinate system under test.
+	 * @param errors	List of errors to complete in this function each time a coordinate system is not supported.
+	 * 
+	 * @since 1.3
+	 */
+	protected void checkCoordinateSystem(final CoordSys coordSys, final ADQLOperand operand, final UnresolvedIdentifiersException errors){
+		if (coordSysRegExp != null && coordSys != null && !coordSys.toFullSTCS().matches(coordSysRegExp))
+			errors.addException(new ParseException("Coordinate system \"" + ((operand instanceof StringConstant) ? ((StringConstant)operand).getValue() : coordSys.toString()) + "\" (= \"" + coordSys.toFullSTCS() + "\") not allowed in this implementation."));	// TODO Missing object position! + List of accepted coordinate systems
+	}
+
+	/**
+	 * <p>Search all STC-S expressions inside the given query, parse them (and so check their syntax) and then determine
+	 * whether the declared coordinate system and the expressed region are allowed in this implementation.</p>
+	 * 
+	 * <p><i>Note:
+	 * 	In the current ADQL language definition, STC-S expressions can be found only as only parameter of the REGION function.
+	 * </i></p>
+	 * 
+	 * @param query			Query in which STC-S expressions must be checked.
+	 * @param binSearch		The object to use in order to search a region name inside the list of allowed functions/regions.
+	 *                 		It is able to perform a binary search inside a sorted array of String objects. The interest of
+	 *                 		this object is its compare function which must be overridden and tells how to compare the item
+	 *                 		to search and the items of the array (basically, a non-case-sensitive comparison between 2 strings).
+	 * @param errors		List of errors to complete in this function each time the STC-S syntax is wrong or each time the declared coordinate system or region is not supported.
+	 * 
+	 * @see STCS#parseRegion(String)
+	 * @see #checkRegion(Region, RegionFunction, BinarySearch, UnresolvedIdentifiersException)
+	 * 
+	 * @since 1.3
+	 */
+	protected void resolveSTCSExpressions(final ADQLQuery query, final BinarySearch<String,String> binSearch, final UnresolvedIdentifiersException errors){
+		// Search REGION functions:
+		ISearchHandler sHandler = new SearchRegionHandler();
+		sHandler.search(query);
+
+		// Parse and check their STC-S expression:
+		String stcs;
+		Region region;
+		for(ADQLObject result : sHandler){
+			try{
+				// get the STC-S expression:
+				stcs = ((StringConstant)((RegionFunction)result).getParameter(0)).getValue();
+
+				// parse the STC-S expression (and so check the syntax):
+				region = STCS.parseRegion(stcs);
+
+				// check whether the regions (this one + the possible inner ones) and the coordinate systems are allowed:
+				checkRegion(region, (RegionFunction)result, binSearch, errors);
+			}catch(ParseException pe){
+				errors.addException(new ParseException(pe.getMessage())); // TODO Missing object position!
+			}
+		}
+	}
+
+	/**
+	 * <p>Check the given region.</p>
+	 * 
+	 * <p>The following points are checked in this function:</p>
+	 * <ul>
+	 * 	<li>whether the coordinate system is allowed</li>
+	 * 	<li>whether the type of region is allowed</li>
+	 * 	<li>whether the inner regions are correct (here this function is called recursively on each inner region).</li>
+	 * </ul>
+	 * 
+	 * @param r			The region to check.
+	 * @param fct		The REGION function containing the region to check.
+	 * @param errors	List of errors to complete in this function if the given region or its inner regions are not supported.
+	 * 
+	 * @see #checkCoordinateSystem(CoordSys, ADQLOperand, UnresolvedIdentifiersException)
+	 * @see #checkGeometryFunction(String, ADQLFunction, BinarySearch, UnresolvedIdentifiersException)
+	 * 
+	 * @since 1.3
+	 */
+	protected void checkRegion(final Region r, final RegionFunction fct, final BinarySearch<String,String> binSearch, final UnresolvedIdentifiersException errors){
+		if (r == null)
+			return;
+
+		// Check the coordinate system (if any):
+		if (r.coordSys != null)
+			checkCoordinateSystem(r.coordSys, fct, errors);
+
+		// Check that the region type is allowed:
+		if (allowedGeo != null){
+			if (allowedGeo.length == 0)
+				errors.addException(new UnresolvedFunction("The region type \"" + r.type + "\" is not available in this implementation!", fct));
+			else
+				checkGeometryFunction((r.type == RegionType.POSITION) ? "POINT" : r.type.toString(), fct, binSearch, errors);
+		}
+
+		// Check all the inner regions:
+		if (r.regions != null){
+			for(Region innerR : r.regions)
+				checkRegion(innerR, fct, binSearch, errors);
+		}
+	}
+
+	/* **************************************************** */
+	/* METHODS CHECKING TYPES UNKNOWN WHILE CHECKING SYNTAX */
+	/* **************************************************** */
+
+	/**
+	 * <p>Search all operands whose the type is not yet known and try to resolve it now
+	 * and to check whether it matches the type expected by the syntactic parser.</p>
+	 * 
+	 * <p>
+	 * 	Only two operands may have an unresolved type: columns and user defined functions.
+	 * 	Indeed, their type can be resolved only if the list of available columns and UDFs is known,
+	 * 	and if columns and UDFs used in the query are resolved successfully.
+	 * </p>
+	 * 
+	 * <p>
+	 * 	When an operand type is still unknown, they will own the three kinds of type and
+	 * 	so this function won't raise an error: it is thus automatically on the expected type.
+	 * 	This behavior is perfectly correct because if the type is not resolved
+	 * 	that means the item/operand has not been resolved in the previous steps and so that
+	 * 	an error about this item has already been raised.
+	 * </p>
+	 * 
+	 * <p><i><b>Important note:</b>
+	 * 	This function does not check the types exactly, but just roughly by considering only three categories:
+	 * 	string, numeric and geometry.
+	 * </i></p>
+	 * 
+	 * @param query		Query in which unknown types must be resolved and checked.
+	 * @param errors	List of errors to complete in this function each time a types does not match to the expected one.
+	 * 
+	 * @see UnknownType
+	 * 
+	 * @since 1.3
+	 */
+	protected void checkTypes(final ADQLQuery query, final UnresolvedIdentifiersException errors){
+		// Search all unknown types:
+		ISearchHandler sHandler = new SearchUnknownTypeHandler();
+		sHandler.search(query);
+
+		// Check whether their type matches the expected one:
+		UnknownType unknown;
+		for(ADQLObject result : sHandler){
+			unknown = (UnknownType)result;
+			switch(unknown.getExpectedType()){
+				case 'G':
+				case 'g':
+					if (!unknown.isGeometry())
+						errors.addException(new ParseException("Type mismatch! A geometry was expected instead of \"" + unknown.toADQL() + "\"."));	// TODO Add the ADQLOperand position!
+					break;
+				case 'N':
+				case 'n':
+					if (!unknown.isNumeric())
+						errors.addException(new ParseException("Type mismatch! A numeric value was expected instead of \"" + unknown.toADQL() + "\"."));	// TODO Add the ADQLOperand position!
+					break;
+				case 'S':
+				case 's':
+					if (!unknown.isString())
+						errors.addException(new ParseException("Type mismatch! A string value was expected instead of \"" + unknown.toADQL() + "\"."));	// TODO Add the ADQLOperand position!
+					break;
+			}
+		}
+	}
+
+	/* ******************************** */
+	/* METHODS CHECKING THE SUB-QUERIES */
+	/* ******************************** */
+
+	/**
+	 * <p>Search all sub-queries found in the given query but not in the clause FROM.
+	 * These sub-queries are then checked using {@link #check(ADQLQuery, Stack)}.</p>
+	 * 
+	 * <b>Fathers stack</b>
+	 * <p>
+	 * 	Each time a sub-query must be checked with {@link #check(ADQLQuery, Stack)},
+	 * 	the list of all columns available in each of its father queries must be provided.
+	 * 	This function is composing itself this stack by adding the given list of available
+	 * 	columns (= all columns resolved in the given query) at the end of the given stack.
+	 * 	If this stack is given empty, then a new stack is created.
+	 * </p>
+	 * <p>
+	 * 	This modification of the given stack is just the execution time of this function.
+	 * 	Before returning, this function removes the last item of the stack.
+	 * </p>
+	 * 
+	 * 
+	 * @param query				Query in which sub-queries must be checked.
+	 * @param fathersList		List of all columns available in the father queries and that should be accessed in sub-queries.
+	 *                      	Each item of this stack is a list of columns available in each father-level query.
+	 *                   		<i>Note: this parameter is NULL if this function is called with the root/father query as parameter.</i>
+	 * @param availableColumns	List of all columns resolved in the given query.
+	 * @param errors			List of errors to complete in this function each time a semantic error is encountered.
+	 * 
+	 * @since 1.3
+	 */
+	protected void checkSubQueries(final ADQLQuery query, Stack<SearchColumnList> fathersList, final SearchColumnList availableColumns, final UnresolvedIdentifiersException errors){
+		// Check sub-queries outside the clause FROM:
+		ISearchHandler sHandler = new SearchSubQueryHandler();
+		sHandler.search(query);
+		if (sHandler.getNbMatch() > 0){
+
+			// Push the list of columns into the father columns stack:
+			if (fathersList == null)
+				fathersList = new Stack<SearchColumnList>();
+			fathersList.push(availableColumns);
+
+			// Check each found sub-query:
+			for(ADQLObject result : sHandler){
+				try{
+					check((ADQLQuery)result, fathersList);
+				}catch(UnresolvedIdentifiersException uie){
+					Iterator<ParseException> itPe = uie.getErrors();
+					while(itPe.hasNext())
+						errors.addException(itPe.next());
+				}
+			}
+
+			// Pop the list of columns from the father columns stack:
+			fathersList.pop();
+		}
+	}
+
 	/* *************** */
 	/* SEARCH HANDLERS */
 	/* *************** */
@@ -462,7 +1226,7 @@ public class DBChecker implements QueryChecker {
 	 * Lets searching all tables.
 	 * 
 	 * @author Gr&eacute;gory Mantelet (CDS)
-	 * @version 07/2011
+	 * @version 1.0 (07/2011)
 	 */
 	private static class SearchTableHandler extends SimpleSearchHandler {
 		@Override
@@ -475,7 +1239,7 @@ public class DBChecker implements QueryChecker {
 	 * Lets searching all wildcards.
 	 * 
 	 * @author Gr&eacute;gory Mantelet (CDS)
-	 * @version 09/2011
+	 * @version 1.0 (09/2011)
 	 */
 	private static class SearchWildCardHandler extends SimpleSearchHandler {
 		@Override
@@ -488,7 +1252,7 @@ public class DBChecker implements QueryChecker {
 	 * Lets searching column references.
 	 * 
 	 * @author Gr&eacute;gory Mantelet (CDS)
-	 * @version 11/2011
+	 * @version 1.0 (11/2011)
 	 */
 	private static class SearchColReferenceHandler extends SimpleSearchHandler {
 		@Override
@@ -527,4 +1291,168 @@ public class DBChecker implements QueryChecker {
 		}
 	}
 
+	/**
+	 * Let searching user defined functions.
+	 * 
+	 * @author Gr&eacute;gory Mantelet (ARI)
+	 * @version 1.3 (10/2014)
+	 * @since 1.3
+	 */
+	private static class SearchUDFHandler extends SimpleSearchHandler {
+		@Override
+		protected boolean match(ADQLObject obj){
+			return (obj instanceof UserDefinedFunction);
+		}
+	}
+
+	/**
+	 * Let searching geometrical functions.
+	 * 
+	 * @author Gr&eacute;gory Mantelet (ARI)
+	 * @version 1.3 (10/2014)
+	 * @since 1.3
+	 */
+	private static class SearchGeometryHandler extends SimpleSearchHandler {
+		@Override
+		protected boolean match(ADQLObject obj){
+			return (obj instanceof GeometryFunction);
+		}
+	}
+
+	/**
+	 * <p>Let searching all ADQL objects whose the type was not known while checking the syntax of the ADQL query.
+	 * These objects are {@link ADQLColumn}s and {@link UserDefinedFunction}s.</p>
+	 * 
+	 * <p><i><b>Important note:</b>
+	 * 	Only {@link UnknownType} instances having an expected type equals to 'S' (or 's' ; for string) or 'N' (or 'n' ; for numeric)
+	 * 	are kept by this handler. Others are ignored.
+	 * </i></p>
+	 * 
+	 * @author Gr&eacute;gory Mantelet (ARI)
+	 * @version 1.3 (10/2014)
+	 * @since 1.3
+	 */
+	private static class SearchUnknownTypeHandler extends SimpleSearchHandler {
+		@Override
+		protected boolean match(ADQLObject obj){
+			if (obj instanceof UnknownType){
+				char expected = ((UnknownType)obj).getExpectedType();
+				return (expected == 'G' || expected == 'g' || expected == 'S' || expected == 's' || expected == 'N' || expected == 'n');
+			}else
+				return false;
+		}
+	}
+
+	/**
+	 * Let searching all explicit declaration of coordinate systems.
+	 * So, only {@link StringConstant} objects will be returned.
+	 * 
+	 * @author Gr&eacute;gory Mantelet (ARI)
+	 * @version 1.3 (10/2014)
+	 * @since 1.3
+	 */
+	private static class SearchCoordSysHandler extends SimpleSearchHandler {
+		@Override
+		protected boolean match(ADQLObject obj){
+			if (obj instanceof PointFunction || obj instanceof BoxFunction || obj instanceof CircleFunction || obj instanceof PolygonFunction)
+				return (((GeometryFunction)obj).getCoordinateSystem() instanceof StringConstant);
+			else
+				return false;
+		}
+
+		@Override
+		protected void addMatch(ADQLObject matchObj, ADQLIterator it){
+			results.add(((GeometryFunction)matchObj).getCoordinateSystem());
+		}
+
+	}
+
+	/**
+	 * Let searching all {@link RegionFunction}s.
+	 * 
+	 * @author Gr&eacute;gory Mantelet (ARI)
+	 * @version 1.3 (10/2014)
+	 * @since 1.3
+	 */
+	private static class SearchRegionHandler extends SimpleSearchHandler {
+		@Override
+		protected boolean match(ADQLObject obj){
+			if (obj instanceof RegionFunction)
+				return (((RegionFunction)obj).getParameter(0) instanceof StringConstant);
+			else
+				return false;
+		}
+
+	}
+
+	/**
+	 * <p>Implement the binary search algorithm over a sorted array.</p>
+	 * 
+	 * <p>
+	 * 	The only difference with the standard implementation of Java is
+	 * 	that this object lets perform research with a different type
+	 * 	of object than the types of array items.
+	 * </p>
+	 * 
+	 * <p>
+	 * 	For that reason, the "compare" function must always be implemented.
+	 * </p>
+	 * 
+	 * @author Gr&eacute;gory Mantelet (ARI)
+	 * @version 1.3 (10/2014)
+	 * 
+	 * @param <T>	Type of items stored in the array.
+	 * @param <S>	Type of the item to search.
+	 * 
+	 * @since 1.3
+	 */
+	protected static abstract class BinarySearch< T, S > {
+		private int s, e, m, comp;
+
+		/**
+		 * <p>Search the given item in the given array.</p>
+		 * 
+		 * <p>
+		 * 	In case the given object matches to several items of the array,
+		 * 	this function will return the smallest index, pointing thus to the first
+		 * 	of all matches.
+		 * </p>
+		 * 
+		 * @param searchItem	Object for which a corresponding array item must be searched.
+		 * @param array			Array in which the given object must be searched.
+		 * 
+		 * @return	The array index of the first item of all matches.
+		 */
+		public int search(final S searchItem, final T[] array){
+			s = 0;
+			e = array.length - 1;
+			while(s < e){
+				// middle of the sorted array:
+				m = s + ((e - s) / 2);
+				// compare the fct with the middle item of the array:
+				comp = compare(searchItem, array[m]);
+				// if the fct is after, trigger the inspection of the right part of the array: 
+				if (comp > 0)
+					s = m + 1;
+				// otherwise, the left part:
+				else
+					e = m;
+			}
+			if (s != e || compare(searchItem, array[s]) != 0)
+				return -1;
+			else
+				return s;
+		}
+
+		/**
+		 * Compare the search item and the array item.
+		 * 
+		 * @param searchItem	Item whose a corresponding value must be found in the array.
+		 * @param arrayItem		An item of the array.
+		 * 
+		 * @return	Negative value if searchItem is less than arrayItem, 0 if they are equals, or a positive value if searchItem is greater.
+		 */
+		protected abstract int compare(final S searchItem, final T arrayItem);
+	}
+
 }
diff --git a/src/adql/db/DBColumn.java b/src/adql/db/DBColumn.java
index a803717..c987e06 100644
--- a/src/adql/db/DBColumn.java
+++ b/src/adql/db/DBColumn.java
@@ -16,7 +16,8 @@ package adql.db;
  * 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 2011 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2011,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 /**
@@ -27,8 +28,8 @@ package adql.db;
  * 	and corresponds to a real column in the "database" with its DB name ({@link #getDBName()}).
  * </p>
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 08/2011
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 1.3 (10/2014)
  */
 public interface DBColumn {
 
@@ -46,6 +47,19 @@ public interface DBColumn {
 	 */
 	public String getDBName();
 
+	/**
+	 * <p>Get the type of this column (as closed as possible from the "database" type).</p>
+	 * 
+	 * <p><i>Note:
+	 * 	The returned type should be as closed as possible from a type listed by the IVOA in the TAP protocol description into the section UPLOAD.
+	 * </i></p>
+	 * 
+	 * @return	Its type.
+	 * 
+	 * @since 1.3
+	 */
+	public DBType getDatatype();
+
 	/**
 	 * Gets the table which contains this {@link DBColumn}.
 	 * 
diff --git a/src/adql/db/DBCommonColumn.java b/src/adql/db/DBCommonColumn.java
index cab30f6..8c5d3ba 100644
--- a/src/adql/db/DBCommonColumn.java
+++ b/src/adql/db/DBCommonColumn.java
@@ -16,12 +16,13 @@ package adql.db;
  * 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 - Astronomishes Rechen Institut (ARI)
+ * Copyright 2014 - Astronomisches Rechen Institut (ARI)
  */
 
 import java.util.ArrayList;
 import java.util.Iterator;
 
+import adql.db.exception.UnresolvedJoin;
 import adql.query.ADQLQuery;
 
 /**
@@ -33,7 +34,7 @@ import adql.query.ADQLQuery;
  * in case of several JOINs.
  * 
  * @author Gr&eacute;gory Mantelet (ARI) - gmantele@ari.uni-heidelberg.de
- * @version 1.2 (11/2013)
+ * @version 1.3 (10/2014)
  * @since 1.2
  */
 public class DBCommonColumn implements DBColumn {
@@ -54,8 +55,13 @@ public class DBCommonColumn implements DBColumn {
 	 * 
 	 * @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 UnresolvedJoin	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){
+	public DBCommonColumn(final DBColumn leftCol, final DBColumn rightCol) throws UnresolvedJoin{
+		// Test whether type of both columns are compatible:
+		if (leftCol.getDatatype() != null && rightCol.getDatatype() != null && !leftCol.getDatatype().isCompatible(rightCol.getDatatype()))
+			throw new UnresolvedJoin("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){
@@ -83,7 +89,6 @@ public class DBCommonColumn implements DBColumn {
 			// add the table to cover:
 			addCoveredTable(rightCol.getTable());
 		}
-
 	}
 
 	/**
@@ -112,6 +117,11 @@ public class DBCommonColumn implements DBColumn {
 		return generalColumnDesc.getDBName();
 	}
 
+	@Override
+	public final DBType getDatatype(){
+		return generalColumnDesc.getDatatype();
+	}
+
 	@Override
 	public final DBTable getTable(){
 		return null;
diff --git a/src/tap/metadata/TAPType.java b/src/adql/db/DBType.java
similarity index 51%
rename from src/tap/metadata/TAPType.java
rename to src/adql/db/DBType.java
index 1990d56..87f6c05 100644
--- a/src/tap/metadata/TAPType.java
+++ b/src/adql/db/DBType.java
@@ -1,14 +1,14 @@
-package tap.metadata;
+package adql.db;
 
 /*
- * This file is part of TAPLibrary.
+ * This file is part of ADQLLibrary.
  * 
- * TAPLibrary is free software: you can redistribute it and/or modify
+ * 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.
  * 
- * TAPLibrary is distributed in the hope that it will be useful,
+ * 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.
@@ -16,35 +16,35 @@ 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 2014 - Astronomishes Rechen Institut (ARI)
+ * Copyright 2014 - Astronomisches Rechen Institut (ARI)
  */
 
 /**
  * 
  * <p>
- * 	Describe a full TAP column type as it is described in the IVOA document.
+ * 	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 TAPDatatype}.
+ * <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&eacute;gory Mantelet (ARI) - gmantele@ari.uni-heidelberg.de
- * @version 2.0 (06/2014)
- * @since 2.0
+ * @author Gr&eacute;gory Mantelet (ARI)
+ * @version 1.3 (10/2014)
+ * @since 1.3
  */
-public class TAPType {
+public class DBType {
 
 	/**
 	 * 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
+	 * @author Gr&eacute;gory Mantelet (ARI)
+	 * @version 1.3 (10/2014)
+	 * @since 1.3
 	 */
-	public static enum TAPDatatype{
+	public static enum DBDatatype{
 		SMALLINT, INTEGER, BIGINT, REAL, DOUBLE, BINARY, VARBINARY, CHAR, VARCHAR, BLOB, CLOB, TIMESTAMP, POINT, REGION;
 	}
 
@@ -52,7 +52,7 @@ public class TAPType {
 	public static final int NO_LENGTH = -1;
 
 	/** Datatype of a column. */
-	public final TAPDatatype type;
+	public final DBDatatype type;
 
 	/** The length parameter (only few datatypes need this parameter: char, varchar, binary and varbinary). */
 	public final int length;
@@ -62,7 +62,7 @@ public class TAPType {
 	 * 
 	 * @param datatype	Column datatype.
 	 */
-	public TAPType(final TAPDatatype datatype){
+	public DBType(final DBDatatype datatype){
 		this(datatype, NO_LENGTH);
 	}
 
@@ -72,22 +72,71 @@ public class TAPType {
 	 * @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){
+	public DBType(final DBDatatype 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 VotType#VotType(TAPType)
-	 */
-	public VotType toVotType(){
-		return new VotType(this);
+	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:
+				return true;
+			default:
+				return false;
+		}
+	}
+
+	public boolean isBinary(){
+		switch(type){
+			case BINARY:
+			case VARBINARY:
+			case BLOB:
+				return true;
+			default:
+				return false;
+		}
+	}
+
+	public boolean isString(){
+		switch(type){
+			case CHAR:
+			case VARCHAR:
+			case CLOB:
+			case TIMESTAMP:
+				return true;
+			default:
+				return false;
+		}
+	}
+
+	public boolean isGeometry(){
+		return (type == DBDatatype.POINT || type == DBDatatype.REGION);
+	}
+
+	public boolean isCompatible(final DBType t){
+		if (t == null)
+			return false;
+		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
diff --git a/src/adql/db/DefaultDBColumn.java b/src/adql/db/DefaultDBColumn.java
index 8496501..a4ed9e3 100644
--- a/src/adql/db/DefaultDBColumn.java
+++ b/src/adql/db/DefaultDBColumn.java
@@ -16,20 +16,27 @@ package adql.db;
  * 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 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 /**
  * Default implementation of {@link DBColumn}.
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 08/2011
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 1.3 (10/2014)
  */
 public class DefaultDBColumn implements DBColumn {
 
+	/** Name of the column in the "database". */
 	protected String dbName;
+	/** Type of the column in the "database".
+	 * <i>Note: This should be one of the types listed by the IVOA in the TAP description.</i>
+	 * @since 1.3 */
+	protected DBType type;
+	/** Table in which this column exists. */
 	protected DBTable table;
-
+	/** Name that this column must have in ADQL queries. */
 	protected String adqlName = null;
 
 	/**
@@ -40,10 +47,30 @@ public class DefaultDBColumn implements DBColumn {
 	 * 					if a whole column reference is given, no split will be done.</b>
 	 * @param table		DB table which contains this column.
 	 * 
-	 * @see #DefaultDBColumn(String, String, DBTable)
+	 * @see #DefaultDBColumn(String, String, DBType, DBTable)
 	 */
 	public DefaultDBColumn(final String dbName, final DBTable table){
-		this(dbName, dbName, table);
+		this(dbName, dbName, null, table);
+	}
+
+	/**
+	 * Builds a default {@link DBColumn} with the given DB name and DB table.
+	 * 
+	 * @param dbName	Database column name (it will be also used for the ADQL name).
+	 * 					<b>Only the column name is expected. Contrary to {@link DefaultDBTable},
+	 * 					if a whole column reference is given, no split will be done.</b>
+	 * @param type		Type of the column.
+	 *            		<i>Note: there is no default value. Consequently if this parameter is NULL,
+	 *            		the type should be considered as unknown. It means that any comparison with
+	 *            		any type will always return 'true'.</i>
+	 * @param table		DB table which contains this column.
+	 * 
+	 * @see #DefaultDBColumn(String, String, DBType, DBTable)
+	 * 
+	 * @since 1.3
+	 */
+	public DefaultDBColumn(final String dbName, final DBType type, final DBTable table){
+		this(dbName, dbName, type, table);
 	}
 
 	/**
@@ -56,13 +83,38 @@ public class DefaultDBColumn implements DBColumn {
 	 * 					<b>Only the column name is expected. Contrary to {@link DefaultDBTable},
 	 * 					if a whole column reference is given, no split will be done.</b>
 	 * @param table		DB table which contains this column.
+	 * 
+	 * @see #DefaultDBColumn(String, String, DBType, DBTable)
 	 */
 	public DefaultDBColumn(final String dbName, final String adqlName, final DBTable table){
+		this(dbName, adqlName, null, table);
+	}
+
+	/**
+	 * Builds a default {@link DBColumn} with the given DB name, DB table and ADQL name.
+	 * 
+	 * @param dbName	Database column name.
+	 * 					<b>Only the column name is expected. Contrary to {@link DefaultDBTable},
+	 * 					if a whole column reference is given, no split will be done.</b>
+	 * @param adqlName	Column name used in ADQL queries.
+	 * 					<b>Only the column name is expected. Contrary to {@link DefaultDBTable},
+	 * 					if a whole column reference is given, no split will be done.</b>
+	 * @param type		Type of the column.
+	 *            		<i>Note: there is no default value. Consequently if this parameter is NULL,
+	 *            		the type should be considered as unknown. It means that any comparison with
+	 *            		any type will always return 'true'.</i>
+	 * @param table		DB table which contains this column.
+	 * 
+	 * @since 1.3
+	 */
+	public DefaultDBColumn(final String dbName, final String adqlName, final DBType type, final DBTable table){
 		this.dbName = dbName;
 		this.adqlName = adqlName;
+		this.type = type;
 		this.table = table;
 	}
 
+	@Override
 	public final String getADQLName(){
 		return adqlName;
 	}
@@ -72,10 +124,38 @@ public class DefaultDBColumn implements DBColumn {
 			this.adqlName = adqlName;
 	}
 
+	@Override
+	public final DBType getDatatype(){
+		return type;
+	}
+
+	/**
+	 * <p>Set the type of this column.</p>
+	 * 
+	 * <p><i>Note 1:
+	 * 	The given type should be as closed as possible from a type listed by the IVOA in the TAP protocol description into the section UPLOAD.
+	 * </i></p>
+	 * 
+	 * <p><i>Note 2:
+	 * 	there is no default value. Consequently if this parameter is NULL,
+	 * 	the type should be considered as unknown. It means that any comparison with
+	 * 	any type will always return 'true'.
+	 * </i></p>
+	 * 
+	 * @param type	New type of this column.
+	 * 
+	 * @since 1.3
+	 */
+	public final void setDatatype(final DBType type){
+		this.type = type;
+	}
+
+	@Override
 	public final String getDBName(){
 		return dbName;
 	}
 
+	@Override
 	public final DBTable getTable(){
 		return table;
 	}
@@ -84,8 +164,9 @@ public class DefaultDBColumn implements DBColumn {
 		this.table = table;
 	}
 
+	@Override
 	public DBColumn copy(final String dbName, final String adqlName, final DBTable dbTable){
-		return new DefaultDBColumn(dbName, adqlName, dbTable);
+		return new DefaultDBColumn(dbName, adqlName, type, dbTable);
 	}
 
 }
diff --git a/src/adql/db/FunctionDef.java b/src/adql/db/FunctionDef.java
new file mode 100644
index 0000000..a8071ee
--- /dev/null
+++ b/src/adql/db/FunctionDef.java
@@ -0,0 +1,476 @@
+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 - Astronomisches Rechen Institut (ARI)
+ */
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import adql.db.DBType.DBDatatype;
+import adql.parser.ParseException;
+import adql.query.operand.function.ADQLFunction;
+
+/**
+ * <p>Definition of any function that could be used in ADQL queries.</p>
+ * 
+ * <p>
+ * 	A such definition can be built manually thanks to the different constructors of this class,
+ * 	or by parsing a string function definition form using the static function {@link #parse(String)}.
+ * </p>
+ * 
+ * <p>
+ * 	The syntax of the expression expected by {@link #parse(String)} is the same as the one used to build
+ * 	the string returned by {@link #toString()}. Here is this syntax:
+ * </p>
+ * <pre>{fctName}([{param1Name} {param1Type}, ...])[ -> {returnType}]</pre>
+ * 
+ * <p>
+ * 	A description of this function may be set thanks to the public class attribute {@link #description}.
+ * </p>
+ * 
+ * @author Gr&eacute;gory Mantelet (ARI)
+ * @version 1.3 (10/2014)
+ * 
+ * @since 1.3
+ */
+public class FunctionDef implements Comparable<FunctionDef> {
+	/** Regular expression for what should be a function or parameter name - a regular identifier. */
+	protected final static String regularIdentifierRegExp = "[a-zA-Z]+[0-9a-zA-Z_]*";
+	/** Rough regular expression for a function return type or a parameter type.
+	 * The exact type is not checked here ; just the type name syntax is tested, not its value.
+	 * This regular expression allows a type to have exactly one parameter (which is generally the length of a character or binary string. */
+	protected final static String typeRegExp = "([a-zA-Z]+[0-9a-zA-Z]*)(\\(\\s*([0-9]+)\\s*\\))?";
+	/** Rough regular expression for a function parameters' list. */
+	protected final static String fctParamsRegExp = "\\s*[^,]+\\s*(,\\s*[^,]+\\s*)*";
+	/** Rough regular expression for a function parameter: a name (see {@link #regularIdentifierRegExp}) and a type (see {@link #typeRegExp}). */
+	protected final static String fctParamRegExp = "\\s*(" + regularIdentifierRegExp + ")\\s+" + typeRegExp + "\\s*";
+	/** Rough regular expression for a whole function definition. */
+	protected final static String fctDefRegExp = "\\s*(" + regularIdentifierRegExp + ")\\s*\\(([a-zA-Z0-9,() \r\n\t]*)\\)(\\s*->\\s*(" + typeRegExp + "))?\\s*";
+
+	/** Pattern of a function definition. This object has been compiled with {@link #fctDefRegExp}. */
+	protected final static Pattern fctPattern = Pattern.compile(fctDefRegExp);
+	/** Pattern of a single parameter definition. This object has been compiled with {@link #fctParamRegExp}. */
+	protected final static Pattern paramPattern = Pattern.compile(fctParamRegExp);
+
+	/** Name of the function. */
+	public final String name;
+
+	/** Description of this function. */
+	public String description = null;
+
+	/** Type of the result returned by this function. */
+	public final DBType returnType;
+	/** Indicate whether the return type is a string. */
+	protected final boolean isString;
+	/** Indicate whether the return type is a numeric. */
+	protected final boolean isNumeric;
+	/** Indicate whether the return type is a geometry. */
+	protected final boolean isGeometry;
+
+	/** Total number of parameters. */
+	public final int nbParams;
+	/** List of all the parameters of this function. */
+	protected final FunctionParam[] params;
+
+	/** <p>String representation of this function.</p>
+	 * <p>The syntax of this representation is the following <i>(items between brackets are optional)</i>:</p>
+	 * <pre>{fctName}([{param1Name} {param1Type}, ...])[ -> {returnType}]</pre> */
+	private final String serializedForm;
+
+	/** <p>String representation of this function dedicated to comparison with any function signature.</p>
+	 * <p>This form is different from the serialized form on the following points:</p>
+	 * <ul>
+	 * 	<li>the function name is always in lower case.</li>
+	 * 	<li>each parameter is represented by a string of 3 characters, one for each kind of type (in the order): numeric, string, geometry.
+	 * 	    Each character is either a 0 or 1, so that indicating whether the parameter is of that kind of type.</li>
+	 * 	<li>no return type.</li>
+	 * </ul>
+	 * <p>So the syntax of this form is the following <i>(items between brackets are optional ; xxx is a string of 3 characters, each being either 0 or 1)</i>:</p>
+	 * <pre>{fctName}([xxx, ...])</pre> */
+	private final String compareForm;
+
+	/**
+	 * <p>Definition of a function parameter.</p>
+	 * 
+	 * <p>This definition is composed of two items: the name and the type of the parameter.</p>
+	 * 
+	 * @author Gr&eacute;gory Mantelet (ARI)
+	 * @version 1.3 (10/2014)
+	 * @since 1.3
+	 */
+	public static final class FunctionParam {
+		/** Parameter name. <i>Ensured not null</i> */
+		public final String name;
+		/** Parameter type. <i>Ensured not null</i> */
+		public final DBType type;
+
+		/**
+		 * Create a function parameter.
+		 * 
+		 * @param paramName	Name of the parameter to create. <i>MUST NOT be NULL</i>
+		 * @param paramType	Type of the parameter to create. <i>MUST NOT be NULL</i>
+		 */
+		public FunctionParam(final String paramName, final DBType paramType){
+			if (paramName == null)
+				throw new NullPointerException("Missing name! The function parameter can not be created.");
+			if (paramType == null)
+				throw new NullPointerException("Missing type! The function parameter can not be created.");
+			this.name = paramName;
+			this.type = paramType;
+		}
+	}
+
+	/**
+	 * <p>Create a function definition.</p>
+	 * 
+	 * <p>The created function will have <b>no return type</b> and <b>no parameter</b>.</p> 
+	 * 
+	 * @param fctName	Name of the function.
+	 */
+	public FunctionDef(final String fctName){
+		this(fctName, null, null);
+	}
+
+	/**
+	 * <p>Create a function definition.</p>
+	 * 
+	 * <p>The created function will have a return type (if the provided one is not null) and <b>no parameter</b>.</p> 
+	 * 
+	 * @param fctName		Name of the function.
+	 * @param returnType	Return type of the function. <i>If NULL, this function will have no return type</i>
+	 */
+	public FunctionDef(final String fctName, final DBType returnType){
+		this(fctName, returnType, null);
+	}
+
+	/**
+	 * <p>Create a function definition.</p>
+	 * 
+	 * <p>The created function will have <b>no return type</b> and some parameters (except if the given array is NULL or empty).</p> 
+	 * 
+	 * @param fctName		Name of the function.
+	 * @param params		Parameters of this function. <i>If NULL or empty, this function will have no parameter.</i>
+	 */
+	public FunctionDef(final String fctName, final FunctionParam[] params){
+		this(fctName, null, params);
+	}
+
+	public FunctionDef(final String fctName, final DBType returnType, final FunctionParam[] params){
+		// Set the name:
+		if (fctName == null)
+			throw new NullPointerException("Missing name! Can not create this function definition.");
+		this.name = fctName;
+
+		// Set the parameters:
+		this.params = (params == null || params.length == 0) ? null : params;
+		this.nbParams = (params == null) ? 0 : params.length;
+
+		// Set the return type;
+		this.returnType = returnType;
+		if (returnType != null){
+			isNumeric = returnType.isNumeric();
+			isString = returnType.isString();
+			isGeometry = returnType.isGeometry();
+		}else
+			isNumeric = isString = isGeometry = false;
+
+		// Serialize in Strings (serializedForm and compareForm) this function definition:
+		StringBuffer bufSer = new StringBuffer(name), bufCmp = new StringBuffer(name.toLowerCase());
+		bufSer.append('(');
+		for(int i = 0; i < nbParams; i++){
+			bufSer.append(params[i].name).append(' ').append(params[i].type);
+			bufCmp.append(params[i].type.isNumeric() ? '1' : '0').append(params[i].type.isString() ? '1' : '0').append(params[i].type.isGeometry() ? '1' : '0');
+			if (i + 1 < nbParams)
+				bufSer.append(", ");
+		}
+		bufSer.append(')');
+		if (returnType != null)
+			bufSer.append(" -> ").append(returnType);
+		serializedForm = bufSer.toString();
+		compareForm = bufCmp.toString();
+	}
+
+	/**
+	 * Tell whether this function returns a numeric.
+	 * 
+	 * @return	<i>true</i> if this function returns a numeric, <i>false</i> otherwise.
+	 */
+	public final boolean isNumeric(){
+		return isNumeric;
+	}
+
+	/**
+	 * Tell whether this function returns a string.
+	 * 
+	 * @return	<i>true</i> if this function returns a string, <i>false</i> otherwise.
+	 */
+	public final boolean isString(){
+		return isString;
+	}
+
+	/**
+	 * Tell whether this function returns a geometry.
+	 * 
+	 * @return	<i>true</i> if this function returns a geometry, <i>false</i> otherwise.
+	 */
+	public final boolean isGeometry(){
+		return isGeometry;
+	}
+
+	/**
+	 * Get the number of parameters required by this function.
+	 * 
+	 * @return	Number of required parameters.
+	 */
+	public final int getNbParams(){
+		return nbParams;
+	}
+
+	/**
+	 * Get the definition of the indParam-th parameter of this function.
+	 * 
+	 * @param indParam	Index of the parameter whose the definition must be returned.
+	 * 
+	 * @return	Definition of the specified parameter.
+	 * 
+	 * @throws ArrayIndexOutOfBoundsException	If the given index is negative or bigger than the number of parameters.
+	 */
+	public final FunctionParam getParam(final int indParam) throws ArrayIndexOutOfBoundsException{
+		if (indParam < 0 || indParam >= nbParams)
+			throw new ArrayIndexOutOfBoundsException(indParam);
+		else
+			return params[indParam];
+	}
+
+	/**
+	 * <p>Let parsing the serialized form of a function definition.</p>
+	 * 
+	 * <p>The expected syntax is <i>(items between brackets are optional)</i>:</p>
+	 * <pre>{fctName}([{param1Name} {param1Type}, ...])[ -> {returnType}]</pre>
+	 * 
+	 * <p>
+	 * 	Allowed parameter types and return types should be one the types listed by the UPLOAD section of the TAP recommendation document.
+	 * 	These types are listed in the enumeration object {@link DBType}.
+	 * 	However, other types should be accepted like the common database types...but it should be better to not rely on that
+	 * 	since the conversion of those types to TAP types should not be exactly what is expected.
+	 * </p>
+	 * 
+	 * @param strDefinition	Serialized function definition to parse.
+	 * 
+	 * @return	The object representation of the given string definition.
+	 * 
+	 * @throws ParseException	If the given string has a wrong syntax or uses unknown types.
+	 */
+	public static FunctionDef parse(final String strDefinition) throws ParseException{
+		if (strDefinition == null)
+			throw new NullPointerException("Missing string definition to build a FunctionDef!");
+
+		// Check the global syntax of the function definition:
+		Matcher m = fctPattern.matcher(strDefinition);
+		if (m.matches()){
+
+			// Get the function name:
+			String fctName = m.group(1);
+
+			// Parse and get the return type:
+			DBType returnType = null;
+			if (m.group(3) != null){
+				returnType = parseType(m.group(5), (m.group(7) == null) ? DBType.NO_LENGTH : Integer.parseInt(m.group(7)));
+				if (returnType == null)
+					throw new ParseException("Unknown return type: \"" + m.group(4).trim() + "\"!");
+			}
+
+			// Get the parameters, if any:
+			String paramsList = m.group(2);
+			FunctionParam[] params = null;
+			if (paramsList != null && paramsList.trim().length() > 0){
+
+				// Check the syntax of the parameters' list:
+				if (!paramsList.matches(fctParamsRegExp))
+					throw new ParseException("Wrong parameters syntax! Expected syntax: \"(<regular_identifier> <type_name> (, <regular_identifier> <type_name>)*)\", where <regular_identifier>=\"[a-zA-Z]+[a-zA-Z0-9_]*\", <type_name> should be one of the types described in the UPLOAD section of the TAP documentation. Examples of good syntax: \"()\", \"(param INTEGER)\", \"(param1 INTEGER, param2 DOUBLE)\"");
+
+				// Split all the parameter definitions:
+				String[] paramsSplit = paramsList.split(",");
+				params = new FunctionParam[paramsSplit.length];
+				DBType paramType;
+
+				// For each parameter definition...
+				for(int i = 0; i < params.length; i++){
+					m = paramPattern.matcher(paramsSplit[i]);
+					if (m.matches()){
+
+						// ...parse and get the parameter type:
+						paramType = parseType(m.group(2), (m.group(4) == null) ? DBType.NO_LENGTH : Integer.parseInt(m.group(4)));
+
+						// ...build the parameter definition object:
+						if (paramType == null)
+							throw new ParseException("Unknown type for the parameter \"" + m.group(1) + "\": \"" + m.group(2) + ((m.group(3) == null) ? "" : m.group(3)) + "\"!");
+						else
+							params[i] = new FunctionParam(m.group(1), paramType);
+					}else
+						// note: should never happen because we have already check the syntax of the whole parameters list before parsing each individual parameter.
+						throw new ParseException("Wrong syntax for the " + (i + 1) + "-th parameter: \"" + paramsSplit[i].trim() + "\"! Expected syntax: \"(<regular_identifier> <type_name> (, <regular_identifier> <type_name>)*)\", where <regular_identifier>=\"[a-zA-Z]+[a-zA-Z0-9_]*\", <type_name> should be one of the types described in the UPLOAD section of the TAP documentation. Examples of good syntax: \"()\", \"(param INTEGER)\", \"(param1 INTEGER, param2 DOUBLE)\"");
+				}
+			}
+
+			// Build the function definition object:
+			return new FunctionDef(fctName, returnType, params);
+		}else
+			throw new ParseException("Wrong function definition syntax! Expected syntax: \"<regular_identifier>(<parameters>?) <return_type>?\", where <regular_identifier>=\"[a-zA-Z]+[a-zA-Z0-9_]*\", <return_type>=\" -> <type_name>\", <parameters>=\"(<regular_identifier> <type_name> (, <regular_identifier> <type_name>)*)\", <type_name> should be one of the types described in the UPLOAD section of the TAP documentation. Examples of good syntax: \"foo()\", \"foo() -> VARCHAR\", \"foo(param INTEGER)\", \"foo(param1 INTEGER, param2 DOUBLE) -> DOUBLE\"");
+	}
+
+	/**
+	 * Parse the given string representation of a datatype.
+	 * 
+	 * @param datatype	String representation of a datatype.
+	 *                	<i>Note: This string must not contain the length parameter or any other parameter.
+	 *                	These latter should have been separated from the datatype before calling this function.</i>
+	 * @param length	Length of this datatype.
+	 *              	<i>Note: This length will be used only for binary (BINARY and VARBINARY)
+	 *              	and character (CHAR and VARCHAR) types.</i> 
+	 * 
+	 * @return	The object representation of the specified datatype.
+	 */
+	private static DBType parseType(String datatype, int length){
+		if (datatype == null)
+			return null;
+
+		try{
+			// Try to find a corresponding DBType item:
+			DBDatatype dbDatatype = DBDatatype.valueOf(datatype.toUpperCase());
+
+			// If there's a match, build the type object representation:
+			length = (length <= 0) ? DBType.NO_LENGTH : length;
+			switch(dbDatatype){
+				case CHAR:
+				case VARCHAR:
+				case BINARY:
+				case VARBINARY:
+					return new DBType(dbDatatype, length);
+				default:
+					return new DBType(dbDatatype);
+			}
+		}catch(IllegalArgumentException iae){
+			// If there's no corresponding DBType item, try to find a match among the most used DB types:
+			datatype = datatype.toLowerCase();
+			if (datatype.equals("bool") || datatype.equals("boolean") || datatype.equals("short"))
+				return new DBType(DBDatatype.SMALLINT);
+			else if (datatype.equals("int2"))
+				return new DBType(DBDatatype.SMALLINT);
+			else if (datatype.equals("int") || datatype.equals("int4"))
+				return new DBType(DBDatatype.INTEGER);
+			else if (datatype.equals("long") || datatype.equals("number") || datatype.equals("bigint") || datatype.equals("int8"))
+				return new DBType(DBDatatype.BIGINT);
+			else if (datatype.equals("float") || datatype.equals("float4"))
+				return new DBType(DBDatatype.REAL);
+			else if (datatype.equals("numeric") || datatype.equals("float8"))
+				return new DBType(DBDatatype.DOUBLE);
+			else if (datatype.equals("byte") || datatype.equals("raw"))
+				return new DBType(DBDatatype.BINARY, length);
+			else if (datatype.equals("unsignedByte"))
+				return new DBType(DBDatatype.VARBINARY, length);
+			else if (datatype.equals("character"))
+				return new DBType(DBDatatype.CHAR, length);
+			else if (datatype.equals("string") || datatype.equals("varchar2"))
+				return new DBType(DBDatatype.VARCHAR, length);
+			else if (datatype.equals("bytea"))
+				return new DBType(DBDatatype.BLOB);
+			else if (datatype.equals("text"))
+				return new DBType(DBDatatype.CLOB);
+			else if (datatype.equals("date") || datatype.equals("time"))
+				return new DBType(DBDatatype.TIMESTAMP);
+			else if (datatype.equals("position"))
+				return new DBType(DBDatatype.POINT);
+			else if (datatype.equals("polygon") || datatype.equals("box") || datatype.equals("circle"))
+				return new DBType(DBDatatype.REGION);
+			else
+				return null;
+		}
+	}
+
+	@Override
+	public String toString(){
+		return serializedForm;
+	}
+
+	@Override
+	public int compareTo(final FunctionDef def){
+		return compareForm.compareTo(def.compareForm);
+	}
+
+	/**
+	 * <p>Compare this function definition with the given ADQL function item.</p>
+	 * 
+	 * <p>
+	 * 	The comparison is done only on the function name and on rough type of the parameters.
+	 * 	"Rough type" means here that just the kind of type is tested: numeric, string or geometry.
+	 * 	Anyway, the return type is never tested by this function, since such information is usually
+	 * 	not part of a function signature.
+	 * </p>
+	 * 
+	 * <p>The notion of "greater" and "less" are defined here according to the three following test steps:</p>
+	 * <ol>
+	 * 	<li><b>Name test:</b> if the name of both function are equals, next steps are evaluated, otherwise the standard string comparison (case insensitive) result is returned.</li>
+	 * 	<li><b>Parameters test:</b> parameters are compared individually. Each time parameters (at the same position in both functions) are equals the next parameter can be tested,
+	 * 	                            and so on until two parameters are different or the end of the parameters' list is reached.
+	 * 	                            Just the kind of type is used for parameter comparison. Each kind of type is tested in the following order: numeric, string and geometry.
+	 * 	                            When a kind of type is not equal for both parameters, the function exits with the appropriate value
+	 * 	                            (1 if the parameter of this function definition is of the kind of type, -1 otherwise).</li>
+	 * 	<li><b>Number of parameters test:</b> in the case where this function definition has N parameters and the given ADQL function has M parameters,
+	 * 	                                      and that the L (= min(N,M)) first parameters have the same type in both functions, the value returns by this function
+	 * 	                                      will be N-M. Thus, if this function definition has more parameters than the given function, a positive value will be
+	 * 	                                      returned. Otherwise a negative value will be returned, or 0 if the number of parameters is the same.</li>
+	 * </ol>
+	 * 
+	 * @param fct	ADQL function item to compare with this function definition.
+	 * 
+	 * @return	A positive value if this function definition is "greater" than the given {@link ADQLFunction},
+	 * 			0 if they are perfectly matching,
+	 * 			or a negative value if this function definition is "less" than the given {@link ADQLFunction}.
+	 */
+	public int compareTo(final ADQLFunction fct){
+		if (fct == null)
+			throw new NullPointerException("Missing ADQL function with which comparing this function definition!");
+
+		// Names comparison:
+		int comp = name.compareToIgnoreCase(fct.getName());
+
+		// If equals, compare the parameters' type:
+		if (comp == 0){
+			for(int i = 0; comp == 0 && i < nbParams && i < fct.getNbParameters(); i++){
+				if (params[i].type.isNumeric() == fct.getParameter(i).isNumeric()){
+					if (params[i].type.isString() == fct.getParameter(i).isString()){
+						if (params[i].type.isGeometry() == fct.getParameter(i).isGeometry())
+							comp = 0;
+						else
+							comp = params[i].type.isGeometry() ? 1 : -1;
+					}else
+						comp = params[i].type.isString() ? 1 : -1;
+				}else
+					comp = params[i].type.isNumeric() ? 1 : -1;
+			}
+
+			// If the first min(N,M) parameters are of the same type, do the last comparison on the number of parameters:
+			if (comp == 0 && nbParams != fct.getNbParameters())
+				comp = nbParams - fct.getNbParameters();
+		}
+
+		return comp;
+	}
+}
diff --git a/src/adql/db/STCS.java b/src/adql/db/STCS.java
new file mode 100644
index 0000000..c4a34e9
--- /dev/null
+++ b/src/adql/db/STCS.java
@@ -0,0 +1,1685 @@
+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 - Astronomisches Rechen Institut (ARI)
+ */
+
+import java.util.ArrayList;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import adql.parser.ADQLQueryFactory;
+import adql.parser.ParseException;
+import adql.query.TextPosition;
+import adql.query.operand.ADQLOperand;
+import adql.query.operand.NegativeOperand;
+import adql.query.operand.NumericConstant;
+import adql.query.operand.StringConstant;
+import adql.query.operand.function.ADQLFunction;
+import adql.query.operand.function.geometry.BoxFunction;
+import adql.query.operand.function.geometry.CircleFunction;
+import adql.query.operand.function.geometry.GeometryFunction;
+import adql.query.operand.function.geometry.PointFunction;
+import adql.query.operand.function.geometry.PolygonFunction;
+import adql.query.operand.function.geometry.RegionFunction;
+
+/**
+ * <p>This class helps dealing with the subset of STC-S expressions described by the section "6 Use of STC-S in TAP (informative)"
+ * of the TAP Recommendation 1.0 (27th March 2010). This subset is limited to the most common coordinate systems and regions.</p>
+ * 
+ * <p><i>Note:
+ * 	No instance of this class can be created. Its usage is only limited to its static functions and classes.
+ * </i></p>
+ * 
+ * <h3>Coordinate system</h3>
+ * <p>
+ * 	The function {@link #parseCoordSys(String)} is able to parse a string containing only the STC-S expression of a coordinate system
+ * 	(or an empty string or null which would be interpreted as the default coordinate system - UNKNOWNFRAME UNKNOWNREFPOS SPHERICAL2).
+ * 	When successful, this parsing returns an object representation of the coordinate system: {@link CoordSys}.
+ * </p>
+ * <p>
+ * 	To serialize into STC-S a coordinate system, you have to create a {@link CoordSys} instance with the desired values
+ * 	and to call the function {@link CoordSys#toSTCS()}. The static function {@link #toSTCS(CoordSys)} is just calling the
+ * 	{@link CoordSys#toSTCS()} on the given coordinate system.
+ * </p>
+ * 
+ * <h3>Geometrical region</h3>
+ * <p>
+ * 	As for the coordinate system, there is a static function to parse the STC-S representation of a geometrical region: {@link #parseRegion(String)}.
+ * 	Here again, when the parsing is successful an object representation is returned: {@link Region}.
+ * </p>
+ * <p>
+ * 	This class lets also serializing into STC-S a region. The procedure is the same as with a coordinate system: create a {@link Region} and then
+ * 	call {@link Region#toString()}.
+ * </p>
+ * <p>
+ * 	The class {@link Region} lets also dealing with the {@link ADQLFunction} implementing a region. It is then possible to create a {@link Region}
+ * 	object from a such {@link ADQLFunction} and to get the corresponding STC-S representation. The static function {@link #toSTCS(GeometryFunction)}
+ * 	is a helpful function which do these both actions in once.
+ * </p>
+ * <p><i>Note:
+ * 	The conversion from {@link ADQLFunction} to {@link Region} or STC-S is possible only if the {@link ADQLFunction} contains constants as parameter.
+ * 	Thus, a such function using a column, a concatenation, a math operation or using another function can not be converted into STC-S using this class.
+ * </i></p>
+ * 
+ * @author Gr&eacute;gory Mantelet (ARI)
+ * @version 1.3 (10/2014)
+ * @since 1.3
+ */
+public final class STCS {
+
+	/**
+	 * Empty private constructor ; in order to prevent any instance creation.
+	 */
+	private STCS(){}
+
+	/* ***************** */
+	/* COORDINATE SYSTEM */
+	/* ***************** */
+
+	/** Regular expression for a STC-S representation of a coordinate system. It takes into account the fact that each part of
+	 * a coordinate system is optional and so that a full coordinate system expression can be reduced to an empty string. */
+	private final static String coordSysRegExp = Frame.regexp + "?\\s*" + RefPos.regexp + "?\\s*" + Flavor.regexp + "?";
+	/** Regular expression of an expression exclusively limited to a coordinate system. */
+	private final static String onlyCoordSysRegExp = "^\\s*" + coordSysRegExp + "\\s*$";
+	/** Regular expression of a default coordinate system: either an empty string or a string containing only default values. */
+	private final static String defaultCoordSysRegExp = "^\\s*" + Frame.DEFAULT + "?\\s*" + RefPos.DEFAULT + "?\\s*" + Flavor.DEFAULT + "?\\s*$";
+	/** Regular expression of a pattern describing a set of allowed coordinate systems. <i>See {@link #buildAllowedRegExp(String)} for more details.</i> */
+	/* With this regular expression, we get the following matching groups:
+	 *       0: All the expression
+	 * 1+(6*N): The N-th part of the coordinate system (N is an unsigned integer between 0 and 2 (included) ; it is reduced to '*' if the two following groups are NULL
+	 * 2+(6*N): A single value for the N-th part
+	 * 3+(6*N): A list of values for the N-th part
+	 * 4+(6*N): First value of the list for the N-th part
+	 * 5+(6*N): All the other values (starting with a |) of the list for the N-th part
+	 * 6+(6*N): Last value of the list for the N-th part.
+	 */
+	private final static String allowedCoordSysRegExp = "^\\s*" + buildAllowedRegExp(Frame.regexp) + "\\s+" + buildAllowedRegExp(RefPos.regexp) + "\\s+" + buildAllowedRegExp(Flavor.regexp) + "\\s*$";
+
+	/** Pattern of an allowed coordinate system pattern. This object has been compiled with {@link #allowedCoordSysRegExp}. */
+	private final static Pattern allowedCoordSysPattern = Pattern.compile(allowedCoordSysRegExp);
+
+	/** Human description of the syntax of a full coordinate system expression. */
+	private final static String COORD_SYS_SYNTAX = "\"[" + Frame.regexp + "] [" + RefPos.regexp + "] [" + Flavor.regexp + "]\" ; an empty string is also allowed and will be interpreted as the coordinate system locally used";
+
+	/**
+	 * Build the regular expression of a string defining the allowed values for one part of the whole coordinate system.
+	 * 
+	 * @param rootRegExp	All allowed part values.
+	 * 
+	 * @return	The corresponding regular expression.
+	 */
+	private static String buildAllowedRegExp(final String rootRegExp){
+		return "(" + rootRegExp + "|\\*|(\\(\\s*" + rootRegExp + "\\s*(\\|\\s*" + rootRegExp + "\\s*)*\\)))";
+	}
+
+	/**
+	 * <p>List of all possible frames in an STC expression.</p>
+	 * 
+	 * <p>
+	 * 	When no value is specified, the default one is {@link #UNKNOWNFRAME}.
+	 * 	The default value is also accessible through the attribute {@link #DEFAULT}
+	 * 	and it is possible to test whether a frame is the default with the function {@link #isDefault()}.
+	 * </p>
+	 * 
+	 * <p><i>Note:
+	 * 	The possible values listed in this enumeration are limited to the subset of STC-S described by the section "6 Use of STC-S in TAP (informative)"
+	 * 	of the TAP Recommendation 1.0 (27th March 2010).
+	 * </i></p>
+	 * 
+	 * @author Gr&eacute;gory Mantelet (ARI)
+	 * @version 1.3 (10/2014)
+	 * @since 1.3
+	 */
+	public static enum Frame{
+		ECLIPTIC, FK4, FK5, GALACTIC, ICRS, UNKNOWNFRAME;
+
+		/** Default value for a frame: {@link #UNKNOWNFRAME}. */
+		public static final Frame DEFAULT = UNKNOWNFRAME;
+
+		/** Regular expression to test whether a string is a valid frame or not. This regular expression does not take into account
+		 * the case of an empty string (which means "default frame"). */
+		public static final String regexp = buildRegexp(Frame.class);
+
+		/**
+		 * Tell whether this frame is the default one.
+		 * 
+		 * @return	<i>true</i> if this is the default frame, <i>false</i>
+		 */
+		public final boolean isDefault(){
+			return this == DEFAULT;
+		}
+	}
+
+	/**
+	 * <p>List of all possible reference positions in an STC expression.</p>
+	 * 
+	 * <p>
+	 * 	When no value is specified, the default one is {@link #UNKNOWNREFPOS}.
+	 * 	The default value is also accessible through the attribute {@link #DEFAULT}
+	 * 	and it is possible to test whether a reference position is the default with the function {@link #isDefault()}.
+	 * </p>
+	 * 
+	 * <p><i>Note:
+	 * 	The possible values listed in this enumeration are limited to the subset of STC-S described by the section "6 Use of STC-S in TAP (informative)"
+	 * 	of the TAP Recommendation 1.0 (27th March 2010).
+	 * </i></p>
+	 * 
+	 * @author Gr&eacute;gory Mantelet (ARI)
+	 * @version 1.3 (10/2014)
+	 * @since 1.3
+	 */
+	public static enum RefPos{
+		BARYCENTER, GEOCENTER, HELIOCENTER, LSR, TOPOCENTER, RELOCATABLE, UNKNOWNREFPOS;
+
+		/** Default value for a reference position: {@link #UNKNOWNREFPOS}. */
+		public static final RefPos DEFAULT = UNKNOWNREFPOS;
+
+		/** Regular expression to test whether a string is a valid reference position or not. This regular expression does not take into account
+		 * the case of an empty string (which means "default reference position"). */
+		public static final String regexp = buildRegexp(RefPos.class);
+
+		/**
+		 * Tell whether this reference position is the default one.
+		 * 
+		 * @return	<i>true</i> if this is the default reference position, <i>false</i>
+		 */
+		public final boolean isDefault(){
+			return this == DEFAULT;
+		}
+	}
+
+	/**
+	 * <p>List of all possible flavors in an STC expression.</p>
+	 * 
+	 * <p>
+	 * 	When no value is specified, the default one is {@link #SPHERICAL2}.
+	 * 	The default value is also accessible through the attribute {@link #DEFAULT}
+	 * 	and it is possible to test whether a flavor is the default with the function {@link #isDefault()}.
+	 * </p>
+	 * 
+	 * <p><i>Note:
+	 * 	The possible values listed in this enumeration are limited to the subset of STC-S described by the section "6 Use of STC-S in TAP (informative)"
+	 * 	of the TAP Recommendation 1.0 (27th March 2010).
+	 * </i></p>
+	 * 
+	 * @author Gr&eacute;gory Mantelet (ARI)
+	 * @version 1.3 (10/2014)
+	 * @since 1.3
+	 */
+	public static enum Flavor{
+		CARTESIAN2, CARTESIAN3, SPHERICAL2;
+
+		/** Default value for a flavor: {@link #SPHERICAL2}. */
+		public static final Flavor DEFAULT = SPHERICAL2;
+
+		/** Regular expression to test whether a string is a valid flavor or not. This regular expression does not take into account
+		 * the case of an empty string (which means "default flavor"). */
+		public static final String regexp = buildRegexp(Flavor.class);
+
+		/**
+		 * Tell whether this flavor is the default one.
+		 * 
+		 * @return	<i>true</i> if this is the default flavor, <i>false</i>
+		 */
+		public final boolean isDefault(){
+			return this == DEFAULT;
+		}
+	}
+
+	/**
+	 * Build a regular expression covering all possible values of the given enumeration.
+	 * 
+	 * @param enumType	Class of an enumeration type.
+	 * 
+	 * @return	The build regular expression or "\s*" if the given enumeration contains no constants/values.
+	 * 
+	 * @throws IllegalArgumentException	If the given class is not an enumeration type.
+	 */
+	private static String buildRegexp(final Class<?> enumType) throws IllegalArgumentException{
+		// The given class must be an enumeration type:
+		if (!enumType.isEnum())
+			throw new IllegalArgumentException("An enum class was expected, but a " + enumType.getName() + " has been given!");
+
+		// Get the enumeration constants/values:
+		Object[] constants = enumType.getEnumConstants();
+		if (constants == null || constants.length == 0)
+			return "\\s*";
+
+		// Concatenate all constants with pipe to build a choice regular expression:
+		StringBuffer buf = new StringBuffer("(");
+		for(int i = 0; i < constants.length; i++){
+			buf.append(constants[i]);
+			if ((i + 1) < constants.length)
+				buf.append('|');
+		}
+		return buf.append(')').toString();
+	}
+
+	/**
+	 * <p>Object representation of an STC coordinate system.</p>
+	 * 
+	 * <p>
+	 * 	A coordinate system is composed of three parts: a frame ({@link #frame}),
+	 * 	a reference position ({@link #refpos}) and a flavor ({@link #flavor}).
+	 * </p>
+	 * 
+	 * <p>
+	 * 	The default value - also corresponding to an empty string - should be:
+	 * 	{@link Frame#UNKNOWNFRAME} {@link RefPos#UNKNOWNREFPOS} {@link Flavor#SPHERICAL2}.
+	 * 	Once built, it is possible to know whether the coordinate system is the default one
+	 * 	or not thanks to function {@link #isDefault()}.
+	 * </p>
+	 * 
+	 * <p>
+	 * 	An instance of this class can be easily serialized into STC-S using {@link #toSTCS()}, {@link #toFullSTCS()}
+	 * 	or {@link #toString()}. {@link #toFullSTCS()} will display default values explicitly
+	 * 	on the contrary to {@link #toSTCS()} which will replace them by empty strings.
+	 * </p>
+	 * 
+	 * <p><i><b>Important note:</b>
+	 * 	The flavors CARTESIAN2 and CARTESIAN3 can not be used with other frame and reference position than
+	 * 	UNKNOWNFRAME and UNKNOWNREFPOS. In the contrary case an {@link IllegalArgumentException} is throw.
+	 * </i></p>
+	 * 
+	 * @author Gr&eacute;gory Mantelet (ARI)
+	 * @version 1.3 (10/2014)
+	 * @since 1.3
+	 */
+	public static class CoordSys {
+		/** First item of a coordinate system expression: the frame. */
+		public final Frame frame;
+
+		/** Second item of a coordinate system expression: the reference position. */
+		public final RefPos refpos;
+
+		/** Third and last item of a coordinate system expression: the flavor. */
+		public final Flavor flavor;
+
+		/** Indicate whether all parts of the coordinate system are set to their default value. */
+		private final boolean isDefault;
+
+		/** STC-S representation of this coordinate system. Default items are not written (that's to say, they are replaced by an empty string). */
+		private final String stcs;
+
+		/** STC-S representation of this coordinate system. Default items are explicitly written. */
+		private final String fullStcs;
+
+		/**
+		 * Build a default coordinate system (UNKNOWNFRAME UNKNOWNREFPOS SPHERICAL2).
+		 */
+		public CoordSys(){
+			this(null, null, null);
+		}
+
+		/**
+		 * Build a coordinate system with the given parts.
+		 * 
+		 * @param fr	Frame part.
+		 * @param rp	Reference position part.
+		 * @param fl	Flavor part.
+		 * 
+		 * @throws IllegalArgumentException	If a cartesian flavor is used with a frame and reference position other than UNKNOWNFRAME and UNKNOWNREFPOS.
+		 */
+		public CoordSys(final Frame fr, final RefPos rp, final Flavor fl) throws IllegalArgumentException{
+			frame = (fr == null) ? Frame.DEFAULT : fr;
+			refpos = (rp == null) ? RefPos.DEFAULT : rp;
+			flavor = (fl == null) ? Flavor.DEFAULT : fl;
+
+			if (flavor != Flavor.SPHERICAL2 && (frame != Frame.UNKNOWNFRAME || refpos != RefPos.UNKNOWNREFPOS))
+				throw new IllegalArgumentException("a coordinate system expressed with a cartesian flavor MUST have an UNKNOWNFRAME and UNKNOWNREFPOS!");
+
+			isDefault = frame.isDefault() && refpos.isDefault() && flavor.isDefault();
+
+			stcs = ((!frame.isDefault() ? frame + " " : "") + (!refpos.isDefault() ? refpos + " " : "") + (!flavor.isDefault() ? flavor : "")).trim();
+			fullStcs = frame + " " + refpos + " " + flavor;
+		}
+
+		/**
+		 * Build a coordinate system by parsing the given STC-S expression.
+		 * 
+		 * @param coordsys	STC-S expression representing a coordinate system. <i>Empty string and NULL are allowed values ; they correspond to a default coordinate system.</i>
+		 * 
+		 * @throws ParseException	If the syntax of the given STC-S expression is wrong or if it is not a coordinate system only.
+		 */
+		public CoordSys(final String coordsys) throws ParseException{
+			CoordSys tmp = new STCSParser().parseCoordSys(coordsys);
+			frame = tmp.frame;
+			refpos = tmp.refpos;
+			flavor = tmp.flavor;
+			isDefault = tmp.isDefault;
+			stcs = tmp.stcs;
+			fullStcs = tmp.fullStcs;
+		}
+
+		/**
+		 * Tell whether this is the default coordinate system (UNKNOWNFRAME UNKNOWNREFPOS SPHERICAL2).
+		 * 
+		 * @return	<i>true</i> if it is the default coordinate system, <i>false</i> otherwise.
+		 */
+		public final boolean isDefault(){
+			return isDefault;
+		}
+
+		/**
+		 * Get the STC-S expression of this coordinate system,
+		 * in which default values are not written (they are replaced by empty strings).
+		 * 
+		 * @return	STC-S representation of this coordinate system.
+		 */
+		public String toSTCS(){
+			return stcs;
+		}
+
+		/**
+		 * Get the STC-S expression of this coordinate system,
+		 * in which default values are explicitly written.
+		 * 
+		 * @return	STC-S representation of this coordinate system.
+		 */
+		public String toFullSTCS(){
+			return fullStcs;
+		}
+
+		/**
+		 * Convert this coordinate system into a STC-S expression.
+		 * 
+		 * @see java.lang.Object#toString()
+		 * @see #toSTCS()
+		 */
+		@Override
+		public String toString(){
+			return stcs;
+		}
+	}
+
+	/**
+	 * Parse the given STC-S representation of a coordinate system.
+	 * 
+	 * @param stcs	STC-S expression of a coordinate system. <i>Note: a NULL or empty string will be interpreted as a default coordinate system.</i>
+	 * 
+	 * @return	The object representation of the specified coordinate system.
+	 * 
+	 * @throws ParseException	If the given expression has a wrong STC-S syntax.
+	 */
+	public static CoordSys parseCoordSys(final String stcs) throws ParseException{
+		return (new STCSParser().parseCoordSys(stcs));
+	}
+
+	/**
+	 * <p>Convert an object representation of a coordinate system into an STC-S expression.</p>
+	 * 
+	 * <p><i>Note:
+	 * 	A NULL object will be interpreted as the default coordinate system and so an empty string will be returned.
+	 * 	Otherwise, this function is equivalent to {@link CoordSys#toSTCS()} (in which default values for each
+	 * 	coordinate system part is not displayed).
+	 * </i></p>
+	 * 
+	 * @param coordSys	The object representation of the coordinate system to convert into STC-S.
+	 * 
+	 * @return	The corresponding STC-S expression.
+	 * 
+	 * @see CoordSys#toSTCS()
+	 * @see CoordSys#toFullSTCS()
+	 */
+	public static String toSTCS(final CoordSys coordSys){
+		if (coordSys == null)
+			return "";
+		else
+			return coordSys.toSTCS();
+	}
+
+	/**
+	 * <p>Build a big regular expression gathering all of the given coordinate system syntaxes.</p>
+	 * 
+	 * <p>
+	 * 	Each item of the given list must respect a strict syntax. Each part of the coordinate system
+	 * 	may be a single value, a list of values or a '*' (meaning all values are allowed).
+	 * 	A list of values must have the following syntax: <code>({value1}|{value2}|...)</code>.
+	 * 	An empty string is NOT here accepted.
+	 * </p>
+	 * 
+	 * <p><i>Example:
+	 * 	<code>(ICRS|FK4|FK5) * SPHERICAL2</code> is OK,
+	 * 	but <code>(ICRS|FK4|FK5) *</code> is not valid because the flavor value is not defined.
+	 * </i></p>
+	 * 
+	 * <p>
+	 * 	Since the default value of each part of a coordinate system should always be possible,
+	 * 	this function ensure these default values are always possible in the returned regular expression.
+	 * 	Thus, if some values except the default one are specified, the default value is automatically appended.
+	 * </p>
+	 * 
+	 * <p><i>Note:
+	 * 	If the given array is NULL, all coordinate systems are allowed.
+	 * 	But if the given array is empty, none except an empty string or the default value will be allowed.
+	 * </i></p>
+	 * 
+	 * @param allowedCoordSys	List of all coordinate systems that are allowed.
+	 * 
+	 * @return	The corresponding regular expression.
+	 * 
+	 * @throws ParseException	If the syntax of one of the given allowed coordinate system is wrong.
+	 */
+	public static String buildCoordSysRegExp(final String[] allowedCoordSys) throws ParseException{
+		// NULL array => all coordinate systems are allowed:
+		if (allowedCoordSys == null)
+			return onlyCoordSysRegExp;
+		// Empty array => no coordinate system (except the default one) is allowed:
+		else if (allowedCoordSys.length == 0)
+			return defaultCoordSysRegExp;
+
+		// The final regular expression must be reduced to a coordinate system and nothing else before:
+		StringBuffer finalRegExp = new StringBuffer("^\\s*(");
+
+		// For each allowed coordinate system:
+		Matcher m;
+		int nbCoordSys = 0;
+		for(int i = 0; i < allowedCoordSys.length; i++){
+
+			// NULL item => skipped!
+			if (allowedCoordSys[i] == null)
+				continue;
+			else{
+				if (nbCoordSys > 0)
+					finalRegExp.append('|');
+				nbCoordSys++;
+			}
+
+			// Check its syntax and identify all of its parts:
+			m = allowedCoordSysPattern.matcher(allowedCoordSys[i].toUpperCase());
+			if (m.matches()){
+				finalRegExp.append('(');
+				for(int g = 0; g < 3; g++){	// See the comment after the Javadoc of #allowedCoordSysRegExp for a complete list of available groups returned by the pattern.
+
+					// SINGLE VALUE:
+					if (m.group(2 + (6 * g)) != null)
+						finalRegExp.append('(').append(defaultChoice(g, m.group(2 + (6 * g)))).append(m.group(2 + (6 * g))).append(')');
+
+					// LIST OF VALUES:
+					else if (m.group(3 + (6 * g)) != null)
+						finalRegExp.append('(').append(defaultChoice(g, m.group(3 + (6 * g)))).append(m.group(3 + (6 * g)).replaceAll("\\s", "").substring(1));
+
+					// JOKER (*):
+					else{
+						switch(g){
+							case 0:
+								finalRegExp.append(Frame.regexp);
+								break;
+							case 1:
+								finalRegExp.append(RefPos.regexp);
+								break;
+							case 2:
+								finalRegExp.append(Flavor.regexp);
+								break;
+						}
+						finalRegExp.append('?');
+					}
+					finalRegExp.append("\\s*");
+				}
+				finalRegExp.append(')');
+			}else
+				throw new ParseException("Wrong allowed coordinate system syntax for the " + (i + 1) + "-th item: \"" + allowedCoordSys[i] + "\"! Expected: \"frameRegExp refposRegExp flavorRegExp\" ; where each xxxRegExp = (xxx | '*' | '('xxx ('|' xxx)*')'), frame=\"" + Frame.regexp + "\", refpos=\"" + RefPos.regexp + "\" and flavor=\"" + Flavor.regexp + "\" ; an empty string is also allowed and will be interpreted as '*' (so all possible values).");
+		}
+
+		// The final regular expression must be reduced to a coordinate system and nothing else after:
+		finalRegExp.append(")\\s*");
+
+		return (nbCoordSys > 0) ? finalRegExp.append(")$").toString() : defaultCoordSysRegExp;
+	}
+
+	/**
+	 * Get the default value appended by a '|' character, ONLY IF the given value does not already contain the default value.
+	 * 
+	 * @param g		Index of the coordinate system part (0: Frame, 1: RefPos, 2: Flavor, another value will return an empty string).
+	 * @param value	Value in which the default value must prefix.
+	 * 
+	 * @return	A prefix for the given value (the default value and a '|' if the default value is not already in the given value, "" otherwise).
+	 */
+	private static String defaultChoice(final int g, final String value){
+		switch(g){
+			case 0:
+				return value.contains(Frame.DEFAULT.toString()) ? "" : Frame.DEFAULT + "|";
+			case 1:
+				return value.contains(RefPos.DEFAULT.toString()) ? "" : RefPos.DEFAULT + "|";
+			case 2:
+				return value.contains(Flavor.DEFAULT.toString()) ? "" : Flavor.DEFAULT + "|";
+			default:
+				return "";
+		}
+	}
+
+	/* ****** */
+	/* REGION */
+	/* ****** */
+
+	/**
+	 * <p>List all possible region types allowed in an STC-S expression.</p>
+	 * 
+	 * <p><i>Note:
+	 * 	The possible values listed in this enumeration are limited to the subset of STC-S described by the section "6 Use of STC-S in TAP (informative)"
+	 * 	of the TAP Recommendation 1.0 (27th March 2010).
+	 * </i></p>
+	 * 
+	 * @author Gr&eacute;gory Mantelet (ARI)
+	 * @version 1.3 (10/2014)
+	 * @since 1.3
+	 */
+	public static enum RegionType{
+		POSITION, CIRCLE, BOX, POLYGON, UNION, INTERSECTION, NOT;
+	}
+
+	/**
+	 * <p>Object representation of an STC region.</p>
+	 * 
+	 * <p>
+	 * 	This class contains a field for each possible parameter of a region. Depending of the region type
+	 * 	some are not used. In such case, these unused fields are set to NULL.
+	 * </p>
+	 * 
+	 * <p>
+	 * 	An instance of this class can be easily serialized into STC-S using {@link #toSTCS()}, {@link #toFullSTCS()}
+	 * 	or {@link #toString()}. {@link #toFullSTCS()} will display default value explicit
+	 * 	on the contrary to {@link #toSTCS()} which will replace them by empty strings.
+	 * </p>
+	 * 
+	 * @author Gr&eacute;gory Mantelet (ARI)
+	 * @version 1.3 (10/2014)
+	 * @since 1.3
+	 */
+	public static class Region {
+		/** Type of the region. */
+		public final RegionType type;
+
+		/** Coordinate system used by this region.
+		 * <i>Note: only the NOT region does not declare a coordinate system ; so only for this region this field is NULL.</i> */
+		public final CoordSys coordSys;
+
+		/** List of coordinates' pairs. The second dimension of this array represents a pair of coordinates ; it is then an array of two elements.
+		 * <i>Note: this field is used by POINT, BOX, CIRCLE and POLYGON.</i> */
+		public final double[][] coordinates;
+
+		/** Width of the BOX region. */
+		public final double width;
+
+		/** Height of the BOX region. */
+		public final double height;
+
+		/** Radius of the CIRCLE region. */
+		public final double radius;
+
+		/** List of regions unified (UNION), intersected (INTERSECTION) or avoided (NOT). */
+		public final Region[] regions;
+
+		/** STC-S representation of this region, in which default values of the coordinate system (if any) are not written (they are replaced by empty strings).
+		 * <i>Note: This attribute is NULL until the first call of the function {@link #toSTCS()} where it is built.</i> */
+		private String stcs = null;
+
+		/** STC-S representation of this region, in which default values of the coordinate system (if any) are explicitly written.
+		 * <i>Note: This attribute is NULL until the first call of the function {@link #toFullSTCS()} where it is built.</i> */
+		private String fullStcs = null;
+
+		/** The ADQL function object representing this region.
+		 * <i>Note: this attribute is NULL until the first call of the function {@link #toGeometry()} or {@link #toGeometry(ADQLQueryFactory)}.</i> */
+		private GeometryFunction geometry = null;
+
+		/**
+		 * <p>Constructor for a POINT/POSITION region.</p>
+		 * 
+		 * <p><i><b>Important note:</b>
+		 * 	The array of coordinates is used like that. No copy is done.
+		 * </i></p>
+		 * 
+		 * @param coordSys		Coordinate system. <i>note: It MAY BE null ; if so, the default coordinate system will be chosen</li>
+		 * @param coordinates	A pair of coordinates ; coordinates[0] and coordinates[1].
+		 */
+		public Region(final CoordSys coordSys, final double[] coordinates){
+			this(coordSys, new double[][]{coordinates});
+		}
+
+		/**
+		 * <p>Constructor for a POINT/POSITION or a POLYGON region.</p>
+		 * 
+		 * <p>Whether it is a polygon or a point depends on the number of given coordinates:</p>
+		 * <ul>
+		 * 	<li>1 item => POINT/POSITION</li>
+		 * 	<li>more items => POLYGON</li>
+		 * </ul>
+		 * 
+		 * <p><i><b>Important note:</b>
+		 * 	The array of coordinates is used like that. No copy is done.
+		 * </i></p>
+		 * 
+		 * @param coordSys		Coordinate system. <i>note: It MAY BE null ; if so, the default coordinate system will be chosen</li>
+		 * @param coordinates	List of coordinates' pairs ; coordinates[n] = 1 pair = 2 items (coordinates[n][0] and coordinates[n][1]) ; if 1 pair, it is a POINT/POSITION, but if more, it is a POLYGON.
+		 */
+		public Region(final CoordSys coordSys, final double[][] coordinates){
+			// Check roughly the coordinates:
+			if (coordinates == null || coordinates.length == 0)
+				throw new NullPointerException("Missing coordinates!");
+			else if (coordinates[0].length != 2)
+				throw new IllegalArgumentException("Wrong number of coordinates! Expected at least 2 pairs of coordinates (so coordinates[0], coordinates[1] and coordinates[n].length = 2).");
+
+			// Decide of the region type in function of the number of coordinates' pairs:
+			type = (coordinates.length > 1) ? RegionType.POLYGON : RegionType.POSITION;
+
+			// Set the coordinate system (if NULL, choose the default one):
+			this.coordSys = (coordSys == null ? new CoordSys() : coordSys);
+
+			// Set the coordinates:
+			this.coordinates = coordinates;
+
+			// Set the other fields as not used:
+			width = Double.NaN;
+			height = Double.NaN;
+			radius = Double.NaN;
+			regions = null;
+		}
+
+		/**
+		 * <p>Constructor for a CIRCLE region.</p>
+		 * 
+		 * <p><i><b>Important note:</b>
+		 * 	The array of coordinates is used like that. No copy is done.
+		 * </i></p>
+		 * 
+		 * @param coordSys		Coordinate system. <i>note: It MAY BE null ; if so, the default coordinate system will be chosen</li>
+		 * @param coordinates	A pair of coordinates ; coordinates[0] and coordinates[1].
+		 * @param radius		The circle radius.
+		 */
+		public Region(final CoordSys coordSys, final double[] coordinates, final double radius){
+			// Check roughly the coordinates:
+			if (coordinates == null || coordinates.length == 0)
+				throw new NullPointerException("Missing coordinates!");
+			else if (coordinates.length != 2)
+				throw new IllegalArgumentException("Wrong number of coordinates! Expected exactly 2 values.");
+
+			// Set the region type:
+			type = RegionType.CIRCLE;
+
+			// Set the coordinate system (if NULL, choose the default one):
+			this.coordSys = (coordSys == null ? new CoordSys() : coordSys);
+
+			// Set the coordinates:
+			this.coordinates = new double[][]{coordinates};
+
+			// Set the radius:
+			this.radius = radius;
+
+			// Set the other fields as not used:
+			width = Double.NaN;
+			height = Double.NaN;
+			regions = null;
+		}
+
+		/**
+		 * <p>Constructor for a BOX region.</p>
+		 * 
+		 * <p><i><b>Important note:</b>
+		 * 	The array of coordinates is used like that. No copy is done.
+		 * </i></p>
+		 * 
+		 * @param coordSys		Coordinate system. <i>note: It MAY BE null ; if so, the default coordinate system will be chosen</li>
+		 * @param coordinates	A pair of coordinates ; coordinates[0] and coordinates[1].
+		 * @param width			Width of the box.
+		 * @param height		Height of the box.
+		 */
+		public Region(final CoordSys coordSys, final double[] coordinates, final double width, final double height){
+			// Check roughly the coordinates:
+			if (coordinates == null || coordinates.length == 0)
+				throw new NullPointerException("Missing coordinates!");
+			else if (coordinates.length != 2)
+				throw new IllegalArgumentException("Wrong number of coordinates! Expected exactly 2 values.");
+
+			// Set the region type:
+			type = RegionType.BOX;
+
+			// Set the coordinate system (if NULL, choose the default one):
+			this.coordSys = (coordSys == null ? new CoordSys() : coordSys);
+
+			// Set the coordinates:
+			this.coordinates = new double[][]{coordinates};
+
+			// Set the size of the box:
+			this.width = width;
+			this.height = height;
+
+			// Set the other fields as not used:
+			radius = Double.NaN;
+			regions = null;
+		}
+
+		/**
+		 * <p>Constructor for a UNION or INTERSECTION region.</p>
+		 * 
+		 * <p><i><b>Important note:</b>
+		 * 	The array of regions is used like that. No copy is done.
+		 * </i></p>
+		 * 
+		 * @param unionOrIntersection	Type of the region to create. <i>Note: It can be ONLY a UNION or INTERSECTION. Another value will throw an IllegalArgumentException).</i>
+		 * @param coordSys				Coordinate system. <i>note: It MAY BE null ; if so, the default coordinate system will be chosen</li>
+		 * @param regions				Regions to unite or to intersect. <i>Note: At least two regions must be provided.</i>
+		 */
+		public Region(final RegionType unionOrIntersection, final CoordSys coordSys, final Region[] regions){
+			// Check the type:
+			if (unionOrIntersection == null)
+				throw new NullPointerException("Missing type of region (UNION or INTERSECTION here)!");
+			else if (unionOrIntersection != RegionType.UNION && unionOrIntersection != RegionType.INTERSECTION)
+				throw new IllegalArgumentException("Wrong region type: \"" + unionOrIntersection + "\"! This constructor lets create only an UNION or INTERSECTION region.");
+
+			// Check the list of regions:
+			if (regions == null || regions.length == 0)
+				throw new NullPointerException("Missing regions to " + (unionOrIntersection == RegionType.UNION ? "unite" : "intersect") + "!");
+			else if (regions.length < 2)
+				throw new IllegalArgumentException("Wrong number of regions! Expected at least 2 regions.");
+
+			// Set the region type:
+			type = unionOrIntersection;
+
+			// Set the coordinate system (if NULL, choose the default one):
+			this.coordSys = (coordSys == null ? new CoordSys() : coordSys);
+
+			// Set the regions:
+			this.regions = regions;
+
+			// Set the other fields as not used:
+			coordinates = null;
+			radius = Double.NaN;
+			width = Double.NaN;
+			height = Double.NaN;
+		}
+
+		/**
+		 * Constructor for a NOT region.
+		 * 
+		 * @param region	Any region to not select.
+		 */
+		public Region(final Region region){
+			// Check the region parameter:
+			if (region == null)
+				throw new NullPointerException("Missing region to NOT select!");
+
+			// Set the region type:
+			type = RegionType.NOT;
+
+			// Set the regions:
+			this.regions = new Region[]{region};
+
+			// Set the other fields as not used:
+			coordSys = null;
+			coordinates = null;
+			radius = Double.NaN;
+			width = Double.NaN;
+			height = Double.NaN;
+		}
+
+		/**
+		 * <p>Build a Region from the given ADQL representation.</p>
+		 * 
+		 * <p><i>Note:
+		 * 	Only {@link PointFunction}, {@link CircleFunction}, {@link BoxFunction}, {@link PolygonFunction} and {@link RegionFunction}
+		 * 	are accepted here. Other extensions of {@link GeometryFunction} will throw an {@link IllegalArgumentException}.
+		 * </i></p>
+		 * 
+		 * @param geometry	The ADQL representation of the region to create here.
+		 * 
+		 * @throws IllegalArgumentException	If the given geometry is neither of {@link PointFunction}, {@link BoxFunction}, {@link PolygonFunction} and {@link RegionFunction}.
+		 * @throws ParseException			If the declared coordinate system, the coordinates or the STC-S definition has a wrong syntax.
+		 */
+		public Region(final GeometryFunction geometry) throws IllegalArgumentException, ParseException{
+			if (geometry == null)
+				throw new NullPointerException("Missing geometry to convert into STCS.Region!");
+
+			if (geometry instanceof PointFunction){
+				type = RegionType.POSITION;
+				coordSys = STCS.parseCoordSys(extractString(geometry.getCoordinateSystem()));
+				coordinates = new double[][]{{extractNumeric(((PointFunction)geometry).getCoord1()),extractNumeric(((PointFunction)geometry).getCoord2())}};
+				width = Double.NaN;
+				height = Double.NaN;
+				radius = Double.NaN;
+				regions = null;
+			}else if (geometry instanceof CircleFunction){
+				type = RegionType.CIRCLE;
+				coordSys = STCS.parseCoordSys(extractString(geometry.getCoordinateSystem()));
+				coordinates = new double[][]{{extractNumeric(((CircleFunction)geometry).getCoord1()),extractNumeric(((CircleFunction)geometry).getCoord2())}};
+				radius = extractNumeric(((CircleFunction)geometry).getRadius());
+				width = Double.NaN;
+				height = Double.NaN;
+				regions = null;
+			}else if (geometry instanceof BoxFunction){
+				type = RegionType.BOX;
+				coordSys = STCS.parseCoordSys(extractString(geometry.getCoordinateSystem()));
+				coordinates = new double[][]{{extractNumeric(((BoxFunction)geometry).getCoord1()),extractNumeric(((BoxFunction)geometry).getCoord2())}};
+				width = extractNumeric(((BoxFunction)geometry).getWidth());
+				height = extractNumeric(((BoxFunction)geometry).getHeight());
+				radius = Double.NaN;
+				regions = null;
+			}else if (geometry instanceof PolygonFunction){
+				PolygonFunction poly = (PolygonFunction)geometry;
+				type = RegionType.POLYGON;
+				coordSys = STCS.parseCoordSys(extractString(poly.getCoordinateSystem()));
+				coordinates = new double[(poly.getNbParameters() - 1) / 2][2];
+				for(int i = 0; i < coordinates.length; i++)
+					coordinates[i] = new double[]{extractNumeric(poly.getParameter(1 + i * 2)),extractNumeric(poly.getParameter(2 + i * 2))};
+				width = Double.NaN;
+				height = Double.NaN;
+				radius = Double.NaN;
+				regions = null;
+			}else if (geometry instanceof RegionFunction){
+				Region r = STCS.parseRegion(extractString(((RegionFunction)geometry).getParameter(0)));
+				type = r.type;
+				coordSys = r.coordSys;
+				coordinates = r.coordinates;
+				width = r.width;
+				height = r.height;
+				radius = r.radius;
+				regions = r.regions;
+			}else
+				throw new IllegalArgumentException("Unknown region type! Only geometrical function PointFunction, CircleFunction, BoxFunction, PolygonFunction and RegionFunction are allowed.");
+		}
+
+		/**
+		 * Extract a string value from the given {@link ADQLOperand}
+		 * which is <b>expected to be a {@link StringConstant} instance</b>.
+		 * 
+		 * @param op	A string operand.
+		 * 
+		 * @return	The string value embedded in the given operand.
+		 * 
+		 * @throws ParseException	If the given operand is not an instance of {@link StringConstant}.
+		 */
+		private static String extractString(final ADQLOperand op) throws ParseException{
+			if (op == null)
+				throw new NullPointerException("Missing operand!");
+			else if (op instanceof StringConstant)
+				return ((StringConstant)op).getValue();
+			else
+				throw new ParseException("Can not convert into STC-S a non string argument (including ADQLColumn and Concatenation)!");
+		}
+
+		/**
+		 * Extract a numeric value from the given {@link ADQLOperand}
+		 * which is <b>expected to be a {@link NumericConstant} instance</b>
+		 * or a {@link NegativeOperand} embedding a {@link NumericConstant}.
+		 * 
+		 * @param op	A numeric operand.
+		 * 
+		 * @return	The numeric value embedded in the given operand.
+		 * 
+		 * @throws ParseException	If the given operand is not an instance of {@link NumericConstant} or a {@link NegativeOperand}.
+		 */
+		private static double extractNumeric(final ADQLOperand op) throws ParseException{
+			if (op == null)
+				throw new NullPointerException("Missing operand!");
+			else if (op instanceof NumericConstant)
+				return Double.parseDouble(((NumericConstant)op).getValue());
+			else if (op instanceof NegativeOperand)
+				return extractNumeric(((NegativeOperand)op).getOperand()) * -1;
+			else
+				throw new ParseException("Can not convert into STC-S a non numeric argument (including ADQLColumn and Operation)!");
+		}
+
+		/**
+		 * <p>Get the STC-S representation of this region (in which default values
+		 * of the coordinate system are not written ; they are replaced by empty strings).</p>
+		 * 
+		 * <p><i>Note:
+		 * 	This function build the STC-S just once and store it in a class attribute.
+		 * 	The value of this attribute is then returned at next calls of this function.
+		 * </i></p>
+		 * 
+		 * @return	Its STC-S representation.
+		 */
+		public String toSTCS(){
+			if (stcs != null)
+				return stcs;
+			else{
+				// Write the region type:
+				StringBuffer buf = new StringBuffer(type.toString());
+
+				// Write the coordinate system (except for NOT):
+				if (type != RegionType.NOT){
+					String coordSysStr = coordSys.toSTCS();
+					if (coordSysStr != null && coordSysStr.length() > 0)
+						buf.append(' ').append(coordSysStr);
+					buf.append(' ');
+				}
+
+				// Write the other parameters (coordinates, regions, ...):
+				switch(type){
+					case POSITION:
+					case POLYGON:
+						appendCoordinates(buf, coordinates);
+						break;
+					case CIRCLE:
+						appendCoordinates(buf, coordinates);
+						buf.append(' ').append(radius);
+						break;
+					case BOX:
+						appendCoordinates(buf, coordinates);
+						buf.append(' ').append(width).append(' ').append(height);
+						break;
+					case UNION:
+					case INTERSECTION:
+					case NOT:
+						buf.append('(');
+						appendRegions(buf, regions, false);
+						buf.append(')');
+						break;
+				}
+
+				// Return the built STC-S:
+				return (stcs = buf.toString());
+			}
+		}
+
+		/**
+		 * <p>Get the STC-S representation of this region (in which default values
+		 * of the coordinate system are explicitly written).</p>
+		 * 
+		 * <p><i>Note:
+		 * 	This function build the STC-S just once and store it in a class attribute.
+		 * 	The value of this attribute is then returned at next calls of this function.
+		 * </i></p>
+		 * 
+		 * @return	Its STC-S representation.
+		 */
+		public String toFullSTCS(){
+			if (fullStcs != null)
+				return fullStcs;
+			else{
+				// Write the region type:
+				StringBuffer buf = new StringBuffer(type.toString());
+
+				// Write the coordinate system (except for NOT):
+				if (type != RegionType.NOT){
+					String coordSysStr = coordSys.toFullSTCS();
+					if (coordSysStr != null && coordSysStr.length() > 0)
+						buf.append(' ').append(coordSysStr);
+					buf.append(' ');
+				}
+
+				// Write the other parameters (coordinates, regions, ...):
+				switch(type){
+					case POSITION:
+					case POLYGON:
+						appendCoordinates(buf, coordinates);
+						break;
+					case CIRCLE:
+						appendCoordinates(buf, coordinates);
+						buf.append(' ').append(radius);
+						break;
+					case BOX:
+						appendCoordinates(buf, coordinates);
+						buf.append(' ').append(width).append(' ').append(height);
+						break;
+					case UNION:
+					case INTERSECTION:
+					case NOT:
+						buf.append('(');
+						appendRegions(buf, regions, true);
+						buf.append(')');
+						break;
+				}
+
+				// Return the built STC-S:
+				return (fullStcs = buf.toString());
+			}
+		}
+
+		/**
+		 * Append all the given coordinates to the given buffer.
+		 * 
+		 * @param buf		Buffer in which coordinates must be appended.
+		 * @param coords	Coordinates to append.
+		 */
+		private static void appendCoordinates(final StringBuffer buf, final double[][] coords){
+			for(int i = 0; i < coords.length; i++){
+				if (i > 0)
+					buf.append(' ');
+				buf.append(coords[i][0]).append(' ').append(coords[i][1]);
+			}
+		}
+
+		/**
+		 * Append all the given regions in the given buffer.
+		 * 
+		 * @param buf			Buffer in which regions must be appended.
+		 * @param regions		Regions to append.
+		 * @param fullCoordSys	Indicate whether the coordinate system of the regions must explicitly display the default values.
+		 */
+		private static void appendRegions(final StringBuffer buf, final Region[] regions, final boolean fullCoordSys){
+			for(int i = 0; i < regions.length; i++){
+				if (i > 0)
+					buf.append(' ');
+				if (fullCoordSys)
+					buf.append(regions[i].toFullSTCS());
+				else
+					buf.append(regions[i].toSTCS());
+			}
+		}
+
+		@Override
+		public String toString(){
+			return toSTCS();
+		}
+
+		/**
+		 * <p>Convert this region into its corresponding ADQL representation.</p>
+		 * 
+		 * <ul>
+		 * 	<li><b>POSITION:</b> {@link PointFunction}</li>
+		 * 	<li><b>CIRCLE:</b> {@link CircleFunction}</li>
+		 * 	<li><b>BOX:</b> {@link BoxFunction}</li>
+		 * 	<li><b>POLYGON:</b> {@link PolygonFunction}</li>
+		 * 	<li><b>UNION, INTERSECTION, NOT:</b> {@link RegionFunction}</li>
+		 * </ul>
+		 * 
+		 * <p><i>Note:
+		 * 	This function is using the default ADQL factory, built using {@link ADQLQueryFactory#ADQLQueryFactory()}.
+		 * </i></p>
+		 * 
+		 * @return	The corresponding ADQL representation.
+		 * 
+		 * @see #toGeometry(ADQLQueryFactory)
+		 */
+		public GeometryFunction toGeometry(){
+			return toGeometry(null);
+		}
+
+		/**
+		 * <p>Convert this region into its corresponding ADQL representation.</p>
+		 * 
+		 * <ul>
+		 * 	<li><b>POSITION:</b> {@link PointFunction}</li>
+		 * 	<li><b>CIRCLE:</b> {@link CircleFunction}</li>
+		 * 	<li><b>BOX:</b> {@link BoxFunction}</li>
+		 * 	<li><b>POLYGON:</b> {@link PolygonFunction}</li>
+		 * 	<li><b>UNION, INTERSECTION, NOT:</b> {@link RegionFunction}</li>
+		 * </ul>
+		 * 
+		 * <p><i>Note:
+		 * 	This function build the ADQL representation just once and store it in a class attribute.
+		 * 	The value of this attribute is then returned at next calls of this function.
+		 * </i></p>
+		 * 
+		 * @param factory	The factory of ADQL objects to use.
+		 * 
+		 * @return	The corresponding ADQL representation.
+		 */
+		public GeometryFunction toGeometry(ADQLQueryFactory factory){
+			if (factory == null)
+				factory = new ADQLQueryFactory();
+
+			try{
+				if (geometry != null)
+					return geometry;
+				else{
+					StringConstant coordSysObj = factory.createStringConstant(coordSys == null ? "" : coordSys.toString());
+					switch(type){
+						case POSITION:
+							return (geometry = factory.createPoint(coordSysObj, toNumericObj(coordinates[0][0], factory), toNumericObj(coordinates[0][1], factory)));
+						case CIRCLE:
+							return (geometry = factory.createCircle(coordSysObj, toNumericObj(coordinates[0][0], factory), toNumericObj(coordinates[0][1], factory), toNumericObj(radius, factory)));
+						case BOX:
+							return (geometry = factory.createBox(coordSysObj, toNumericObj(coordinates[0][0], factory), toNumericObj(coordinates[0][1], factory), toNumericObj(width, factory), toNumericObj(height, factory)));
+						case POLYGON:
+							ArrayList<ADQLOperand> coords = new ArrayList<ADQLOperand>(coordinates.length * 2);
+							for(int i = 0; i < coordinates.length; i++){
+								coords.add(toNumericObj(coordinates[i][0], factory));
+								coords.add(toNumericObj(coordinates[i][1], factory));
+							}
+							return (geometry = factory.createPolygon(coordSysObj, coords));
+						default:
+							return (geometry = factory.createRegion(factory.createStringConstant(toString())));
+					}
+				}
+			}catch(Exception pe){
+				return null;
+			}
+		}
+
+		/**
+		 * <p>Convert a numeric value into an ADQL representation:</p>
+		 * 
+		 * <ul>
+		 * 	<li>If negative: NegativeOperand(NumericConstant(val))</li>
+		 * 	<li>Otherwise: NumericConstant(val)</li>
+		 * </ul>
+		 * 
+		 * @param val		The value to embed in an ADQL object.
+		 * @param factory	The factory to use to created ADQL objects.
+		 * 
+		 * @return	The representing ADQL representation.
+		 * 
+		 * @throws Exception	If an error occurs while creating the ADQL object.
+		 */
+		private ADQLOperand toNumericObj(final double val, final ADQLQueryFactory factory) throws Exception{
+			if (val >= 0)
+				return factory.createNumericConstant("" + val);
+			else
+				return factory.createNegativeOperand(factory.createNumericConstant("" + (val * -1)));
+		}
+	}
+
+	/**
+	 * Parse the given STC-S expression representing a geometrical region.
+	 * 
+	 * @param stcsRegion	STC-S expression of a region. <i>Note: MUST be different from NULL.</i>
+	 * 
+	 * @return	The object representation of the specified geometrical region.
+	 * 
+	 * @throws ParseException	If the given expression is NULL, empty string or if the STC-S syntax is wrong.
+	 */
+	public static Region parseRegion(final String stcsRegion) throws ParseException{
+		if (stcsRegion == null || stcsRegion.trim().length() == 0)
+			throw new ParseException("Missing STC-S expression to parse!");
+		return (new STCSParser().parseRegion(stcsRegion));
+	}
+
+	/**
+	 * Convert into STC-S the given object representation of a geometrical region.
+	 * 
+	 * @param region	Region to convert into STC-S.
+	 * 
+	 * @return	The corresponding STC-S expression.
+	 */
+	public static String toSTCS(final Region region){
+		if (region == null)
+			throw new NullPointerException("Missing region to serialize into STC-S!");
+		return region.toSTCS();
+	}
+
+	/**
+	 * <p>Convert into STC-S the given ADQL representation of a geometrical function.</p>
+	 * 
+	 * <p><i><b>Important note:</b>
+	 * 	Only {@link PointFunction}, {@link CircleFunction}, {@link BoxFunction}, {@link PolygonFunction}
+	 * 	and {@link RegionFunction} are accepted here. Other extensions of {@link GeometryFunction} will
+	 * 	throw an {@link IllegalArgumentException}.
+	 * </i></p>
+	 * 
+	 * @param region	ADQL representation of the region to convert into STC-S.
+	 * 
+	 * @return	The corresponding STC-S expression.
+	 * 
+	 * @throws ParseException	If the given object is NULL or not of the good type.
+	 * 
+	 * @see {@link Region#Region(GeometryFunction)}
+	 */
+	public static String toSTCS(final GeometryFunction region) throws ParseException{
+		if (region == null)
+			throw new NullPointerException("Missing region to serialize into STC-S!");
+		return (new Region(region)).toSTCS();
+	}
+
+	/* *************************** */
+	/* PARSER OF STC-S EXPRESSIONS */
+	/* *************************** */
+
+	/**
+	 * Let parse any STC-S expression.
+	 * 
+	 * @author Gr&eacute;gory Mantelet (ARI)
+	 * @version 1.3 (10/2014)
+	 * @since 1.3
+	 */
+	private static class STCSParser {
+		/** Regular expression of a numerical value. */
+		private final static String numericRegExp = "(\\+|-)?(\\d+(\\.\\d*)?|\\.\\d+)([Ee](\\+|-)?\\d+)?";
+
+		/** Position of the next characters to read in the STC-S expression to parse. */
+		private int pos;
+		/** Full STC-S expression to parse. */
+		private String stcs;
+		/** Last read token (can be a numeric, a string, a region type, ...). */
+		private String token;
+		/** Buffer used to read tokens. */
+		private StringBuffer buffer;
+
+		/**
+		 * Exception sent when the end of the expression
+		 * (EOE = End Of Expression) is reached.
+		 * 
+		 * @author Gr&eacute;gory Mantelet (ARI)
+		 * @version 1.3 (10/2014)
+		 * @since 1.3
+		 */
+		private static class EOEException extends ParseException {
+			private static final long serialVersionUID = 1L;
+
+			/** Build a simple EOEException. */
+			public EOEException(){
+				super("Unexpected End Of Expression!");
+			}
+		}
+
+		/**
+		 * Build the STC-S parser.
+		 */
+		public STCSParser(){}
+
+		/**
+		 * Parse the given STC-S expression, expected as a coordinate system.
+		 * 
+		 * @param stcs	The STC-S expression to parse.
+		 * 
+		 * @return	The corresponding object representation of the specified coordinate system.
+		 * 
+		 * @throws ParseException	If the syntax of the given STC-S expression is wrong or if it is not a coordinate system.
+		 */
+		public CoordSys parseCoordSys(final String stcs) throws ParseException{
+			init(stcs);
+			CoordSys coordsys = null;
+			try{
+				coordsys = coordSys();
+				end(COORD_SYS_SYNTAX);
+				return coordsys;
+			}catch(EOEException ex){
+				ex.printStackTrace();
+				return new CoordSys();
+			}
+		}
+
+		/**
+		 * Parse the given STC-S expression, expected as a geometrical region.
+		 * 
+		 * @param stcs	The STC-S expression to parse.
+		 * 
+		 * @return	The corresponding object representation of the specified geometrical region.
+		 * 
+		 * @throws ParseException	If the syntax of the given STC-S expression is wrong or if it is not a geometrical region.
+		 */
+		public Region parseRegion(final String stcs) throws ParseException{
+			init(stcs);
+			Region region = region();
+			end("\"POSITION <coordsys> <coordPair>\", \"CIRCLE <coordSys> <coordPair> <numeric>\", \"BOX <coordSys> <coordPair> <coordPair>\", \"POLYGON <coordSys> <coordPair> <coordPair> <coordPair> [<coordPair> ...]\", \"UNION <coordSys> ( <region> <region> [<region> ...] )\", \"INTERSECTION [<coordSys>] ( <region> <region> [<region> ...] )\" or \"NOT ( <region> )\"");
+			return region;
+		}
+
+		/**
+		 * Prepare the parser in order to read the given STC-S expression.
+		 * 
+		 * @param newStcs	New STC-S expression to parse from now.
+		 */
+		private void init(final String newStcs){
+			stcs = (newStcs == null) ? "" : newStcs.replaceAll("\\s", " ");
+			token = null;
+			buffer = new StringBuffer();
+			pos = 0;
+		}
+
+		/**
+		 * Finalize the parsing.
+		 * No more characters (except eventually some space characters) should remain in the STC-S expression to parse.
+		 * 
+		 * @param expectedSyntax	Description of the good syntax expected. This description is used only to write the
+		 *                      	{@link ParseException} in case other non-space characters are found among the remaining characters.
+		 * 
+		 * @throws ParseException	If other non-space characters remains. 
+		 */
+		private void end(final String expectedSyntax) throws ParseException{
+			// Skip all spaces:
+			skipSpaces();
+
+			// If there is still some characters, they are not expected, and so throw an exception:
+			if (stcs.length() > 0 && pos < stcs.length())
+				throw new ParseException("Incorrect syntax: \"" + stcs.substring(pos) + "\" was unexpected! Expected syntax: " + expectedSyntax + ".", new TextPosition(1, pos, 1, stcs.length()));
+
+			// Reset the buffer, token and the STC-S expression to parse:
+			buffer = null;
+			stcs = null;
+			token = null;
+		}
+
+		/**
+		 * Tool function which skip all next space characters until the next meaningful characters. 
+		 */
+		private void skipSpaces(){
+			while(pos < stcs.length() && stcs.charAt(pos) == ' ')
+				pos++;
+		}
+
+		/**
+		 * <p>Get the next meaningful word. This word can be a numeric, any string constant or a region type.</p>
+		 * 
+		 * <p>
+		 * 	In case the end of the expression is reached before getting any meaningful character, an {@link EOEException} is thrown.
+		 * </p>
+		 * 
+		 * @return	The full read word/token.
+		 * 
+		 * @throws EOEException	If the end of the STC-S expression is reached before getting any meaningful character.
+		 */
+		private String nextToken() throws EOEException{
+			// Skip all spaces:
+			skipSpaces();
+
+			// Fetch all characters until word separator (a space or a open/close parenthesis):
+			while(pos < stcs.length() && stcs.charAt(pos) != ' ' && stcs.charAt(pos) != '(' && stcs.charAt(pos) != ')')
+				buffer.append(stcs.charAt(pos++));
+
+			// If no character has been fetched while at least one was expected, throw an exception:
+			if (buffer.length() == 0)
+				throw new EOEException();
+
+			// Save the read token and reset the buffer:
+			token = buffer.toString();
+			buffer.delete(0, token.length());
+
+			return token;
+		}
+
+		/**
+		 * Read the next token as a numeric.
+		 * If not a numeric, a {@link ParseException} is thrown.
+		 * 
+		 * @return	The read numerical value.
+		 * 
+		 * @throws ParseException	If the next token is not a numerical expression.
+		 */
+		private double numeric() throws ParseException{
+			if (nextToken().matches(numericRegExp))
+				return Double.parseDouble(token);
+			else
+				throw new ParseException("a numeric was expected!", new TextPosition(1, pos - token.length(), 1, pos));	// TODO Check the begin and end!
+		}
+
+		/**
+		 * Read the next 2 tokens as a coordinate pairs (so as 2 numerical values).
+		 * If not 2 numeric, a {@link ParseException} is thrown.
+		 * 
+		 * @return	The read coordinate pairs.
+		 * 
+		 * @throws ParseException	If the next 2 tokens are not 2 numerical expressions.
+		 */
+		private double[] coordPair() throws ParseException{
+			skipSpaces();
+			int startPos = pos;
+			try{
+				return new double[]{numeric(),numeric()};
+			}catch(ParseException pe){
+				if (pe instanceof EOEException)
+					throw pe;
+				else
+					throw new ParseException("a coordinates pair (2 numerics separated by one or more spaces) was expected!", new TextPosition(1, startPos, 1, pos));	// TODO Check the begin and end!
+			}
+		}
+
+		/**
+		 * Read and parse the next tokens as a coordinate system expression.
+		 * If they do not match, a {@link ParseException} is thrown.
+		 * 
+		 * @return	The object representation of the read coordinate system.
+		 * 
+		 * @throws ParseException	If the next tokens are not representing a valid coordinate system.
+		 */
+		private CoordSys coordSys() throws ParseException{
+			// Skip all spaces:
+			skipSpaces();
+
+			// Backup the current position:
+			/* (because every parts of a coordinate system are optional ;
+			 * like this, it will be possible to go back in the expression
+			 * to parse if optional parts are not written) */
+			String oldToken = token;
+			int startPos = pos;
+
+			Frame fr = null;
+			RefPos rp = null;
+			Flavor fl = null;
+
+			try{
+				// Read the token:
+				nextToken();
+				// Try to parse it as a frame:
+				if ((fr = frame()) != null){
+					// if success, go the next token:
+					startPos = pos;
+					oldToken = token;
+					nextToken();
+				}
+				// Try to parse the last read token as a reference position:
+				if ((rp = refpos()) != null){
+					// if success, go the next token:
+					startPos = pos;
+					oldToken = token;
+					nextToken();
+				}
+				// Try to parse the last read token as a flavor:
+				if ((fl = flavor()) == null){
+					// if NOT a success, go back "in time" (go back to the position before reading the token):
+					pos = startPos;
+					token = oldToken;
+				}
+			}catch(EOEException ex){
+				/* End Of Expression may happen here since all parts of a coordinate system are optional.
+				 * So, there is no need to treat the error. */
+			}
+
+			// Build the object representation of the read coordinate system:
+			/* Note: if nothing has been read for one or all parts of the coordinate system,
+			 * the NULL value will be replaced automatically in the constructor
+			 * by the default value of the corresponding part(s). */
+			try{
+				return new CoordSys(fr, rp, fl);
+			}catch(IllegalArgumentException iae){
+				throw new ParseException(iae.getMessage(), new TextPosition(1, startPos, 1, pos));
+			}
+		}
+
+		/**
+		 * Parse the last read token as FRAME.
+		 * 
+		 * @return	The corresponding enumeration item, or NULL if the last token is not a valid FRAME item.
+		 */
+		private Frame frame(){
+			try{
+				return Frame.valueOf(token.toUpperCase());
+			}catch(IllegalArgumentException iae){
+				return null;
+			}
+		}
+
+		/**
+		 * Parse the last read token as REFERENCE POSITION.
+		 * 
+		 * @return	The corresponding enumeration item, or NULL if the last token is not a valid REFERENCE POSITION item.
+		 */
+		private RefPos refpos(){
+			try{
+				return RefPos.valueOf(token.toUpperCase());
+			}catch(IllegalArgumentException iae){
+				return null;
+			}
+		}
+
+		/**
+		 * Parse the last read token as FLAVOR.
+		 * 
+		 * @return	The corresponding enumeration item, or NULL if the last token is not a valid FLAVOR item.
+		 */
+		private Flavor flavor(){
+			try{
+				return Flavor.valueOf(token.toUpperCase());
+			}catch(IllegalArgumentException iae){
+				return null;
+			}
+		}
+
+		/**
+		 * Read and parse the next tokens as a geometrical region.
+		 * If they do not match, a {@link ParseException} is thrown.
+		 * 
+		 * @return	The object representation of the read geometrical region.
+		 * 
+		 * @throws ParseException	If the next tokens are not representing a valid geometrical region.
+		 */
+		private Region region() throws ParseException{
+			// Skip all spaces:
+			skipSpaces();
+
+			// Read the next token (it should be the region type):
+			int startPos = pos;
+			token = nextToken().toUpperCase();
+
+			/* Identify the region type, next the expected parameters and finally build the corresponding object representation */
+			// POSITION case:
+			if (token.equals("POSITION")){
+				try{
+					CoordSys coordSys = coordSys();
+					double[] coords = coordPair();
+					return new Region(coordSys, coords);
+				}catch(Exception e){
+					throw buildException(e, "\"POSITION <coordSys> <coordPair>\", where coordPair=\"<numeric> <numeric>\" and coordSys=" + COORD_SYS_SYNTAX, startPos);
+				}
+			}
+			// CIRCLE case:
+			else if (token.equals("CIRCLE")){
+				try{
+					CoordSys coordSys = coordSys();
+					double[] coords = coordPair();
+					double radius = numeric();
+					return new Region(coordSys, coords, radius);
+				}catch(Exception e){
+					throw buildException(e, "\"CIRCLE <coordSys> <coordPair> <radius>\", where coordPair=\"<numeric> <numeric>\", radius=\"<numeric>\" and coordSys=" + COORD_SYS_SYNTAX, startPos);
+				}
+			}
+			// BOX case:
+			else if (token.equals("BOX")){
+				try{
+					CoordSys coordSys = coordSys();
+					double[] coords = coordPair();
+					double width = numeric(), height = numeric();
+					return new Region(coordSys, coords, width, height);
+				}catch(Exception e){
+					throw buildException(e, "\"BOX <coordSys> <coordPair> <width> <height>\", where coordPair=\"<numeric> <numeric>\", width and height=\"<numeric>\" and coordSys=" + COORD_SYS_SYNTAX, startPos);
+				}
+			}
+			// POLYGON case:
+			else if (token.equals("POLYGON")){
+				try{
+					CoordSys coordSys = coordSys();
+					ArrayList<Double> coordinates = new ArrayList<Double>(6);
+					double[] coords;
+					for(int i = 0; i < 3; i++){
+						coords = coordPair();
+						coordinates.add(coords[0]);
+						coordinates.add(coords[1]);
+					}
+					boolean moreCoord = true;
+					int posBackup;
+					do{
+						posBackup = pos;
+						try{
+							coords = coordPair();
+							coordinates.add(coords[0]);
+							coordinates.add(coords[1]);
+						}catch(ParseException pe){
+							moreCoord = false;
+							pos = posBackup;
+						}
+					}while(moreCoord);
+					double[][] allCoords = new double[coordinates.size() / 2][2];
+					for(int i = 0; i < coordinates.size() && i + 1 < coordinates.size(); i += 2)
+						allCoords[i / 2] = new double[]{coordinates.get(i),coordinates.get(i + 1)};
+					return new Region(coordSys, allCoords);
+				}catch(Exception e){
+					throw buildException(e, "\"POLYGON <coordSys> <coordPair> <coordPair> <coordPair> [<coordPair> ...]\", where coordPair=\"<numeric> <numeric>\" and coordSys=" + COORD_SYS_SYNTAX, startPos);
+				}
+			}
+			// UNION & INTERSECTION cases:
+			else if (token.equals("UNION") || token.equals("INTERSECTION")){
+				RegionType type = (token.equals("UNION") ? RegionType.UNION : RegionType.INTERSECTION);
+				try{
+					CoordSys coordSys = coordSys();
+					ArrayList<Region> regions = new ArrayList<Region>(2);
+
+					skipSpaces();
+					if (stcs.charAt(pos) != '(')
+						throw buildException(new ParseException("a opening parenthesis - ( - was expected!", new TextPosition(1, pos, 1, pos + 1)), "\"" + type + " <coordSys> ( <region> <region> [<region> ...] )\", where coordSys=" + COORD_SYS_SYNTAX, startPos);
+					else
+						pos++;
+
+					// parse and add the FIRST region:
+					regions.add(region());
+
+					// parse and add the SECOND region:
+					regions.add(region());
+
+					skipSpaces();
+					while(stcs.charAt(pos) != ')'){
+						regions.add(region());
+						skipSpaces();
+					}
+					pos++;
+
+					return new Region(type, coordSys, regions.toArray(new Region[regions.size()]));
+				}catch(Exception e){
+					if (e instanceof ParseException && e.getMessage().startsWith("Incorrect syntax: \""))
+						throw (ParseException)e;
+					else
+						throw buildException(e, "\"" + type + " <coordSys> ( <region> <region> [<region> ...] )\", where coordSys=" + COORD_SYS_SYNTAX, startPos);
+				}
+			}
+			// NOT case:
+			else if (token.equals("NOT")){
+				try{
+					skipSpaces();
+					if (stcs.charAt(pos) != '(')
+						throw buildException(new ParseException("an opening parenthesis - ( - was expected!", new TextPosition(1, pos, 1, pos + 1)), "\"NOT ( <region> )\"", startPos);
+					else
+						pos++;
+					Region region = region();
+					skipSpaces();
+					if (stcs.charAt(pos) != ')')
+						throw buildException(new ParseException("a closing parenthesis - ) - was expected!", new TextPosition(1, pos, 1, pos + 1)), "\"NOT ( <region> )\"", startPos);
+					else
+						pos++;
+					return new Region(region);
+				}catch(Exception e){
+					if (e instanceof ParseException && e.getMessage().startsWith("Incorrect syntax: "))
+						throw (ParseException)e;
+					else
+						throw buildException(e, "\"NOT ( <region> )\"", startPos);
+				}
+			}
+			// Otherwise, the region type is not known and so a ParseException is thrown:
+			else
+				throw new ParseException("Unknown STC region type: \"" + token + "\"!", new TextPosition(1, startPos, 1, pos));
+		}
+
+		/**
+		 * Build a {@link ParseException} based on the given one and by adding the human description of what was expected, if needed.
+		 * 
+		 * @param ex				Root exception.
+		 * @param expectedSyntax	Human description of what was expected.
+		 * @param startPos			Position of the first character of the wrong part of expression.
+		 * 
+		 * @return	The build exception.
+		 */
+		private ParseException buildException(final Exception ex, final String expectedSyntax, int startPos){
+			if (ex instanceof EOEException)
+				return new ParseException("Unexpected End Of Expression! Expected syntax: " + expectedSyntax + ".", new TextPosition(1, startPos, 1, pos));
+			else if (ex instanceof ParseException)
+				return new ParseException("Incorrect syntax: " + ex.getMessage() + " Expected syntax: " + expectedSyntax + ".", (((ParseException)ex).getPosition() != null ? ((ParseException)ex).getPosition() : new TextPosition(1, startPos, 1, pos)));
+			else
+				return new ParseException(ex.getMessage(), new TextPosition(1, startPos, 1, pos));
+		}
+	}
+}
diff --git a/src/adql/db/SearchColumnList.java b/src/adql/db/SearchColumnList.java
index 8411e4b..e385cb7 100644
--- a/src/adql/db/SearchColumnList.java
+++ b/src/adql/db/SearchColumnList.java
@@ -17,7 +17,7 @@ package adql.db;
  * along with ADQLLibrary.  If not, see <http://www.gnu.org/licenses/>.
  * 
  * Copyright 2012-2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
- *                       Astronomishes Rechen Institut (ARI)
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import java.util.ArrayList;
diff --git a/src/adql/db/exception/UnresolvedFunction.java b/src/adql/db/exception/UnresolvedFunction.java
new file mode 100644
index 0000000..b6eb459
--- /dev/null
+++ b/src/adql/db/exception/UnresolvedFunction.java
@@ -0,0 +1,124 @@
+package adql.db.exception;
+
+/*
+ * 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 - Astronomisches Rechen Institut (ARI)
+ */
+
+import adql.parser.ParseException;
+import adql.query.operand.function.ADQLFunction;
+
+/**
+ * Exception thrown when a function can not be resolved by the library.
+ * 
+ * @author Gr&eacute;gory Mantelet (ARI)
+ * @version 1.3 (10/2014)
+ * @since 1.3
+ */
+public class UnresolvedFunction extends ParseException {
+	private static final long serialVersionUID = 1L;
+
+	/** Function which can not be resolved. */
+	protected final ADQLFunction functionInError;
+
+	/**
+	 * Build the exception with just a message.
+	 * 
+	 * @param message	Description of the error.
+	 */
+	public UnresolvedFunction(final String message){
+		super(message);
+		functionInError = null;
+	}
+
+	/**
+	 * Build the exception with the unresolved function in parameter.
+	 * The position of this function in the ADQL query can be retrieved and used afterwards.
+	 * 
+	 * @param fct	The unresolved function.
+	 */
+	public UnresolvedFunction(final ADQLFunction fct){
+		super("Unresolved function: \"" + fct.toADQL() + "\"! No UDF has been defined or found with the signature: " + getFctSignature(fct) + "."); // TODO Add the position of the function in the ADQL query!
+		functionInError = fct;
+	}
+
+	/**
+	 * Build the exception with a message but also with the unresolved function in parameter.
+	 * The position of this function in the ADQL query can be retrieved and used afterwards.
+	 * 
+	 * @param message	Description of the error.
+	 * @param fct		The unresolved function.
+	 */
+	public UnresolvedFunction(final String message, final ADQLFunction fct){
+		super(message); // TODO Add the position of the function in the ADQL query!
+		functionInError = fct;
+	}
+
+	/**
+	 * Get the unresolved function at the origin of this exception.
+	 * 
+	 * @return	The unresolved function. <i>Note: MAY be NULL</i>
+	 */
+	public final ADQLFunction getFunction(){
+		return functionInError;
+	}
+
+	/**
+	 * <p>Get the signature of the function given in parameter.</p>
+	 * 
+	 * <p>
+	 * 	In this signature, just the name and the type of all the parameters are written.
+	 * 	The return type is never part of a function signature.
+	 * </p>
+	 * 
+	 * <p><i>Note 1:
+	 * 	A parameter type can be either "NUMERIC", "STRING" or "GEOMETRY". In order to be the most generic has possible,
+	 * 	no more precision about a type is returned here. If the parameter is none of these type kinds, "???" is returned.
+	 * </i></p>
+	 * 
+	 * <p><i>Note 2:
+	 * 	If the given object is NULL, an empty string is returned.
+	 * </i></p>
+	 * 
+	 * @param fct	Function whose the signature must be returned.
+	 * 
+	 * @return	The corresponding signature.
+	 */
+	public static String getFctSignature(final ADQLFunction fct){
+		if (fct == null)
+			return "";
+
+		StringBuffer buf = new StringBuffer(fct.getName().toLowerCase());
+		buf.append('(');
+		for(int i = 0; i < fct.getNbParameters(); i++){
+			if (fct.getParameter(i).isNumeric())
+				buf.append("NUMERIC");
+			else if (fct.getParameter(i).isString())
+				buf.append("STRING");
+			else if (fct.getParameter(i).isGeometry())
+				buf.append("GEOMETRY");
+			else
+				buf.append("???");
+
+			if ((i + 1) < fct.getNbParameters())
+				buf.append(", ");
+		}
+		buf.append(')');
+		return buf.toString();
+	}
+
+}
diff --git a/src/adql/db/exception/UnresolvedIdentifiersException.java b/src/adql/db/exception/UnresolvedIdentifiersException.java
index b141bef..8c20187 100644
--- a/src/adql/db/exception/UnresolvedIdentifiersException.java
+++ b/src/adql/db/exception/UnresolvedIdentifiersException.java
@@ -71,6 +71,10 @@ public class UnresolvedIdentifiersException extends ParseException implements It
 				String tableName = ((UnresolvedTableException)pe).getTableName();
 				if (tableName != null && !tableName.trim().isEmpty())
 					addIdentifierName(tableName + " " + pe.getPosition());
+				/*}else if (pe instanceof UnresolvedFunction){	// TODO MANAGE ALSO THE UNRESOLVED_FUNCTIONs!
+					String fctName = (((UnresolvedFunction)pe).getFunction() == null) ? null : ((UnresolvedFunction)pe).getFunction().getName();
+					if (fctName != null && !fctName.trim().isEmpty())
+						addIdentifierName(fctName + " " + pe.getPosition());*/
 			}else if (pe instanceof UnresolvedIdentifiersException)
 				addIdentifierName(((UnresolvedIdentifiersException)pe).unresolvedIdentifiers);
 		}
@@ -109,6 +113,7 @@ public class UnresolvedIdentifiersException extends ParseException implements It
 		return exceptions.iterator();
 	}
 
+	@Override
 	public final Iterator<ParseException> iterator(){
 		return getErrors();
 	}
@@ -120,7 +125,11 @@ public class UnresolvedIdentifiersException extends ParseException implements It
 	 */
 	@Override
 	public String getMessage(){
-		return exceptions.size() + " unresolved identifiers" + ((unresolvedIdentifiers != null) ? (": " + unresolvedIdentifiers) : "") + " !";
+		StringBuffer buf = new StringBuffer();
+		buf.append(exceptions.size()).append(" unresolved identifiers").append(((unresolvedIdentifiers != null) ? (": " + unresolvedIdentifiers) : "")).append('!');
+		for(ParseException pe : exceptions)
+			buf.append("\n  - ").append(pe.getMessage());
+		return buf.toString();
 	}
 
 }
diff --git a/src/adql/db/exception/UnresolvedJoin.java b/src/adql/db/exception/UnresolvedJoin.java
index c92440e..819beaa 100644
--- a/src/adql/db/exception/UnresolvedJoin.java
+++ b/src/adql/db/exception/UnresolvedJoin.java
@@ -16,7 +16,7 @@ package adql.db.exception;
  * 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 2013-2014 - Astronomishes Rechen Institut (ARI)
+ * Copyright 2013-2014 - Astronomisches Rechen Institut (ARI)
  */
 
 import adql.parser.ParseException;
diff --git a/src/adql/parser/ADQLParser.java b/src/adql/parser/ADQLParser.java
index b1649f8..b454aad 100644
--- a/src/adql/parser/ADQLParser.java
+++ b/src/adql/parser/ADQLParser.java
@@ -4,7 +4,6 @@ package adql.parser;
 import java.io.FileReader;
 import java.io.IOException;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Stack;
 import java.util.Vector;
 
@@ -32,7 +31,6 @@ import adql.query.operand.ADQLColumn;
 import adql.query.operand.ADQLOperand;
 import adql.query.operand.Concatenation;
 import adql.query.operand.OperationType;
-import adql.query.operand.StringConstant;
 import adql.query.operand.function.ADQLFunction;
 import adql.query.operand.function.MathFunction;
 import adql.query.operand.function.MathFunctionType;
@@ -61,7 +59,7 @@ import adql.translator.TranslationException;
 * @see ADQLQueryFactory
 *
 * @author Gr&eacute;gory Mantelet (CDS;ARI) - gmantele@ari.uni-heidelberg.de
-* @version 1.2 (03/2014)
+* @version 1.3 (10/2014)
 */
 public class ADQLParser implements ADQLParserConstants {
 
@@ -80,9 +78,6 @@ public class ADQLParser implements ADQLParserConstants {
 	/** The first token of a table/column name. This token is extracted by {@link #Identifier()}. */
 	private Token currentIdentifierToken = null;
 
-	/** List of all allowed coordinate systems. */
-	private ArrayList<String> allowedCoordSys = new ArrayList<String>();
-
 	/**
 	* Builds an ADQL parser without a query to parse.
 	*/
@@ -320,24 +315,6 @@ public class ADQLParser implements ADQLParserConstants {
 		return Query();
 	}
 
-	public final void addCoordinateSystem(final String coordSys){
-		allowedCoordSys.add(coordSys);
-	}
-
-	public final void setCoordinateSystems(final Collection<String> coordSys){
-		allowedCoordSys.clear();
-		if (coordSys != null)
-			allowedCoordSys.addAll(coordSys);
-	}
-
-	public final boolean isAllowedCoordSys(final String coordSys){
-		for(String cs : allowedCoordSys){
-			if (cs.equalsIgnoreCase(coordSys))
-				return true;
-		}
-		return false;
-	}
-
 	public final void setDebug(boolean debug){
 		if (debug)
 			enable_tracing();
@@ -493,1190 +470,1598 @@ public class ADQLParser implements ADQLParserConstants {
 	* @throws ParseException	If the query syntax is incorrect.
 	*/
 	final public ADQLQuery Query() throws ParseException{
-		trace_call("Query");
+		ADQLQuery q = null;
+		q = QueryExpression();
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case 0:
+				jj_consume_token(0);
+				break;
+			case EOQ:
+				jj_consume_token(EOQ);
+				break;
+			default:
+				jj_la1[0] = jj_gen;
+				jj_consume_token(-1);
+				throw new ParseException();
+		}
+		// check the query:
+		if (queryChecker != null)
+			queryChecker.check(q);
+
+		{
+			if (true)
+				return q;
+		}
+		throw new Error("Missing return statement in function");
+	}
+
+	final public ADQLQuery QueryExpression() throws ParseException{
 		try{
-			ADQLQuery q = null;
-			q = QueryExpression();
+			// create the query:
+			query = queryFactory.createQuery();
+			stackQuery.push(query);
+		}catch(Exception ex){
+			{
+				if (true)
+					throw generateParseException(ex);
+			}
+		}
+		Select();
+		From();
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case WHERE:
+				Where();
+				break;
+			default:
+				jj_la1[1] = jj_gen;
+				;
+		}
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case GROUP_BY:
+				GroupBy();
+				break;
+			default:
+				jj_la1[2] = jj_gen;
+				;
+		}
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case HAVING:
+				Having();
+				break;
+			default:
+				jj_la1[3] = jj_gen;
+				;
+		}
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case ORDER_BY:
+				OrderBy();
+				break;
+			default:
+				jj_la1[4] = jj_gen;
+				;
+		}
+		// get the previous query (!= null if the current query is a sub-query):
+		ADQLQuery previousQuery = stackQuery.pop();
+		if (stackQuery.isEmpty())
+			query = null;
+		else
+			query = stackQuery.peek();
+
+		{
+			if (true)
+				return previousQuery;
+		}
+		throw new Error("Missing return statement in function");
+	}
+
+	final public ADQLQuery SubQueryExpression() throws ParseException{
+		ADQLQuery q = null;
+		jj_consume_token(LEFT_PAR);
+		q = QueryExpression();
+		jj_consume_token(RIGHT_PAR);
+		{
+			if (true)
+				return q;
+		}
+		throw new Error("Missing return statement in function");
+	}
+
+	final public void Select() throws ParseException{
+		ClauseSelect select = query.getSelect();
+		SelectItem item = null;
+		Token t = null;
+		jj_consume_token(SELECT);
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case QUANTIFIER:
+				t = jj_consume_token(QUANTIFIER);
+				select.setDistinctColumns(t.image.equalsIgnoreCase("DISTINCT"));
+				break;
+			default:
+				jj_la1[5] = jj_gen;
+				;
+		}
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case TOP:
+				jj_consume_token(TOP);
+				t = jj_consume_token(UNSIGNED_INTEGER);
+				try{
+					select.setLimit(Integer.parseInt(t.image));
+				}catch(NumberFormatException nfe){
+					{
+						if (true)
+							throw new ParseException("[l." + t.beginLine + ";c." + t.beginColumn + "] The TOP limit (\u005c"" + t.image + "\u005c") isn't a regular unsigned integer !");
+					}
+				}
+				break;
+			default:
+				jj_la1[6] = jj_gen;
+				;
+		}
+		item = SelectItem();
+		select.add(item);
+		label_1: while(true){
 			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case 0:
-					jj_consume_token(0);
-					break;
-				case EOQ:
-					jj_consume_token(EOQ);
+				case COMMA:
+					;
 					break;
 				default:
-					jj_la1[0] = jj_gen;
-					jj_consume_token(-1);
-					throw new ParseException();
+					jj_la1[7] = jj_gen;
+					break label_1;
 			}
-			// check the query:
-			if (queryChecker != null)
-				queryChecker.check(q);
+			jj_consume_token(COMMA);
+			item = SelectItem();
+			select.add(item);
+		}
+	}
 
+	final public SelectItem SelectItem() throws ParseException{
+		IdentifierItems identifiers = new IdentifierItems(true);
+		IdentifierItem id = null, label = null;
+		ADQLOperand op = null;
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case ASTERISK:
+				jj_consume_token(ASTERISK);
+				{
+					if (true)
+						return new SelectAllColumns(query);
+				}
+				break;
+			default:
+				jj_la1[11] = jj_gen;
+				if (jj_2_1(7)){
+					id = Identifier();
+					jj_consume_token(DOT);
+					identifiers.append(id);
+					switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+						case DELIMITED_IDENTIFIER:
+						case REGULAR_IDENTIFIER:
+							id = Identifier();
+							jj_consume_token(DOT);
+							identifiers.append(id);
+							switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+								case DELIMITED_IDENTIFIER:
+								case REGULAR_IDENTIFIER:
+									id = Identifier();
+									jj_consume_token(DOT);
+									identifiers.append(id);
+									break;
+								default:
+									jj_la1[8] = jj_gen;
+									;
+							}
+							break;
+						default:
+							jj_la1[9] = jj_gen;
+							;
+					}
+					jj_consume_token(ASTERISK);
+					try{
+						;
+						{
+							if (true)
+								return new SelectAllColumns(queryFactory.createTable(identifiers, null));
+						}
+					}catch(Exception ex){
+						{
+							if (true)
+								throw generateParseException(ex);
+						}
+					}
+				}else{
+					switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+						case LEFT_PAR:
+						case PLUS:
+						case MINUS:
+						case AVG:
+						case MAX:
+						case MIN:
+						case SUM:
+						case COUNT:
+						case BOX:
+						case CENTROID:
+						case CIRCLE:
+						case POINT:
+						case POLYGON:
+						case REGION:
+						case CONTAINS:
+						case INTERSECTS:
+						case AREA:
+						case COORD1:
+						case COORD2:
+						case COORDSYS:
+						case DISTANCE:
+						case ABS:
+						case CEILING:
+						case DEGREES:
+						case EXP:
+						case FLOOR:
+						case LOG:
+						case LOG10:
+						case MOD:
+						case PI:
+						case POWER:
+						case RADIANS:
+						case RAND:
+						case ROUND:
+						case SQRT:
+						case TRUNCATE:
+						case ACOS:
+						case ASIN:
+						case ATAN:
+						case ATAN2:
+						case COS:
+						case COT:
+						case SIN:
+						case TAN:
+						case STRING_LITERAL:
+						case DELIMITED_IDENTIFIER:
+						case REGULAR_IDENTIFIER:
+						case SCIENTIFIC_NUMBER:
+						case UNSIGNED_FLOAT:
+						case UNSIGNED_INTEGER:
+							op = ValueExpression();
+							switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+								case AS:
+									jj_consume_token(AS);
+									label = Identifier();
+									break;
+								default:
+									jj_la1[10] = jj_gen;
+									;
+							}
+							break;
+						default:
+							jj_la1[12] = jj_gen;
+							jj_consume_token(-1);
+							throw new ParseException();
+					}
+				}
+		}
+		try{
+			SelectItem item = queryFactory.createSelectItem(op, (label == null) ? null : label.identifier);
+			if (label != null)
+				item.setCaseSensitive(label.caseSensitivity);
 			{
 				if (true)
-					return q;
+					return item;
+			}
+		}catch(Exception ex){
+			{
+				if (true)
+					throw generateParseException(ex);
 			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("Query");
 		}
+		throw new Error("Missing return statement in function");
 	}
 
-	final public ADQLQuery QueryExpression() throws ParseException{
-		trace_call("QueryExpression");
+	final public void From() throws ParseException{
+		FromContent content = null, content2 = null;
 		try{
-			try{
-				// create the query:
-				query = queryFactory.createQuery();
-				stackQuery.push(query);
-			}catch(Exception ex){
-				{
-					if (true)
-						throw generateParseException(ex);
+			jj_consume_token(FROM);
+			content = TableRef();
+			label_2: while(true){
+				switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+					case COMMA:
+						;
+						break;
+					default:
+						jj_la1[13] = jj_gen;
+						break label_2;
 				}
+				jj_consume_token(COMMA);
+				content2 = TableRef();
+				content = queryFactory.createJoin(JoinType.CROSS, content, content2);
 			}
-			Select();
-			From();
-			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case WHERE:
-					Where();
-					break;
-				default:
-					jj_la1[1] = jj_gen;
-					;
+			query.setFrom(content);
+		}catch(Exception ex){
+			{
+				if (true)
+					throw generateParseException(ex);
 			}
+		}
+	}
+
+	final public void Where() throws ParseException{
+		ClauseConstraints where = query.getWhere();
+		ADQLConstraint condition;
+		jj_consume_token(WHERE);
+		ConditionsList(where);
+	}
+
+	final public void GroupBy() throws ParseException{
+		ClauseADQL<ColumnReference> groupBy = query.getGroupBy();
+		ColumnReference colRef = null;
+		jj_consume_token(GROUP_BY);
+		colRef = ColumnRef();
+		groupBy.add(colRef);
+		label_3: while(true){
 			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case GROUP_BY:
-					GroupBy();
-					break;
-				default:
-					jj_la1[2] = jj_gen;
+				case COMMA:
 					;
-			}
-			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case HAVING:
-					Having();
 					break;
 				default:
-					jj_la1[3] = jj_gen;
-					;
+					jj_la1[14] = jj_gen;
+					break label_3;
 			}
+			jj_consume_token(COMMA);
+			colRef = ColumnRef();
+			groupBy.add(colRef);
+		}
+	}
+
+	final public void Having() throws ParseException{
+		ClauseConstraints having = query.getHaving();
+		jj_consume_token(HAVING);
+		ConditionsList(having);
+	}
+
+	final public void OrderBy() throws ParseException{
+		ClauseADQL<ADQLOrder> orderBy = query.getOrderBy();
+		ADQLOrder order = null;
+		jj_consume_token(ORDER_BY);
+		order = OrderItem();
+		orderBy.add(order);
+		label_4: while(true){
 			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case ORDER_BY:
-					OrderBy();
+				case COMMA:
+					;
 					break;
 				default:
-					jj_la1[4] = jj_gen;
-					;
+					jj_la1[15] = jj_gen;
+					break label_4;
 			}
-			// get the previous query (!= null if the current query is a sub-query):
-			ADQLQuery previousQuery = stackQuery.pop();
-			if (stackQuery.isEmpty())
-				query = null;
-			else
-				query = stackQuery.peek();
+			jj_consume_token(COMMA);
+			order = OrderItem();
+			orderBy.add(order);
+		}
+	}
 
-			{
-				if (true)
-					return previousQuery;
-			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("QueryExpression");
+	/* *************************** */
+	/* COLUMN AND TABLE REFERENCES */
+	/* *************************** */
+	final public IdentifierItem Identifier() throws ParseException{
+		Token t;
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case REGULAR_IDENTIFIER:
+				t = jj_consume_token(REGULAR_IDENTIFIER);
+				{
+					if (true)
+						return new IdentifierItem(t, false);
+				}
+				break;
+			case DELIMITED_IDENTIFIER:
+				t = jj_consume_token(DELIMITED_IDENTIFIER);
+				{
+					if (true)
+						return new IdentifierItem(t, true);
+				}
+				break;
+			default:
+				jj_la1[16] = jj_gen;
+				jj_consume_token(-1);
+				throw new ParseException();
 		}
+		throw new Error("Missing return statement in function");
 	}
 
-	final public ADQLQuery SubQueryExpression() throws ParseException{
-		trace_call("SubQueryExpression");
+	/**
+	 * Extracts the name of a table with its possible catalog and schema prefixes.
+	 * 
+	 * @return A {@link IdentifierItems} which contains at most three items: catalogName, schemaName and tableName.
+	 */
+	final public IdentifierItems TableName() throws ParseException{
+		IdentifierItems identifiers = new IdentifierItems(true);
+		IdentifierItem id = null;
+		id = Identifier();
+		identifiers.append(id);
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case DOT:
+				jj_consume_token(DOT);
+				id = Identifier();
+				identifiers.append(id);
+				break;
+			default:
+				jj_la1[17] = jj_gen;
+				;
+		}
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case DOT:
+				jj_consume_token(DOT);
+				id = Identifier();
+				identifiers.append(id);
+				break;
+			default:
+				jj_la1[18] = jj_gen;
+				;
+		}
+		{
+			if (true)
+				return identifiers;
+		}
+		throw new Error("Missing return statement in function");
+	}
+
+	/**
+	 * Extracts the name of a column with its possible catalog, schema and table prefixes.
+	 * 
+	 * @return A {@link IdentifierItems} which contains at most four items: catalogName, schemaName, tableName and columnName.
+	 */
+	final public IdentifierItems ColumnName() throws ParseException{
+		IdentifierItem id;
+		IdentifierItems table = null, identifiers = new IdentifierItems(false);
+		id = Identifier();
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case DOT:
+				jj_consume_token(DOT);
+				table = TableName();
+				break;
+			default:
+				jj_la1[19] = jj_gen;
+				;
+		}
+		identifiers.append(id);
+		if (table != null){
+			for(int i = 0; i < table.size(); i++)
+				identifiers.append(table.get(i));
+		}
+		{
+			if (true)
+				return identifiers;
+		}
+		throw new Error("Missing return statement in function");
+	}
+
+	final public ADQLColumn Column() throws ParseException{
+		IdentifierItems identifiers;
+		identifiers = ColumnName();
 		try{
-			ADQLQuery q = null;
-			jj_consume_token(LEFT_PAR);
-			q = QueryExpression();
-			jj_consume_token(RIGHT_PAR);
 			{
 				if (true)
-					return q;
+					return queryFactory.createColumn(identifiers);
+			}
+		}catch(Exception ex){
+			{
+				if (true)
+					throw generateParseException(ex);
 			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("SubQueryExpression");
 		}
+		throw new Error("Missing return statement in function");
 	}
 
-	final public void Select() throws ParseException{
-		trace_call("Select");
+	final public ColumnReference ColumnRef() throws ParseException{
+		IdentifierItems identifiers = null;
+		Token ind = null;
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case DELIMITED_IDENTIFIER:
+			case REGULAR_IDENTIFIER:
+				identifiers = ColumnName();
+				break;
+			case UNSIGNED_INTEGER:
+				ind = jj_consume_token(UNSIGNED_INTEGER);
+				break;
+			default:
+				jj_la1[20] = jj_gen;
+				jj_consume_token(-1);
+				throw new ParseException();
+		}
 		try{
-			ClauseSelect select = query.getSelect();
-			SelectItem item = null;
-			Token t = null;
-			jj_consume_token(SELECT);
-			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case QUANTIFIER:
-					t = jj_consume_token(QUANTIFIER);
-					select.setDistinctColumns(t.image.equalsIgnoreCase("DISTINCT"));
-					break;
-				default:
-					jj_la1[5] = jj_gen;
-					;
+			ColumnReference colRef = null;
+			if (identifiers != null)
+				colRef = queryFactory.createColRef(identifiers);
+			else
+				colRef = queryFactory.createColRef(Integer.parseInt(ind.image), new TextPosition(ind));
+			{
+				if (true)
+					return colRef;
 			}
-			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case TOP:
-					jj_consume_token(TOP);
-					t = jj_consume_token(UNSIGNED_INTEGER);
-					try{
-						select.setLimit(Integer.parseInt(t.image));
-					}catch(NumberFormatException nfe){
-						{
-							if (true)
-								throw new ParseException("[l." + t.beginLine + ";c." + t.beginColumn + "] The TOP limit (\u005c"" + t.image + "\u005c") isn't a regular unsigned integer !");
-						}
-					}
-					break;
-				default:
-					jj_la1[6] = jj_gen;
-					;
+		}catch(Exception ex){
+			{
+				if (true)
+					throw generateParseException(ex);
 			}
-			item = SelectItem();
-			select.add(item);
-			label_1: while(true){
+		}
+		throw new Error("Missing return statement in function");
+	}
+
+	final public ADQLOrder OrderItem() throws ParseException{
+		IdentifierItems identifiers = null;
+		Token ind = null, desc = null;
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case DELIMITED_IDENTIFIER:
+			case REGULAR_IDENTIFIER:
+				identifiers = ColumnName();
+				break;
+			case UNSIGNED_INTEGER:
+				ind = jj_consume_token(UNSIGNED_INTEGER);
+				break;
+			default:
+				jj_la1[21] = jj_gen;
+				jj_consume_token(-1);
+				throw new ParseException();
+		}
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case ASC:
+			case DESC:
 				switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-					case COMMA:
-						;
+					case ASC:
+						jj_consume_token(ASC);
+						break;
+					case DESC:
+						desc = jj_consume_token(DESC);
 						break;
 					default:
-						jj_la1[7] = jj_gen;
-						break label_1;
+						jj_la1[22] = jj_gen;
+						jj_consume_token(-1);
+						throw new ParseException();
 				}
-				jj_consume_token(COMMA);
-				item = SelectItem();
-				select.add(item);
+				break;
+			default:
+				jj_la1[23] = jj_gen;
+				;
+		}
+		try{
+			ADQLOrder order = null;
+			if (identifiers != null)
+				order = queryFactory.createOrder(identifiers, desc != null);
+			else
+				order = queryFactory.createOrder(Integer.parseInt(ind.image), desc != null, new TextPosition(ind));
+			{
+				if (true)
+					return order;
+			}
+		}catch(Exception ex){
+			{
+				if (true)
+					throw generateParseException(ex);
 			}
-		}finally{
-			trace_return("Select");
 		}
+		throw new Error("Missing return statement in function");
 	}
 
-	final public SelectItem SelectItem() throws ParseException{
-		trace_call("SelectItem");
+	final public FromContent SimpleTableRef() throws ParseException{
+		IdentifierItem alias = null;
+		IdentifierItems identifiers = null;
+		ADQLQuery subQuery = null;
+		FromContent content = null;
 		try{
-			IdentifierItems identifiers = new IdentifierItems(true);
-			IdentifierItem id = null, label = null;
-			ADQLOperand op = null;
 			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case ASTERISK:
-					jj_consume_token(ASTERISK);
+				case DELIMITED_IDENTIFIER:
+				case REGULAR_IDENTIFIER:
+					identifiers = TableName();
+					switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+						case AS:
+						case DELIMITED_IDENTIFIER:
+						case REGULAR_IDENTIFIER:
+							switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+								case AS:
+									jj_consume_token(AS);
+									break;
+								default:
+									jj_la1[24] = jj_gen;
+									;
+							}
+							alias = Identifier();
+							break;
+						default:
+							jj_la1[25] = jj_gen;
+							;
+					}
 					{
 						if (true)
-							return new SelectAllColumns(query);
+							return queryFactory.createTable(identifiers, alias);
 					}
 					break;
 				default:
-					jj_la1[11] = jj_gen;
-					if (jj_2_1(7)){
-						id = Identifier();
-						jj_consume_token(DOT);
-						identifiers.append(id);
+					jj_la1[27] = jj_gen;
+					if (jj_2_2(2)){
+						subQuery = SubQueryExpression();
 						switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-							case DELIMITED_IDENTIFIER:
-							case REGULAR_IDENTIFIER:
-								id = Identifier();
-								jj_consume_token(DOT);
-								identifiers.append(id);
-								switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-									case DELIMITED_IDENTIFIER:
-									case REGULAR_IDENTIFIER:
-										id = Identifier();
-										jj_consume_token(DOT);
-										identifiers.append(id);
-										break;
-									default:
-										jj_la1[8] = jj_gen;
-										;
-								}
+							case AS:
+								jj_consume_token(AS);
 								break;
 							default:
-								jj_la1[9] = jj_gen;
+								jj_la1[26] = jj_gen;
 								;
 						}
-						jj_consume_token(ASTERISK);
-						try{
-							;
-							{
-								if (true)
-									return new SelectAllColumns(queryFactory.createTable(identifiers, null));
-							}
-						}catch(Exception ex){
-							{
-								if (true)
-									throw generateParseException(ex);
-							}
+						alias = Identifier();
+						{
+							if (true)
+								return queryFactory.createTable(subQuery, alias);
 						}
 					}else{
 						switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
 							case LEFT_PAR:
-							case PLUS:
-							case MINUS:
-							case AVG:
-							case MAX:
-							case MIN:
-							case SUM:
-							case COUNT:
-							case BOX:
-							case CENTROID:
-							case CIRCLE:
-							case POINT:
-							case POLYGON:
-							case REGION:
-							case CONTAINS:
-							case INTERSECTS:
-							case AREA:
-							case COORD1:
-							case COORD2:
-							case COORDSYS:
-							case DISTANCE:
-							case ABS:
-							case CEILING:
-							case DEGREES:
-							case EXP:
-							case FLOOR:
-							case LOG:
-							case LOG10:
-							case MOD:
-							case PI:
-							case POWER:
-							case RADIANS:
-							case RAND:
-							case ROUND:
-							case SQRT:
-							case TRUNCATE:
-							case ACOS:
-							case ASIN:
-							case ATAN:
-							case ATAN2:
-							case COS:
-							case COT:
-							case SIN:
-							case TAN:
-							case STRING_LITERAL:
-							case DELIMITED_IDENTIFIER:
-							case REGULAR_IDENTIFIER:
-							case SCIENTIFIC_NUMBER:
-							case UNSIGNED_FLOAT:
-							case UNSIGNED_INTEGER:
-								op = ValueExpression();
-								switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-									case AS:
-										jj_consume_token(AS);
-										label = Identifier();
-										break;
-									default:
-										jj_la1[10] = jj_gen;
-										;
+								jj_consume_token(LEFT_PAR);
+								content = JoinedTable();
+								jj_consume_token(RIGHT_PAR);
+								{
+									if (true)
+										return content;
 								}
 								break;
 							default:
-								jj_la1[12] = jj_gen;
+								jj_la1[28] = jj_gen;
 								jj_consume_token(-1);
 								throw new ParseException();
 						}
 					}
 			}
-			try{
-				SelectItem item = queryFactory.createSelectItem(op, (label == null) ? null : label.identifier);
-				if (label != null)
-					item.setCaseSensitive(label.caseSensitivity);
-				{
-					if (true)
-						return item;
-				}
-			}catch(Exception ex){
-				{
-					if (true)
-						throw generateParseException(ex);
-				}
+		}catch(Exception ex){
+			{
+				if (true)
+					throw generateParseException(ex);
 			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("SelectItem");
 		}
+		throw new Error("Missing return statement in function");
 	}
 
-	final public void From() throws ParseException{
-		trace_call("From");
-		try{
-			FromContent content = null, content2 = null;
-			try{
-				jj_consume_token(FROM);
-				content = TableRef();
-				label_2: while(true){
-					switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-						case COMMA:
-							;
-							break;
-						default:
-							jj_la1[13] = jj_gen;
-							break label_2;
-					}
-					jj_consume_token(COMMA);
-					content2 = TableRef();
-					content = queryFactory.createJoin(JoinType.CROSS, content, content2);
-				}
-				query.setFrom(content);
-			}catch(Exception ex){
-				{
-					if (true)
-						throw generateParseException(ex);
-				}
+	final public FromContent TableRef() throws ParseException{
+		FromContent content;
+		content = SimpleTableRef();
+		label_5: while(true){
+			if (jj_2_3(2)){
+				;
+			}else{
+				break label_5;
 			}
-		}finally{
-			trace_return("From");
+			content = JoinSpecification(content);
 		}
-	}
-
-	final public void Where() throws ParseException{
-		trace_call("Where");
-		try{
-			ClauseConstraints where = query.getWhere();
-			ADQLConstraint condition;
-			jj_consume_token(WHERE);
-			ConditionsList(where);
-		}finally{
-			trace_return("Where");
+		{
+			if (true)
+				return content;
 		}
+		throw new Error("Missing return statement in function");
 	}
 
-	final public void GroupBy() throws ParseException{
-		trace_call("GroupBy");
-		try{
-			ClauseADQL<ColumnReference> groupBy = query.getGroupBy();
-			ColumnReference colRef = null;
-			jj_consume_token(GROUP_BY);
-			colRef = ColumnRef();
-			groupBy.add(colRef);
-			label_3: while(true){
-				switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-					case COMMA:
-						;
-						break;
-					default:
-						jj_la1[14] = jj_gen;
-						break label_3;
-				}
-				jj_consume_token(COMMA);
-				colRef = ColumnRef();
-				groupBy.add(colRef);
+	final public FromContent JoinedTable() throws ParseException{
+		FromContent content;
+		content = SimpleTableRef();
+		label_6: while(true){
+			content = JoinSpecification(content);
+			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+				case NATURAL:
+				case INNER:
+				case RIGHT:
+				case LEFT:
+				case FULL:
+				case JOIN:
+					;
+					break;
+				default:
+					jj_la1[29] = jj_gen;
+					break label_6;
 			}
-		}finally{
-			trace_return("GroupBy");
-		}
-	}
-
-	final public void Having() throws ParseException{
-		trace_call("Having");
-		try{
-			ClauseConstraints having = query.getHaving();
-			jj_consume_token(HAVING);
-			ConditionsList(having);
-		}finally{
-			trace_return("Having");
 		}
-	}
-
-	final public void OrderBy() throws ParseException{
-		trace_call("OrderBy");
-		try{
-			ClauseADQL<ADQLOrder> orderBy = query.getOrderBy();
-			ADQLOrder order = null;
-			jj_consume_token(ORDER_BY);
-			order = OrderItem();
-			orderBy.add(order);
-			label_4: while(true){
-				switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-					case COMMA:
-						;
-						break;
-					default:
-						jj_la1[15] = jj_gen;
-						break label_4;
-				}
-				jj_consume_token(COMMA);
-				order = OrderItem();
-				orderBy.add(order);
-			}
-		}finally{
-			trace_return("OrderBy");
+		{
+			if (true)
+				return content;
 		}
+		throw new Error("Missing return statement in function");
 	}
 
-	/* *************************** */
-	/* COLUMN AND TABLE REFERENCES */
-	/* *************************** */
-	final public IdentifierItem Identifier() throws ParseException{
-		trace_call("Identifier");
+	final public ADQLJoin JoinSpecification(FromContent leftTable) throws ParseException{
+		boolean natural = false;
+		JoinType type = JoinType.INNER;
+		ClauseConstraints condition = new ClauseConstraints("ON");
+		ArrayList<ADQLColumn> lstColumns = new ArrayList<ADQLColumn>();
+		IdentifierItem id;
+		FromContent rightTable;
 		try{
-			Token t;
 			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case REGULAR_IDENTIFIER:
-					t = jj_consume_token(REGULAR_IDENTIFIER);
+				case NATURAL:
+					jj_consume_token(NATURAL);
+					natural = true;
+					switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+						case INNER:
+						case RIGHT:
+						case LEFT:
+						case FULL:
+							switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+								case INNER:
+									jj_consume_token(INNER);
+									break;
+								case RIGHT:
+								case LEFT:
+								case FULL:
+									switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+										case LEFT:
+											jj_consume_token(LEFT);
+											type = JoinType.OUTER_LEFT;
+											break;
+										case RIGHT:
+											jj_consume_token(RIGHT);
+											type = JoinType.OUTER_RIGHT;
+											break;
+										case FULL:
+											jj_consume_token(FULL);
+											type = JoinType.OUTER_FULL;
+											break;
+										default:
+											jj_la1[30] = jj_gen;
+											jj_consume_token(-1);
+											throw new ParseException();
+									}
+									switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+										case OUTER:
+											jj_consume_token(OUTER);
+											break;
+										default:
+											jj_la1[31] = jj_gen;
+											;
+									}
+									break;
+								default:
+									jj_la1[32] = jj_gen;
+									jj_consume_token(-1);
+									throw new ParseException();
+							}
+							break;
+						default:
+							jj_la1[33] = jj_gen;
+							;
+					}
+					jj_consume_token(JOIN);
+					rightTable = TableRef();
 					{
 						if (true)
-							return new IdentifierItem(t, false);
+							return queryFactory.createJoin(type, leftTable, rightTable);
 					}
 					break;
-				case DELIMITED_IDENTIFIER:
-					t = jj_consume_token(DELIMITED_IDENTIFIER);
-					{
-						if (true)
-							return new IdentifierItem(t, true);
+				case INNER:
+				case RIGHT:
+				case LEFT:
+				case FULL:
+				case JOIN:
+					switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+						case INNER:
+						case RIGHT:
+						case LEFT:
+						case FULL:
+							switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+								case INNER:
+									jj_consume_token(INNER);
+									break;
+								case RIGHT:
+								case LEFT:
+								case FULL:
+									switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+										case LEFT:
+											jj_consume_token(LEFT);
+											type = JoinType.OUTER_LEFT;
+											break;
+										case RIGHT:
+											jj_consume_token(RIGHT);
+											type = JoinType.OUTER_RIGHT;
+											break;
+										case FULL:
+											jj_consume_token(FULL);
+											type = JoinType.OUTER_FULL;
+											break;
+										default:
+											jj_la1[34] = jj_gen;
+											jj_consume_token(-1);
+											throw new ParseException();
+									}
+									switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+										case OUTER:
+											jj_consume_token(OUTER);
+											break;
+										default:
+											jj_la1[35] = jj_gen;
+											;
+									}
+									break;
+								default:
+									jj_la1[36] = jj_gen;
+									jj_consume_token(-1);
+									throw new ParseException();
+							}
+							break;
+						default:
+							jj_la1[37] = jj_gen;
+							;
+					}
+					jj_consume_token(JOIN);
+					rightTable = TableRef();
+					switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+						case ON:
+							jj_consume_token(ON);
+							ConditionsList(condition);
+							{
+								if (true)
+									return queryFactory.createJoin(type, leftTable, rightTable, condition);
+							}
+							break;
+						case USING:
+							jj_consume_token(USING);
+							jj_consume_token(LEFT_PAR);
+							id = Identifier();
+							lstColumns.add(queryFactory.createColumn(id));
+							label_7: while(true){
+								switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+									case COMMA:
+										;
+										break;
+									default:
+										jj_la1[38] = jj_gen;
+										break label_7;
+								}
+								jj_consume_token(COMMA);
+								id = Identifier();
+								lstColumns.add(queryFactory.createColumn(id));
+							}
+							jj_consume_token(RIGHT_PAR);
+							{
+								if (true)
+									return queryFactory.createJoin(type, leftTable, rightTable, lstColumns);
+							}
+							break;
+						default:
+							jj_la1[39] = jj_gen;
+							jj_consume_token(-1);
+							throw new ParseException();
 					}
 					break;
 				default:
-					jj_la1[16] = jj_gen;
+					jj_la1[40] = jj_gen;
 					jj_consume_token(-1);
 					throw new ParseException();
 			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("Identifier");
+		}catch(Exception ex){
+			{
+				if (true)
+					throw generateParseException(ex);
+			}
 		}
+		throw new Error("Missing return statement in function");
 	}
 
-	/**
-	 * Extracts the name of a table with its possible catalog and schema prefixes.
-	 * 
-	 * @return A {@link IdentifierItems} which contains at most three items: catalogName, schemaName and tableName.
-	 */
-	final public IdentifierItems TableName() throws ParseException{
-		trace_call("TableName");
-		try{
-			IdentifierItems identifiers = new IdentifierItems(true);
-			IdentifierItem id = null;
-			id = Identifier();
-			identifiers.append(id);
+	/* ****** */
+	/* STRING */
+	/* ****** */
+	final public String String() throws ParseException{
+		Token t;
+		String str = "";
+		label_8: while(true){
+			t = jj_consume_token(STRING_LITERAL);
+			str += t.image.substring(1, t.image.length() - 1).replaceAll("''", "'");
 			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case DOT:
-					jj_consume_token(DOT);
-					id = Identifier();
-					identifiers.append(id);
-					break;
-				default:
-					jj_la1[17] = jj_gen;
+				case STRING_LITERAL:
 					;
-			}
-			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case DOT:
-					jj_consume_token(DOT);
-					id = Identifier();
-					identifiers.append(id);
 					break;
 				default:
-					jj_la1[18] = jj_gen;
-					;
-			}
-			{
-				if (true)
-					return identifiers;
+					jj_la1[41] = jj_gen;
+					break label_8;
 			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("TableName");
 		}
+		{
+			if (true)
+				return str;
+		}
+		throw new Error("Missing return statement in function");
 	}
 
-	/**
-	 * Extracts the name of a column with its possible catalog, schema and table prefixes.
-	 * 
-	 * @return A {@link IdentifierItems} which contains at most four items: catalogName, schemaName, tableName and columnName.
-	 */
-	final public IdentifierItems ColumnName() throws ParseException{
-		trace_call("ColumnName");
-		try{
-			IdentifierItem id;
-			IdentifierItems table = null, identifiers = new IdentifierItems(false);
-			id = Identifier();
-			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case DOT:
-					jj_consume_token(DOT);
-					table = TableName();
-					break;
-				default:
-					jj_la1[19] = jj_gen;
-					;
-			}
-			identifiers.append(id);
-			if (table != null){
-				for(int i = 0; i < table.size(); i++)
-					identifiers.append(table.get(i));
-			}
-			{
-				if (true)
-					return identifiers;
-			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("ColumnName");
+	/* ************* */
+	/* NUMERIC TYPES */
+	/* ************* */
+	final public String UnsignedNumeric() throws ParseException{
+		Token t;
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case SCIENTIFIC_NUMBER:
+				t = jj_consume_token(SCIENTIFIC_NUMBER);
+				break;
+			case UNSIGNED_FLOAT:
+				t = jj_consume_token(UNSIGNED_FLOAT);
+				break;
+			case UNSIGNED_INTEGER:
+				t = jj_consume_token(UNSIGNED_INTEGER);
+				break;
+			default:
+				jj_la1[42] = jj_gen;
+				jj_consume_token(-1);
+				throw new ParseException();
+		}
+		{
+			if (true)
+				return t.image;
 		}
+		throw new Error("Missing return statement in function");
 	}
 
-	final public ADQLColumn Column() throws ParseException{
-		trace_call("Column");
-		try{
-			IdentifierItems identifiers;
-			identifiers = ColumnName();
-			try{
-				{
-					if (true)
-						return queryFactory.createColumn(identifiers);
-				}
-			}catch(Exception ex){
-				{
-					if (true)
-						throw generateParseException(ex);
+	final public String UnsignedFloat() throws ParseException{
+		Token t;
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case UNSIGNED_INTEGER:
+				t = jj_consume_token(UNSIGNED_INTEGER);
+				break;
+			case UNSIGNED_FLOAT:
+				t = jj_consume_token(UNSIGNED_FLOAT);
+				break;
+			default:
+				jj_la1[43] = jj_gen;
+				jj_consume_token(-1);
+				throw new ParseException();
+		}
+		{
+			if (true)
+				return t.image;
+		}
+		throw new Error("Missing return statement in function");
+	}
+
+	final public String SignedInteger() throws ParseException{
+		Token sign = null, number;
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case PLUS:
+			case MINUS:
+				switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+					case PLUS:
+						sign = jj_consume_token(PLUS);
+						break;
+					case MINUS:
+						sign = jj_consume_token(MINUS);
+						break;
+					default:
+						jj_la1[44] = jj_gen;
+						jj_consume_token(-1);
+						throw new ParseException();
 				}
-			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("Column");
+				break;
+			default:
+				jj_la1[45] = jj_gen;
+				;
+		}
+		number = jj_consume_token(UNSIGNED_INTEGER);
+		{
+			if (true)
+				return ((sign == null) ? "" : sign.image) + number.image;
 		}
+		throw new Error("Missing return statement in function");
 	}
 
-	final public ColumnReference ColumnRef() throws ParseException{
-		trace_call("ColumnRef");
+	/* *********** */
+	/* EXPRESSIONS */
+	/* *********** */
+	final public ADQLOperand NumericValueExpressionPrimary() throws ParseException{
+		String expr;
+		ADQLColumn column;
+		ADQLOperand op;
 		try{
-			IdentifierItems identifiers = null;
-			Token ind = null;
 			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+				case SCIENTIFIC_NUMBER:
+				case UNSIGNED_FLOAT:
+				case UNSIGNED_INTEGER:
+					// unsigned_value_specification
+					expr = UnsignedNumeric();
+					{
+						if (true)
+							return queryFactory.createNumericConstant(expr);
+					}
+					break;
 				case DELIMITED_IDENTIFIER:
 				case REGULAR_IDENTIFIER:
-					identifiers = ColumnName();
+					column = Column();
+					column.setExpectedType('N');
+					{
+						if (true)
+							return column;
+					}
 					break;
-				case UNSIGNED_INTEGER:
-					ind = jj_consume_token(UNSIGNED_INTEGER);
+				case AVG:
+				case MAX:
+				case MIN:
+				case SUM:
+				case COUNT:
+					op = SqlFunction();
+					{
+						if (true)
+							return op;
+					}
+					break;
+				case LEFT_PAR:
+					jj_consume_token(LEFT_PAR);
+					op = NumericExpression();
+					jj_consume_token(RIGHT_PAR);
+					{
+						if (true)
+							return queryFactory.createWrappedOperand(op);
+					}
 					break;
 				default:
-					jj_la1[20] = jj_gen;
+					jj_la1[46] = jj_gen;
 					jj_consume_token(-1);
 					throw new ParseException();
 			}
-			try{
-				ColumnReference colRef = null;
-				if (identifiers != null)
-					colRef = queryFactory.createColRef(identifiers);
-				else
-					colRef = queryFactory.createColRef(Integer.parseInt(ind.image), new TextPosition(ind));
-				{
-					if (true)
-						return colRef;
-				}
-			}catch(Exception ex){
-				{
-					if (true)
-						throw generateParseException(ex);
-				}
+		}catch(Exception ex){
+			{
+				if (true)
+					throw generateParseException(ex);
 			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("ColumnRef");
 		}
+		throw new Error("Missing return statement in function");
 	}
 
-	final public ADQLOrder OrderItem() throws ParseException{
-		trace_call("OrderItem");
+	final public ADQLOperand StringValueExpressionPrimary() throws ParseException{
+		String expr;
+		ADQLColumn column;
+		ADQLOperand op;
 		try{
-			IdentifierItems identifiers = null;
-			Token ind = null, desc = null;
 			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+				case STRING_LITERAL:
+					// string
+					expr = String();
+					{
+						if (true)
+							return queryFactory.createStringConstant(expr);
+					}
+					break;
 				case DELIMITED_IDENTIFIER:
 				case REGULAR_IDENTIFIER:
-					identifiers = ColumnName();
+					column = Column();
+					column.setExpectedType('S');
+					{
+						if (true)
+							return column;
+					}
 					break;
-				case UNSIGNED_INTEGER:
-					ind = jj_consume_token(UNSIGNED_INTEGER);
+				case LEFT_PAR:
+					jj_consume_token(LEFT_PAR);
+					op = StringExpression();
+					jj_consume_token(RIGHT_PAR);
+					{
+						if (true)
+							return queryFactory.createWrappedOperand(op);
+					}
 					break;
 				default:
-					jj_la1[21] = jj_gen;
+					jj_la1[47] = jj_gen;
 					jj_consume_token(-1);
 					throw new ParseException();
 			}
-			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case ASC:
-				case DESC:
-					switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-						case ASC:
-							jj_consume_token(ASC);
-							break;
-						case DESC:
-							desc = jj_consume_token(DESC);
-							break;
-						default:
-							jj_la1[22] = jj_gen;
-							jj_consume_token(-1);
-							throw new ParseException();
-					}
-					break;
-				default:
-					jj_la1[23] = jj_gen;
-					;
-			}
-			try{
-				ADQLOrder order = null;
-				if (identifiers != null)
-					order = queryFactory.createOrder(identifiers, desc != null);
-				else
-					order = queryFactory.createOrder(Integer.parseInt(ind.image), desc != null, new TextPosition(ind));
-				{
-					if (true)
-						return order;
-				}
-			}catch(Exception ex){
-				{
-					if (true)
-						throw generateParseException(ex);
-				}
+		}catch(Exception ex){
+			{
+				if (true)
+					throw generateParseException(ex);
 			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("OrderItem");
 		}
+		throw new Error("Missing return statement in function");
 	}
 
-	final public FromContent SimpleTableRef() throws ParseException{
-		trace_call("SimpleTableRef");
+	final public ADQLOperand ValueExpression() throws ParseException{
+		ADQLOperand valueExpr = null;
 		try{
-			IdentifierItem alias = null;
-			IdentifierItems identifiers = null;
-			ADQLQuery subQuery = null;
-			FromContent content = null;
-			try{
+			if (jj_2_4(2147483647)){
+				valueExpr = NumericExpression();
+			}else if (jj_2_5(2147483647)){
+				valueExpr = StringExpression();
+			}else if (jj_2_6(2147483647)){
+				jj_consume_token(LEFT_PAR);
+				valueExpr = ValueExpression();
+				jj_consume_token(RIGHT_PAR);
+				valueExpr = queryFactory.createWrappedOperand(valueExpr);
+			}else if (jj_2_7(2147483647)){
+				valueExpr = UserDefinedFunction();
+			}else{
 				switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-					case DELIMITED_IDENTIFIER:
-					case REGULAR_IDENTIFIER:
-						identifiers = TableName();
-						switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-							case AS:
-							case DELIMITED_IDENTIFIER:
-							case REGULAR_IDENTIFIER:
-								switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-									case AS:
-										jj_consume_token(AS);
-										break;
-									default:
-										jj_la1[24] = jj_gen;
-										;
-								}
-								alias = Identifier();
-								break;
-							default:
-								jj_la1[25] = jj_gen;
-								;
-						}
-						{
-							if (true)
-								return queryFactory.createTable(identifiers, alias);
-						}
+					case BOX:
+					case CENTROID:
+					case CIRCLE:
+					case POINT:
+					case POLYGON:
+					case REGION:
+						valueExpr = GeometryValueFunction();
 						break;
 					default:
-						jj_la1[27] = jj_gen;
-						if (jj_2_2(2)){
-							subQuery = SubQueryExpression();
-							switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-								case AS:
-									jj_consume_token(AS);
-									break;
-								default:
-									jj_la1[26] = jj_gen;
-									;
-							}
-							alias = Identifier();
-							{
-								if (true)
-									return queryFactory.createTable(subQuery, alias);
-							}
+						jj_la1[48] = jj_gen;
+						if (jj_2_8(2147483647)){
+							valueExpr = Column();
+						}else if (jj_2_9(2147483647)){
+							valueExpr = StringFactor();
 						}else{
 							switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
 								case LEFT_PAR:
-									jj_consume_token(LEFT_PAR);
-									content = JoinedTable();
-									jj_consume_token(RIGHT_PAR);
-									{
-										if (true)
-											return content;
-									}
+								case PLUS:
+								case MINUS:
+								case AVG:
+								case MAX:
+								case MIN:
+								case SUM:
+								case COUNT:
+								case CONTAINS:
+								case INTERSECTS:
+								case AREA:
+								case COORD1:
+								case COORD2:
+								case DISTANCE:
+								case ABS:
+								case CEILING:
+								case DEGREES:
+								case EXP:
+								case FLOOR:
+								case LOG:
+								case LOG10:
+								case MOD:
+								case PI:
+								case POWER:
+								case RADIANS:
+								case RAND:
+								case ROUND:
+								case SQRT:
+								case TRUNCATE:
+								case ACOS:
+								case ASIN:
+								case ATAN:
+								case ATAN2:
+								case COS:
+								case COT:
+								case SIN:
+								case TAN:
+								case DELIMITED_IDENTIFIER:
+								case REGULAR_IDENTIFIER:
+								case SCIENTIFIC_NUMBER:
+								case UNSIGNED_FLOAT:
+								case UNSIGNED_INTEGER:
+									valueExpr = Factor();
 									break;
 								default:
-									jj_la1[28] = jj_gen;
+									jj_la1[49] = jj_gen;
 									jj_consume_token(-1);
 									throw new ParseException();
 							}
 						}
 				}
-			}catch(Exception ex){
-				{
-					if (true)
-						throw generateParseException(ex);
-				}
-			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("SimpleTableRef");
-		}
-	}
-
-	final public FromContent TableRef() throws ParseException{
-		trace_call("TableRef");
-		try{
-			FromContent content;
-			content = SimpleTableRef();
-			label_5: while(true){
-				if (jj_2_3(2)){
-					;
-				}else{
-					break label_5;
-				}
-				content = JoinSpecification(content);
 			}
 			{
 				if (true)
-					return content;
+					return valueExpr;
+			}
+		}catch(Exception ex){
+			{
+				if (true)
+					throw generateParseException(ex);
 			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("TableRef");
 		}
+		throw new Error("Missing return statement in function");
 	}
 
-	final public FromContent JoinedTable() throws ParseException{
-		trace_call("JoinedTable");
-		try{
-			FromContent content;
-			content = SimpleTableRef();
-			label_6: while(true){
-				content = JoinSpecification(content);
+	final public ADQLOperand NumericExpression() throws ParseException{
+		Token sign = null;
+		ADQLOperand leftOp, rightOp = null;
+		leftOp = NumericTerm();
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case PLUS:
+			case MINUS:
 				switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-					case NATURAL:
-					case INNER:
-					case RIGHT:
-					case LEFT:
-					case FULL:
-					case JOIN:
-						;
+					case PLUS:
+						sign = jj_consume_token(PLUS);
+						break;
+					case MINUS:
+						sign = jj_consume_token(MINUS);
 						break;
 					default:
-						jj_la1[29] = jj_gen;
-						break label_6;
+						jj_la1[50] = jj_gen;
+						jj_consume_token(-1);
+						throw new ParseException();
+				}
+				rightOp = NumericExpression();
+				break;
+			default:
+				jj_la1[51] = jj_gen;
+				;
+		}
+		if (sign == null){
+			if (true)
+				return leftOp;
+		}else{
+			try{
+				{
+					if (true)
+						return queryFactory.createOperation(leftOp, OperationType.getOperator(sign.image), rightOp);
+				}
+			}catch(Exception ex){
+				{
+					if (true)
+						throw generateParseException(ex);
 				}
 			}
-			{
-				if (true)
-					return content;
-			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("JoinedTable");
 		}
+		throw new Error("Missing return statement in function");
 	}
 
-	final public ADQLJoin JoinSpecification(FromContent leftTable) throws ParseException{
-		trace_call("JoinSpecification");
-		try{
-			boolean natural = false;
-			JoinType type = JoinType.INNER;
-			ClauseConstraints condition = new ClauseConstraints("ON");
-			ArrayList<ADQLColumn> lstColumns = new ArrayList<ADQLColumn>();
-			IdentifierItem id;
-			FromContent rightTable;
-			try{
+	final public ADQLOperand NumericTerm() throws ParseException{
+		Token sign = null;
+		ADQLOperand leftOp, rightOp = null;
+		leftOp = Factor();
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case ASTERISK:
+			case DIVIDE:
 				switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-					case NATURAL:
-						jj_consume_token(NATURAL);
-						natural = true;
-						switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-							case INNER:
-							case RIGHT:
-							case LEFT:
-							case FULL:
-								switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-									case INNER:
-										jj_consume_token(INNER);
-										break;
-									case RIGHT:
-									case LEFT:
-									case FULL:
-										switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-											case LEFT:
-												jj_consume_token(LEFT);
-												type = JoinType.OUTER_LEFT;
-												break;
-											case RIGHT:
-												jj_consume_token(RIGHT);
-												type = JoinType.OUTER_RIGHT;
-												break;
-											case FULL:
-												jj_consume_token(FULL);
-												type = JoinType.OUTER_FULL;
-												break;
-											default:
-												jj_la1[30] = jj_gen;
-												jj_consume_token(-1);
-												throw new ParseException();
-										}
-										switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-											case OUTER:
-												jj_consume_token(OUTER);
-												break;
-											default:
-												jj_la1[31] = jj_gen;
-												;
-										}
-										break;
-									default:
-										jj_la1[32] = jj_gen;
-										jj_consume_token(-1);
-										throw new ParseException();
-								}
-								break;
-							default:
-								jj_la1[33] = jj_gen;
-								;
-						}
-						jj_consume_token(JOIN);
-						rightTable = TableRef();
-						{
-							if (true)
-								return queryFactory.createJoin(type, leftTable, rightTable);
-						}
+					case ASTERISK:
+						sign = jj_consume_token(ASTERISK);
 						break;
-					case INNER:
-					case RIGHT:
-					case LEFT:
-					case FULL:
-					case JOIN:
-						switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-							case INNER:
-							case RIGHT:
-							case LEFT:
-							case FULL:
-								switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-									case INNER:
-										jj_consume_token(INNER);
-										break;
-									case RIGHT:
-									case LEFT:
-									case FULL:
-										switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-											case LEFT:
-												jj_consume_token(LEFT);
-												type = JoinType.OUTER_LEFT;
-												break;
-											case RIGHT:
-												jj_consume_token(RIGHT);
-												type = JoinType.OUTER_RIGHT;
-												break;
-											case FULL:
-												jj_consume_token(FULL);
-												type = JoinType.OUTER_FULL;
-												break;
-											default:
-												jj_la1[34] = jj_gen;
-												jj_consume_token(-1);
-												throw new ParseException();
-										}
-										switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-											case OUTER:
-												jj_consume_token(OUTER);
-												break;
-											default:
-												jj_la1[35] = jj_gen;
-												;
-										}
-										break;
-									default:
-										jj_la1[36] = jj_gen;
-										jj_consume_token(-1);
-										throw new ParseException();
-								}
-								break;
-							default:
-								jj_la1[37] = jj_gen;
-								;
-						}
-						jj_consume_token(JOIN);
-						rightTable = TableRef();
-						switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-							case ON:
-								jj_consume_token(ON);
-								ConditionsList(condition);
-								{
-									if (true)
-										return queryFactory.createJoin(type, leftTable, rightTable, condition);
-								}
-								break;
-							case USING:
-								jj_consume_token(USING);
-								jj_consume_token(LEFT_PAR);
-								id = Identifier();
-								lstColumns.add(queryFactory.createColumn(id));
-								label_7: while(true){
-									switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-										case COMMA:
-											;
-											break;
-										default:
-											jj_la1[38] = jj_gen;
-											break label_7;
-									}
-									jj_consume_token(COMMA);
-									id = Identifier();
-									lstColumns.add(queryFactory.createColumn(id));
-								}
-								jj_consume_token(RIGHT_PAR);
-								{
-									if (true)
-										return queryFactory.createJoin(type, leftTable, rightTable, lstColumns);
-								}
-								break;
-							default:
-								jj_la1[39] = jj_gen;
-								jj_consume_token(-1);
-								throw new ParseException();
-						}
+					case DIVIDE:
+						sign = jj_consume_token(DIVIDE);
 						break;
 					default:
-						jj_la1[40] = jj_gen;
+						jj_la1[52] = jj_gen;
 						jj_consume_token(-1);
 						throw new ParseException();
 				}
+				rightOp = NumericTerm();
+				break;
+			default:
+				jj_la1[53] = jj_gen;
+				;
+		}
+		if (sign == null){
+			if (true)
+				return leftOp;
+		}else{
+			try{
+				{
+					if (true)
+						return queryFactory.createOperation(leftOp, OperationType.getOperator(sign.image), rightOp);
+				}
 			}catch(Exception ex){
 				{
 					if (true)
 						throw generateParseException(ex);
 				}
 			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("JoinSpecification");
 		}
+		throw new Error("Missing return statement in function");
 	}
 
-	/* ****** */
-	/* STRING */
-	/* ****** */
-	final public String String() throws ParseException{
-		trace_call("String");
-		try{
-			Token t;
-			String str = "";
-			label_8: while(true){
-				t = jj_consume_token(STRING_LITERAL);
-				str += t.image;
+	final public ADQLOperand Factor() throws ParseException{
+		boolean negative = false;;
+		ADQLOperand op;
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case PLUS:
+			case MINUS:
 				switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-					case STRING_LITERAL:
-						;
+					case PLUS:
+						jj_consume_token(PLUS);
+						break;
+					case MINUS:
+						jj_consume_token(MINUS);
+						negative = true;
 						break;
 					default:
-						jj_la1[41] = jj_gen;
-						break label_8;
+						jj_la1[54] = jj_gen;
+						jj_consume_token(-1);
+						throw new ParseException();
 				}
-			}
-			{
-				if (true)
-					return (str != null) ? str.substring(1, str.length() - 1) : str;
-			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("String");
+				break;
+			default:
+				jj_la1[55] = jj_gen;
+				;
 		}
-	}
-
-	/* ************* */
-	/* NUMERIC TYPES */
-	/* ************* */
-	final public String UnsignedNumeric() throws ParseException{
-		trace_call("UnsignedNumeric");
-		try{
-			Token t;
+		if (jj_2_10(2)){
+			op = NumericFunction();
+		}else{
 			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+				case LEFT_PAR:
+				case AVG:
+				case MAX:
+				case MIN:
+				case SUM:
+				case COUNT:
+				case DELIMITED_IDENTIFIER:
+				case REGULAR_IDENTIFIER:
 				case SCIENTIFIC_NUMBER:
-					t = jj_consume_token(SCIENTIFIC_NUMBER);
-					break;
 				case UNSIGNED_FLOAT:
-					t = jj_consume_token(UNSIGNED_FLOAT);
-					break;
 				case UNSIGNED_INTEGER:
-					t = jj_consume_token(UNSIGNED_INTEGER);
+					op = NumericValueExpressionPrimary();
 					break;
 				default:
-					jj_la1[42] = jj_gen;
+					jj_la1[56] = jj_gen;
 					jj_consume_token(-1);
 					throw new ParseException();
 			}
-			{
-				if (true)
-					return t.image;
+		}
+		if (negative){
+			try{
+				op = queryFactory.createNegativeOperand(op);
+			}catch(Exception ex){
+				{
+					if (true)
+						throw generateParseException(ex);
+				}
 			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("UnsignedNumeric");
 		}
+
+		{
+			if (true)
+				return op;
+		}
+		throw new Error("Missing return statement in function");
 	}
 
-	final public String UnsignedFloat() throws ParseException{
-		trace_call("UnsignedFloat");
-		try{
-			Token t;
+	final public ADQLOperand StringExpression() throws ParseException{
+		ADQLOperand leftOp;
+		ADQLOperand rightOp = null;
+		leftOp = StringFactor();
+		label_9: while(true){
 			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case UNSIGNED_INTEGER:
-					t = jj_consume_token(UNSIGNED_INTEGER);
-					break;
-				case UNSIGNED_FLOAT:
-					t = jj_consume_token(UNSIGNED_FLOAT);
+				case CONCAT:
+					;
 					break;
 				default:
-					jj_la1[43] = jj_gen;
-					jj_consume_token(-1);
-					throw new ParseException();
+					jj_la1[57] = jj_gen;
+					break label_9;
 			}
-			{
-				if (true)
-					return t.image;
+			jj_consume_token(CONCAT);
+			rightOp = StringFactor();
+			if (!(leftOp instanceof Concatenation)){
+				try{
+					ADQLOperand temp = leftOp;
+					leftOp = queryFactory.createConcatenation();
+					((Concatenation)leftOp).add(temp);
+				}catch(Exception ex){
+					{
+						if (true)
+							throw generateParseException(ex);
+					}
+				}
 			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("UnsignedFloat");
+			((Concatenation)leftOp).add(rightOp);
 		}
+		{
+			if (true)
+				return leftOp;
+		}
+		throw new Error("Missing return statement in function");
 	}
 
-	final public String SignedInteger() throws ParseException{
-		trace_call("SignedInteger");
-		try{
-			Token sign = null, number;
-			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case PLUS:
-				case MINUS:
+	final public ADQLOperand StringFactor() throws ParseException{
+		ADQLOperand op;
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case COORDSYS:
+				op = ExtractCoordSys();
+				break;
+			default:
+				jj_la1[58] = jj_gen;
+				if (jj_2_11(2)){
+					op = UserDefinedFunction();
+					((UserDefinedFunction)op).setExpectedType('S');
+				}else{
 					switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-						case PLUS:
-							sign = jj_consume_token(PLUS);
-							break;
-						case MINUS:
-							sign = jj_consume_token(MINUS);
+						case LEFT_PAR:
+						case STRING_LITERAL:
+						case DELIMITED_IDENTIFIER:
+						case REGULAR_IDENTIFIER:
+							op = StringValueExpressionPrimary();
 							break;
 						default:
-							jj_la1[44] = jj_gen;
+							jj_la1[59] = jj_gen;
 							jj_consume_token(-1);
 							throw new ParseException();
 					}
-					break;
-				default:
-					jj_la1[45] = jj_gen;
-					;
-			}
-			number = jj_consume_token(UNSIGNED_INTEGER);
+				}
+		}
+		{
+			if (true)
+				return op;
+		}
+		throw new Error("Missing return statement in function");
+	}
+
+	final public GeometryValue<GeometryFunction> GeometryExpression() throws ParseException{
+		ADQLColumn col = null;
+		GeometryFunction gf = null;
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case DELIMITED_IDENTIFIER:
+			case REGULAR_IDENTIFIER:
+				col = Column();
+				break;
+			case BOX:
+			case CENTROID:
+			case CIRCLE:
+			case POINT:
+			case POLYGON:
+			case REGION:
+				gf = GeometryValueFunction();
+				break;
+			default:
+				jj_la1[60] = jj_gen;
+				jj_consume_token(-1);
+				throw new ParseException();
+		}
+		if (col != null){
+			col.setExpectedType('G');
 			{
 				if (true)
-					return ((sign == null) ? "" : sign.image) + number.image;
+					return new GeometryValue<GeometryFunction>(col);
 			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("SignedInteger");
+		}else{
+			if (true)
+				return new GeometryValue<GeometryFunction>(gf);
 		}
+		throw new Error("Missing return statement in function");
 	}
 
-	/* *********** */
-	/* EXPRESSIONS */
-	/* *********** */
-	final public ADQLOperand ValueExpressionPrimary() throws ParseException{
-		trace_call("ValueExpressionPrimary");
+	/* ********************************** */
+	/* BOOLEAN EXPRESSIONS (WHERE clause) */
+	/* ********************************** */
+	final public ClauseConstraints ConditionsList(ClauseConstraints clause) throws ParseException{
+		ADQLConstraint constraint = null;
+		Token op = null;
+		boolean notOp = false;
 		try{
-			String expr;
-			ADQLColumn column;
-			ADQLOperand op;
-			try{
+			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+				case NOT:
+					jj_consume_token(NOT);
+					notOp = true;
+					break;
+				default:
+					jj_la1[61] = jj_gen;
+					;
+			}
+			constraint = Constraint();
+			if (notOp)
+				constraint = queryFactory.createNot(constraint);
+			notOp = false;
+			if (clause instanceof ADQLConstraint)
+				clause.add(constraint);
+			else
+				clause.add(constraint);
+			label_10: while(true){
 				switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-					case SCIENTIFIC_NUMBER:
-					case UNSIGNED_FLOAT:
-					case UNSIGNED_INTEGER:
-						// unsigned_value_specification
-						expr = UnsignedNumeric();
-						{
-							if (true)
-								return queryFactory.createNumericConstant(expr);
-						}
-						break;
-					case STRING_LITERAL:
-						expr = String();
-						{
-							if (true)
-								return queryFactory.createStringConstant(expr);
-						}
-						break;
-					case DELIMITED_IDENTIFIER:
-					case REGULAR_IDENTIFIER:
-						column = Column();
-						{
-							if (true)
-								return column;
-						}
+					case AND:
+					case OR:
+						;
 						break;
-					case AVG:
-					case MAX:
-					case MIN:
-					case SUM:
-					case COUNT:
-						op = SqlFunction();
-						{
-							if (true)
-								return op;
-						}
+					default:
+						jj_la1[62] = jj_gen;
+						break label_10;
+				}
+				switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+					case AND:
+						op = jj_consume_token(AND);
 						break;
-					case LEFT_PAR:
-						jj_consume_token(LEFT_PAR);
-						op = ValueExpression();
-						jj_consume_token(RIGHT_PAR);
-						{
-							if (true)
-								return queryFactory.createWrappedOperand(op);
-						}
+					case OR:
+						op = jj_consume_token(OR);
 						break;
 					default:
-						jj_la1[46] = jj_gen;
+						jj_la1[63] = jj_gen;
 						jj_consume_token(-1);
 						throw new ParseException();
 				}
-			}catch(Exception ex){
-				{
-					if (true)
-						throw generateParseException(ex);
+				switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+					case NOT:
+						jj_consume_token(NOT);
+						notOp = true;
+						break;
+					default:
+						jj_la1[64] = jj_gen;
+						;
 				}
+				constraint = Constraint();
+				if (notOp)
+					constraint = queryFactory.createNot(constraint);
+				notOp = false;
+				if (clause instanceof ADQLConstraint)
+					clause.add(op.image, constraint);
+				else
+					clause.add(op.image, constraint);
 			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("ValueExpressionPrimary");
+		}catch(Exception ex){
+			{
+				if (true)
+					throw generateParseException(ex);
+			}
+		}
+		{
+			if (true)
+				return clause;
 		}
+		throw new Error("Missing return statement in function");
 	}
 
-	final public ADQLOperand ValueExpression() throws ParseException{
-		trace_call("ValueExpression");
-		try{
-			ADQLOperand valueExpr = null;
+	final public ADQLConstraint Constraint() throws ParseException{
+		ADQLConstraint constraint = null;
+		if (jj_2_12(2147483647)){
+			constraint = Predicate();
+		}else{
 			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case BOX:
-				case CENTROID:
-				case CIRCLE:
-				case POINT:
-				case POLYGON:
-				case REGION:
-					valueExpr = GeometryValueFunction();
-					break;
-				default:
-					jj_la1[47] = jj_gen;
-					if (jj_2_4(2147483647)){
-						valueExpr = NumericExpression();
-					}else if (jj_2_5(2147483647)){
-						valueExpr = StringExpression();
-					}else if (jj_2_6(2147483647)){
-						valueExpr = StringExpression();
-					}else{
+				case LEFT_PAR:
+					jj_consume_token(LEFT_PAR);
+					try{
+						constraint = queryFactory.createGroupOfConstraints();
+					}catch(Exception ex){
+						{
+							if (true)
+								throw generateParseException(ex);
+						}
+					}
+					ConditionsList((ConstraintsGroup)constraint);
+					jj_consume_token(RIGHT_PAR);
+					break;
+				default:
+					jj_la1[65] = jj_gen;
+					jj_consume_token(-1);
+					throw new ParseException();
+			}
+		}
+		{
+			if (true)
+				return constraint;
+		}
+		throw new Error("Missing return statement in function");
+	}
+
+	final public ADQLConstraint Predicate() throws ParseException{
+		ADQLQuery q = null;
+		ADQLColumn column = null;
+		ADQLOperand strExpr1 = null, strExpr2 = null;
+		ADQLOperand op;
+		Token notToken = null;
+		ADQLConstraint constraint = null;
+		try{
+			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+				case EXISTS:
+					jj_consume_token(EXISTS);
+					q = SubQueryExpression();
+					{
+						if (true)
+							return queryFactory.createExists(q);
+					}
+					break;
+				default:
+					jj_la1[70] = jj_gen;
+					if (jj_2_14(2147483647)){
+						column = Column();
+						jj_consume_token(IS);
+						switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+							case NOT:
+								notToken = jj_consume_token(NOT);
+								break;
+							default:
+								jj_la1[66] = jj_gen;
+								;
+						}
+						jj_consume_token(NULL);
+						{
+							if (true)
+								return queryFactory.createIsNull((notToken != null), column);
+						}
+					}else if (jj_2_15(2147483647)){
+						strExpr1 = StringExpression();
+						switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+							case NOT:
+								notToken = jj_consume_token(NOT);
+								break;
+							default:
+								jj_la1[67] = jj_gen;
+								;
+						}
+						jj_consume_token(LIKE);
+						strExpr2 = StringExpression();
+						{
+							if (true)
+								return queryFactory.createComparison(strExpr1, (notToken == null) ? ComparisonOperator.LIKE : ComparisonOperator.NOTLIKE, strExpr2);
+						}
+					}else{
 						switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
 							case LEFT_PAR:
 							case PLUS:
@@ -1686,11 +2071,18 @@ public class ADQLParser implements ADQLParserConstants {
 							case MIN:
 							case SUM:
 							case COUNT:
+							case BOX:
+							case CENTROID:
+							case CIRCLE:
+							case POINT:
+							case POLYGON:
+							case REGION:
 							case CONTAINS:
 							case INTERSECTS:
 							case AREA:
 							case COORD1:
 							case COORD2:
+							case COORDSYS:
 							case DISTANCE:
 							case ABS:
 							case CEILING:
@@ -1721,1548 +2113,866 @@ public class ADQLParser implements ADQLParserConstants {
 							case SCIENTIFIC_NUMBER:
 							case UNSIGNED_FLOAT:
 							case UNSIGNED_INTEGER:
-								valueExpr = NumericExpression();
+								op = ValueExpression();
+								switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+									case EQUAL:
+									case NOT_EQUAL:
+									case LESS_THAN:
+									case LESS_EQUAL_THAN:
+									case GREATER_THAN:
+									case GREATER_EQUAL_THAN:
+										constraint = ComparisonEnd(op);
+										break;
+									default:
+										jj_la1[68] = jj_gen;
+										if (jj_2_13(2)){
+											constraint = BetweenEnd(op);
+										}else{
+											switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+												case NOT:
+												case IN:
+													constraint = InEnd(op);
+													break;
+												default:
+													jj_la1[69] = jj_gen;
+													jj_consume_token(-1);
+													throw new ParseException();
+											}
+										}
+								}
 								break;
 							default:
-								jj_la1[48] = jj_gen;
+								jj_la1[71] = jj_gen;
 								jj_consume_token(-1);
 								throw new ParseException();
 						}
 					}
 			}
+		}catch(Exception ex){
 			{
 				if (true)
-					return valueExpr;
+					throw generateParseException(ex);
 			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("ValueExpression");
 		}
+		{
+			if (true)
+				return constraint;
+		}
+		throw new Error("Missing return statement in function");
 	}
 
-	final public ADQLOperand NumericExpression() throws ParseException{
-		trace_call("NumericExpression");
+	final public Comparison ComparisonEnd(ADQLOperand leftOp) throws ParseException{
+		Token comp;
+		ADQLOperand rightOp;
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case EQUAL:
+				comp = jj_consume_token(EQUAL);
+				break;
+			case NOT_EQUAL:
+				comp = jj_consume_token(NOT_EQUAL);
+				break;
+			case LESS_THAN:
+				comp = jj_consume_token(LESS_THAN);
+				break;
+			case LESS_EQUAL_THAN:
+				comp = jj_consume_token(LESS_EQUAL_THAN);
+				break;
+			case GREATER_THAN:
+				comp = jj_consume_token(GREATER_THAN);
+				break;
+			case GREATER_EQUAL_THAN:
+				comp = jj_consume_token(GREATER_EQUAL_THAN);
+				break;
+			default:
+				jj_la1[72] = jj_gen;
+				jj_consume_token(-1);
+				throw new ParseException();
+		}
+		rightOp = ValueExpression();
+		try{
+			{
+				if (true)
+					return queryFactory.createComparison(leftOp, ComparisonOperator.getOperator(comp.image), rightOp);
+			}
+		}catch(Exception ex){
+			{
+				if (true)
+					throw generateParseException(ex);
+			}
+		}
+		throw new Error("Missing return statement in function");
+	}
+
+	final public Between BetweenEnd(ADQLOperand leftOp) throws ParseException{
+		Token notToken = null;
+		ADQLOperand min, max;
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case NOT:
+				notToken = jj_consume_token(NOT);
+				break;
+			default:
+				jj_la1[73] = jj_gen;
+				;
+		}
+		jj_consume_token(BETWEEN);
+		min = ValueExpression();
+		jj_consume_token(AND);
+		max = ValueExpression();
 		try{
-			Token sign = null;
-			ADQLOperand leftOp, rightOp = null;
-			leftOp = NumericTerm();
+			{
+				if (true)
+					return queryFactory.createBetween((notToken != null), leftOp, min, max);
+			}
+		}catch(Exception ex){
+			{
+				if (true)
+					throw generateParseException(ex);
+			}
+		}
+		throw new Error("Missing return statement in function");
+	}
+
+	final public In InEnd(ADQLOperand leftOp) throws ParseException{
+		Token not = null;
+		ADQLQuery q = null;
+		ADQLOperand item;
+		Vector<ADQLOperand> items = new Vector<ADQLOperand>();
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case NOT:
+				not = jj_consume_token(NOT);
+				break;
+			default:
+				jj_la1[74] = jj_gen;
+				;
+		}
+		jj_consume_token(IN);
+		if (jj_2_16(2)){
+			q = SubQueryExpression();
+		}else{
 			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case PLUS:
-				case MINUS:
-					switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-						case PLUS:
-							sign = jj_consume_token(PLUS);
-							break;
-						case MINUS:
-							sign = jj_consume_token(MINUS);
-							break;
-						default:
-							jj_la1[49] = jj_gen;
-							jj_consume_token(-1);
-							throw new ParseException();
+				case LEFT_PAR:
+					jj_consume_token(LEFT_PAR);
+					item = ValueExpression();
+					items.add(item);
+					label_11: while(true){
+						switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+							case COMMA:
+								;
+								break;
+							default:
+								jj_la1[75] = jj_gen;
+								break label_11;
+						}
+						jj_consume_token(COMMA);
+						item = ValueExpression();
+						items.add(item);
 					}
-					rightOp = NumericExpression();
+					jj_consume_token(RIGHT_PAR);
 					break;
 				default:
-					jj_la1[50] = jj_gen;
-					;
+					jj_la1[76] = jj_gen;
+					jj_consume_token(-1);
+					throw new ParseException();
 			}
-			if (sign == null){
+		}
+		try{
+			if (q != null){
 				if (true)
-					return leftOp;
+					return queryFactory.createIn(leftOp, q, not != null);
 			}else{
-				try{
-					{
-						if (true)
-							return queryFactory.createOperation(leftOp, OperationType.getOperator(sign.image), rightOp);
-					}
-				}catch(Exception ex){
-					{
-						if (true)
-							throw generateParseException(ex);
-					}
+				ADQLOperand[] list = new ADQLOperand[items.size()];
+				int i = 0;
+				for(ADQLOperand op : items)
+					list[i++] = op;
+				{
+					if (true)
+						return queryFactory.createIn(leftOp, list, not != null);
 				}
 			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("NumericExpression");
+		}catch(Exception ex){
+			{
+				if (true)
+					throw generateParseException(ex);
+			}
 		}
+		throw new Error("Missing return statement in function");
 	}
 
-	final public ADQLOperand NumericTerm() throws ParseException{
-		trace_call("NumericTerm");
+	/* ************* */
+	/* SQL FUNCTIONS */
+	/* ************* */
+	final public SQLFunction SqlFunction() throws ParseException{
+		Token fct, all = null, distinct = null;
+		ADQLOperand op = null;
+		SQLFunction funct = null;
 		try{
-			Token sign = null;
-			ADQLOperand leftOp, rightOp = null;
-			leftOp = Factor();
 			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case ASTERISK:
-				case DIVIDE:
+				case COUNT:
+					jj_consume_token(COUNT);
+					jj_consume_token(LEFT_PAR);
+					switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+						case QUANTIFIER:
+							distinct = jj_consume_token(QUANTIFIER);
+							break;
+						default:
+							jj_la1[77] = jj_gen;
+							;
+					}
 					switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
 						case ASTERISK:
-							sign = jj_consume_token(ASTERISK);
+							all = jj_consume_token(ASTERISK);
 							break;
-						case DIVIDE:
-							sign = jj_consume_token(DIVIDE);
+						case LEFT_PAR:
+						case PLUS:
+						case MINUS:
+						case AVG:
+						case MAX:
+						case MIN:
+						case SUM:
+						case COUNT:
+						case BOX:
+						case CENTROID:
+						case CIRCLE:
+						case POINT:
+						case POLYGON:
+						case REGION:
+						case CONTAINS:
+						case INTERSECTS:
+						case AREA:
+						case COORD1:
+						case COORD2:
+						case COORDSYS:
+						case DISTANCE:
+						case ABS:
+						case CEILING:
+						case DEGREES:
+						case EXP:
+						case FLOOR:
+						case LOG:
+						case LOG10:
+						case MOD:
+						case PI:
+						case POWER:
+						case RADIANS:
+						case RAND:
+						case ROUND:
+						case SQRT:
+						case TRUNCATE:
+						case ACOS:
+						case ASIN:
+						case ATAN:
+						case ATAN2:
+						case COS:
+						case COT:
+						case SIN:
+						case TAN:
+						case STRING_LITERAL:
+						case DELIMITED_IDENTIFIER:
+						case REGULAR_IDENTIFIER:
+						case SCIENTIFIC_NUMBER:
+						case UNSIGNED_FLOAT:
+						case UNSIGNED_INTEGER:
+							op = ValueExpression();
 							break;
 						default:
-							jj_la1[51] = jj_gen;
+							jj_la1[78] = jj_gen;
 							jj_consume_token(-1);
 							throw new ParseException();
 					}
-					rightOp = NumericTerm();
+					jj_consume_token(RIGHT_PAR);
+					funct = queryFactory.createSQLFunction((all != null) ? SQLFunctionType.COUNT_ALL : SQLFunctionType.COUNT, op, distinct != null && distinct.image.equalsIgnoreCase("distinct"));
 					break;
-				default:
-					jj_la1[52] = jj_gen;
-					;
-			}
-			if (sign == null){
-				if (true)
-					return leftOp;
-			}else{
-				try{
-					{
-						if (true)
-							return queryFactory.createOperation(leftOp, OperationType.getOperator(sign.image), rightOp);
-					}
-				}catch(Exception ex){
-					{
-						if (true)
-							throw generateParseException(ex);
-					}
-				}
-			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("NumericTerm");
-		}
-	}
-
-	final public ADQLOperand Factor() throws ParseException{
-		trace_call("Factor");
-		try{
-			boolean negative = false;;
-			ADQLOperand op;
-			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case PLUS:
-				case MINUS:
+				case AVG:
+				case MAX:
+				case MIN:
+				case SUM:
 					switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-						case PLUS:
-							jj_consume_token(PLUS);
+						case AVG:
+							fct = jj_consume_token(AVG);
 							break;
-						case MINUS:
-							jj_consume_token(MINUS);
-							negative = true;
+						case MAX:
+							fct = jj_consume_token(MAX);
+							break;
+						case MIN:
+							fct = jj_consume_token(MIN);
+							break;
+						case SUM:
+							fct = jj_consume_token(SUM);
 							break;
 						default:
-							jj_la1[53] = jj_gen;
+							jj_la1[79] = jj_gen;
 							jj_consume_token(-1);
 							throw new ParseException();
 					}
+					jj_consume_token(LEFT_PAR);
+					switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+						case QUANTIFIER:
+							distinct = jj_consume_token(QUANTIFIER);
+							break;
+						default:
+							jj_la1[80] = jj_gen;
+							;
+					}
+					op = ValueExpression();
+					jj_consume_token(RIGHT_PAR);
+					funct = queryFactory.createSQLFunction(SQLFunctionType.valueOf(fct.image.toUpperCase()), op, distinct != null && distinct.image.equalsIgnoreCase("distinct"));
 					break;
 				default:
-					jj_la1[54] = jj_gen;
-					;
-			}
-			if (jj_2_7(2)){
-				op = NumericFunction();
-			}else{
-				switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-					case LEFT_PAR:
-					case AVG:
-					case MAX:
-					case MIN:
-					case SUM:
-					case COUNT:
-					case STRING_LITERAL:
-					case DELIMITED_IDENTIFIER:
-					case REGULAR_IDENTIFIER:
-					case SCIENTIFIC_NUMBER:
-					case UNSIGNED_FLOAT:
-					case UNSIGNED_INTEGER:
-						op = ValueExpressionPrimary();
-						break;
-					default:
-						jj_la1[55] = jj_gen;
-						jj_consume_token(-1);
-						throw new ParseException();
-				}
-			}
-			if (negative){
-				try{
-					op = queryFactory.createNegativeOperand(op);
-				}catch(Exception ex){
-					{
-						if (true)
-							throw generateParseException(ex);
-					}
-				}
+					jj_la1[81] = jj_gen;
+					jj_consume_token(-1);
+					throw new ParseException();
 			}
-
+		}catch(Exception ex){
 			{
 				if (true)
-					return op;
+					throw generateParseException(ex);
 			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("Factor");
 		}
+		{
+			if (true)
+				return funct;
+		}
+		throw new Error("Missing return statement in function");
 	}
 
-	final public ADQLOperand StringExpression() throws ParseException{
-		trace_call("StringExpression");
-		try{
-			ADQLOperand leftOp;
-			ADQLOperand rightOp = null;
-			leftOp = StringFactor();
-			label_9: while(true){
-				switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-					case CONCAT:
-						;
-						break;
-					default:
-						jj_la1[56] = jj_gen;
-						break label_9;
-				}
-				jj_consume_token(CONCAT);
-				rightOp = StringFactor();
-				if (!(leftOp instanceof Concatenation)){
-					try{
-						ADQLOperand temp = leftOp;
-						leftOp = queryFactory.createConcatenation();
-						((Concatenation)leftOp).add(temp);
-					}catch(Exception ex){
-						{
-							if (true)
-								throw generateParseException(ex);
-						}
-					}
-				}
-				((Concatenation)leftOp).add(rightOp);
-			}
-			{
-				if (true)
-					return leftOp;
-			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("StringExpression");
+	/* ************** */
+	/* ADQL FUNCTIONS */
+	/* ************** */
+	final public ADQLOperand[] Coordinates() throws ParseException{
+		ADQLOperand[] ops = new ADQLOperand[2];
+		ops[0] = NumericExpression();
+		jj_consume_token(COMMA);
+		ops[1] = NumericExpression();
+		{
+			if (true)
+				return ops;
 		}
+		throw new Error("Missing return statement in function");
 	}
 
-	final public ADQLOperand StringFactor() throws ParseException{
-		trace_call("StringFactor");
+	final public GeometryFunction GeometryFunction() throws ParseException{
+		Token t = null;
+		GeometryValue<GeometryFunction> gvf1, gvf2;
+		GeometryValue<PointFunction> gvp1, gvp2;
+		GeometryFunction gf = null;
+		PointFunction p1 = null, p2 = null;
+		ADQLColumn col1 = null, col2 = null;
 		try{
-			ADQLOperand op;
 			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case COORDSYS:
-					op = ExtractCoordSys();
+				case CONTAINS:
+				case INTERSECTS:
+					switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+						case CONTAINS:
+							t = jj_consume_token(CONTAINS);
+							break;
+						case INTERSECTS:
+							t = jj_consume_token(INTERSECTS);
+							break;
+						default:
+							jj_la1[82] = jj_gen;
+							jj_consume_token(-1);
+							throw new ParseException();
+					}
+					jj_consume_token(LEFT_PAR);
+					gvf1 = GeometryExpression();
+					jj_consume_token(COMMA);
+					gvf2 = GeometryExpression();
+					jj_consume_token(RIGHT_PAR);
+					if (t.image.equalsIgnoreCase("contains"))
+						gf = queryFactory.createContains(gvf1, gvf2);
+					else
+						gf = queryFactory.createIntersects(gvf1, gvf2);
 					break;
-				default:
-					jj_la1[57] = jj_gen;
-					if (jj_2_8(2)){
-						op = UserDefinedFunction();
-					}else{
-						switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-							case LEFT_PAR:
-							case AVG:
-							case MAX:
-							case MIN:
-							case SUM:
-							case COUNT:
-							case STRING_LITERAL:
-							case DELIMITED_IDENTIFIER:
-							case REGULAR_IDENTIFIER:
-							case SCIENTIFIC_NUMBER:
-							case UNSIGNED_FLOAT:
-							case UNSIGNED_INTEGER:
-								op = ValueExpressionPrimary();
-								break;
-							default:
-								jj_la1[58] = jj_gen;
-								jj_consume_token(-1);
-								throw new ParseException();
-						}
-					}
-			}
-			{
-				if (true)
-					return op;
-			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("StringFactor");
-		}
-	}
-
-	final public GeometryValue<GeometryFunction> GeometryExpression() throws ParseException{
-		trace_call("GeometryExpression");
-		try{
-			ADQLColumn col = null;
-			GeometryFunction gf = null;
-			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case DELIMITED_IDENTIFIER:
-				case REGULAR_IDENTIFIER:
-					col = Column();
+				case AREA:
+					jj_consume_token(AREA);
+					jj_consume_token(LEFT_PAR);
+					gvf1 = GeometryExpression();
+					jj_consume_token(RIGHT_PAR);
+					gf = queryFactory.createArea(gvf1);
 					break;
-				case BOX:
-				case CENTROID:
-				case CIRCLE:
-				case POINT:
-				case POLYGON:
-				case REGION:
-					gf = GeometryValueFunction();
+				case COORD1:
+					jj_consume_token(COORD1);
+					jj_consume_token(LEFT_PAR);
+					switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+						case POINT:
+							p1 = Point();
+							gf = queryFactory.createCoord1(p1);
+							break;
+						case DELIMITED_IDENTIFIER:
+						case REGULAR_IDENTIFIER:
+							col1 = Column();
+							col1.setExpectedType('G');
+							gf = queryFactory.createCoord1(col1);
+							break;
+						default:
+							jj_la1[83] = jj_gen;
+							jj_consume_token(-1);
+							throw new ParseException();
+					}
+					jj_consume_token(RIGHT_PAR);
 					break;
-				default:
-					jj_la1[59] = jj_gen;
-					jj_consume_token(-1);
-					throw new ParseException();
-			}
-			if (col != null){
-				if (true)
-					return new GeometryValue<GeometryFunction>(col);
-			}else{
-				if (true)
-					return new GeometryValue<GeometryFunction>(gf);
-			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("GeometryExpression");
-		}
-	}
-
-	/* ********************************** */
-	/* BOOLEAN EXPRESSIONS (WHERE clause) */
-	/* ********************************** */
-	final public ClauseConstraints ConditionsList(ClauseConstraints clause) throws ParseException{
-		trace_call("ConditionsList");
-		try{
-			ADQLConstraint constraint = null;
-			Token op = null;
-			boolean notOp = false;
-			try{
-				switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-					case NOT:
-						jj_consume_token(NOT);
-						notOp = true;
-						break;
-					default:
-						jj_la1[60] = jj_gen;
-						;
-				}
-				constraint = Constraint();
-				if (notOp)
-					constraint = queryFactory.createNot(constraint);
-				notOp = false;
-				if (clause instanceof ADQLConstraint)
-					clause.add(constraint);
-				else
-					clause.add(constraint);
-				label_10: while(true){
+				case COORD2:
+					jj_consume_token(COORD2);
+					jj_consume_token(LEFT_PAR);
 					switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-						case AND:
-						case OR:
-							;
+						case POINT:
+							p1 = Point();
+							gf = queryFactory.createCoord2(p1);
+							break;
+						case DELIMITED_IDENTIFIER:
+						case REGULAR_IDENTIFIER:
+							col1 = Column();
+							col1.setExpectedType('G');
+							gf = queryFactory.createCoord2(col1);
 							break;
 						default:
-							jj_la1[61] = jj_gen;
-							break label_10;
+							jj_la1[84] = jj_gen;
+							jj_consume_token(-1);
+							throw new ParseException();
 					}
+					jj_consume_token(RIGHT_PAR);
+					break;
+				case DISTANCE:
+					jj_consume_token(DISTANCE);
+					jj_consume_token(LEFT_PAR);
 					switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-						case AND:
-							op = jj_consume_token(AND);
+						case POINT:
+							p1 = Point();
 							break;
-						case OR:
-							op = jj_consume_token(OR);
+						case DELIMITED_IDENTIFIER:
+						case REGULAR_IDENTIFIER:
+							col1 = Column();
 							break;
 						default:
-							jj_la1[62] = jj_gen;
+							jj_la1[85] = jj_gen;
 							jj_consume_token(-1);
 							throw new ParseException();
 					}
+					if (p1 != null)
+						gvp1 = new GeometryValue<PointFunction>(p1);
+					else{
+						col1.setExpectedType('G');
+						gvp1 = new GeometryValue<PointFunction>(col1);
+					}
+					jj_consume_token(COMMA);
 					switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-						case NOT:
-							jj_consume_token(NOT);
-							notOp = true;
+						case POINT:
+							p2 = Point();
+							break;
+						case DELIMITED_IDENTIFIER:
+						case REGULAR_IDENTIFIER:
+							col2 = Column();
 							break;
 						default:
-							jj_la1[63] = jj_gen;
-							;
+							jj_la1[86] = jj_gen;
+							jj_consume_token(-1);
+							throw new ParseException();
 					}
-					constraint = Constraint();
-					if (notOp)
-						constraint = queryFactory.createNot(constraint);
-					notOp = false;
-					if (clause instanceof ADQLConstraint)
-						clause.add(op.image, constraint);
-					else
-						clause.add(op.image, constraint);
-				}
-			}catch(Exception ex){
-				{
-					if (true)
-						throw generateParseException(ex);
-				}
+					if (p2 != null)
+						gvp2 = new GeometryValue<PointFunction>(p2);
+					else{
+						col2.setExpectedType('G');
+						gvp2 = new GeometryValue<PointFunction>(col2);
+					}
+					jj_consume_token(RIGHT_PAR);
+					gf = queryFactory.createDistance(gvp1, gvp2);
+					break;
+				default:
+					jj_la1[87] = jj_gen;
+					jj_consume_token(-1);
+					throw new ParseException();
 			}
+		}catch(Exception ex){
 			{
 				if (true)
-					return clause;
+					throw generateParseException(ex);
 			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("ConditionsList");
 		}
-	}
-
-	final public ADQLConstraint Constraint() throws ParseException{
-		trace_call("Constraint");
-		try{
-			ADQLConstraint constraint = null;
-			if (jj_2_9(2147483647)){
-				constraint = Predicate();
-			}else{
-				switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-					case LEFT_PAR:
-						jj_consume_token(LEFT_PAR);
-						try{
-							constraint = queryFactory.createGroupOfConstraints();
-						}catch(Exception ex){
-							{
-								if (true)
-									throw generateParseException(ex);
-							}
-						}
-						ConditionsList((ConstraintsGroup)constraint);
-						jj_consume_token(RIGHT_PAR);
-						break;
-					default:
-						jj_la1[64] = jj_gen;
-						jj_consume_token(-1);
-						throw new ParseException();
-				}
-			}
-			{
-				if (true)
-					return constraint;
-			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("Constraint");
+		{
+			if (true)
+				return gf;
 		}
+		throw new Error("Missing return statement in function");
 	}
 
-	final public ADQLConstraint Predicate() throws ParseException{
-		trace_call("Predicate");
-		try{
-			ADQLQuery q = null;
-			ADQLColumn column = null;
-			ADQLOperand strExpr1 = null, strExpr2 = null;
-			ADQLOperand op;
-			Token notToken = null;
-			ADQLConstraint constraint = null;
-			try{
-				switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-					case EXISTS:
-						jj_consume_token(EXISTS);
-						q = SubQueryExpression();
-						{
-							if (true)
-								return queryFactory.createExists(q);
-						}
-						break;
-					default:
-						jj_la1[69] = jj_gen;
-						if (jj_2_11(2147483647)){
-							column = Column();
-							jj_consume_token(IS);
-							switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-								case NOT:
-									notToken = jj_consume_token(NOT);
-									break;
-								default:
-									jj_la1[65] = jj_gen;
-									;
-							}
-							jj_consume_token(NULL);
-							{
-								if (true)
-									return queryFactory.createIsNull((notToken != null), column);
-							}
-						}else if (jj_2_12(2147483647)){
-							strExpr1 = StringExpression();
-							switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-								case NOT:
-									notToken = jj_consume_token(NOT);
-									break;
-								default:
-									jj_la1[66] = jj_gen;
-									;
-							}
-							jj_consume_token(LIKE);
-							strExpr2 = StringExpression();
-							{
-								if (true)
-									return queryFactory.createComparison(strExpr1, (notToken == null) ? ComparisonOperator.LIKE : ComparisonOperator.NOTLIKE, strExpr2);
-							}
-						}else{
-							switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-								case LEFT_PAR:
-								case PLUS:
-								case MINUS:
-								case AVG:
-								case MAX:
-								case MIN:
-								case SUM:
-								case COUNT:
-								case BOX:
-								case CENTROID:
-								case CIRCLE:
-								case POINT:
-								case POLYGON:
-								case REGION:
-								case CONTAINS:
-								case INTERSECTS:
-								case AREA:
-								case COORD1:
-								case COORD2:
-								case COORDSYS:
-								case DISTANCE:
-								case ABS:
-								case CEILING:
-								case DEGREES:
-								case EXP:
-								case FLOOR:
-								case LOG:
-								case LOG10:
-								case MOD:
-								case PI:
-								case POWER:
-								case RADIANS:
-								case RAND:
-								case ROUND:
-								case SQRT:
-								case TRUNCATE:
-								case ACOS:
-								case ASIN:
-								case ATAN:
-								case ATAN2:
-								case COS:
-								case COT:
-								case SIN:
-								case TAN:
-								case STRING_LITERAL:
-								case DELIMITED_IDENTIFIER:
-								case REGULAR_IDENTIFIER:
-								case SCIENTIFIC_NUMBER:
-								case UNSIGNED_FLOAT:
-								case UNSIGNED_INTEGER:
-									op = ValueExpression();
-									switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-										case EQUAL:
-										case NOT_EQUAL:
-										case LESS_THAN:
-										case LESS_EQUAL_THAN:
-										case GREATER_THAN:
-										case GREATER_EQUAL_THAN:
-											constraint = ComparisonEnd(op);
-											break;
-										default:
-											jj_la1[67] = jj_gen;
-											if (jj_2_10(2)){
-												constraint = BetweenEnd(op);
-											}else{
-												switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-													case NOT:
-													case IN:
-														constraint = InEnd(op);
-														break;
-													default:
-														jj_la1[68] = jj_gen;
-														jj_consume_token(-1);
-														throw new ParseException();
-												}
-											}
-									}
-									break;
-								default:
-									jj_la1[70] = jj_gen;
-									jj_consume_token(-1);
-									throw new ParseException();
-							}
-						}
-				}
-			}catch(Exception ex){
-				{
-					if (true)
-						throw generateParseException(ex);
-				}
-			}
-			{
-				if (true)
-					return constraint;
-			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("Predicate");
+	final public ADQLOperand CoordinateSystem() throws ParseException{
+		ADQLOperand coordSys = null;
+		coordSys = StringExpression();
+		{
+			if (true)
+				return coordSys;
 		}
+		throw new Error("Missing return statement in function");
 	}
 
-	final public Comparison ComparisonEnd(ADQLOperand leftOp) throws ParseException{
-		trace_call("ComparisonEnd");
+	final public GeometryFunction GeometryValueFunction() throws ParseException{
+		ADQLOperand coordSys;
+		ADQLOperand width, height;
+		ADQLOperand[] coords, tmp;
+		Vector<ADQLOperand> vCoords;
+		ADQLOperand op = null;
+		GeometryValue<GeometryFunction> gvf = null;
+		GeometryFunction gf = null;
 		try{
-			Token comp;
-			ADQLOperand rightOp;
 			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case EQUAL:
-					comp = jj_consume_token(EQUAL);
-					break;
-				case NOT_EQUAL:
-					comp = jj_consume_token(NOT_EQUAL);
-					break;
-				case LESS_THAN:
-					comp = jj_consume_token(LESS_THAN);
-					break;
-				case LESS_EQUAL_THAN:
-					comp = jj_consume_token(LESS_EQUAL_THAN);
-					break;
-				case GREATER_THAN:
-					comp = jj_consume_token(GREATER_THAN);
-					break;
-				case GREATER_EQUAL_THAN:
-					comp = jj_consume_token(GREATER_EQUAL_THAN);
+				case BOX:
+					jj_consume_token(BOX);
+					jj_consume_token(LEFT_PAR);
+					coordSys = CoordinateSystem();
+					jj_consume_token(COMMA);
+					coords = Coordinates();
+					jj_consume_token(COMMA);
+					width = NumericExpression();
+					jj_consume_token(COMMA);
+					height = NumericExpression();
+					jj_consume_token(RIGHT_PAR);
+					gf = queryFactory.createBox(coordSys, coords[0], coords[1], width, height);
 					break;
-				default:
-					jj_la1[71] = jj_gen;
-					jj_consume_token(-1);
-					throw new ParseException();
-			}
-			rightOp = ValueExpression();
-			try{
-				{
-					if (true)
-						return queryFactory.createComparison(leftOp, ComparisonOperator.getOperator(comp.image), rightOp);
-				}
-			}catch(Exception ex){
-				{
-					if (true)
-						throw generateParseException(ex);
-				}
-			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("ComparisonEnd");
-		}
-	}
-
-	final public Between BetweenEnd(ADQLOperand leftOp) throws ParseException{
-		trace_call("BetweenEnd");
-		try{
-			Token notToken = null;
-			ADQLOperand min, max;
-			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case NOT:
-					notToken = jj_consume_token(NOT);
-					break;
-				default:
-					jj_la1[72] = jj_gen;
-					;
-			}
-			jj_consume_token(BETWEEN);
-			min = ValueExpression();
-			jj_consume_token(AND);
-			max = ValueExpression();
-			try{
-				{
-					if (true)
-						return queryFactory.createBetween((notToken != null), leftOp, min, max);
-				}
-			}catch(Exception ex){
-				{
-					if (true)
-						throw generateParseException(ex);
-				}
-			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("BetweenEnd");
-		}
-	}
-
-	final public In InEnd(ADQLOperand leftOp) throws ParseException{
-		trace_call("InEnd");
-		try{
-			Token not = null;
-			ADQLQuery q = null;
-			ADQLOperand item;
-			Vector<ADQLOperand> items = new Vector<ADQLOperand>();
-			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case NOT:
-					not = jj_consume_token(NOT);
-					break;
-				default:
-					jj_la1[73] = jj_gen;
-					;
-			}
-			jj_consume_token(IN);
-			if (jj_2_13(2)){
-				q = SubQueryExpression();
-			}else{
-				switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-					case LEFT_PAR:
-						jj_consume_token(LEFT_PAR);
-						item = ValueExpression();
-						items.add(item);
-						label_11: while(true){
-							switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-								case COMMA:
-									;
-									break;
-								default:
-									jj_la1[74] = jj_gen;
-									break label_11;
-							}
-							jj_consume_token(COMMA);
-							item = ValueExpression();
-							items.add(item);
-						}
-						jj_consume_token(RIGHT_PAR);
-						break;
-					default:
-						jj_la1[75] = jj_gen;
-						jj_consume_token(-1);
-						throw new ParseException();
-				}
-			}
-			try{
-				if (q != null){
-					if (true)
-						return queryFactory.createIn(leftOp, q, not != null);
-				}else{
-					ADQLOperand[] list = new ADQLOperand[items.size()];
-					int i = 0;
-					for(ADQLOperand op : items)
-						list[i++] = op;
-					{
-						if (true)
-							return queryFactory.createIn(leftOp, list, not != null);
-					}
-				}
-			}catch(Exception ex){
-				{
-					if (true)
-						throw generateParseException(ex);
-				}
-			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("InEnd");
-		}
-	}
-
-	/* ************* */
-	/* SQL FUNCTIONS */
-	/* ************* */
-	final public SQLFunction SqlFunction() throws ParseException{
-		trace_call("SqlFunction");
-		try{
-			Token fct, all = null, distinct = null;
-			ADQLOperand op = null;
-			SQLFunction funct = null;
-			try{
-				switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-					case COUNT:
-						jj_consume_token(COUNT);
-						jj_consume_token(LEFT_PAR);
-						switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-							case QUANTIFIER:
-								distinct = jj_consume_token(QUANTIFIER);
-								break;
-							default:
-								jj_la1[76] = jj_gen;
-								;
-						}
-						switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-							case ASTERISK:
-								all = jj_consume_token(ASTERISK);
-								break;
-							case LEFT_PAR:
-							case PLUS:
-							case MINUS:
-							case AVG:
-							case MAX:
-							case MIN:
-							case SUM:
-							case COUNT:
-							case BOX:
-							case CENTROID:
-							case CIRCLE:
-							case POINT:
-							case POLYGON:
-							case REGION:
-							case CONTAINS:
-							case INTERSECTS:
-							case AREA:
-							case COORD1:
-							case COORD2:
-							case COORDSYS:
-							case DISTANCE:
-							case ABS:
-							case CEILING:
-							case DEGREES:
-							case EXP:
-							case FLOOR:
-							case LOG:
-							case LOG10:
-							case MOD:
-							case PI:
-							case POWER:
-							case RADIANS:
-							case RAND:
-							case ROUND:
-							case SQRT:
-							case TRUNCATE:
-							case ACOS:
-							case ASIN:
-							case ATAN:
-							case ATAN2:
-							case COS:
-							case COT:
-							case SIN:
-							case TAN:
-							case STRING_LITERAL:
-							case DELIMITED_IDENTIFIER:
-							case REGULAR_IDENTIFIER:
-							case SCIENTIFIC_NUMBER:
-							case UNSIGNED_FLOAT:
-							case UNSIGNED_INTEGER:
-								op = ValueExpression();
-								break;
-							default:
-								jj_la1[77] = jj_gen;
-								jj_consume_token(-1);
-								throw new ParseException();
-						}
-						jj_consume_token(RIGHT_PAR);
-						funct = queryFactory.createSQLFunction((all != null) ? SQLFunctionType.COUNT_ALL : SQLFunctionType.COUNT, op, distinct != null && distinct.image.equalsIgnoreCase("distinct"));
-						break;
-					case AVG:
-					case MAX:
-					case MIN:
-					case SUM:
-						switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-							case AVG:
-								fct = jj_consume_token(AVG);
-								break;
-							case MAX:
-								fct = jj_consume_token(MAX);
-								break;
-							case MIN:
-								fct = jj_consume_token(MIN);
-								break;
-							case SUM:
-								fct = jj_consume_token(SUM);
-								break;
-							default:
-								jj_la1[78] = jj_gen;
-								jj_consume_token(-1);
-								throw new ParseException();
-						}
-						jj_consume_token(LEFT_PAR);
-						switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-							case QUANTIFIER:
-								distinct = jj_consume_token(QUANTIFIER);
-								break;
-							default:
-								jj_la1[79] = jj_gen;
-								;
-						}
-						op = ValueExpression();
-						jj_consume_token(RIGHT_PAR);
-						funct = queryFactory.createSQLFunction(SQLFunctionType.valueOf(fct.image.toUpperCase()), op, distinct != null && distinct.image.equalsIgnoreCase("distinct"));
-						break;
-					default:
-						jj_la1[80] = jj_gen;
-						jj_consume_token(-1);
-						throw new ParseException();
-				}
-			}catch(Exception ex){
-				{
-					if (true)
-						throw generateParseException(ex);
-				}
-			}
-			{
-				if (true)
-					return funct;
-			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("SqlFunction");
-		}
-	}
-
-	/* ************** */
-	/* ADQL FUNCTIONS */
-	/* ************** */
-	final public ADQLOperand[] Coordinates() throws ParseException{
-		trace_call("Coordinates");
-		try{
-			ADQLOperand[] ops = new ADQLOperand[2];
-			ops[0] = NumericExpression();
-			jj_consume_token(COMMA);
-			ops[1] = NumericExpression();
-			{
-				if (true)
-					return ops;
-			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("Coordinates");
-		}
-	}
-
-	final public GeometryFunction GeometryFunction() throws ParseException{
-		trace_call("GeometryFunction");
-		try{
-			Token t = null;
-			GeometryValue<GeometryFunction> gvf1, gvf2;
-			GeometryValue<PointFunction> gvp1, gvp2;
-			GeometryFunction gf = null;
-			PointFunction p1 = null, p2 = null;
-			ADQLColumn col1 = null, col2 = null;
-			try{
-				switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-					case CONTAINS:
-					case INTERSECTS:
-						switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-							case CONTAINS:
-								t = jj_consume_token(CONTAINS);
-								break;
-							case INTERSECTS:
-								t = jj_consume_token(INTERSECTS);
-								break;
-							default:
-								jj_la1[81] = jj_gen;
-								jj_consume_token(-1);
-								throw new ParseException();
-						}
-						jj_consume_token(LEFT_PAR);
-						gvf1 = GeometryExpression();
-						jj_consume_token(COMMA);
-						gvf2 = GeometryExpression();
-						jj_consume_token(RIGHT_PAR);
-						if (t.image.equalsIgnoreCase("contains"))
-							gf = queryFactory.createContains(gvf1, gvf2);
-						else
-							gf = queryFactory.createIntersects(gvf1, gvf2);
-						break;
-					case AREA:
-						jj_consume_token(AREA);
-						jj_consume_token(LEFT_PAR);
-						gvf1 = GeometryExpression();
-						jj_consume_token(RIGHT_PAR);
-						gf = queryFactory.createArea(gvf1);
-						break;
-					case COORD1:
-						jj_consume_token(COORD1);
-						jj_consume_token(LEFT_PAR);
-						switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-							case POINT:
-								p1 = Point();
-								gf = queryFactory.createCoord1(p1);
-								break;
-							case DELIMITED_IDENTIFIER:
-							case REGULAR_IDENTIFIER:
-								col1 = Column();
-								gf = queryFactory.createCoord1(col1);
-								break;
-							default:
-								jj_la1[82] = jj_gen;
-								jj_consume_token(-1);
-								throw new ParseException();
-						}
-						jj_consume_token(RIGHT_PAR);
-						break;
-					case COORD2:
-						jj_consume_token(COORD2);
-						jj_consume_token(LEFT_PAR);
-						switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-							case POINT:
-								p1 = Point();
-								gf = queryFactory.createCoord2(p1);
-								break;
-							case DELIMITED_IDENTIFIER:
-							case REGULAR_IDENTIFIER:
-								col1 = Column();
-								gf = queryFactory.createCoord2(col1);
-								break;
-							default:
-								jj_la1[83] = jj_gen;
-								jj_consume_token(-1);
-								throw new ParseException();
-						}
-						jj_consume_token(RIGHT_PAR);
-						break;
-					case DISTANCE:
-						jj_consume_token(DISTANCE);
-						jj_consume_token(LEFT_PAR);
-						switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-							case POINT:
-								p1 = Point();
-								break;
-							case DELIMITED_IDENTIFIER:
-							case REGULAR_IDENTIFIER:
-								col1 = Column();
-								break;
-							default:
-								jj_la1[84] = jj_gen;
-								jj_consume_token(-1);
-								throw new ParseException();
-						}
-						if (p1 != null)
-							gvp1 = new GeometryValue<PointFunction>(p1);
-						else
-							gvp1 = new GeometryValue<PointFunction>(col1);
-						jj_consume_token(COMMA);
-						switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-							case POINT:
-								p2 = Point();
-								break;
-							case DELIMITED_IDENTIFIER:
-							case REGULAR_IDENTIFIER:
-								col2 = Column();
-								break;
-							default:
-								jj_la1[85] = jj_gen;
-								jj_consume_token(-1);
-								throw new ParseException();
-						}
-						if (p2 != null)
-							gvp2 = new GeometryValue<PointFunction>(p2);
-						else
-							gvp2 = new GeometryValue<PointFunction>(col2);
-						jj_consume_token(RIGHT_PAR);
-						gf = queryFactory.createDistance(gvp1, gvp2);
-						break;
-					default:
-						jj_la1[86] = jj_gen;
-						jj_consume_token(-1);
-						throw new ParseException();
-				}
-			}catch(Exception ex){
-				{
-					if (true)
-						throw generateParseException(ex);
-				}
-			}
-			{
-				if (true)
-					return gf;
-			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("GeometryFunction");
-		}
-	}
-
-	final public ADQLOperand CoordinateSystem() throws ParseException{
-		trace_call("CoordinateSystem");
-		try{
-			Token oldToken = token;
-			ADQLOperand coordSys = null;
-			coordSys = StringExpression();
-			if (allowedCoordSys.size() > 0){
-				TextPosition position = new TextPosition(oldToken.next, token);
-				if (coordSys == null){
-					if (true)
-						throw new ParseException("A coordinate system must always be provided !", position);
-				}
-				if (coordSys instanceof StringConstant && !isAllowedCoordSys(((StringConstant)coordSys).getValue())){
-					if (true)
-						throw new ParseException("\u005c"" + coordSys.toADQL() + "\u005c" is not an allowed coordinate systems !", position);
-				}
-			}
-
-			{
-				if (true)
-					return coordSys;
-			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("CoordinateSystem");
-		}
-	}
-
-	final public GeometryFunction GeometryValueFunction() throws ParseException{
-		trace_call("GeometryValueFunction");
-		try{
-			ADQLOperand coordSys;
-			ADQLOperand width, height;
-			ADQLOperand[] coords, tmp;
-			Vector<ADQLOperand> vCoords;
-			ADQLOperand op = null;
-			GeometryValue<GeometryFunction> gvf = null;
-			GeometryFunction gf = null;
-			try{
-				switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-					case BOX:
-						jj_consume_token(BOX);
-						jj_consume_token(LEFT_PAR);
-						coordSys = CoordinateSystem();
-						jj_consume_token(COMMA);
-						coords = Coordinates();
-						jj_consume_token(COMMA);
-						width = NumericExpression();
-						jj_consume_token(COMMA);
-						height = NumericExpression();
-						jj_consume_token(RIGHT_PAR);
-						gf = queryFactory.createBox(coordSys, coords[0], coords[1], width, height);
-						break;
-					case CENTROID:
-						jj_consume_token(CENTROID);
-						jj_consume_token(LEFT_PAR);
-						gvf = GeometryExpression();
-						jj_consume_token(RIGHT_PAR);
-						gf = queryFactory.createCentroid(gvf);
-						break;
-					case CIRCLE:
-						jj_consume_token(CIRCLE);
-						jj_consume_token(LEFT_PAR);
-						coordSys = CoordinateSystem();
-						jj_consume_token(COMMA);
-						coords = Coordinates();
-						jj_consume_token(COMMA);
-						width = NumericExpression();
-						jj_consume_token(RIGHT_PAR);
-						gf = queryFactory.createCircle(coordSys, coords[0], coords[1], width);
-						break;
-					case POINT:
-						gf = Point();
-						break;
-					case POLYGON:
-						jj_consume_token(POLYGON);
-						jj_consume_token(LEFT_PAR);
-						coordSys = CoordinateSystem();
-						vCoords = new Vector<ADQLOperand>();
-						jj_consume_token(COMMA);
-						tmp = Coordinates();
-						vCoords.add(tmp[0]);
-						vCoords.add(tmp[1]);
-						jj_consume_token(COMMA);
-						tmp = Coordinates();
-						vCoords.add(tmp[0]);
-						vCoords.add(tmp[1]);
-						jj_consume_token(COMMA);
-						tmp = Coordinates();
-						vCoords.add(tmp[0]);
-						vCoords.add(tmp[1]);
-						label_12: while(true){
-							switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-								case COMMA:
-									;
-									break;
-								default:
-									jj_la1[87] = jj_gen;
-									break label_12;
-							}
-							jj_consume_token(COMMA);
-							tmp = Coordinates();
-							vCoords.add(tmp[0]);
-							vCoords.add(tmp[1]);
-						}
-						jj_consume_token(RIGHT_PAR);
-						gf = queryFactory.createPolygon(coordSys, vCoords);
-						break;
-					case REGION:
-						jj_consume_token(REGION);
-						jj_consume_token(LEFT_PAR);
-						op = StringExpression();
-						jj_consume_token(RIGHT_PAR);
-						gf = queryFactory.createRegion(op);
-						break;
-					default:
-						jj_la1[88] = jj_gen;
-						jj_consume_token(-1);
-						throw new ParseException();
-				}
-			}catch(Exception ex){
-				{
-					if (true)
-						throw generateParseException(ex);
-				}
-			}
-			{
-				if (true)
-					return gf;
-			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("GeometryValueFunction");
-		}
-	}
-
-	final public PointFunction Point() throws ParseException{
-		trace_call("Point");
-		try{
-			ADQLOperand coordSys;
-			ADQLOperand[] coords;
-			jj_consume_token(POINT);
-			jj_consume_token(LEFT_PAR);
-			coordSys = CoordinateSystem();
-			jj_consume_token(COMMA);
-			coords = Coordinates();
-			jj_consume_token(RIGHT_PAR);
-			try{
-				{
-					if (true)
-						return queryFactory.createPoint(coordSys, coords[0], coords[1]);
-				}
-			}catch(Exception ex){
-				{
-					if (true)
-						throw generateParseException(ex);
-				}
-			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("Point");
-		}
-	}
-
-	final public GeometryFunction ExtractCoordSys() throws ParseException{
-		trace_call("ExtractCoordSys");
-		try{
-			GeometryValue<GeometryFunction> gvf;
-			jj_consume_token(COORDSYS);
-			jj_consume_token(LEFT_PAR);
-			gvf = GeometryExpression();
-			jj_consume_token(RIGHT_PAR);
-			try{
-				{
-					if (true)
-						return queryFactory.createExtractCoordSys(gvf);
-				}
-			}catch(Exception ex){
-				{
-					if (true)
-						throw generateParseException(ex);
-				}
-			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("ExtractCoordSys");
-		}
-	}
-
-	/* ***************** */
-	/* NUMERIC FUNCTIONS */
-	/* ***************** */
-	final public ADQLFunction NumericFunction() throws ParseException{
-		trace_call("NumericFunction");
-		try{
-			ADQLFunction fct;
-			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case ABS:
-				case CEILING:
-				case DEGREES:
-				case EXP:
-				case FLOOR:
-				case LOG:
-				case LOG10:
-				case MOD:
-				case PI:
-				case POWER:
-				case RADIANS:
-				case RAND:
-				case ROUND:
-				case SQRT:
-				case TRUNCATE:
-					fct = MathFunction();
+				case CENTROID:
+					jj_consume_token(CENTROID);
+					jj_consume_token(LEFT_PAR);
+					gvf = GeometryExpression();
+					jj_consume_token(RIGHT_PAR);
+					gf = queryFactory.createCentroid(gvf);
 					break;
-				case ACOS:
-				case ASIN:
-				case ATAN:
-				case ATAN2:
-				case COS:
-				case COT:
-				case SIN:
-				case TAN:
-					fct = TrigFunction();
+				case CIRCLE:
+					jj_consume_token(CIRCLE);
+					jj_consume_token(LEFT_PAR);
+					coordSys = CoordinateSystem();
+					jj_consume_token(COMMA);
+					coords = Coordinates();
+					jj_consume_token(COMMA);
+					width = NumericExpression();
+					jj_consume_token(RIGHT_PAR);
+					gf = queryFactory.createCircle(coordSys, coords[0], coords[1], width);
 					break;
-				case CONTAINS:
-				case INTERSECTS:
-				case AREA:
-				case COORD1:
-				case COORD2:
-				case DISTANCE:
-					fct = GeometryFunction();
+				case POINT:
+					gf = Point();
 					break;
-				case REGULAR_IDENTIFIER:
-					fct = UserDefinedFunction();
+				case POLYGON:
+					jj_consume_token(POLYGON);
+					jj_consume_token(LEFT_PAR);
+					coordSys = CoordinateSystem();
+					vCoords = new Vector<ADQLOperand>();
+					jj_consume_token(COMMA);
+					tmp = Coordinates();
+					vCoords.add(tmp[0]);
+					vCoords.add(tmp[1]);
+					jj_consume_token(COMMA);
+					tmp = Coordinates();
+					vCoords.add(tmp[0]);
+					vCoords.add(tmp[1]);
+					jj_consume_token(COMMA);
+					tmp = Coordinates();
+					vCoords.add(tmp[0]);
+					vCoords.add(tmp[1]);
+					label_12: while(true){
+						switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+							case COMMA:
+								;
+								break;
+							default:
+								jj_la1[88] = jj_gen;
+								break label_12;
+						}
+						jj_consume_token(COMMA);
+						tmp = Coordinates();
+						vCoords.add(tmp[0]);
+						vCoords.add(tmp[1]);
+					}
+					jj_consume_token(RIGHT_PAR);
+					gf = queryFactory.createPolygon(coordSys, vCoords);
+					break;
+				case REGION:
+					jj_consume_token(REGION);
+					jj_consume_token(LEFT_PAR);
+					op = StringExpression();
+					jj_consume_token(RIGHT_PAR);
+					gf = queryFactory.createRegion(op);
 					break;
 				default:
 					jj_la1[89] = jj_gen;
 					jj_consume_token(-1);
 					throw new ParseException();
 			}
+		}catch(Exception ex){
 			{
 				if (true)
-					return fct;
+					throw generateParseException(ex);
 			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("NumericFunction");
 		}
+		{
+			if (true)
+				return gf;
+		}
+		throw new Error("Missing return statement in function");
 	}
 
-	final public MathFunction MathFunction() throws ParseException{
-		trace_call("MathFunction");
+	final public PointFunction Point() throws ParseException{
+		ADQLOperand coordSys;
+		ADQLOperand[] coords;
+		jj_consume_token(POINT);
+		jj_consume_token(LEFT_PAR);
+		coordSys = CoordinateSystem();
+		jj_consume_token(COMMA);
+		coords = Coordinates();
+		jj_consume_token(RIGHT_PAR);
 		try{
-			Token fct = null;
-			ADQLOperand param1 = null, param2 = null;
-			String integerValue = null;
-			try{
-				switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-					case ABS:
-						fct = jj_consume_token(ABS);
-						jj_consume_token(LEFT_PAR);
-						param1 = NumericExpression();
-						jj_consume_token(RIGHT_PAR);
-						break;
-					case CEILING:
-						fct = jj_consume_token(CEILING);
-						jj_consume_token(LEFT_PAR);
-						param1 = NumericExpression();
-						jj_consume_token(RIGHT_PAR);
-						break;
-					case DEGREES:
-						fct = jj_consume_token(DEGREES);
-						jj_consume_token(LEFT_PAR);
-						param1 = NumericExpression();
-						jj_consume_token(RIGHT_PAR);
-						break;
-					case EXP:
-						fct = jj_consume_token(EXP);
-						jj_consume_token(LEFT_PAR);
-						param1 = NumericExpression();
-						jj_consume_token(RIGHT_PAR);
-						break;
-					case FLOOR:
-						fct = jj_consume_token(FLOOR);
-						jj_consume_token(LEFT_PAR);
-						param1 = NumericExpression();
-						jj_consume_token(RIGHT_PAR);
-						break;
-					case LOG:
-						fct = jj_consume_token(LOG);
-						jj_consume_token(LEFT_PAR);
-						param1 = NumericExpression();
-						jj_consume_token(RIGHT_PAR);
-						break;
-					case LOG10:
-						fct = jj_consume_token(LOG10);
-						jj_consume_token(LEFT_PAR);
-						param1 = NumericExpression();
-						jj_consume_token(RIGHT_PAR);
-						break;
-					case MOD:
-						fct = jj_consume_token(MOD);
-						jj_consume_token(LEFT_PAR);
-						param1 = NumericExpression();
-						jj_consume_token(COMMA);
-						param2 = NumericExpression();
-						jj_consume_token(RIGHT_PAR);
-						break;
-					case PI:
-						fct = jj_consume_token(PI);
-						jj_consume_token(LEFT_PAR);
-						jj_consume_token(RIGHT_PAR);
-						break;
-					case POWER:
-						fct = jj_consume_token(POWER);
-						jj_consume_token(LEFT_PAR);
-						param1 = NumericExpression();
-						jj_consume_token(COMMA);
-						param2 = NumericExpression();
-						jj_consume_token(RIGHT_PAR);
-						break;
-					case RADIANS:
-						fct = jj_consume_token(RADIANS);
-						jj_consume_token(LEFT_PAR);
-						param1 = NumericExpression();
-						jj_consume_token(RIGHT_PAR);
-						break;
-					case RAND:
-						fct = jj_consume_token(RAND);
-						jj_consume_token(LEFT_PAR);
-						switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-							case LEFT_PAR:
-							case PLUS:
-							case MINUS:
-							case AVG:
-							case MAX:
-							case MIN:
-							case SUM:
-							case COUNT:
-							case CONTAINS:
-							case INTERSECTS:
-							case AREA:
-							case COORD1:
-							case COORD2:
-							case DISTANCE:
-							case ABS:
-							case CEILING:
-							case DEGREES:
-							case EXP:
-							case FLOOR:
-							case LOG:
-							case LOG10:
-							case MOD:
-							case PI:
-							case POWER:
-							case RADIANS:
-							case RAND:
-							case ROUND:
-							case SQRT:
-							case TRUNCATE:
-							case ACOS:
-							case ASIN:
-							case ATAN:
-							case ATAN2:
-							case COS:
-							case COT:
-							case SIN:
-							case TAN:
-							case STRING_LITERAL:
-							case DELIMITED_IDENTIFIER:
-							case REGULAR_IDENTIFIER:
-							case SCIENTIFIC_NUMBER:
-							case UNSIGNED_FLOAT:
-							case UNSIGNED_INTEGER:
-								param1 = NumericExpression();
-								break;
-							default:
-								jj_la1[90] = jj_gen;
-								;
-						}
-						jj_consume_token(RIGHT_PAR);
-						break;
-					case ROUND:
-						fct = jj_consume_token(ROUND);
-						jj_consume_token(LEFT_PAR);
-						param1 = NumericExpression();
-						switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-							case COMMA:
-								jj_consume_token(COMMA);
-								integerValue = SignedInteger();
-								param2 = queryFactory.createNumericConstant(integerValue);
-								break;
-							default:
-								jj_la1[91] = jj_gen;
-								;
-						}
-						jj_consume_token(RIGHT_PAR);
-						break;
-					case SQRT:
-						fct = jj_consume_token(SQRT);
-						jj_consume_token(LEFT_PAR);
-						param1 = NumericExpression();
-						jj_consume_token(RIGHT_PAR);
-						break;
-					case TRUNCATE:
-						fct = jj_consume_token(TRUNCATE);
-						jj_consume_token(LEFT_PAR);
-						param1 = NumericExpression();
-						switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-							case COMMA:
-								jj_consume_token(COMMA);
-								integerValue = SignedInteger();
-								param2 = queryFactory.createNumericConstant(integerValue);
-								break;
-							default:
-								jj_la1[92] = jj_gen;
-								;
-						}
-						jj_consume_token(RIGHT_PAR);
-						break;
-					default:
-						jj_la1[93] = jj_gen;
-						jj_consume_token(-1);
-						throw new ParseException();
-				}
-				if (param1 != null){
-					if (true)
-						return queryFactory.createMathFunction(MathFunctionType.valueOf(fct.image.toUpperCase()), param1, param2);
-				}else{
-					if (true)
-						return null;
-				}
-			}catch(Exception ex){
-				{
-					if (true)
-						throw generateParseException(ex);
-				}
+			{
+				if (true)
+					return queryFactory.createPoint(coordSys, coords[0], coords[1]);
+			}
+		}catch(Exception ex){
+			{
+				if (true)
+					throw generateParseException(ex);
 			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("MathFunction");
 		}
+		throw new Error("Missing return statement in function");
 	}
 
-	final public MathFunction TrigFunction() throws ParseException{
-		trace_call("TrigFunction");
+	final public GeometryFunction ExtractCoordSys() throws ParseException{
+		GeometryValue<GeometryFunction> gvf;
+		jj_consume_token(COORDSYS);
+		jj_consume_token(LEFT_PAR);
+		gvf = GeometryExpression();
+		jj_consume_token(RIGHT_PAR);
+		try{
+			{
+				if (true)
+					return queryFactory.createExtractCoordSys(gvf);
+			}
+		}catch(Exception ex){
+			{
+				if (true)
+					throw generateParseException(ex);
+			}
+		}
+		throw new Error("Missing return statement in function");
+	}
+
+	/* ***************** */
+	/* NUMERIC FUNCTIONS */
+	/* ***************** */
+	final public ADQLFunction NumericFunction() throws ParseException{
+		ADQLFunction fct;
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case ABS:
+			case CEILING:
+			case DEGREES:
+			case EXP:
+			case FLOOR:
+			case LOG:
+			case LOG10:
+			case MOD:
+			case PI:
+			case POWER:
+			case RADIANS:
+			case RAND:
+			case ROUND:
+			case SQRT:
+			case TRUNCATE:
+				fct = MathFunction();
+				break;
+			case ACOS:
+			case ASIN:
+			case ATAN:
+			case ATAN2:
+			case COS:
+			case COT:
+			case SIN:
+			case TAN:
+				fct = TrigFunction();
+				break;
+			case CONTAINS:
+			case INTERSECTS:
+			case AREA:
+			case COORD1:
+			case COORD2:
+			case DISTANCE:
+				fct = GeometryFunction();
+				break;
+			case REGULAR_IDENTIFIER:
+				fct = UserDefinedFunction();
+				((UserDefinedFunction)fct).setExpectedType('N');
+				break;
+			default:
+				jj_la1[90] = jj_gen;
+				jj_consume_token(-1);
+				throw new ParseException();
+		}
+		{
+			if (true)
+				return fct;
+		}
+		throw new Error("Missing return statement in function");
+	}
+
+	final public MathFunction MathFunction() throws ParseException{
+		Token fct = null;
+		ADQLOperand param1 = null, param2 = null;
+		String integerValue = null;
 		try{
-			Token fct = null;
-			ADQLOperand param1 = null, param2 = null;
 			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case ACOS:
-					fct = jj_consume_token(ACOS);
+				case ABS:
+					fct = jj_consume_token(ABS);
+					jj_consume_token(LEFT_PAR);
+					param1 = NumericExpression();
+					jj_consume_token(RIGHT_PAR);
+					break;
+				case CEILING:
+					fct = jj_consume_token(CEILING);
+					jj_consume_token(LEFT_PAR);
+					param1 = NumericExpression();
+					jj_consume_token(RIGHT_PAR);
+					break;
+				case DEGREES:
+					fct = jj_consume_token(DEGREES);
+					jj_consume_token(LEFT_PAR);
+					param1 = NumericExpression();
+					jj_consume_token(RIGHT_PAR);
+					break;
+				case EXP:
+					fct = jj_consume_token(EXP);
+					jj_consume_token(LEFT_PAR);
+					param1 = NumericExpression();
+					jj_consume_token(RIGHT_PAR);
+					break;
+				case FLOOR:
+					fct = jj_consume_token(FLOOR);
+					jj_consume_token(LEFT_PAR);
+					param1 = NumericExpression();
+					jj_consume_token(RIGHT_PAR);
+					break;
+				case LOG:
+					fct = jj_consume_token(LOG);
 					jj_consume_token(LEFT_PAR);
 					param1 = NumericExpression();
 					jj_consume_token(RIGHT_PAR);
 					break;
-				case ASIN:
-					fct = jj_consume_token(ASIN);
+				case LOG10:
+					fct = jj_consume_token(LOG10);
 					jj_consume_token(LEFT_PAR);
 					param1 = NumericExpression();
 					jj_consume_token(RIGHT_PAR);
 					break;
-				case ATAN:
-					fct = jj_consume_token(ATAN);
+				case MOD:
+					fct = jj_consume_token(MOD);
 					jj_consume_token(LEFT_PAR);
 					param1 = NumericExpression();
+					jj_consume_token(COMMA);
+					param2 = NumericExpression();
+					jj_consume_token(RIGHT_PAR);
+					break;
+				case PI:
+					fct = jj_consume_token(PI);
+					jj_consume_token(LEFT_PAR);
 					jj_consume_token(RIGHT_PAR);
 					break;
-				case ATAN2:
-					fct = jj_consume_token(ATAN2);
+				case POWER:
+					fct = jj_consume_token(POWER);
 					jj_consume_token(LEFT_PAR);
 					param1 = NumericExpression();
 					jj_consume_token(COMMA);
 					param2 = NumericExpression();
 					jj_consume_token(RIGHT_PAR);
 					break;
-				case COS:
-					fct = jj_consume_token(COS);
+				case RADIANS:
+					fct = jj_consume_token(RADIANS);
 					jj_consume_token(LEFT_PAR);
 					param1 = NumericExpression();
 					jj_consume_token(RIGHT_PAR);
 					break;
-				case COT:
-					fct = jj_consume_token(COT);
+				case RAND:
+					fct = jj_consume_token(RAND);
+					jj_consume_token(LEFT_PAR);
+					switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+						case LEFT_PAR:
+						case PLUS:
+						case MINUS:
+						case AVG:
+						case MAX:
+						case MIN:
+						case SUM:
+						case COUNT:
+						case CONTAINS:
+						case INTERSECTS:
+						case AREA:
+						case COORD1:
+						case COORD2:
+						case DISTANCE:
+						case ABS:
+						case CEILING:
+						case DEGREES:
+						case EXP:
+						case FLOOR:
+						case LOG:
+						case LOG10:
+						case MOD:
+						case PI:
+						case POWER:
+						case RADIANS:
+						case RAND:
+						case ROUND:
+						case SQRT:
+						case TRUNCATE:
+						case ACOS:
+						case ASIN:
+						case ATAN:
+						case ATAN2:
+						case COS:
+						case COT:
+						case SIN:
+						case TAN:
+						case DELIMITED_IDENTIFIER:
+						case REGULAR_IDENTIFIER:
+						case SCIENTIFIC_NUMBER:
+						case UNSIGNED_FLOAT:
+						case UNSIGNED_INTEGER:
+							param1 = NumericExpression();
+							break;
+						default:
+							jj_la1[91] = jj_gen;
+							;
+					}
+					jj_consume_token(RIGHT_PAR);
+					break;
+				case ROUND:
+					fct = jj_consume_token(ROUND);
 					jj_consume_token(LEFT_PAR);
 					param1 = NumericExpression();
+					switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+						case COMMA:
+							jj_consume_token(COMMA);
+							integerValue = SignedInteger();
+							param2 = queryFactory.createNumericConstant(integerValue);
+							break;
+						default:
+							jj_la1[92] = jj_gen;
+							;
+					}
 					jj_consume_token(RIGHT_PAR);
 					break;
-				case SIN:
-					fct = jj_consume_token(SIN);
+				case SQRT:
+					fct = jj_consume_token(SQRT);
 					jj_consume_token(LEFT_PAR);
 					param1 = NumericExpression();
 					jj_consume_token(RIGHT_PAR);
 					break;
-				case TAN:
-					fct = jj_consume_token(TAN);
+				case TRUNCATE:
+					fct = jj_consume_token(TRUNCATE);
 					jj_consume_token(LEFT_PAR);
 					param1 = NumericExpression();
+					switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+						case COMMA:
+							jj_consume_token(COMMA);
+							integerValue = SignedInteger();
+							param2 = queryFactory.createNumericConstant(integerValue);
+							break;
+						default:
+							jj_la1[93] = jj_gen;
+							;
+					}
 					jj_consume_token(RIGHT_PAR);
 					break;
 				default:
@@ -3270,131 +2980,203 @@ public class ADQLParser implements ADQLParserConstants {
 					jj_consume_token(-1);
 					throw new ParseException();
 			}
-			try{
-				if (param1 != null){
-					if (true)
-						return queryFactory.createMathFunction(MathFunctionType.valueOf(fct.image.toUpperCase()), param1, param2);
-				}else{
-					if (true)
-						return null;
-				}
-			}catch(Exception ex){
-				{
-					if (true)
-						throw generateParseException(ex);
-				}
+			if (param1 != null){
+				if (true)
+					return queryFactory.createMathFunction(MathFunctionType.valueOf(fct.image.toUpperCase()), param1, param2);
+			}else{
+				if (true)
+					return null;
+			}
+		}catch(Exception ex){
+			{
+				if (true)
+					throw generateParseException(ex);
+			}
+		}
+		throw new Error("Missing return statement in function");
+	}
+
+	final public MathFunction TrigFunction() throws ParseException{
+		Token fct = null;
+		ADQLOperand param1 = null, param2 = null;
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case ACOS:
+				fct = jj_consume_token(ACOS);
+				jj_consume_token(LEFT_PAR);
+				param1 = NumericExpression();
+				jj_consume_token(RIGHT_PAR);
+				break;
+			case ASIN:
+				fct = jj_consume_token(ASIN);
+				jj_consume_token(LEFT_PAR);
+				param1 = NumericExpression();
+				jj_consume_token(RIGHT_PAR);
+				break;
+			case ATAN:
+				fct = jj_consume_token(ATAN);
+				jj_consume_token(LEFT_PAR);
+				param1 = NumericExpression();
+				jj_consume_token(RIGHT_PAR);
+				break;
+			case ATAN2:
+				fct = jj_consume_token(ATAN2);
+				jj_consume_token(LEFT_PAR);
+				param1 = NumericExpression();
+				jj_consume_token(COMMA);
+				param2 = NumericExpression();
+				jj_consume_token(RIGHT_PAR);
+				break;
+			case COS:
+				fct = jj_consume_token(COS);
+				jj_consume_token(LEFT_PAR);
+				param1 = NumericExpression();
+				jj_consume_token(RIGHT_PAR);
+				break;
+			case COT:
+				fct = jj_consume_token(COT);
+				jj_consume_token(LEFT_PAR);
+				param1 = NumericExpression();
+				jj_consume_token(RIGHT_PAR);
+				break;
+			case SIN:
+				fct = jj_consume_token(SIN);
+				jj_consume_token(LEFT_PAR);
+				param1 = NumericExpression();
+				jj_consume_token(RIGHT_PAR);
+				break;
+			case TAN:
+				fct = jj_consume_token(TAN);
+				jj_consume_token(LEFT_PAR);
+				param1 = NumericExpression();
+				jj_consume_token(RIGHT_PAR);
+				break;
+			default:
+				jj_la1[95] = jj_gen;
+				jj_consume_token(-1);
+				throw new ParseException();
+		}
+		try{
+			if (param1 != null){
+				if (true)
+					return queryFactory.createMathFunction(MathFunctionType.valueOf(fct.image.toUpperCase()), param1, param2);
+			}else{
+				if (true)
+					return null;
+			}
+		}catch(Exception ex){
+			{
+				if (true)
+					throw generateParseException(ex);
 			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("TrigFunction");
 		}
+		throw new Error("Missing return statement in function");
 	}
 
-	/* /!\ WARNING: The function name may be prefixed by "udf_" but there is no way to check it here ! */
 	final public UserDefinedFunction UserDefinedFunction() throws ParseException{
-		trace_call("UserDefinedFunction");
-		try{
-			Token fct;
-			Vector<ADQLOperand> params = new Vector<ADQLOperand>();
-			ADQLOperand op;
-			fct = jj_consume_token(REGULAR_IDENTIFIER);
-			jj_consume_token(LEFT_PAR);
-			switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-				case LEFT_PAR:
-				case PLUS:
-				case MINUS:
-				case AVG:
-				case MAX:
-				case MIN:
-				case SUM:
-				case COUNT:
-				case BOX:
-				case CENTROID:
-				case CIRCLE:
-				case POINT:
-				case POLYGON:
-				case REGION:
-				case CONTAINS:
-				case INTERSECTS:
-				case AREA:
-				case COORD1:
-				case COORD2:
-				case COORDSYS:
-				case DISTANCE:
-				case ABS:
-				case CEILING:
-				case DEGREES:
-				case EXP:
-				case FLOOR:
-				case LOG:
-				case LOG10:
-				case MOD:
-				case PI:
-				case POWER:
-				case RADIANS:
-				case RAND:
-				case ROUND:
-				case SQRT:
-				case TRUNCATE:
-				case ACOS:
-				case ASIN:
-				case ATAN:
-				case ATAN2:
-				case COS:
-				case COT:
-				case SIN:
-				case TAN:
-				case STRING_LITERAL:
-				case DELIMITED_IDENTIFIER:
-				case REGULAR_IDENTIFIER:
-				case SCIENTIFIC_NUMBER:
-				case UNSIGNED_FLOAT:
-				case UNSIGNED_INTEGER:
+		Token fct;
+		Vector<ADQLOperand> params = new Vector<ADQLOperand>();
+		ADQLOperand op;
+		fct = jj_consume_token(REGULAR_IDENTIFIER);
+		jj_consume_token(LEFT_PAR);
+		switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+			case LEFT_PAR:
+			case PLUS:
+			case MINUS:
+			case AVG:
+			case MAX:
+			case MIN:
+			case SUM:
+			case COUNT:
+			case BOX:
+			case CENTROID:
+			case CIRCLE:
+			case POINT:
+			case POLYGON:
+			case REGION:
+			case CONTAINS:
+			case INTERSECTS:
+			case AREA:
+			case COORD1:
+			case COORD2:
+			case COORDSYS:
+			case DISTANCE:
+			case ABS:
+			case CEILING:
+			case DEGREES:
+			case EXP:
+			case FLOOR:
+			case LOG:
+			case LOG10:
+			case MOD:
+			case PI:
+			case POWER:
+			case RADIANS:
+			case RAND:
+			case ROUND:
+			case SQRT:
+			case TRUNCATE:
+			case ACOS:
+			case ASIN:
+			case ATAN:
+			case ATAN2:
+			case COS:
+			case COT:
+			case SIN:
+			case TAN:
+			case STRING_LITERAL:
+			case DELIMITED_IDENTIFIER:
+			case REGULAR_IDENTIFIER:
+			case SCIENTIFIC_NUMBER:
+			case UNSIGNED_FLOAT:
+			case UNSIGNED_INTEGER:
+				op = ValueExpression();
+				params.add(op);
+				label_13: while(true){
+					switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
+						case COMMA:
+							;
+							break;
+						default:
+							jj_la1[96] = jj_gen;
+							break label_13;
+					}
+					jj_consume_token(COMMA);
 					op = ValueExpression();
 					params.add(op);
-					label_13: while(true){
-						switch((jj_ntk == -1) ? jj_ntk() : jj_ntk){
-							case COMMA:
-								;
-								break;
-							default:
-								jj_la1[95] = jj_gen;
-								break label_13;
-						}
-						jj_consume_token(COMMA);
-						op = ValueExpression();
-						params.add(op);
-					}
-					break;
-				default:
-					jj_la1[96] = jj_gen;
-					;
-			}
-			jj_consume_token(RIGHT_PAR);
-			//System.out.println("INFO [ADQLParser]: \""+fct.image+"\" (from line "+fct.beginLine+" and column "+fct.beginColumn+" to line "+token.endLine+" and column "+(token.endColumn+1)+") is considered as an user defined function !");
-			try{
-				ADQLOperand[] parameters = new ADQLOperand[params.size()];
-				for(int i = 0; i < params.size(); i++)
-					parameters[i] = params.get(i);
-				{
-					if (true)
-						return queryFactory.createUserDefinedFunction(fct.image, parameters);
-				}
-			}catch(UnsupportedOperationException uoe){
-				{
-					if (true)
-						throw new ParseException(uoe.getMessage(), new TextPosition(fct, token));
-				}
-			}catch(Exception ex){
-				{
-					if (true)
-						throw generateParseException(ex);
 				}
+				break;
+			default:
+				jj_la1[97] = jj_gen;
+				;
+		}
+		jj_consume_token(RIGHT_PAR);
+		//System.out.println("INFO [ADQLParser]: \""+fct.image+"\" (from line "+fct.beginLine+" and column "+fct.beginColumn+" to line "+token.endLine+" and column "+(token.endColumn+1)+") is considered as an user defined function !");
+		try{
+			//  Build the parameters list:
+			ADQLOperand[] parameters = new ADQLOperand[params.size()];
+			for(int i = 0; i < params.size(); i++)
+				parameters[i] = params.get(i);
+
+			// Create the UDF function:
+			{
+				if (true)
+					return queryFactory.createUserDefinedFunction(fct.image, parameters);
+			}
+		}catch(UnsupportedOperationException uoe){
+			/* This catch clause is just for backward compatibility:
+			* if the createUserDefinedFunction(...) is overridden and
+			* the function can not be identified a such exception may be thrown). */
+			{
+				if (true)
+					throw new ParseException(uoe.getMessage(), new TextPosition(fct, token));
+			}
+		}catch(Exception ex){
+			{
+				if (true)
+					throw generateParseException(ex);
 			}
-			throw new Error("Missing return statement in function");
-		}finally{
-			trace_return("UserDefinedFunction");
 		}
+		throw new Error("Missing return statement in function");
 	}
 
 	private boolean jj_2_1(int xla){
@@ -3553,32 +3335,94 @@ public class ADQLParser implements ADQLParserConstants {
 		}
 	}
 
-	private boolean jj_3R_16(){
-		if (jj_scan_token(LEFT_PAR))
+	private boolean jj_2_14(int xla){
+		jj_la = xla;
+		jj_lastpos = jj_scanpos = token;
+		try{
+			return !jj_3_14();
+		}catch(LookaheadSuccess ls){
 			return true;
-		if (jj_3R_28())
+		}finally{
+			jj_save(13, xla);
+		}
+	}
+
+	private boolean jj_2_15(int xla){
+		jj_la = xla;
+		jj_lastpos = jj_scanpos = token;
+		try{
+			return !jj_3_15();
+		}catch(LookaheadSuccess ls){
 			return true;
-		if (jj_scan_token(RIGHT_PAR))
+		}finally{
+			jj_save(14, xla);
+		}
+	}
+
+	private boolean jj_2_16(int xla){
+		jj_la = xla;
+		jj_lastpos = jj_scanpos = token;
+		try{
+			return !jj_3_16();
+		}catch(LookaheadSuccess ls){
+			return true;
+		}finally{
+			jj_save(15, xla);
+		}
+	}
+
+	private boolean jj_3R_185(){
+		if (jj_scan_token(NOT))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_113(){
-		if (jj_3R_101())
+	private boolean jj_3R_146(){
+		if (jj_scan_token(COMMA))
+			return true;
+		if (jj_3R_46())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_152(){
+	private boolean jj_3R_149(){
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_164())
+		if (jj_3R_161()){
+			jj_scanpos = xsp;
+			if (jj_3_1()){
+				jj_scanpos = xsp;
+				if (jj_3R_162())
+					return true;
+			}
+		}
+		return false;
+	}
+
+	private boolean jj_3R_150(){
+		if (jj_scan_token(COMMA))
+			return true;
+		if (jj_3R_149())
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_145(){
+		if (jj_3R_109())
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_163(){
+		Token xsp;
+		xsp = jj_scanpos;
+		if (jj_3R_185())
 			jj_scanpos = xsp;
-		if (jj_3R_165())
+		if (jj_3R_186())
 			return true;
 		while(true){
 			xsp = jj_scanpos;
-			if (jj_3R_166()){
+			if (jj_3R_187()){
 				jj_scanpos = xsp;
 				break;
 			}
@@ -3586,113 +3430,138 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3R_122(){
-		if (jj_3R_144())
+	private boolean jj_3R_148(){
+		if (jj_scan_token(TOP))
+			return true;
+		if (jj_scan_token(UNSIGNED_INTEGER))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_121(){
-		if (jj_3R_143())
+	private boolean jj_3R_147(){
+		if (jj_scan_token(QUANTIFIER))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_120(){
-		if (jj_3R_142())
+	private boolean jj_3R_48(){
+		if (jj_scan_token(SELECT))
+			return true;
+		Token xsp;
+		xsp = jj_scanpos;
+		if (jj_3R_147())
+			jj_scanpos = xsp;
+		xsp = jj_scanpos;
+		if (jj_3R_148())
+			jj_scanpos = xsp;
+		if (jj_3R_149())
 			return true;
+		while(true){
+			xsp = jj_scanpos;
+			if (jj_3R_150()){
+				jj_scanpos = xsp;
+				break;
+			}
+		}
 		return false;
 	}
 
-	private boolean jj_3R_119(){
-		if (jj_3R_141())
+	private boolean jj_3R_144(){
+		if (jj_3R_21())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_112(){
-		if (jj_3R_23())
+	private boolean jj_3R_16(){
+		if (jj_scan_token(LEFT_PAR))
+			return true;
+		if (jj_3R_31())
+			return true;
+		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_162(){
-		if (jj_3R_49())
+	private boolean jj_3R_53(){
+		if (jj_3R_74())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_64(){
+	private boolean jj_3R_122(){
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_112()){
+		if (jj_3R_144()){
 			jj_scanpos = xsp;
-			if (jj_3R_113())
+			if (jj_3R_145())
 				return true;
 		}
 		return false;
 	}
 
-	private boolean jj_3R_32(){
-		if (jj_3R_49())
+	private boolean jj_3R_55(){
+		if (jj_3R_76())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3_8(){
-		if (jj_3R_20())
+	private boolean jj_3_11(){
+		if (jj_3R_24())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_31(){
-		if (jj_3R_48())
+	private boolean jj_3R_54(){
+		if (jj_3R_75())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_18(){
+	private boolean jj_3R_35(){
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_31()){
+		if (jj_3R_54()){
 			jj_scanpos = xsp;
-			if (jj_3_8()){
+			if (jj_3_11()){
 				jj_scanpos = xsp;
-				if (jj_3R_32())
+				if (jj_3R_55())
 					return true;
 			}
 		}
 		return false;
 	}
 
-	private boolean jj_3R_28(){
-		if (jj_3R_44())
+	private boolean jj_3R_133(){
+		if (jj_3R_155())
 			return true;
-		if (jj_3R_118())
+		return false;
+	}
+
+	private boolean jj_3R_132(){
+		if (jj_3R_154())
 			return true;
-		Token xsp;
-		xsp = jj_scanpos;
-		if (jj_3R_119())
-			jj_scanpos = xsp;
-		xsp = jj_scanpos;
-		if (jj_3R_120())
-			jj_scanpos = xsp;
-		xsp = jj_scanpos;
-		if (jj_3R_121())
-			jj_scanpos = xsp;
-		xsp = jj_scanpos;
-		if (jj_3R_122())
-			jj_scanpos = xsp;
 		return false;
 	}
 
-	private boolean jj_3R_65(){
-		if (jj_3R_41())
+	private boolean jj_3R_131(){
+		if (jj_3R_153())
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_130(){
+		if (jj_3R_152())
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_123(){
+		if (jj_3R_46())
 			return true;
 		Token xsp;
 		while(true){
 			xsp = jj_scanpos;
-			if (jj_3R_114()){
+			if (jj_3R_146()){
 				jj_scanpos = xsp;
 				break;
 			}
@@ -3700,23 +3569,15 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3R_182(){
-		if (jj_scan_token(COMMA))
-			return true;
-		if (jj_3R_191())
-			return true;
-		return false;
-	}
-
-	private boolean jj_3R_43(){
+	private boolean jj_3R_47(){
 		if (jj_scan_token(CONCAT))
 			return true;
-		if (jj_3R_18())
+		if (jj_3R_35())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_181(){
+	private boolean jj_3R_174(){
 		if (jj_scan_token(COMMA))
 			return true;
 		if (jj_3R_191())
@@ -3724,25 +3585,42 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3R_106(){
-		if (jj_scan_token(FULL))
+	private boolean jj_3R_173(){
+		if (jj_scan_token(COMMA))
+			return true;
+		if (jj_3R_191())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_170(){
-		if (jj_scan_token(MINUS))
+	private boolean jj_3R_31(){
+		if (jj_3R_48())
 			return true;
+		if (jj_3R_129())
+			return true;
+		Token xsp;
+		xsp = jj_scanpos;
+		if (jj_3R_130())
+			jj_scanpos = xsp;
+		xsp = jj_scanpos;
+		if (jj_3R_131())
+			jj_scanpos = xsp;
+		xsp = jj_scanpos;
+		if (jj_3R_132())
+			jj_scanpos = xsp;
+		xsp = jj_scanpos;
+		if (jj_3R_133())
+			jj_scanpos = xsp;
 		return false;
 	}
 
-	private boolean jj_3R_24(){
-		if (jj_3R_18())
+	private boolean jj_3R_27(){
+		if (jj_3R_35())
 			return true;
 		Token xsp;
 		while(true){
 			xsp = jj_scanpos;
-			if (jj_3R_43()){
+			if (jj_3R_47()){
 				jj_scanpos = xsp;
 				break;
 			}
@@ -3750,24 +3628,30 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3_7(){
-		if (jj_3R_19())
+	private boolean jj_3R_73(){
+		if (jj_scan_token(MINUS))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_161(){
+	private boolean jj_3_10(){
+		if (jj_3R_23())
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_52(){
 		Token xsp;
 		xsp = jj_scanpos;
 		if (jj_scan_token(8)){
 			jj_scanpos = xsp;
-			if (jj_3R_170())
+			if (jj_3R_73())
 				return true;
 		}
 		return false;
 	}
 
-	private boolean jj_3R_160(){
+	private boolean jj_3R_168(){
 		Token xsp;
 		xsp = jj_scanpos;
 		if (jj_scan_token(10)){
@@ -3775,198 +3659,284 @@ public class ADQLParser implements ADQLParserConstants {
 			if (jj_scan_token(11))
 				return true;
 		}
-		if (jj_3R_130())
+		if (jj_3R_135())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_148(){
+	private boolean jj_3R_112(){
+		if (jj_scan_token(FULL))
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_34(){
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_161())
+		if (jj_3R_52())
 			jj_scanpos = xsp;
 		xsp = jj_scanpos;
-		if (jj_3_7()){
+		if (jj_3_10()){
 			jj_scanpos = xsp;
-			if (jj_3R_162())
+			if (jj_3R_53())
 				return true;
 		}
 		return false;
 	}
 
-	private boolean jj_3R_20(){
+	private boolean jj_3R_24(){
 		if (jj_scan_token(REGULAR_IDENTIFIER))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_65())
+		if (jj_3R_123())
 			jj_scanpos = xsp;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_109(){
-		if (jj_scan_token(FULL))
+	private boolean jj_3R_156(){
+		Token xsp;
+		xsp = jj_scanpos;
+		if (jj_scan_token(8)){
+			jj_scanpos = xsp;
+			if (jj_scan_token(9))
+				return true;
+		}
+		if (jj_3R_108())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_180(){
-		if (jj_3R_102())
+	private boolean jj_3R_19(){
+		if (jj_3R_34())
 			return true;
-		return false;
-	}
-
-	private boolean jj_3R_145(){
 		Token xsp;
 		xsp = jj_scanpos;
 		if (jj_scan_token(8)){
 			jj_scanpos = xsp;
-			if (jj_scan_token(9))
-				return true;
+			if (jj_scan_token(9)){
+				jj_scanpos = xsp;
+				if (jj_scan_token(10)){
+					jj_scanpos = xsp;
+					if (jj_scan_token(11))
+						return true;
+				}
+			}
 		}
-		if (jj_3R_102())
+		return false;
+	}
+
+	private boolean jj_3R_172(){
+		if (jj_3R_108())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_93(){
+	private boolean jj_3R_20(){
+		if (jj_3R_35())
+			return true;
+		if (jj_scan_token(CONCAT))
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_135(){
+		if (jj_3R_34())
+			return true;
+		Token xsp;
+		xsp = jj_scanpos;
+		if (jj_3R_168())
+			jj_scanpos = xsp;
+		return false;
+	}
+
+	private boolean jj_3R_100(){
 		if (jj_scan_token(TAN))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_102())
+		if (jj_3R_108())
 			return true;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_92(){
+	private boolean jj_3_9(){
+		if (jj_3R_22())
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_99(){
 		if (jj_scan_token(SIN))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_102())
+		if (jj_3R_108())
 			return true;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_130(){
-		if (jj_3R_148())
+	private boolean jj_3_8(){
+		if (jj_3R_21())
 			return true;
-		Token xsp;
-		xsp = jj_scanpos;
-		if (jj_3R_160())
-			jj_scanpos = xsp;
 		return false;
 	}
 
-	private boolean jj_3R_91(){
+	private boolean jj_3R_98(){
 		if (jj_scan_token(COT))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_102())
+		if (jj_3R_108())
 			return true;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_90(){
+	private boolean jj_3R_115(){
+		if (jj_scan_token(FULL))
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_97(){
 		if (jj_scan_token(COS))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_102())
+		if (jj_3R_108())
 			return true;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_89(){
+	private boolean jj_3_7(){
+		if (jj_scan_token(REGULAR_IDENTIFIER))
+			return true;
+		if (jj_scan_token(LEFT_PAR))
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_96(){
 		if (jj_scan_token(ATAN2))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_102())
+		if (jj_3R_108())
 			return true;
 		if (jj_scan_token(COMMA))
 			return true;
-		if (jj_3R_102())
+		if (jj_3R_108())
 			return true;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_88(){
+	private boolean jj_3_6(){
+		if (jj_scan_token(LEFT_PAR))
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_95(){
 		if (jj_scan_token(ATAN))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_102())
+		if (jj_3R_108())
 			return true;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_105(){
-		if (jj_scan_token(RIGHT))
-			return true;
+	private boolean jj_3_5(){
+		Token xsp;
+		xsp = jj_scanpos;
+		if (jj_scan_token(63)){
+			jj_scanpos = xsp;
+			if (jj_3R_20())
+				return true;
+		}
 		return false;
 	}
 
-	private boolean jj_3R_87(){
+	private boolean jj_3R_94(){
 		if (jj_scan_token(ASIN))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_102())
+		if (jj_3R_108())
 			return true;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_86(){
+	private boolean jj_3_4(){
+		Token xsp;
+		xsp = jj_scanpos;
+		if (jj_3R_18()){
+			jj_scanpos = xsp;
+			if (jj_3R_19())
+				return true;
+		}
+		return false;
+	}
+
+	private boolean jj_3R_18(){
+		Token xsp;
+		xsp = jj_scanpos;
+		if (jj_scan_token(8)){
+			jj_scanpos = xsp;
+			if (jj_scan_token(9))
+				return true;
+		}
+		return false;
+	}
+
+	private boolean jj_3R_93(){
 		if (jj_scan_token(ACOS))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_102())
+		if (jj_3R_108())
 			return true;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_51(){
+	private boolean jj_3R_58(){
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_86()){
+		if (jj_3R_93()){
 			jj_scanpos = xsp;
-			if (jj_3R_87()){
+			if (jj_3R_94()){
 				jj_scanpos = xsp;
-				if (jj_3R_88()){
+				if (jj_3R_95()){
 					jj_scanpos = xsp;
-					if (jj_3R_89()){
+					if (jj_3R_96()){
 						jj_scanpos = xsp;
-						if (jj_3R_90()){
+						if (jj_3R_97()){
 							jj_scanpos = xsp;
-							if (jj_3R_91()){
+							if (jj_3R_98()){
 								jj_scanpos = xsp;
-								if (jj_3R_92()){
+								if (jj_3R_99()){
 									jj_scanpos = xsp;
-									if (jj_3R_93())
+									if (jj_3R_100())
 										return true;
 								}
 							}
@@ -3978,128 +3948,155 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3_6(){
-		if (jj_3R_18())
+	private boolean jj_3R_69(){
+		if (jj_3R_34())
 			return true;
-		if (jj_scan_token(CONCAT))
+		return false;
+	}
+
+	private boolean jj_3R_68(){
+		if (jj_3R_35())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3_5(){
-		if (jj_scan_token(COORDSYS))
+	private boolean jj_3R_67(){
+		if (jj_3R_21())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_85(){
+	private boolean jj_3R_108(){
+		if (jj_3R_135())
+			return true;
+		Token xsp;
+		xsp = jj_scanpos;
+		if (jj_3R_156())
+			jj_scanpos = xsp;
+		return false;
+	}
+
+	private boolean jj_3R_92(){
 		if (jj_scan_token(TRUNCATE))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_102())
+		if (jj_3R_108())
 			return true;
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_182())
+		if (jj_3R_174())
 			jj_scanpos = xsp;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3_4(){
-		Token xsp;
-		xsp = jj_scanpos;
-		if (jj_scan_token(8)){
-			jj_scanpos = xsp;
-			if (jj_scan_token(9))
-				return true;
-		}
+	private boolean jj_3R_66(){
+		if (jj_3R_109())
+			return true;
 		return false;
 	}
 
-	private boolean jj_3R_84(){
+	private boolean jj_3R_91(){
 		if (jj_scan_token(SQRT))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_102())
+		if (jj_3R_108())
 			return true;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_83(){
+	private boolean jj_3R_65(){
+		if (jj_3R_24())
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_90(){
 		if (jj_scan_token(ROUND))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_102())
+		if (jj_3R_108())
 			return true;
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_181())
+		if (jj_3R_173())
 			jj_scanpos = xsp;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_82(){
+	private boolean jj_3R_64(){
+		if (jj_scan_token(LEFT_PAR))
+			return true;
+		if (jj_3R_46())
+			return true;
+		if (jj_scan_token(RIGHT_PAR))
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_89(){
 		if (jj_scan_token(RAND))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_180())
+		if (jj_3R_172())
 			jj_scanpos = xsp;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_81(){
+	private boolean jj_3R_63(){
+		if (jj_3R_27())
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_88(){
 		if (jj_scan_token(RADIANS))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_102())
+		if (jj_3R_108())
 			return true;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_102(){
-		if (jj_3R_130())
-			return true;
-		Token xsp;
-		xsp = jj_scanpos;
-		if (jj_3R_145())
-			jj_scanpos = xsp;
-		return false;
-	}
-
-	private boolean jj_3R_80(){
+	private boolean jj_3R_87(){
 		if (jj_scan_token(POWER))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_102())
+		if (jj_3R_108())
 			return true;
 		if (jj_scan_token(COMMA))
 			return true;
-		if (jj_3R_102())
+		if (jj_3R_108())
 			return true;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_79(){
+	private boolean jj_3R_62(){
+		if (jj_3R_108())
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_86(){
 		if (jj_scan_token(PI))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
@@ -4109,153 +4106,150 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3R_78(){
+	private boolean jj_3R_111(){
+		if (jj_scan_token(RIGHT))
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_85(){
 		if (jj_scan_token(MOD))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_102())
+		if (jj_3R_108())
 			return true;
 		if (jj_scan_token(COMMA))
 			return true;
-		if (jj_3R_102())
+		if (jj_3R_108())
 			return true;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_59(){
-		if (jj_3R_102())
-			return true;
-		return false;
-	}
-
-	private boolean jj_3R_77(){
+	private boolean jj_3R_84(){
 		if (jj_scan_token(LOG10))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_102())
+		if (jj_3R_108())
 			return true;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_58(){
-		if (jj_3R_24())
-			return true;
-		return false;
-	}
-
-	private boolean jj_3R_76(){
+	private boolean jj_3R_83(){
 		if (jj_scan_token(LOG))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_102())
+		if (jj_3R_108())
 			return true;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_57(){
-		if (jj_3R_24())
-			return true;
-		return false;
-	}
-
-	private boolean jj_3R_75(){
+	private boolean jj_3R_82(){
 		if (jj_scan_token(FLOOR))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_102())
+		if (jj_3R_108())
 			return true;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_70(){
+	private boolean jj_3R_81(){
+		if (jj_scan_token(EXP))
+			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_41())
+		if (jj_3R_108())
 			return true;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_56(){
-		if (jj_3R_102())
-			return true;
-		return false;
-	}
-
-	private boolean jj_3R_74(){
-		if (jj_scan_token(EXP))
+	private boolean jj_3R_80(){
+		if (jj_scan_token(DEGREES))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_102())
+		if (jj_3R_108())
 			return true;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_73(){
-		if (jj_scan_token(DEGREES))
-			return true;
+	private boolean jj_3R_126(){
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_102())
+		if (jj_3R_27())
 			return true;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_69(){
-		if (jj_3R_117())
+	private boolean jj_3R_79(){
+		if (jj_scan_token(CEILING))
+			return true;
+		if (jj_scan_token(LEFT_PAR))
+			return true;
+		if (jj_3R_108())
+			return true;
+		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_55(){
-		if (jj_3R_101())
+	private boolean jj_3R_125(){
+		if (jj_3R_21())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_72(){
-		if (jj_scan_token(CEILING))
+	private boolean jj_3R_78(){
+		if (jj_scan_token(ABS))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_102())
+		if (jj_3R_108())
 			return true;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_41(){
+	private boolean jj_3R_46(){
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_55()){
+		if (jj_3R_62()){
 			jj_scanpos = xsp;
-			if (jj_3R_56()){
+			if (jj_3R_63()){
 				jj_scanpos = xsp;
-				if (jj_3R_57()){
+				if (jj_3R_64()){
 					jj_scanpos = xsp;
-					if (jj_3R_58()){
+					if (jj_3R_65()){
 						jj_scanpos = xsp;
-						if (jj_3R_59())
-							return true;
+						if (jj_3R_66()){
+							jj_scanpos = xsp;
+							if (jj_3R_67()){
+								jj_scanpos = xsp;
+								if (jj_3R_68()){
+									jj_scanpos = xsp;
+									if (jj_3R_69())
+										return true;
+								}
+							}
+						}
 					}
 				}
 			}
@@ -4263,74 +4257,44 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3R_68(){
-		if (jj_3R_23())
-			return true;
-		return false;
-	}
-
-	private boolean jj_3R_71(){
-		if (jj_scan_token(ABS))
-			return true;
-		if (jj_scan_token(LEFT_PAR))
-			return true;
-		if (jj_3R_102())
-			return true;
-		if (jj_scan_token(RIGHT_PAR))
-			return true;
-		return false;
-	}
-
-	private boolean jj_3R_67(){
-		if (jj_3R_116())
-			return true;
-		return false;
-	}
-
-	private boolean jj_3R_108(){
-		if (jj_scan_token(RIGHT))
-			return true;
-		return false;
-	}
-
-	private boolean jj_3R_66(){
-		if (jj_3R_115())
+	private boolean jj_3R_124(){
+		if (jj_3R_22())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_50(){
+	private boolean jj_3R_57(){
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_71()){
+		if (jj_3R_78()){
 			jj_scanpos = xsp;
-			if (jj_3R_72()){
+			if (jj_3R_79()){
 				jj_scanpos = xsp;
-				if (jj_3R_73()){
+				if (jj_3R_80()){
 					jj_scanpos = xsp;
-					if (jj_3R_74()){
+					if (jj_3R_81()){
 						jj_scanpos = xsp;
-						if (jj_3R_75()){
+						if (jj_3R_82()){
 							jj_scanpos = xsp;
-							if (jj_3R_76()){
+							if (jj_3R_83()){
 								jj_scanpos = xsp;
-								if (jj_3R_77()){
+								if (jj_3R_84()){
 									jj_scanpos = xsp;
-									if (jj_3R_78()){
+									if (jj_3R_85()){
 										jj_scanpos = xsp;
-										if (jj_3R_79()){
+										if (jj_3R_86()){
 											jj_scanpos = xsp;
-											if (jj_3R_80()){
+											if (jj_3R_87()){
 												jj_scanpos = xsp;
-												if (jj_3R_81()){
+												if (jj_3R_88()){
 													jj_scanpos = xsp;
-													if (jj_3R_82()){
+													if (jj_3R_89()){
 														jj_scanpos = xsp;
-														if (jj_3R_83()){
+														if (jj_3R_90()){
 															jj_scanpos = xsp;
-															if (jj_3R_84()){
+															if (jj_3R_91()){
 																jj_scanpos = xsp;
-																if (jj_3R_85())
+																if (jj_3R_92())
 																	return true;
 															}
 														}
@@ -4349,91 +4313,113 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3R_179(){
-		if (jj_scan_token(COMMA))
+	private boolean jj_3R_121(){
+		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_14())
+		if (jj_3R_108())
 			return true;
-		return false;
-	}
-
-	private boolean jj_3R_36(){
-		if (jj_3R_20())
+		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_35(){
-		if (jj_3R_52())
+	private boolean jj_3R_120(){
+		if (jj_3R_143())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_34(){
-		if (jj_3R_51())
+	private boolean jj_3R_41(){
+		if (jj_3R_24())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_49(){
+	private boolean jj_3R_76(){
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_66()){
+		if (jj_3R_124()){
 			jj_scanpos = xsp;
-			if (jj_3R_67()){
+			if (jj_3R_125()){
 				jj_scanpos = xsp;
-				if (jj_3R_68()){
-					jj_scanpos = xsp;
-					if (jj_3R_69()){
-						jj_scanpos = xsp;
-						if (jj_3R_70())
-							return true;
-					}
-				}
+				if (jj_3R_126())
+					return true;
 			}
 		}
 		return false;
 	}
 
-	private boolean jj_3R_104(){
-		if (jj_scan_token(LEFT))
+	private boolean jj_3R_40(){
+		if (jj_3R_59())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_33(){
-		if (jj_3R_50())
+	private boolean jj_3R_114(){
+		if (jj_scan_token(RIGHT))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_61(){
+	private boolean jj_3R_119(){
+		if (jj_3R_21())
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_39(){
+		if (jj_3R_58())
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_118(){
+		if (jj_3R_142())
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_38(){
+		if (jj_3R_57())
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_23(){
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_104()){
+		if (jj_3R_38()){
 			jj_scanpos = xsp;
-			if (jj_3R_105()){
+			if (jj_3R_39()){
 				jj_scanpos = xsp;
-				if (jj_3R_106())
-					return true;
+				if (jj_3R_40()){
+					jj_scanpos = xsp;
+					if (jj_3R_41())
+						return true;
+				}
 			}
 		}
-		xsp = jj_scanpos;
-		if (jj_scan_token(25))
-			jj_scanpos = xsp;
 		return false;
 	}
 
-	private boolean jj_3R_19(){
+	private boolean jj_3R_201(){
+		if (jj_scan_token(COMMA))
+			return true;
+		if (jj_3R_14())
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_74(){
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_33()){
+		if (jj_3R_118()){
 			jj_scanpos = xsp;
-			if (jj_3R_34()){
+			if (jj_3R_119()){
 				jj_scanpos = xsp;
-				if (jj_3R_35()){
+				if (jj_3R_120()){
 					jj_scanpos = xsp;
-					if (jj_3R_36())
+					if (jj_3R_121())
 						return true;
 				}
 			}
@@ -4441,7 +4427,62 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3R_192(){
+	private boolean jj_3R_110(){
+		if (jj_scan_token(LEFT))
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_70(){
+		Token xsp;
+		xsp = jj_scanpos;
+		if (jj_3R_110()){
+			jj_scanpos = xsp;
+			if (jj_3R_111()){
+				jj_scanpos = xsp;
+				if (jj_3R_112())
+					return true;
+			}
+		}
+		xsp = jj_scanpos;
+		if (jj_scan_token(25))
+			jj_scanpos = xsp;
+		return false;
+	}
+
+	private boolean jj_3R_75(){
+		if (jj_scan_token(COORDSYS))
+			return true;
+		if (jj_scan_token(LEFT_PAR))
+			return true;
+		if (jj_3R_122())
+			return true;
+		if (jj_scan_token(RIGHT_PAR))
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_178(){
+		if (jj_3R_21())
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_171(){
+		if (jj_scan_token(COMMA))
+			return true;
+		if (jj_3R_170())
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_176(){
+		if (jj_3R_21())
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_197(){
 		Token xsp;
 		xsp = jj_scanpos;
 		if (jj_scan_token(8)){
@@ -4455,45 +4496,53 @@ public class ADQLParser implements ADQLParserConstants {
 	private boolean jj_3R_191(){
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_192())
+		if (jj_3R_197())
 			jj_scanpos = xsp;
 		if (jj_scan_token(UNSIGNED_INTEGER))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_45(){
+	private boolean jj_3R_49(){
 		Token xsp;
 		xsp = jj_scanpos;
 		if (jj_scan_token(24)){
 			jj_scanpos = xsp;
-			if (jj_3R_61())
+			if (jj_3R_70())
 				return true;
 		}
 		return false;
 	}
 
-	private boolean jj_3R_48(){
-		if (jj_scan_token(COORDSYS))
+	private boolean jj_3R_141(){
+		if (jj_scan_token(REGION))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_64())
+		if (jj_3R_27())
 			return true;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_159(){
+	private boolean jj_3R_158(){
+		if (jj_scan_token(POINT))
+			return true;
+		if (jj_scan_token(LEFT_PAR))
+			return true;
+		if (jj_3R_169())
+			return true;
 		if (jj_scan_token(COMMA))
 			return true;
-		if (jj_3R_158())
+		if (jj_3R_170())
+			return true;
+		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_115(){
+	private boolean jj_3R_142(){
 		Token xsp;
 		xsp = jj_scanpos;
 		if (jj_scan_token(99)){
@@ -4507,13 +4556,7 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3R_186(){
-		if (jj_3R_23())
-			return true;
-		return false;
-	}
-
-	private boolean jj_3R_178(){
+	private boolean jj_3R_200(){
 		if (jj_scan_token(USING))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
@@ -4523,7 +4566,7 @@ public class ADQLParser implements ADQLParserConstants {
 		Token xsp;
 		while(true){
 			xsp = jj_scanpos;
-			if (jj_3R_179()){
+			if (jj_3R_201()){
 				jj_scanpos = xsp;
 				break;
 			}
@@ -4533,26 +4576,52 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3R_107(){
+	private boolean jj_3R_113(){
 		if (jj_scan_token(LEFT))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_184(){
-		if (jj_3R_23())
+	private boolean jj_3R_140(){
+		if (jj_scan_token(POLYGON))
+			return true;
+		if (jj_scan_token(LEFT_PAR))
+			return true;
+		if (jj_3R_169())
+			return true;
+		if (jj_scan_token(COMMA))
+			return true;
+		if (jj_3R_170())
+			return true;
+		if (jj_scan_token(COMMA))
+			return true;
+		if (jj_3R_170())
+			return true;
+		if (jj_scan_token(COMMA))
+			return true;
+		if (jj_3R_170())
+			return true;
+		Token xsp;
+		while(true){
+			xsp = jj_scanpos;
+			if (jj_3R_171()){
+				jj_scanpos = xsp;
+				break;
+			}
+		}
+		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_62(){
+	private boolean jj_3R_71(){
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_107()){
+		if (jj_3R_113()){
 			jj_scanpos = xsp;
-			if (jj_3R_108()){
+			if (jj_3R_114()){
 				jj_scanpos = xsp;
-				if (jj_3R_109())
+				if (jj_3R_115())
 					return true;
 			}
 		}
@@ -4562,55 +4631,33 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3R_177(){
+	private boolean jj_3R_199(){
 		if (jj_scan_token(ON))
 			return true;
-		if (jj_3R_152())
-			return true;
-		return false;
-	}
-
-	private boolean jj_3R_129(){
-		if (jj_scan_token(REGION))
-			return true;
-		if (jj_scan_token(LEFT_PAR))
-			return true;
-		if (jj_3R_24())
-			return true;
-		if (jj_scan_token(RIGHT_PAR))
+		if (jj_3R_163())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_147(){
-		if (jj_scan_token(POINT))
-			return true;
-		if (jj_scan_token(LEFT_PAR))
-			return true;
-		if (jj_3R_157())
-			return true;
-		if (jj_scan_token(COMMA))
-			return true;
+	private boolean jj_3R_139(){
 		if (jj_3R_158())
 			return true;
-		if (jj_scan_token(RIGHT_PAR))
-			return true;
 		return false;
 	}
 
-	private boolean jj_3R_133(){
+	private boolean jj_3R_37(){
 		if (jj_scan_token(STRING_LITERAL))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_116(){
+	private boolean jj_3R_22(){
 		Token xsp;
-		if (jj_3R_133())
+		if (jj_3R_37())
 			return true;
 		while(true){
 			xsp = jj_scanpos;
-			if (jj_3R_133()){
+			if (jj_3R_37()){
 				jj_scanpos = xsp;
 				break;
 			}
@@ -4618,130 +4665,122 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3R_46(){
+	private boolean jj_3R_138(){
+		if (jj_scan_token(CIRCLE))
+			return true;
+		if (jj_scan_token(LEFT_PAR))
+			return true;
+		if (jj_3R_169())
+			return true;
+		if (jj_scan_token(COMMA))
+			return true;
+		if (jj_3R_170())
+			return true;
+		if (jj_scan_token(COMMA))
+			return true;
+		if (jj_3R_108())
+			return true;
+		if (jj_scan_token(RIGHT_PAR))
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_50(){
 		Token xsp;
 		xsp = jj_scanpos;
 		if (jj_scan_token(24)){
 			jj_scanpos = xsp;
-			if (jj_3R_62())
+			if (jj_3R_71())
 				return true;
 		}
 		return false;
 	}
 
-	private boolean jj_3R_30(){
+	private boolean jj_3R_33(){
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_46())
+		if (jj_3R_50())
 			jj_scanpos = xsp;
 		if (jj_scan_token(JOIN))
 			return true;
-		if (jj_3R_47())
+		if (jj_3R_51())
 			return true;
 		xsp = jj_scanpos;
-		if (jj_3R_177()){
+		if (jj_3R_199()){
 			jj_scanpos = xsp;
-			if (jj_3R_178())
+			if (jj_3R_200())
 				return true;
 		}
 		return false;
 	}
 
-	private boolean jj_3R_128(){
-		if (jj_scan_token(POLYGON))
+	private boolean jj_3R_137(){
+		if (jj_scan_token(CENTROID))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_157())
-			return true;
-		if (jj_scan_token(COMMA))
-			return true;
-		if (jj_3R_158())
-			return true;
-		if (jj_scan_token(COMMA))
-			return true;
-		if (jj_3R_158())
-			return true;
-		if (jj_scan_token(COMMA))
-			return true;
-		if (jj_3R_158())
+		if (jj_3R_122())
 			return true;
-		Token xsp;
-		while(true){
-			xsp = jj_scanpos;
-			if (jj_3R_159()){
-				jj_scanpos = xsp;
-				break;
-			}
-		}
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_29(){
+	private boolean jj_3R_32(){
 		if (jj_scan_token(NATURAL))
 			return true;
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_45())
+		if (jj_3R_49())
 			jj_scanpos = xsp;
 		if (jj_scan_token(JOIN))
 			return true;
-		if (jj_3R_47())
+		if (jj_3R_51())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_127(){
-		if (jj_3R_147())
+	private boolean jj_3R_136(){
+		if (jj_scan_token(BOX))
 			return true;
-		return false;
-	}
-
-	private boolean jj_3R_171(){
-		Token xsp;
-		xsp = jj_scanpos;
-		if (jj_scan_token(22))
-			jj_scanpos = xsp;
-		if (jj_3R_14())
+		if (jj_scan_token(LEFT_PAR))
 			return true;
-		return false;
-	}
-
-	private boolean jj_3R_126(){
-		if (jj_scan_token(CIRCLE))
+		if (jj_3R_169())
 			return true;
-		if (jj_scan_token(LEFT_PAR))
+		if (jj_scan_token(COMMA))
 			return true;
-		if (jj_3R_157())
+		if (jj_3R_170())
 			return true;
 		if (jj_scan_token(COMMA))
 			return true;
-		if (jj_3R_158())
+		if (jj_3R_108())
 			return true;
 		if (jj_scan_token(COMMA))
 			return true;
-		if (jj_3R_102())
+		if (jj_3R_108())
 			return true;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_125(){
-		if (jj_scan_token(CENTROID))
-			return true;
-		if (jj_scan_token(LEFT_PAR))
-			return true;
-		if (jj_3R_64())
+	private boolean jj_3R_192(){
+		Token xsp;
+		xsp = jj_scanpos;
+		if (jj_scan_token(22))
+			jj_scanpos = xsp;
+		if (jj_3R_14())
 			return true;
-		if (jj_scan_token(RIGHT_PAR))
+		return false;
+	}
+
+	private boolean jj_3R_182(){
+		if (jj_3R_21())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_169(){
+	private boolean jj_3R_190(){
 		Token xsp;
 		xsp = jj_scanpos;
 		if (jj_scan_token(45)){
@@ -4752,26 +4791,55 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3R_124(){
-		if (jj_scan_token(BOX))
-			return true;
-		if (jj_scan_token(LEFT_PAR))
-			return true;
-		if (jj_3R_157())
+	private boolean jj_3R_183(){
+		if (jj_3R_46())
 			return true;
-		if (jj_scan_token(COMMA))
+		return false;
+	}
+
+	private boolean jj_3R_180(){
+		if (jj_3R_21())
 			return true;
+		return false;
+	}
+
+	private boolean jj_3R_109(){
+		Token xsp;
+		xsp = jj_scanpos;
+		if (jj_3R_136()){
+			jj_scanpos = xsp;
+			if (jj_3R_137()){
+				jj_scanpos = xsp;
+				if (jj_3R_138()){
+					jj_scanpos = xsp;
+					if (jj_3R_139()){
+						jj_scanpos = xsp;
+						if (jj_3R_140()){
+							jj_scanpos = xsp;
+							if (jj_3R_141())
+								return true;
+						}
+					}
+				}
+			}
+		}
+		return false;
+	}
+
+	private boolean jj_3R_181(){
 		if (jj_3R_158())
 			return true;
-		if (jj_scan_token(COMMA))
-			return true;
-		if (jj_3R_102())
-			return true;
-		if (jj_scan_token(COMMA))
-			return true;
-		if (jj_3R_102())
+		return false;
+	}
+
+	private boolean jj_3R_177(){
+		if (jj_3R_158())
 			return true;
-		if (jj_scan_token(RIGHT_PAR))
+		return false;
+	}
+
+	private boolean jj_3R_175(){
+		if (jj_3R_158())
 			return true;
 		return false;
 	}
@@ -4779,39 +4847,45 @@ public class ADQLParser implements ADQLParserConstants {
 	private boolean jj_3R_17(){
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_29()){
+		if (jj_3R_32()){
 			jj_scanpos = xsp;
-			if (jj_3R_30())
+			if (jj_3R_33())
 				return true;
 		}
 		return false;
 	}
 
-	private boolean jj_3R_176(){
+	private boolean jj_3R_169(){
+		if (jj_3R_27())
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_198(){
 		if (jj_3R_17())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_111(){
+	private boolean jj_3R_117(){
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_172())
+		if (jj_3R_193())
 			return true;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_172(){
-		if (jj_3R_63())
+	private boolean jj_3R_193(){
+		if (jj_3R_72())
 			return true;
 		Token xsp;
-		if (jj_3R_176())
+		if (jj_3R_198())
 			return true;
 		while(true){
 			xsp = jj_scanpos;
-			if (jj_3R_176()){
+			if (jj_3R_198()){
 				jj_scanpos = xsp;
 				break;
 			}
@@ -4819,59 +4893,30 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3R_190(){
-		if (jj_3R_23())
+	private boolean jj_3R_179(){
+		if (jj_3R_158())
 			return true;
 		return false;
 	}
 
 	private boolean jj_3_3(){
-		if (jj_3R_17())
-			return true;
-		return false;
-	}
-
-	private boolean jj_3R_101(){
-		Token xsp;
-		xsp = jj_scanpos;
-		if (jj_3R_124()){
-			jj_scanpos = xsp;
-			if (jj_3R_125()){
-				jj_scanpos = xsp;
-				if (jj_3R_126()){
-					jj_scanpos = xsp;
-					if (jj_3R_127()){
-						jj_scanpos = xsp;
-						if (jj_3R_128()){
-							jj_scanpos = xsp;
-							if (jj_3R_129())
-								return true;
-						}
-					}
-				}
-			}
-		}
-		return false;
-	}
-
-	private boolean jj_3R_149(){
-		if (jj_3R_41())
+		if (jj_3R_17())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_110(){
-		if (jj_3R_103())
+	private boolean jj_3R_116(){
+		if (jj_3R_77())
 			return true;
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_171())
+		if (jj_3R_192())
 			jj_scanpos = xsp;
 		return false;
 	}
 
-	private boolean jj_3R_47(){
-		if (jj_3R_63())
+	private boolean jj_3R_51(){
+		if (jj_3R_72())
 			return true;
 		Token xsp;
 		while(true){
@@ -4884,18 +4929,6 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3R_188(){
-		if (jj_3R_23())
-			return true;
-		return false;
-	}
-
-	private boolean jj_3R_185(){
-		if (jj_3R_147())
-			return true;
-		return false;
-	}
-
 	private boolean jj_3_2(){
 		if (jj_3R_16())
 			return true;
@@ -4908,62 +4941,24 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3R_183(){
-		if (jj_3R_147())
-			return true;
-		return false;
-	}
-
-	private boolean jj_3R_189(){
-		if (jj_3R_147())
-			return true;
-		return false;
-	}
-
-	private boolean jj_3R_157(){
-		if (jj_3R_24())
-			return true;
-		return false;
-	}
-
-	private boolean jj_3R_187(){
-		if (jj_3R_147())
-			return true;
-		return false;
-	}
-
-	private boolean jj_3R_63(){
-		Token xsp;
-		xsp = jj_scanpos;
-		if (jj_3R_110()){
-			jj_scanpos = xsp;
-			if (jj_3_2()){
-				jj_scanpos = xsp;
-				if (jj_3R_111())
-					return true;
-			}
-		}
-		return false;
-	}
-
-	private boolean jj_3R_98(){
+	private boolean jj_3R_105(){
 		if (jj_scan_token(DISTANCE))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_187()){
+		if (jj_3R_179()){
 			jj_scanpos = xsp;
-			if (jj_3R_188())
+			if (jj_3R_180())
 				return true;
 		}
 		if (jj_scan_token(COMMA))
 			return true;
 		xsp = jj_scanpos;
-		if (jj_3R_189()){
+		if (jj_3R_181()){
 			jj_scanpos = xsp;
-			if (jj_3R_190())
+			if (jj_3R_182())
 				return true;
 		}
 		if (jj_scan_token(RIGHT_PAR))
@@ -4971,16 +4966,16 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3R_97(){
+	private boolean jj_3R_104(){
 		if (jj_scan_token(COORD2))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_185()){
+		if (jj_3R_177()){
 			jj_scanpos = xsp;
-			if (jj_3R_186())
+			if (jj_3R_178())
 				return true;
 		}
 		if (jj_scan_token(RIGHT_PAR))
@@ -4988,16 +4983,16 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3R_96(){
+	private boolean jj_3R_103(){
 		if (jj_scan_token(COORD1))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_183()){
+		if (jj_3R_175()){
 			jj_scanpos = xsp;
-			if (jj_3R_184())
+			if (jj_3R_176())
 				return true;
 		}
 		if (jj_scan_token(RIGHT_PAR))
@@ -5005,39 +5000,33 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3R_95(){
+	private boolean jj_3R_102(){
 		if (jj_scan_token(AREA))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_64())
+		if (jj_3R_122())
 			return true;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_168(){
-		if (jj_3R_42())
-			return true;
-		return false;
-	}
-
-	private boolean jj_3R_155(){
+	private boolean jj_3R_72(){
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_168()){
+		if (jj_3R_116()){
 			jj_scanpos = xsp;
-			if (jj_scan_token(101))
-				return true;
+			if (jj_3_2()){
+				jj_scanpos = xsp;
+				if (jj_3R_117())
+					return true;
+			}
 		}
-		xsp = jj_scanpos;
-		if (jj_3R_169())
-			jj_scanpos = xsp;
 		return false;
 	}
 
-	private boolean jj_3R_94(){
+	private boolean jj_3R_101(){
 		Token xsp;
 		xsp = jj_scanpos;
 		if (jj_scan_token(58)){
@@ -5047,62 +5036,57 @@ public class ADQLParser implements ADQLParserConstants {
 		}
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_64())
+		if (jj_3R_122())
 			return true;
 		if (jj_scan_token(COMMA))
 			return true;
-		if (jj_3R_64())
+		if (jj_3R_122())
 			return true;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_146(){
+	private boolean jj_3R_157(){
 		if (jj_scan_token(COMMA))
 			return true;
-		if (jj_3R_41())
+		if (jj_3R_46())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_167(){
-		if (jj_3R_42())
+	private boolean jj_3R_189(){
+		if (jj_3R_36())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_153(){
+	private boolean jj_3R_166(){
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_167()){
+		if (jj_3R_189()){
 			jj_scanpos = xsp;
 			if (jj_scan_token(101))
 				return true;
 		}
+		xsp = jj_scanpos;
+		if (jj_3R_190())
+			jj_scanpos = xsp;
 		return false;
 	}
 
-	private boolean jj_3R_60(){
-		if (jj_scan_token(DOT))
-			return true;
-		if (jj_3R_103())
-			return true;
-		return false;
-	}
-
-	private boolean jj_3R_52(){
+	private boolean jj_3R_59(){
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_94()){
+		if (jj_3R_101()){
 			jj_scanpos = xsp;
-			if (jj_3R_95()){
+			if (jj_3R_102()){
 				jj_scanpos = xsp;
-				if (jj_3R_96()){
+				if (jj_3R_103()){
 					jj_scanpos = xsp;
-					if (jj_3R_97()){
+					if (jj_3R_104()){
 						jj_scanpos = xsp;
-						if (jj_3R_98())
+						if (jj_3R_105())
 							return true;
 					}
 				}
@@ -5111,7 +5095,7 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3R_135(){
+	private boolean jj_3R_160(){
 		Token xsp;
 		xsp = jj_scanpos;
 		if (jj_scan_token(47)){
@@ -5130,30 +5114,24 @@ public class ADQLParser implements ADQLParserConstants {
 		xsp = jj_scanpos;
 		if (jj_scan_token(19))
 			jj_scanpos = xsp;
-		if (jj_3R_41())
+		if (jj_3R_46())
 			return true;
 		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_158(){
-		if (jj_3R_102())
+	private boolean jj_3R_170(){
+		if (jj_3R_108())
 			return true;
 		if (jj_scan_token(COMMA))
 			return true;
-		if (jj_3R_102())
-			return true;
-		return false;
-	}
-
-	private boolean jj_3R_23(){
-		if (jj_3R_42())
+		if (jj_3R_108())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_134(){
+	private boolean jj_3R_159(){
 		if (jj_scan_token(COUNT))
 			return true;
 		if (jj_scan_token(LEFT_PAR))
@@ -5165,7 +5143,7 @@ public class ADQLParser implements ADQLParserConstants {
 		xsp = jj_scanpos;
 		if (jj_scan_token(10)){
 			jj_scanpos = xsp;
-			if (jj_3R_149())
+			if (jj_3R_183())
 				return true;
 		}
 		if (jj_scan_token(RIGHT_PAR))
@@ -5173,77 +5151,83 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3R_42(){
-		if (jj_3R_14())
+	private boolean jj_3R_188(){
+		if (jj_3R_36())
 			return true;
-		Token xsp;
-		xsp = jj_scanpos;
-		if (jj_3R_60())
-			jj_scanpos = xsp;
 		return false;
 	}
 
-	private boolean jj_3R_132(){
-		if (jj_scan_token(DOT))
-			return true;
-		if (jj_3R_14())
-			return true;
+	private boolean jj_3R_164(){
+		Token xsp;
+		xsp = jj_scanpos;
+		if (jj_3R_188()){
+			jj_scanpos = xsp;
+			if (jj_scan_token(101))
+				return true;
+		}
 		return false;
 	}
 
-	private boolean jj_3R_131(){
+	private boolean jj_3R_56(){
 		if (jj_scan_token(DOT))
 			return true;
-		if (jj_3R_14())
+		if (jj_3R_77())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_117(){
+	private boolean jj_3R_143(){
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_134()){
+		if (jj_3R_159()){
 			jj_scanpos = xsp;
-			if (jj_3R_135())
+			if (jj_3R_160())
 				return true;
 		}
 		return false;
 	}
 
-	private boolean jj_3R_103(){
+	private boolean jj_3R_21(){
+		if (jj_3R_36())
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_36(){
 		if (jj_3R_14())
 			return true;
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_131())
-			jj_scanpos = xsp;
-		xsp = jj_scanpos;
-		if (jj_3R_132())
+		if (jj_3R_56())
 			jj_scanpos = xsp;
 		return false;
 	}
 
-	private boolean jj_3R_26(){
-		if (jj_scan_token(DELIMITED_IDENTIFIER))
+	private boolean jj_3R_128(){
+		if (jj_scan_token(DOT))
+			return true;
+		if (jj_3R_14())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_25(){
-		if (jj_scan_token(REGULAR_IDENTIFIER))
+	private boolean jj_3R_127(){
+		if (jj_scan_token(DOT))
+			return true;
+		if (jj_3R_14())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_123(){
+	private boolean jj_3R_134(){
 		if (jj_scan_token(LEFT_PAR))
 			return true;
-		if (jj_3R_41())
+		if (jj_3R_46())
 			return true;
 		Token xsp;
 		while(true){
 			xsp = jj_scanpos;
-			if (jj_3R_146()){
+			if (jj_3R_157()){
 				jj_scanpos = xsp;
 				break;
 			}
@@ -5253,24 +5237,13 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3R_14(){
-		Token xsp;
-		xsp = jj_scanpos;
-		if (jj_3R_25()){
-			jj_scanpos = xsp;
-			if (jj_3R_26())
-				return true;
-		}
-		return false;
-	}
-
-	private boolean jj_3_13(){
+	private boolean jj_3_16(){
 		if (jj_3R_16())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_100(){
+	private boolean jj_3R_107(){
 		Token xsp;
 		xsp = jj_scanpos;
 		if (jj_scan_token(35))
@@ -5278,124 +5251,80 @@ public class ADQLParser implements ADQLParserConstants {
 		if (jj_scan_token(IN))
 			return true;
 		xsp = jj_scanpos;
-		if (jj_3_13()){
+		if (jj_3_16()){
 			jj_scanpos = xsp;
-			if (jj_3R_123())
+			if (jj_3R_134())
 				return true;
 		}
 		return false;
 	}
 
-	private boolean jj_3R_156(){
-		if (jj_scan_token(COMMA))
-			return true;
-		if (jj_3R_155())
-			return true;
-		return false;
-	}
-
-	private boolean jj_3R_144(){
-		if (jj_scan_token(ORDER_BY))
-			return true;
-		if (jj_3R_155())
+	private boolean jj_3R_77(){
+		if (jj_3R_14())
 			return true;
 		Token xsp;
-		while(true){
-			xsp = jj_scanpos;
-			if (jj_3R_156()){
-				jj_scanpos = xsp;
-				break;
-			}
-		}
+		xsp = jj_scanpos;
+		if (jj_3R_127())
+			jj_scanpos = xsp;
+		xsp = jj_scanpos;
+		if (jj_3R_128())
+			jj_scanpos = xsp;
 		return false;
 	}
 
-	private boolean jj_3R_143(){
-		if (jj_scan_token(HAVING))
-			return true;
-		if (jj_3R_152())
+	private boolean jj_3R_29(){
+		if (jj_scan_token(DELIMITED_IDENTIFIER))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_154(){
-		if (jj_scan_token(COMMA))
-			return true;
-		if (jj_3R_153())
+	private boolean jj_3R_28(){
+		if (jj_scan_token(REGULAR_IDENTIFIER))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_22(){
+	private boolean jj_3R_26(){
 		Token xsp;
 		xsp = jj_scanpos;
 		if (jj_scan_token(35))
 			jj_scanpos = xsp;
 		if (jj_scan_token(BETWEEN))
 			return true;
-		if (jj_3R_41())
+		if (jj_3R_46())
 			return true;
 		if (jj_scan_token(AND))
 			return true;
-		if (jj_3R_41())
+		if (jj_3R_46())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_163(){
-		if (jj_scan_token(AS))
-			return true;
-		if (jj_3R_14())
+	private boolean jj_3R_61(){
+		if (jj_3R_107())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_142(){
-		if (jj_scan_token(GROUP_BY))
-			return true;
-		if (jj_3R_153())
-			return true;
+	private boolean jj_3R_14(){
 		Token xsp;
-		while(true){
-			xsp = jj_scanpos;
-			if (jj_3R_154()){
-				jj_scanpos = xsp;
-				break;
-			}
-		}
-		return false;
-	}
-
-	private boolean jj_3R_54(){
-		if (jj_3R_100())
-			return true;
-		return false;
-	}
-
-	private boolean jj_3_10(){
-		if (jj_3R_22())
-			return true;
-		return false;
-	}
-
-	private boolean jj_3R_140(){
-		if (jj_scan_token(COMMA))
-			return true;
-		if (jj_3R_47())
-			return true;
+		xsp = jj_scanpos;
+		if (jj_3R_28()){
+			jj_scanpos = xsp;
+			if (jj_3R_29())
+				return true;
+		}
 		return false;
 	}
 
-	private boolean jj_3R_141(){
-		if (jj_scan_token(WHERE))
-			return true;
-		if (jj_3R_152())
+	private boolean jj_3_13(){
+		if (jj_3R_26())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3_12(){
-		if (jj_3R_24())
+	private boolean jj_3_15(){
+		if (jj_3R_27())
 			return true;
 		Token xsp;
 		xsp = jj_scanpos;
@@ -5406,21 +5335,29 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3R_53(){
-		if (jj_3R_99())
+	private boolean jj_3R_60(){
+		if (jj_3R_106())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3_11(){
-		if (jj_3R_23())
+	private boolean jj_3R_167(){
+		if (jj_scan_token(COMMA))
+			return true;
+		if (jj_3R_166())
+			return true;
+		return false;
+	}
+
+	private boolean jj_3_14(){
+		if (jj_3R_21())
 			return true;
 		if (jj_scan_token(IS))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_99(){
+	private boolean jj_3R_106(){
 		Token xsp;
 		xsp = jj_scanpos;
 		if (jj_scan_token(12)){
@@ -5440,28 +5377,20 @@ public class ADQLParser implements ADQLParserConstants {
 				}
 			}
 		}
-		if (jj_3R_41())
-			return true;
-		return false;
-	}
-
-	private boolean jj_3R_27(){
-		if (jj_3R_14())
-			return true;
-		if (jj_scan_token(DOT))
+		if (jj_3R_46())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_118(){
-		if (jj_scan_token(FROM))
+	private boolean jj_3R_155(){
+		if (jj_scan_token(ORDER_BY))
 			return true;
-		if (jj_3R_47())
+		if (jj_3R_166())
 			return true;
 		Token xsp;
 		while(true){
 			xsp = jj_scanpos;
-			if (jj_3R_140()){
+			if (jj_3R_167()){
 				jj_scanpos = xsp;
 				break;
 			}
@@ -5469,24 +5398,32 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3R_40(){
-		if (jj_3R_41())
+	private boolean jj_3R_154(){
+		if (jj_scan_token(HAVING))
+			return true;
+		if (jj_3R_163())
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_45(){
+		if (jj_3R_46())
 			return true;
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_53()){
+		if (jj_3R_60()){
 			jj_scanpos = xsp;
-			if (jj_3_10()){
+			if (jj_3_13()){
 				jj_scanpos = xsp;
-				if (jj_3R_54())
+				if (jj_3R_61())
 					return true;
 			}
 		}
 		return false;
 	}
 
-	private boolean jj_3R_39(){
-		if (jj_3R_24())
+	private boolean jj_3R_44(){
+		if (jj_3R_27())
 			return true;
 		Token xsp;
 		xsp = jj_scanpos;
@@ -5494,13 +5431,21 @@ public class ADQLParser implements ADQLParserConstants {
 			jj_scanpos = xsp;
 		if (jj_scan_token(LIKE))
 			return true;
-		if (jj_3R_24())
+		if (jj_3R_27())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_38(){
-		if (jj_3R_23())
+	private boolean jj_3R_165(){
+		if (jj_scan_token(COMMA))
+			return true;
+		if (jj_3R_164())
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_43(){
+		if (jj_3R_21())
 			return true;
 		if (jj_scan_token(IS))
 			return true;
@@ -5513,7 +5458,31 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3R_37(){
+	private boolean jj_3R_184(){
+		if (jj_scan_token(AS))
+			return true;
+		if (jj_3R_14())
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_153(){
+		if (jj_scan_token(GROUP_BY))
+			return true;
+		if (jj_3R_164())
+			return true;
+		Token xsp;
+		while(true){
+			xsp = jj_scanpos;
+			if (jj_3R_165()){
+				jj_scanpos = xsp;
+				break;
+			}
+		}
+		return false;
+	}
+
+	private boolean jj_3R_42(){
 		if (jj_scan_token(EXISTS))
 			return true;
 		if (jj_3R_16())
@@ -5522,37 +5491,39 @@ public class ADQLParser implements ADQLParserConstants {
 	}
 
 	private boolean jj_3R_151(){
-		if (jj_3R_41())
+		if (jj_scan_token(COMMA))
+			return true;
+		if (jj_3R_51())
+			return true;
+		return false;
+	}
+
+	private boolean jj_3R_152(){
+		if (jj_scan_token(WHERE))
 			return true;
-		Token xsp;
-		xsp = jj_scanpos;
 		if (jj_3R_163())
-			jj_scanpos = xsp;
+			return true;
 		return false;
 	}
 
-	private boolean jj_3R_15(){
+	private boolean jj_3R_30(){
 		if (jj_3R_14())
 			return true;
 		if (jj_scan_token(DOT))
 			return true;
-		Token xsp;
-		xsp = jj_scanpos;
-		if (jj_3R_27())
-			jj_scanpos = xsp;
 		return false;
 	}
 
-	private boolean jj_3R_21(){
+	private boolean jj_3R_25(){
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_37()){
+		if (jj_3R_42()){
 			jj_scanpos = xsp;
-			if (jj_3R_38()){
+			if (jj_3R_43()){
 				jj_scanpos = xsp;
-				if (jj_3R_39()){
+				if (jj_3R_44()){
 					jj_scanpos = xsp;
-					if (jj_3R_40())
+					if (jj_3R_45())
 						return true;
 				}
 			}
@@ -5560,150 +5531,116 @@ public class ADQLParser implements ADQLParserConstants {
 		return false;
 	}
 
-	private boolean jj_3_9(){
-		if (jj_3R_21())
+	private boolean jj_3R_129(){
+		if (jj_scan_token(FROM))
 			return true;
-		return false;
-	}
-
-	private boolean jj_3R_150(){
-		if (jj_scan_token(ASTERISK))
+		if (jj_3R_51())
 			return true;
+		Token xsp;
+		while(true){
+			xsp = jj_scanpos;
+			if (jj_3R_151()){
+				jj_scanpos = xsp;
+				break;
+			}
+		}
 		return false;
 	}
 
-	private boolean jj_3R_174(){
-		if (jj_scan_token(LEFT_PAR))
-			return true;
-		if (jj_3R_152())
-			return true;
-		if (jj_scan_token(RIGHT_PAR))
+	private boolean jj_3_12(){
+		if (jj_3R_25())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_173(){
-		if (jj_3R_21())
+	private boolean jj_3R_162(){
+		if (jj_3R_46())
 			return true;
-		return false;
-	}
-
-	private boolean jj_3R_165(){
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_173()){
+		if (jj_3R_184())
 			jj_scanpos = xsp;
-			if (jj_3R_174())
-				return true;
-		}
-		return false;
-	}
-
-	private boolean jj_3R_175(){
-		if (jj_scan_token(NOT))
-			return true;
 		return false;
 	}
 
-	private boolean jj_3_1(){
+	private boolean jj_3R_15(){
 		if (jj_3R_14())
 			return true;
 		if (jj_scan_token(DOT))
 			return true;
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_15())
+		if (jj_3R_30())
 			jj_scanpos = xsp;
-		if (jj_scan_token(ASTERISK))
+		return false;
+	}
+
+	private boolean jj_3R_195(){
+		if (jj_scan_token(LEFT_PAR))
+			return true;
+		if (jj_3R_163())
+			return true;
+		if (jj_scan_token(RIGHT_PAR))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_166(){
-		Token xsp;
-		xsp = jj_scanpos;
-		if (jj_scan_token(33)){
-			jj_scanpos = xsp;
-			if (jj_scan_token(34))
-				return true;
-		}
-		xsp = jj_scanpos;
-		if (jj_3R_175())
-			jj_scanpos = xsp;
-		if (jj_3R_165())
+	private boolean jj_3R_194(){
+		if (jj_3R_25())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_138(){
+	private boolean jj_3R_186(){
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_150()){
+		if (jj_3R_194()){
 			jj_scanpos = xsp;
-			if (jj_3_1()){
-				jj_scanpos = xsp;
-				if (jj_3R_151())
-					return true;
-			}
+			if (jj_3R_195())
+				return true;
 		}
 		return false;
 	}
 
-	private boolean jj_3R_139(){
-		if (jj_scan_token(COMMA))
-			return true;
-		if (jj_3R_138())
-			return true;
-		return false;
-	}
-
-	private boolean jj_3R_114(){
-		if (jj_scan_token(COMMA))
-			return true;
-		if (jj_3R_41())
+	private boolean jj_3R_196(){
+		if (jj_scan_token(NOT))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_137(){
-		if (jj_scan_token(TOP))
-			return true;
-		if (jj_scan_token(UNSIGNED_INTEGER))
+	private boolean jj_3R_187(){
+		Token xsp;
+		xsp = jj_scanpos;
+		if (jj_scan_token(33)){
+			jj_scanpos = xsp;
+			if (jj_scan_token(34))
+				return true;
+		}
+		xsp = jj_scanpos;
+		if (jj_3R_196())
+			jj_scanpos = xsp;
+		if (jj_3R_186())
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_136(){
-		if (jj_scan_token(QUANTIFIER))
+	private boolean jj_3R_161(){
+		if (jj_scan_token(ASTERISK))
 			return true;
 		return false;
 	}
 
-	private boolean jj_3R_164(){
-		if (jj_scan_token(NOT))
+	private boolean jj_3_1(){
+		if (jj_3R_14())
 			return true;
-		return false;
-	}
-
-	private boolean jj_3R_44(){
-		if (jj_scan_token(SELECT))
+		if (jj_scan_token(DOT))
 			return true;
 		Token xsp;
 		xsp = jj_scanpos;
-		if (jj_3R_136())
-			jj_scanpos = xsp;
-		xsp = jj_scanpos;
-		if (jj_3R_137())
+		if (jj_3R_15())
 			jj_scanpos = xsp;
-		if (jj_3R_138())
+		if (jj_scan_token(ASTERISK))
 			return true;
-		while(true){
-			xsp = jj_scanpos;
-			if (jj_3R_139()){
-				jj_scanpos = xsp;
-				break;
-			}
-		}
 		return false;
 	}
 
@@ -5718,7 +5655,7 @@ public class ADQLParser implements ADQLParserConstants {
 	private Token jj_scanpos, jj_lastpos;
 	private int jj_la;
 	private int jj_gen;
-	final private int[] jj_la1 = new int[97];
+	final private int[] jj_la1 = new int[98];
 	static private int[] jj_la1_0;
 	static private int[] jj_la1_1;
 	static private int[] jj_la1_2;
@@ -5731,22 +5668,22 @@ public class ADQLParser implements ADQLParserConstants {
 	}
 
 	private static void jj_la1_init_0(){
-		jj_la1_0 = new int[]{0x41,0x0,0x0,0x0,0x0,0x80000,0x100000,0x20,0x0,0x0,0x400000,0x400,0x304,0x20,0x20,0x20,0x0,0x10,0x10,0x10,0x0,0x0,0x0,0x0,0x400000,0x400000,0x400000,0x0,0x4,0x3d800000,0x1c000000,0x2000000,0x1d000000,0x1d000000,0x1c000000,0x2000000,0x1d000000,0x1d000000,0x20,0xc0000000,0x3d800000,0x0,0x0,0x0,0x300,0x300,0x4,0x0,0x304,0x300,0x300,0xc00,0xc00,0x300,0x300,0x4,0x80,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x3f000,0x0,0x0,0x304,0x3f000,0x0,0x0,0x20,0x4,0x80000,0x704,0x0,0x80000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20,0x0,0x0,0x304,0x20,0x20,0x0,0x0,0x20,0x304,};
+		jj_la1_0 = new int[]{0x41,0x0,0x0,0x0,0x0,0x80000,0x100000,0x20,0x0,0x0,0x400000,0x400,0x304,0x20,0x20,0x20,0x0,0x10,0x10,0x10,0x0,0x0,0x0,0x0,0x400000,0x400000,0x400000,0x0,0x4,0x3d800000,0x1c000000,0x2000000,0x1d000000,0x1d000000,0x1c000000,0x2000000,0x1d000000,0x1d000000,0x20,0xc0000000,0x3d800000,0x0,0x0,0x0,0x300,0x300,0x4,0x4,0x0,0x304,0x300,0x300,0xc00,0xc00,0x300,0x300,0x4,0x80,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x3f000,0x0,0x0,0x304,0x3f000,0x0,0x0,0x20,0x4,0x80000,0x704,0x0,0x80000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20,0x0,0x0,0x304,0x20,0x20,0x0,0x0,0x20,0x304,};
 	}
 
 	private static void jj_la1_init_1(){
-		jj_la1_1 = new int[]{0x0,0x1,0x400,0x800,0x1000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xffff8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x6000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf8000,0x3f00000,0x7c0f8000,0x0,0x0,0x0,0x0,0x0,0x0,0xf8000,0x0,0x80000000,0xf8000,0x3f00000,0x8,0x6,0x6,0x8,0x0,0x8,0x8,0x0,0x108,0x200,0xffff8000,0x0,0x8,0x8,0x0,0x0,0x0,0xffff8000,0x78000,0x0,0xf8000,0xc000000,0x800000,0x800000,0x800000,0x800000,0x7c000000,0x0,0x3f00000,0x7c000000,0x7c0f8000,0x0,0x0,0x0,0x0,0x0,0xffff8000,};
+		jj_la1_1 = new int[]{0x0,0x1,0x400,0x800,0x1000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xffff8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x6000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf8000,0x0,0x3f00000,0x7c0f8000,0x0,0x0,0x0,0x0,0x0,0x0,0xf8000,0x0,0x80000000,0x0,0x3f00000,0x8,0x6,0x6,0x8,0x0,0x8,0x8,0x0,0x108,0x200,0xffff8000,0x0,0x8,0x8,0x0,0x0,0x0,0xffff8000,0x78000,0x0,0xf8000,0xc000000,0x800000,0x800000,0x800000,0x800000,0x7c000000,0x0,0x3f00000,0x7c000000,0x7c0f8000,0x0,0x0,0x0,0x0,0x0,0xffff8000,};
 	}
 
 	private static void jj_la1_init_2(){
-		jj_la1_2 = new int[]{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20ffffff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000000,0x0,0x0,0x0,0x0,0x20000000,0x0,0x20ffffff,0x0,0x0,0x0,0x0,0x0,0x0,0x20000000,0x0,0x0,0x20000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20ffffff,0x0,0x0,0x0,0x0,0x0,0x0,0x20ffffff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0xffffff,0x20ffffff,0x0,0x0,0xfffe,0xff0000,0x0,0x20ffffff,};
+		jj_la1_2 = new int[]{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20ffffff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000000,0x0,0x0,0x0,0x0,0x0,0x20000000,0x0,0xffffff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20ffffff,0x0,0x0,0x0,0x0,0x0,0x0,0x20ffffff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0xffffff,0xffffff,0x0,0x0,0xfffe,0xff0000,0x0,0x20ffffff,};
 	}
 
 	private static void jj_la1_init_3(){
-		jj_la1_3 = new int[]{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x3,0x0,0x0,0x3b,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x23,0x23,0x0,0x0,0x0,0x3,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x38,0x30,0x0,0x0,0x3b,0x0,0x3b,0x0,0x0,0x0,0x0,0x0,0x0,0x3b,0x0,0x0,0x3b,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3b,0x0,0x0,0x0,0x0,0x0,0x0,0x3b,0x0,0x0,0x0,0x0,0x3,0x3,0x3,0x3,0x0,0x0,0x0,0x2,0x3b,0x0,0x0,0x0,0x0,0x0,0x3b,};
+		jj_la1_3 = new int[]{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x3,0x0,0x0,0x3b,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x23,0x23,0x0,0x0,0x0,0x3,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x38,0x30,0x0,0x0,0x3b,0x3,0x0,0x3b,0x0,0x0,0x0,0x0,0x0,0x0,0x3b,0x0,0x0,0x3,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3b,0x0,0x0,0x0,0x0,0x0,0x0,0x3b,0x0,0x0,0x0,0x0,0x3,0x3,0x3,0x3,0x0,0x0,0x0,0x2,0x3b,0x0,0x0,0x0,0x0,0x0,0x3b,};
 	}
 
-	final private JJCalls[] jj_2_rtns = new JJCalls[13];
+	final private JJCalls[] jj_2_rtns = new JJCalls[16];
 	private boolean jj_rescan = false;
 	private int jj_gc = 0;
 
@@ -5766,7 +5703,7 @@ public class ADQLParser implements ADQLParserConstants {
 		token = new Token();
 		jj_ntk = -1;
 		jj_gen = 0;
-		for(int i = 0; i < 97; i++)
+		for(int i = 0; i < 98; i++)
 			jj_la1[i] = -1;
 		for(int i = 0; i < jj_2_rtns.length; i++)
 			jj_2_rtns[i] = new JJCalls();
@@ -5788,7 +5725,7 @@ public class ADQLParser implements ADQLParserConstants {
 		token = new Token();
 		jj_ntk = -1;
 		jj_gen = 0;
-		for(int i = 0; i < 97; i++)
+		for(int i = 0; i < 98; i++)
 			jj_la1[i] = -1;
 		for(int i = 0; i < jj_2_rtns.length; i++)
 			jj_2_rtns[i] = new JJCalls();
@@ -5801,7 +5738,7 @@ public class ADQLParser implements ADQLParserConstants {
 		token = new Token();
 		jj_ntk = -1;
 		jj_gen = 0;
-		for(int i = 0; i < 97; i++)
+		for(int i = 0; i < 98; i++)
 			jj_la1[i] = -1;
 		for(int i = 0; i < jj_2_rtns.length; i++)
 			jj_2_rtns[i] = new JJCalls();
@@ -5814,7 +5751,7 @@ public class ADQLParser implements ADQLParserConstants {
 		token = new Token();
 		jj_ntk = -1;
 		jj_gen = 0;
-		for(int i = 0; i < 97; i++)
+		for(int i = 0; i < 98; i++)
 			jj_la1[i] = -1;
 		for(int i = 0; i < jj_2_rtns.length; i++)
 			jj_2_rtns[i] = new JJCalls();
@@ -5826,7 +5763,7 @@ public class ADQLParser implements ADQLParserConstants {
 		token = new Token();
 		jj_ntk = -1;
 		jj_gen = 0;
-		for(int i = 0; i < 97; i++)
+		for(int i = 0; i < 98; i++)
 			jj_la1[i] = -1;
 		for(int i = 0; i < jj_2_rtns.length; i++)
 			jj_2_rtns[i] = new JJCalls();
@@ -5838,7 +5775,7 @@ public class ADQLParser implements ADQLParserConstants {
 		token = new Token();
 		jj_ntk = -1;
 		jj_gen = 0;
-		for(int i = 0; i < 97; i++)
+		for(int i = 0; i < 98; i++)
 			jj_la1[i] = -1;
 		for(int i = 0; i < jj_2_rtns.length; i++)
 			jj_2_rtns[i] = new JJCalls();
@@ -5864,7 +5801,6 @@ public class ADQLParser implements ADQLParserConstants {
 					}
 				}
 			}
-			trace_token(token, "");
 			return token;
 		}
 		token = oldToken;
@@ -5912,7 +5848,6 @@ public class ADQLParser implements ADQLParserConstants {
 			token = token.next = token_source.getNextToken();
 		jj_ntk = -1;
 		jj_gen++;
-		trace_token(token, " (in getNextToken)");
 		return token;
 	}
 
@@ -5976,7 +5911,7 @@ public class ADQLParser implements ADQLParserConstants {
 			la1tokens[jj_kind] = true;
 			jj_kind = -1;
 		}
-		for(int i = 0; i < 97; i++){
+		for(int i = 0; i < 98; i++){
 			if (jj_la1[i] == jj_gen){
 				for(int j = 0; j < 32; j++){
 					if ((jj_la1_0[i] & (1 << j)) != 0){
@@ -6011,68 +5946,15 @@ public class ADQLParser implements ADQLParserConstants {
 		return new ParseException(token, exptokseq, tokenImage);
 	}
 
-	private int trace_indent = 0;
-	private boolean trace_enabled = true;
-
 	/** Enable tracing. */
-	final public void enable_tracing(){
-		trace_enabled = true;
-	}
+	final public void enable_tracing(){}
 
 	/** Disable tracing. */
-	final public void disable_tracing(){
-		trace_enabled = false;
-	}
-
-	private void trace_call(String s){
-		if (trace_enabled){
-			for(int i = 0; i < trace_indent; i++){
-				System.out.print(" ");
-			}
-			System.out.println("Call:   " + s);
-		}
-		trace_indent = trace_indent + 2;
-	}
-
-	private void trace_return(String s){
-		trace_indent = trace_indent - 2;
-		if (trace_enabled){
-			for(int i = 0; i < trace_indent; i++){
-				System.out.print(" ");
-			}
-			System.out.println("Return: " + s);
-		}
-	}
-
-	private void trace_token(Token t, String where){
-		if (trace_enabled){
-			for(int i = 0; i < trace_indent; i++){
-				System.out.print(" ");
-			}
-			System.out.print("Consumed token: <" + tokenImage[t.kind]);
-			if (t.kind != 0 && !tokenImage[t.kind].equals("\"" + t.image + "\"")){
-				System.out.print(": \"" + t.image + "\"");
-			}
-			System.out.println(" at line " + t.beginLine + " column " + t.beginColumn + ">" + where);
-		}
-	}
-
-	private void trace_scan(Token t1, int t2){
-		if (trace_enabled){
-			for(int i = 0; i < trace_indent; i++){
-				System.out.print(" ");
-			}
-			System.out.print("Visited token: <" + tokenImage[t1.kind]);
-			if (t1.kind != 0 && !tokenImage[t1.kind].equals("\"" + t1.image + "\"")){
-				System.out.print(": \"" + t1.image + "\"");
-			}
-			System.out.println(" at line " + t1.beginLine + " column " + t1.beginColumn + ">; Expected token: <" + tokenImage[t2] + ">");
-		}
-	}
+	final public void disable_tracing(){}
 
 	private void jj_rescan_token(){
 		jj_rescan = true;
-		for(int i = 0; i < 13; i++){
+		for(int i = 0; i < 16; i++){
 			try{
 				JJCalls p = jj_2_rtns[i];
 				do{
@@ -6119,6 +6001,15 @@ public class ADQLParser implements ADQLParserConstants {
 							case 12:
 								jj_3_13();
 								break;
+							case 13:
+								jj_3_14();
+								break;
+							case 14:
+								jj_3_15();
+								break;
+							case 15:
+								jj_3_16();
+								break;
 						}
 					}
 					p = p.next;
diff --git a/src/adql/parser/ADQLParserConstants.java b/src/adql/parser/ADQLParserConstants.java
index cf75565..e69af55 100644
--- a/src/adql/parser/ADQLParserConstants.java
+++ b/src/adql/parser/ADQLParserConstants.java
@@ -1,213 +1,318 @@
 /* Generated By:JavaCC: Do not edit this line. ADQLParserConstants.java */
 package adql.parser;
 
+
 /**
  * Token literal values and constants.
  * Generated by org.javacc.parser.OtherFilesGen#start()
  */
 public interface ADQLParserConstants {
 
-	/** End of File. */
-	int EOF = 0;
-	/** RegularExpression Id. */
-	int LEFT_PAR = 2;
-	/** RegularExpression Id. */
-	int RIGHT_PAR = 3;
-	/** RegularExpression Id. */
-	int DOT = 4;
-	/** RegularExpression Id. */
-	int COMMA = 5;
-	/** RegularExpression Id. */
-	int EOQ = 6;
-	/** RegularExpression Id. */
-	int CONCAT = 7;
-	/** RegularExpression Id. */
-	int PLUS = 8;
-	/** RegularExpression Id. */
-	int MINUS = 9;
-	/** RegularExpression Id. */
-	int ASTERISK = 10;
-	/** RegularExpression Id. */
-	int DIVIDE = 11;
-	/** RegularExpression Id. */
-	int EQUAL = 12;
-	/** RegularExpression Id. */
-	int NOT_EQUAL = 13;
-	/** RegularExpression Id. */
-	int LESS_THAN = 14;
-	/** RegularExpression Id. */
-	int LESS_EQUAL_THAN = 15;
-	/** RegularExpression Id. */
-	int GREATER_THAN = 16;
-	/** RegularExpression Id. */
-	int GREATER_EQUAL_THAN = 17;
-	/** RegularExpression Id. */
-	int SELECT = 18;
-	/** RegularExpression Id. */
-	int QUANTIFIER = 19;
-	/** RegularExpression Id. */
-	int TOP = 20;
-	/** RegularExpression Id. */
-	int FROM = 21;
-	/** RegularExpression Id. */
-	int AS = 22;
-	/** RegularExpression Id. */
-	int NATURAL = 23;
-	/** RegularExpression Id. */
-	int INNER = 24;
-	/** RegularExpression Id. */
-	int OUTER = 25;
-	/** RegularExpression Id. */
-	int RIGHT = 26;
-	/** RegularExpression Id. */
-	int LEFT = 27;
-	/** RegularExpression Id. */
-	int FULL = 28;
-	/** RegularExpression Id. */
-	int JOIN = 29;
-	/** RegularExpression Id. */
-	int ON = 30;
-	/** RegularExpression Id. */
-	int USING = 31;
-	/** RegularExpression Id. */
-	int WHERE = 32;
-	/** RegularExpression Id. */
-	int AND = 33;
-	/** RegularExpression Id. */
-	int OR = 34;
-	/** RegularExpression Id. */
-	int NOT = 35;
-	/** RegularExpression Id. */
-	int IS = 36;
-	/** RegularExpression Id. */
-	int NULL = 37;
-	/** RegularExpression Id. */
-	int BETWEEN = 38;
-	/** RegularExpression Id. */
-	int LIKE = 39;
-	/** RegularExpression Id. */
-	int IN = 40;
-	/** RegularExpression Id. */
-	int EXISTS = 41;
-	/** RegularExpression Id. */
-	int GROUP_BY = 42;
-	/** RegularExpression Id. */
-	int HAVING = 43;
-	/** RegularExpression Id. */
-	int ORDER_BY = 44;
-	/** RegularExpression Id. */
-	int ASC = 45;
-	/** RegularExpression Id. */
-	int DESC = 46;
-	/** RegularExpression Id. */
-	int AVG = 47;
-	/** RegularExpression Id. */
-	int MAX = 48;
-	/** RegularExpression Id. */
-	int MIN = 49;
-	/** RegularExpression Id. */
-	int SUM = 50;
-	/** RegularExpression Id. */
-	int COUNT = 51;
-	/** RegularExpression Id. */
-	int BOX = 52;
-	/** RegularExpression Id. */
-	int CENTROID = 53;
-	/** RegularExpression Id. */
-	int CIRCLE = 54;
-	/** RegularExpression Id. */
-	int POINT = 55;
-	/** RegularExpression Id. */
-	int POLYGON = 56;
-	/** RegularExpression Id. */
-	int REGION = 57;
-	/** RegularExpression Id. */
-	int CONTAINS = 58;
-	/** RegularExpression Id. */
-	int INTERSECTS = 59;
-	/** RegularExpression Id. */
-	int AREA = 60;
-	/** RegularExpression Id. */
-	int COORD1 = 61;
-	/** RegularExpression Id. */
-	int COORD2 = 62;
-	/** RegularExpression Id. */
-	int COORDSYS = 63;
-	/** RegularExpression Id. */
-	int DISTANCE = 64;
-	/** RegularExpression Id. */
-	int ABS = 65;
-	/** RegularExpression Id. */
-	int CEILING = 66;
-	/** RegularExpression Id. */
-	int DEGREES = 67;
-	/** RegularExpression Id. */
-	int EXP = 68;
-	/** RegularExpression Id. */
-	int FLOOR = 69;
-	/** RegularExpression Id. */
-	int LOG = 70;
-	/** RegularExpression Id. */
-	int LOG10 = 71;
-	/** RegularExpression Id. */
-	int MOD = 72;
-	/** RegularExpression Id. */
-	int PI = 73;
-	/** RegularExpression Id. */
-	int POWER = 74;
-	/** RegularExpression Id. */
-	int RADIANS = 75;
-	/** RegularExpression Id. */
-	int RAND = 76;
-	/** RegularExpression Id. */
-	int ROUND = 77;
-	/** RegularExpression Id. */
-	int SQRT = 78;
-	/** RegularExpression Id. */
-	int TRUNCATE = 79;
-	/** RegularExpression Id. */
-	int ACOS = 80;
-	/** RegularExpression Id. */
-	int ASIN = 81;
-	/** RegularExpression Id. */
-	int ATAN = 82;
-	/** RegularExpression Id. */
-	int ATAN2 = 83;
-	/** RegularExpression Id. */
-	int COS = 84;
-	/** RegularExpression Id. */
-	int COT = 85;
-	/** RegularExpression Id. */
-	int SIN = 86;
-	/** RegularExpression Id. */
-	int TAN = 87;
-	/** RegularExpression Id. */
-	int STRING_LITERAL = 93;
-	/** RegularExpression Id. */
-	int DELIMITED_IDENTIFIER = 96;
-	/** RegularExpression Id. */
-	int REGULAR_IDENTIFIER = 97;
-	/** RegularExpression Id. */
-	int Letter = 98;
-	/** RegularExpression Id. */
-	int SCIENTIFIC_NUMBER = 99;
-	/** RegularExpression Id. */
-	int UNSIGNED_FLOAT = 100;
-	/** RegularExpression Id. */
-	int UNSIGNED_INTEGER = 101;
-	/** RegularExpression Id. */
-	int DIGIT = 102;
+  /** End of File. */
+  int EOF = 0;
+  /** RegularExpression Id. */
+  int LEFT_PAR = 2;
+  /** RegularExpression Id. */
+  int RIGHT_PAR = 3;
+  /** RegularExpression Id. */
+  int DOT = 4;
+  /** RegularExpression Id. */
+  int COMMA = 5;
+  /** RegularExpression Id. */
+  int EOQ = 6;
+  /** RegularExpression Id. */
+  int CONCAT = 7;
+  /** RegularExpression Id. */
+  int PLUS = 8;
+  /** RegularExpression Id. */
+  int MINUS = 9;
+  /** RegularExpression Id. */
+  int ASTERISK = 10;
+  /** RegularExpression Id. */
+  int DIVIDE = 11;
+  /** RegularExpression Id. */
+  int EQUAL = 12;
+  /** RegularExpression Id. */
+  int NOT_EQUAL = 13;
+  /** RegularExpression Id. */
+  int LESS_THAN = 14;
+  /** RegularExpression Id. */
+  int LESS_EQUAL_THAN = 15;
+  /** RegularExpression Id. */
+  int GREATER_THAN = 16;
+  /** RegularExpression Id. */
+  int GREATER_EQUAL_THAN = 17;
+  /** RegularExpression Id. */
+  int SELECT = 18;
+  /** RegularExpression Id. */
+  int QUANTIFIER = 19;
+  /** RegularExpression Id. */
+  int TOP = 20;
+  /** RegularExpression Id. */
+  int FROM = 21;
+  /** RegularExpression Id. */
+  int AS = 22;
+  /** RegularExpression Id. */
+  int NATURAL = 23;
+  /** RegularExpression Id. */
+  int INNER = 24;
+  /** RegularExpression Id. */
+  int OUTER = 25;
+  /** RegularExpression Id. */
+  int RIGHT = 26;
+  /** RegularExpression Id. */
+  int LEFT = 27;
+  /** RegularExpression Id. */
+  int FULL = 28;
+  /** RegularExpression Id. */
+  int JOIN = 29;
+  /** RegularExpression Id. */
+  int ON = 30;
+  /** RegularExpression Id. */
+  int USING = 31;
+  /** RegularExpression Id. */
+  int WHERE = 32;
+  /** RegularExpression Id. */
+  int AND = 33;
+  /** RegularExpression Id. */
+  int OR = 34;
+  /** RegularExpression Id. */
+  int NOT = 35;
+  /** RegularExpression Id. */
+  int IS = 36;
+  /** RegularExpression Id. */
+  int NULL = 37;
+  /** RegularExpression Id. */
+  int BETWEEN = 38;
+  /** RegularExpression Id. */
+  int LIKE = 39;
+  /** RegularExpression Id. */
+  int IN = 40;
+  /** RegularExpression Id. */
+  int EXISTS = 41;
+  /** RegularExpression Id. */
+  int GROUP_BY = 42;
+  /** RegularExpression Id. */
+  int HAVING = 43;
+  /** RegularExpression Id. */
+  int ORDER_BY = 44;
+  /** RegularExpression Id. */
+  int ASC = 45;
+  /** RegularExpression Id. */
+  int DESC = 46;
+  /** RegularExpression Id. */
+  int AVG = 47;
+  /** RegularExpression Id. */
+  int MAX = 48;
+  /** RegularExpression Id. */
+  int MIN = 49;
+  /** RegularExpression Id. */
+  int SUM = 50;
+  /** RegularExpression Id. */
+  int COUNT = 51;
+  /** RegularExpression Id. */
+  int BOX = 52;
+  /** RegularExpression Id. */
+  int CENTROID = 53;
+  /** RegularExpression Id. */
+  int CIRCLE = 54;
+  /** RegularExpression Id. */
+  int POINT = 55;
+  /** RegularExpression Id. */
+  int POLYGON = 56;
+  /** RegularExpression Id. */
+  int REGION = 57;
+  /** RegularExpression Id. */
+  int CONTAINS = 58;
+  /** RegularExpression Id. */
+  int INTERSECTS = 59;
+  /** RegularExpression Id. */
+  int AREA = 60;
+  /** RegularExpression Id. */
+  int COORD1 = 61;
+  /** RegularExpression Id. */
+  int COORD2 = 62;
+  /** RegularExpression Id. */
+  int COORDSYS = 63;
+  /** RegularExpression Id. */
+  int DISTANCE = 64;
+  /** RegularExpression Id. */
+  int ABS = 65;
+  /** RegularExpression Id. */
+  int CEILING = 66;
+  /** RegularExpression Id. */
+  int DEGREES = 67;
+  /** RegularExpression Id. */
+  int EXP = 68;
+  /** RegularExpression Id. */
+  int FLOOR = 69;
+  /** RegularExpression Id. */
+  int LOG = 70;
+  /** RegularExpression Id. */
+  int LOG10 = 71;
+  /** RegularExpression Id. */
+  int MOD = 72;
+  /** RegularExpression Id. */
+  int PI = 73;
+  /** RegularExpression Id. */
+  int POWER = 74;
+  /** RegularExpression Id. */
+  int RADIANS = 75;
+  /** RegularExpression Id. */
+  int RAND = 76;
+  /** RegularExpression Id. */
+  int ROUND = 77;
+  /** RegularExpression Id. */
+  int SQRT = 78;
+  /** RegularExpression Id. */
+  int TRUNCATE = 79;
+  /** RegularExpression Id. */
+  int ACOS = 80;
+  /** RegularExpression Id. */
+  int ASIN = 81;
+  /** RegularExpression Id. */
+  int ATAN = 82;
+  /** RegularExpression Id. */
+  int ATAN2 = 83;
+  /** RegularExpression Id. */
+  int COS = 84;
+  /** RegularExpression Id. */
+  int COT = 85;
+  /** RegularExpression Id. */
+  int SIN = 86;
+  /** RegularExpression Id. */
+  int TAN = 87;
+  /** RegularExpression Id. */
+  int STRING_LITERAL = 93;
+  /** RegularExpression Id. */
+  int DELIMITED_IDENTIFIER = 96;
+  /** RegularExpression Id. */
+  int REGULAR_IDENTIFIER = 97;
+  /** RegularExpression Id. */
+  int Letter = 98;
+  /** RegularExpression Id. */
+  int SCIENTIFIC_NUMBER = 99;
+  /** RegularExpression Id. */
+  int UNSIGNED_FLOAT = 100;
+  /** RegularExpression Id. */
+  int UNSIGNED_INTEGER = 101;
+  /** RegularExpression Id. */
+  int DIGIT = 102;
 
-	/** Lexical state. */
-	int DEFAULT = 0;
-	/** Lexical state. */
-	int WithinComment = 1;
-	/** Lexical state. */
-	int WithinString = 2;
-	/** Lexical state. */
-	int WithinDelimitedId = 3;
+  /** Lexical state. */
+  int DEFAULT = 0;
+  /** Lexical state. */
+  int WithinComment = 1;
+  /** Lexical state. */
+  int WithinString = 2;
+  /** Lexical state. */
+  int WithinDelimitedId = 3;
 
-	/** Literal token values. */
-	String[] tokenImage = {"<EOF>","<token of kind 1>","\"(\"","\")\"","\".\"","\",\"","\";\"","\"||\"","\"+\"","\"-\"","\"*\"","\"/\"","\"=\"","<NOT_EQUAL>","\"<\"","\"<=\"","\">\"","\">=\"","\"SELECT\"","<QUANTIFIER>","\"TOP\"","\"FROM\"","\"AS\"","\"NATURAL\"","\"INNER\"","\"OUTER\"","\"RIGHT\"","\"LEFT\"","\"FULL\"","\"JOIN\"","\"ON\"","\"USING\"","\"WHERE\"","\"AND\"","\"OR\"","\"NOT\"","\"IS\"","\"NULL\"","\"BETWEEN\"","\"LIKE\"","\"IN\"","\"EXISTS\"","\"GROUP BY\"","\"HAVING\"","\"ORDER BY\"","\"ASC\"","\"DESC\"","\"AVG\"","\"MAX\"","\"MIN\"","\"SUM\"","\"COUNT\"","\"BOX\"","\"CENTROID\"","\"CIRCLE\"","\"POINT\"","\"POLYGON\"","\"REGION\"","\"CONTAINS\"","\"INTERSECTS\"","\"AREA\"","\"COORD1\"","\"COORD2\"","\"COORDSYS\"","\"DISTANCE\"","\"ABS\"","\"CEILING\"","\"DEGREES\"","\"EXP\"","\"FLOOR\"","\"LOG\"","\"LOG10\"","\"MOD\"","\"PI\"","\"POWER\"","\"RADIANS\"","\"RAND\"","\"ROUND\"","\"SQRT\"","\"TRUNCATE\"","\"ACOS\"","\"ASIN\"","\"ATAN\"","\"ATAN2\"","\"COS\"","\"COT\"","\"SIN\"","\"TAN\"","<token of kind 88>","<token of kind 89>","<token of kind 90>","\"\\\'\"","<token of kind 92>","\"\\\'\"","\"\\\"\"","<token of kind 95>","\"\\\"\"","<REGULAR_IDENTIFIER>","<Letter>","<SCIENTIFIC_NUMBER>","<UNSIGNED_FLOAT>","<UNSIGNED_INTEGER>","<DIGIT>",};
+  /** Literal token values. */
+  String[] tokenImage = {
+    "<EOF>",
+    "<token of kind 1>",
+    "\"(\"",
+    "\")\"",
+    "\".\"",
+    "\",\"",
+    "\";\"",
+    "\"||\"",
+    "\"+\"",
+    "\"-\"",
+    "\"*\"",
+    "\"/\"",
+    "\"=\"",
+    "<NOT_EQUAL>",
+    "\"<\"",
+    "\"<=\"",
+    "\">\"",
+    "\">=\"",
+    "\"SELECT\"",
+    "<QUANTIFIER>",
+    "\"TOP\"",
+    "\"FROM\"",
+    "\"AS\"",
+    "\"NATURAL\"",
+    "\"INNER\"",
+    "\"OUTER\"",
+    "\"RIGHT\"",
+    "\"LEFT\"",
+    "\"FULL\"",
+    "\"JOIN\"",
+    "\"ON\"",
+    "\"USING\"",
+    "\"WHERE\"",
+    "\"AND\"",
+    "\"OR\"",
+    "\"NOT\"",
+    "\"IS\"",
+    "\"NULL\"",
+    "\"BETWEEN\"",
+    "\"LIKE\"",
+    "\"IN\"",
+    "\"EXISTS\"",
+    "\"GROUP BY\"",
+    "\"HAVING\"",
+    "\"ORDER BY\"",
+    "\"ASC\"",
+    "\"DESC\"",
+    "\"AVG\"",
+    "\"MAX\"",
+    "\"MIN\"",
+    "\"SUM\"",
+    "\"COUNT\"",
+    "\"BOX\"",
+    "\"CENTROID\"",
+    "\"CIRCLE\"",
+    "\"POINT\"",
+    "\"POLYGON\"",
+    "\"REGION\"",
+    "\"CONTAINS\"",
+    "\"INTERSECTS\"",
+    "\"AREA\"",
+    "\"COORD1\"",
+    "\"COORD2\"",
+    "\"COORDSYS\"",
+    "\"DISTANCE\"",
+    "\"ABS\"",
+    "\"CEILING\"",
+    "\"DEGREES\"",
+    "\"EXP\"",
+    "\"FLOOR\"",
+    "\"LOG\"",
+    "\"LOG10\"",
+    "\"MOD\"",
+    "\"PI\"",
+    "\"POWER\"",
+    "\"RADIANS\"",
+    "\"RAND\"",
+    "\"ROUND\"",
+    "\"SQRT\"",
+    "\"TRUNCATE\"",
+    "\"ACOS\"",
+    "\"ASIN\"",
+    "\"ATAN\"",
+    "\"ATAN2\"",
+    "\"COS\"",
+    "\"COT\"",
+    "\"SIN\"",
+    "\"TAN\"",
+    "<token of kind 88>",
+    "<token of kind 89>",
+    "<token of kind 90>",
+    "\"\\\'\"",
+    "<token of kind 92>",
+    "\"\\\'\"",
+    "\"\\\"\"",
+    "<token of kind 95>",
+    "\"\\\"\"",
+    "<REGULAR_IDENTIFIER>",
+    "<Letter>",
+    "<SCIENTIFIC_NUMBER>",
+    "<UNSIGNED_FLOAT>",
+    "<UNSIGNED_INTEGER>",
+    "<DIGIT>",
+  };
 
 }
diff --git a/src/adql/parser/ADQLParserTokenManager.java b/src/adql/parser/ADQLParserTokenManager.java
index 4e4d9cc..1544161 100644
--- a/src/adql/parser/ADQLParserTokenManager.java
+++ b/src/adql/parser/ADQLParserTokenManager.java
@@ -1,6 +1,5 @@
 /* Generated By:JavaCC: Do not edit this line. ADQLParserTokenManager.java */
 package adql.parser;
-
 import java.util.Stack;
 import java.util.Vector;
 import java.util.ArrayList;
@@ -21,1716 +20,1842 @@ import adql.translator.PostgreSQLTranslator;
 import adql.translator.TranslationException;
 
 /** Token Manager. */
-public class ADQLParserTokenManager implements ADQLParserConstants {
-
-	/** Debug output. */
-	public java.io.PrintStream debugStream = System.out;
-
-	/** Set debug output. */
-	public void setDebugStream(java.io.PrintStream ds){
-		debugStream = ds;
-	}
-
-	private final int jjStopStringLiteralDfa_3(int pos, long active0, long active1){
-		switch(pos){
-			default:
-				return -1;
-		}
-	}
-
-	private final int jjStartNfa_3(int pos, long active0, long active1){
-		return jjMoveNfa_3(jjStopStringLiteralDfa_3(pos, active0, active1), pos + 1);
-	}
-
-	private int jjStopAtPos(int pos, int kind){
-		jjmatchedKind = kind;
-		jjmatchedPos = pos;
-		return pos + 1;
-	}
-
-	private int jjMoveStringLiteralDfa0_3(){
-		switch(curChar){
-			case 34:
-				return jjStartNfaWithStates_3(0, 96, 1);
-			default:
-				return jjMoveNfa_3(0, 0);
-		}
-	}
-
-	private int jjStartNfaWithStates_3(int pos, int kind, int state){
-		jjmatchedKind = kind;
-		jjmatchedPos = pos;
-		try{
-			curChar = input_stream.readChar();
-		}catch(java.io.IOException e){
-			return pos + 1;
-		}
-		return jjMoveNfa_3(state, pos + 1);
-	}
-
-	static final long[] jjbitVec0 = {0x0L,0x0L,0xffffffffffffffffL,0xffffffffffffffffL};
-
-	private int jjMoveNfa_3(int startState, int curPos){
-		int startsAt = 0;
-		jjnewStateCnt = 3;
-		int i = 1;
-		jjstateSet[0] = startState;
-		int kind = 0x7fffffff;
-		for(;;){
-			if (++jjround == 0x7fffffff)
-				ReInitRounds();
-			if (curChar < 64){
-				long l = 1L << curChar;
-				do{
-					switch(jjstateSet[--i]){
-						case 0:
-							if ((0xfffffffbffffffffL & l) != 0L){
-								if (kind > 95)
-									kind = 95;
-							}else if (curChar == 34)
-								jjstateSet[jjnewStateCnt++] = 1;
-							break;
-						case 1:
-							if (curChar == 34 && kind > 95)
-								kind = 95;
-							break;
-						case 2:
-							if (curChar == 34)
-								jjstateSet[jjnewStateCnt++] = 1;
-							break;
-						default:
-							break;
-					}
-				}while(i != startsAt);
-			}else if (curChar < 128){
-				long l = 1L << (curChar & 077);
-				do{
-					switch(jjstateSet[--i]){
-						case 0:
-							kind = 95;
-							break;
-						default:
-							break;
-					}
-				}while(i != startsAt);
-			}else{
-				int i2 = (curChar & 0xff) >> 6;
-				long l2 = 1L << (curChar & 077);
-				do{
-					switch(jjstateSet[--i]){
-						case 0:
-							if ((jjbitVec0[i2] & l2) != 0L && kind > 95)
-								kind = 95;
-							break;
-						default:
-							break;
-					}
-				}while(i != startsAt);
-			}
-			if (kind != 0x7fffffff){
-				jjmatchedKind = kind;
-				jjmatchedPos = curPos;
-				kind = 0x7fffffff;
-			}
-			++curPos;
-			if ((i = jjnewStateCnt) == (startsAt = 3 - (jjnewStateCnt = startsAt)))
-				return curPos;
-			try{
-				curChar = input_stream.readChar();
-			}catch(java.io.IOException e){
-				return curPos;
-			}
-		}
-	}
-
-	private final int jjStopStringLiteralDfa_0(int pos, long active0, long active1){
-		switch(pos){
-			case 0:
-				if ((active0 & 0xefff1ffdffb40000L) != 0L || (active1 & 0xf0fff4L) != 0L){
-					jjmatchedKind = 97;
-					return 37;
-				}
-				if ((active0 & 0x1000a00200400000L) != 0L || (active1 & 0xf0002L) != 0L){
-					jjmatchedKind = 97;
-					return 16;
-				}
-				if ((active0 & 0x10L) != 0L)
-					return 38;
-				if ((active0 & 0x400000000000L) != 0L || (active1 & 0x9L) != 0L){
-					jjmatchedKind = 97;
-					return 13;
-				}
-				if ((active0 & 0xc000L) != 0L)
-					return 3;
-				if ((active0 & 0x200L) != 0L)
-					return 19;
-				return -1;
-			case 1:
-				if ((active0 & 0xf7ffceebbeb40000L) != 0L || (active1 & 0xfdfdfeL) != 0L){
-					if (jjmatchedPos != 1){
-						jjmatchedKind = 97;
-						jjmatchedPos = 1;
-					}
-					return 37;
-				}
-				if ((active0 & 0x800311441400000L) != 0L || (active1 & 0x20200L) != 0L)
-					return 37;
-				if ((active1 & 0x1L) != 0L){
-					if (jjmatchedPos != 1){
-						jjmatchedKind = 97;
-						jjmatchedPos = 1;
-					}
-					return 12;
-				}
-				return -1;
-			case 2:
-				if ((active1 & 0x1L) != 0L){
-					if (jjmatchedPos != 2){
-						jjmatchedKind = 97;
-						jjmatchedPos = 2;
-					}
-					return 11;
-				}
-				if ((active0 & 0x17a00a00100000L) != 0L || (active1 & 0xf001d2L) != 0L)
-					return 37;
-				if ((active0 & 0xffe85ee1bfa40000L) != 0L || (active1 & 0xffc2cL) != 0L){
-					if (jjmatchedPos != 2){
-						jjmatchedKind = 97;
-						jjmatchedPos = 2;
-					}
-					return 37;
-				}
-				return -1;
-			case 3:
-				if ((active0 & 0xefe81e4187840000L) != 0L || (active1 & 0xac2cL) != 0L){
-					if (jjmatchedPos != 3){
-						jjmatchedKind = 97;
-						jjmatchedPos = 3;
-					}
-					return 37;
-				}
-				if ((active0 & 0x100040a038200000L) != 0L || (active1 & 0xf5000L) != 0L)
-					return 37;
-				if ((active1 & 0x80L) != 0L){
-					if (jjmatchedPos != 3){
-						jjmatchedKind = 97;
-						jjmatchedPos = 3;
-					}
-					return 21;
-				}
-				if ((active1 & 0x1L) != 0L){
-					if (jjmatchedPos != 3){
-						jjmatchedKind = 97;
-						jjmatchedPos = 3;
-					}
-					return 10;
-				}
-				return -1;
-			case 4:
-				if ((active0 & 0xef601e4000840000L) != 0L || (active1 & 0x880dL) != 0L){
-					jjmatchedKind = 97;
-					jjmatchedPos = 4;
-					return 37;
-				}
-				if ((active0 & 0x88000187000000L) != 0L || (active1 & 0x2420L) != 0L)
-					return 37;
-				if ((active1 & 0x80080L) != 0L)
-					return 21;
-				return -1;
-			case 5:
-				if ((active0 & 0x2400a0000040000L) != 0L)
-					return 37;
-				if ((active0 & 0x6000000000000000L) != 0L)
-					return 21;
-				if ((active0 & 0x8d20004000800000L) != 0L || (active1 & 0x880dL) != 0L){
-					jjmatchedKind = 97;
-					jjmatchedPos = 5;
-					return 37;
-				}
-				if ((active0 & 0x140000000000L) != 0L){
-					if (jjmatchedPos < 4){
-						jjmatchedKind = 97;
-						jjmatchedPos = 4;
-					}
-					return -1;
-				}
-				return -1;
-			case 6:
-				if ((active0 & 0x8c20000000000000L) != 0L || (active1 & 0x8001L) != 0L){
-					jjmatchedKind = 97;
-					jjmatchedPos = 6;
-					return 37;
-				}
-				if ((active0 & 0x100004000800000L) != 0L || (active1 & 0x80cL) != 0L)
-					return 37;
-				if ((active0 & 0x140000000000L) != 0L){
-					if (jjmatchedPos < 4){
-						jjmatchedKind = 97;
-						jjmatchedPos = 4;
-					}
-					return -1;
-				}
-				return -1;
-			case 7:
-				if ((active0 & 0x800000000000000L) != 0L){
-					jjmatchedKind = 97;
-					jjmatchedPos = 7;
-					return 37;
-				}
-				if ((active0 & 0x8420000000000000L) != 0L || (active1 & 0x8001L) != 0L)
-					return 37;
-				if ((active0 & 0x140000000000L) != 0L){
-					if (jjmatchedPos < 4){
-						jjmatchedKind = 97;
-						jjmatchedPos = 4;
-					}
-					return -1;
-				}
-				return -1;
-			case 8:
-				if ((active0 & 0x800000000000000L) != 0L){
-					jjmatchedKind = 97;
-					jjmatchedPos = 8;
-					return 37;
-				}
-				return -1;
-			default:
-				return -1;
-		}
-	}
-
-	private final int jjStartNfa_0(int pos, long active0, long active1){
-		return jjMoveNfa_0(jjStopStringLiteralDfa_0(pos, active0, active1), pos + 1);
-	}
-
-	private int jjMoveStringLiteralDfa0_0(){
-		switch(curChar){
-			case 34:
-				return jjStopAtPos(0, 94);
-			case 39:
-				return jjStopAtPos(0, 91);
-			case 40:
-				return jjStopAtPos(0, 2);
-			case 41:
-				return jjStopAtPos(0, 3);
-			case 42:
-				return jjStopAtPos(0, 10);
-			case 43:
-				return jjStopAtPos(0, 8);
-			case 44:
-				return jjStopAtPos(0, 5);
-			case 45:
-				return jjStartNfaWithStates_0(0, 9, 19);
-			case 46:
-				return jjStartNfaWithStates_0(0, 4, 38);
-			case 47:
-				return jjStopAtPos(0, 11);
-			case 59:
-				return jjStopAtPos(0, 6);
-			case 60:
-				jjmatchedKind = 14;
-				return jjMoveStringLiteralDfa1_0(0x8000L, 0x0L);
-			case 61:
-				return jjStopAtPos(0, 12);
-			case 62:
-				jjmatchedKind = 16;
-				return jjMoveStringLiteralDfa1_0(0x20000L, 0x0L);
-			case 65:
-			case 97:
-				return jjMoveStringLiteralDfa1_0(0x1000a00200400000L, 0xf0002L);
-			case 66:
-			case 98:
-				return jjMoveStringLiteralDfa1_0(0x10004000000000L, 0x0L);
-			case 67:
-			case 99:
-				return jjMoveStringLiteralDfa1_0(0xe468000000000000L, 0x300004L);
-			case 68:
-			case 100:
-				return jjMoveStringLiteralDfa1_0(0x400000000000L, 0x9L);
-			case 69:
-			case 101:
-				return jjMoveStringLiteralDfa1_0(0x20000000000L, 0x10L);
-			case 70:
-			case 102:
-				return jjMoveStringLiteralDfa1_0(0x10200000L, 0x20L);
-			case 71:
-			case 103:
-				return jjMoveStringLiteralDfa1_0(0x40000000000L, 0x0L);
-			case 72:
-			case 104:
-				return jjMoveStringLiteralDfa1_0(0x80000000000L, 0x0L);
-			case 73:
-			case 105:
-				return jjMoveStringLiteralDfa1_0(0x800011001000000L, 0x0L);
-			case 74:
-			case 106:
-				return jjMoveStringLiteralDfa1_0(0x20000000L, 0x0L);
-			case 76:
-			case 108:
-				return jjMoveStringLiteralDfa1_0(0x8008000000L, 0xc0L);
-			case 77:
-			case 109:
-				return jjMoveStringLiteralDfa1_0(0x3000000000000L, 0x100L);
-			case 78:
-			case 110:
-				return jjMoveStringLiteralDfa1_0(0x2800800000L, 0x0L);
-			case 79:
-			case 111:
-				return jjMoveStringLiteralDfa1_0(0x100442000000L, 0x0L);
-			case 80:
-			case 112:
-				return jjMoveStringLiteralDfa1_0(0x180000000000000L, 0x600L);
-			case 82:
-			case 114:
-				return jjMoveStringLiteralDfa1_0(0x200000004000000L, 0x3800L);
-			case 83:
-			case 115:
-				return jjMoveStringLiteralDfa1_0(0x4000000040000L, 0x404000L);
-			case 84:
-			case 116:
-				return jjMoveStringLiteralDfa1_0(0x100000L, 0x808000L);
-			case 85:
-			case 117:
-				return jjMoveStringLiteralDfa1_0(0x80000000L, 0x0L);
-			case 87:
-			case 119:
-				return jjMoveStringLiteralDfa1_0(0x100000000L, 0x0L);
-			case 124:
-				return jjMoveStringLiteralDfa1_0(0x80L, 0x0L);
-			default:
-				return jjMoveNfa_0(0, 0);
-		}
-	}
-
-	private int jjMoveStringLiteralDfa1_0(long active0, long active1){
-		try{
-			curChar = input_stream.readChar();
-		}catch(java.io.IOException e){
-			jjStopStringLiteralDfa_0(0, active0, active1);
-			return 1;
-		}
-		switch(curChar){
-			case 61:
-				if ((active0 & 0x8000L) != 0L)
-					return jjStopAtPos(1, 15);
-				else if ((active0 & 0x20000L) != 0L)
-					return jjStopAtPos(1, 17);
-				break;
-			case 65:
-			case 97:
-				return jjMoveStringLiteralDfa2_0(active0, 0x1080000800000L, active1, 0x801800L);
-			case 66:
-			case 98:
-				return jjMoveStringLiteralDfa2_0(active0, 0L, active1, 0x2L);
-			case 67:
-			case 99:
-				return jjMoveStringLiteralDfa2_0(active0, 0L, active1, 0x10000L);
-			case 69:
-			case 101:
-				return jjMoveStringLiteralDfa2_0(active0, 0x220404008040000L, active1, 0xcL);
-			case 72:
-			case 104:
-				return jjMoveStringLiteralDfa2_0(active0, 0x100000000L, active1, 0L);
-			case 73:
-			case 105:
-				if ((active1 & 0x200L) != 0L)
-					return jjStartNfaWithStates_0(1, 73, 37);
-				return jjMoveStringLiteralDfa2_0(active0, 0x42008004000000L, active1, 0x400001L);
-			case 76:
-			case 108:
-				return jjMoveStringLiteralDfa2_0(active0, 0L, active1, 0x20L);
-			case 78:
-			case 110:
-				if ((active0 & 0x40000000L) != 0L)
-					return jjStartNfaWithStates_0(1, 30, 37);
-				else if ((active0 & 0x10000000000L) != 0L){
-					jjmatchedKind = 40;
-					jjmatchedPos = 1;
-				}
-				return jjMoveStringLiteralDfa2_0(active0, 0x800000201000000L, active1, 0L);
-			case 79:
-			case 111:
-				return jjMoveStringLiteralDfa2_0(active0, 0xe598000820100000L, active1, 0x3025c0L);
-			case 81:
-			case 113:
-				return jjMoveStringLiteralDfa2_0(active0, 0L, active1, 0x4000L);
-			case 82:
-			case 114:
-				if ((active0 & 0x400000000L) != 0L){
-					jjmatchedKind = 34;
-					jjmatchedPos = 1;
-				}
-				return jjMoveStringLiteralDfa2_0(active0, 0x1000140000200000L, active1, 0x8000L);
-			case 83:
-			case 115:
-				if ((active0 & 0x400000L) != 0L){
-					jjmatchedKind = 22;
-					jjmatchedPos = 1;
-				}else if ((active0 & 0x1000000000L) != 0L)
-					return jjStartNfaWithStates_0(1, 36, 37);
-				return jjMoveStringLiteralDfa2_0(active0, 0x200080000000L, active1, 0x20000L);
-			case 84:
-			case 116:
-				return jjMoveStringLiteralDfa2_0(active0, 0L, active1, 0xc0000L);
-			case 85:
-			case 117:
-				return jjMoveStringLiteralDfa2_0(active0, 0x4002012000000L, active1, 0L);
-			case 86:
-			case 118:
-				return jjMoveStringLiteralDfa2_0(active0, 0x800000000000L, active1, 0L);
-			case 88:
-			case 120:
-				return jjMoveStringLiteralDfa2_0(active0, 0x20000000000L, active1, 0x10L);
-			case 124:
-				if ((active0 & 0x80L) != 0L)
-					return jjStopAtPos(1, 7);
-				break;
-			default:
-				break;
-		}
-		return jjStartNfa_0(0, active0, active1);
-	}
-
-	private int jjMoveStringLiteralDfa2_0(long old0, long active0, long old1, long active1){
-		if (((active0 &= old0) | (active1 &= old1)) == 0L)
-			return jjStartNfa_0(0, old0, old1);
-		try{
-			curChar = input_stream.readChar();
-		}catch(java.io.IOException e){
-			jjStopStringLiteralDfa_0(1, active0, active1);
-			return 2;
-		}
-		switch(curChar){
-			case 65:
-			case 97:
-				return jjMoveStringLiteralDfa3_0(active0, 0L, active1, 0xc0000L);
-			case 67:
-			case 99:
-				if ((active0 & 0x200000000000L) != 0L)
-					return jjStartNfaWithStates_0(2, 45, 37);
-				break;
-			case 68:
-			case 100:
-				if ((active0 & 0x200000000L) != 0L)
-					return jjStartNfaWithStates_0(2, 33, 37);
-				else if ((active1 & 0x100L) != 0L)
-					return jjStartNfaWithStates_0(2, 72, 37);
-				return jjMoveStringLiteralDfa3_0(active0, 0x100000000000L, active1, 0x800L);
-			case 69:
-			case 101:
-				return jjMoveStringLiteralDfa3_0(active0, 0x1000000100000000L, active1, 0L);
-			case 70:
-			case 102:
-				return jjMoveStringLiteralDfa3_0(active0, 0x8000000L, active1, 0L);
-			case 71:
-			case 103:
-				if ((active0 & 0x800000000000L) != 0L)
-					return jjStartNfaWithStates_0(2, 47, 37);
-				else if ((active1 & 0x40L) != 0L){
-					jjmatchedKind = 70;
-					jjmatchedPos = 2;
-				}
-				return jjMoveStringLiteralDfa3_0(active0, 0x200000004000000L, active1, 0x88L);
-			case 73:
-			case 105:
-				return jjMoveStringLiteralDfa3_0(active0, 0x800200a0000000L, active1, 0x20004L);
-			case 75:
-			case 107:
-				return jjMoveStringLiteralDfa3_0(active0, 0x8000000000L, active1, 0L);
-			case 76:
-			case 108:
-				return jjMoveStringLiteralDfa3_0(active0, 0x100002010040000L, active1, 0L);
-			case 77:
-			case 109:
-				if ((active0 & 0x4000000000000L) != 0L)
-					return jjStartNfaWithStates_0(2, 50, 37);
-				break;
-			case 78:
-			case 110:
-				if ((active0 & 0x2000000000000L) != 0L)
-					return jjStartNfaWithStates_0(2, 49, 37);
-				else if ((active1 & 0x400000L) != 0L)
-					return jjStartNfaWithStates_0(2, 86, 37);
-				else if ((active1 & 0x800000L) != 0L)
-					return jjStartNfaWithStates_0(2, 87, 37);
-				return jjMoveStringLiteralDfa3_0(active0, 0x420000001000000L, active1, 0x1000L);
-			case 79:
-			case 111:
-				return jjMoveStringLiteralDfa3_0(active0, 0xe000040000200000L, active1, 0x10020L);
-			case 80:
-			case 112:
-				if ((active0 & 0x100000L) != 0L)
-					return jjStartNfaWithStates_0(2, 20, 37);
-				else if ((active1 & 0x10L) != 0L)
-					return jjStartNfaWithStates_0(2, 68, 37);
-				break;
-			case 82:
-			case 114:
-				return jjMoveStringLiteralDfa3_0(active0, 0x40000000000000L, active1, 0x4000L);
-			case 83:
-			case 115:
-				if ((active1 & 0x2L) != 0L)
-					return jjStartNfaWithStates_0(2, 65, 37);
-				else if ((active1 & 0x100000L) != 0L)
-					return jjStartNfaWithStates_0(2, 84, 37);
-				return jjMoveStringLiteralDfa3_0(active0, 0x400000000000L, active1, 0x1L);
-			case 84:
-			case 116:
-				if ((active0 & 0x800000000L) != 0L)
-					return jjStartNfaWithStates_0(2, 35, 37);
-				else if ((active1 & 0x200000L) != 0L)
-					return jjStartNfaWithStates_0(2, 85, 37);
-				return jjMoveStringLiteralDfa3_0(active0, 0x800004002800000L, active1, 0L);
-			case 85:
-			case 117:
-				return jjMoveStringLiteralDfa3_0(active0, 0x8000000000000L, active1, 0xa000L);
-			case 86:
-			case 118:
-				return jjMoveStringLiteralDfa3_0(active0, 0x80000000000L, active1, 0L);
-			case 87:
-			case 119:
-				return jjMoveStringLiteralDfa3_0(active0, 0L, active1, 0x400L);
-			case 88:
-			case 120:
-				if ((active0 & 0x1000000000000L) != 0L)
-					return jjStartNfaWithStates_0(2, 48, 37);
-				else if ((active0 & 0x10000000000000L) != 0L)
-					return jjStartNfaWithStates_0(2, 52, 37);
-				break;
-			default:
-				break;
-		}
-		return jjStartNfa_0(1, active0, active1);
-	}
-
-	private int jjMoveStringLiteralDfa3_0(long old0, long active0, long old1, long active1){
-		if (((active0 &= old0) | (active1 &= old1)) == 0L)
-			return jjStartNfa_0(1, old0, old1);
-		try{
-			curChar = input_stream.readChar();
-		}catch(java.io.IOException e){
-			jjStopStringLiteralDfa_0(2, active0, active1);
-			return 3;
-		}
-		switch(curChar){
-			case 49:
-				return jjMoveStringLiteralDfa4_0(active0, 0L, active1, 0x80L);
-			case 65:
-			case 97:
-				if ((active0 & 0x1000000000000000L) != 0L)
-					return jjStartNfaWithStates_0(3, 60, 37);
-				break;
-			case 67:
-			case 99:
-				if ((active0 & 0x400000000000L) != 0L)
-					return jjStartNfaWithStates_0(3, 46, 37);
-				return jjMoveStringLiteralDfa4_0(active0, 0x40000000000000L, active1, 0L);
-			case 68:
-			case 100:
-				if ((active1 & 0x1000L) != 0L)
-					return jjStartNfaWithStates_0(3, 76, 37);
-				break;
-			case 69:
-			case 101:
-				if ((active0 & 0x8000000000L) != 0L)
-					return jjStartNfaWithStates_0(3, 39, 37);
-				return jjMoveStringLiteralDfa4_0(active0, 0x800100003040000L, active1, 0x400L);
-			case 72:
-			case 104:
-				return jjMoveStringLiteralDfa4_0(active0, 0x4000000L, active1, 0L);
-			case 73:
-			case 105:
-				return jjMoveStringLiteralDfa4_0(active0, 0x200080000000000L, active1, 0x800L);
-			case 76:
-			case 108:
-				if ((active0 & 0x10000000L) != 0L)
-					return jjStartNfaWithStates_0(3, 28, 37);
-				else if ((active0 & 0x2000000000L) != 0L)
-					return jjStartNfaWithStates_0(3, 37, 37);
-				return jjMoveStringLiteralDfa4_0(active0, 0L, active1, 0x4L);
-			case 77:
-			case 109:
-				if ((active0 & 0x200000L) != 0L)
-					return jjStartNfaWithStates_0(3, 21, 37);
-				break;
-			case 78:
-			case 110:
-				if ((active0 & 0x20000000L) != 0L)
-					return jjStartNfaWithStates_0(3, 29, 37);
-				else if ((active1 & 0x20000L) != 0L)
-					return jjStartNfaWithStates_0(3, 81, 37);
-				else if ((active1 & 0x40000L) != 0L){
-					jjmatchedKind = 82;
-					jjmatchedPos = 3;
-				}
-				return jjMoveStringLiteralDfa4_0(active0, 0x88000080000000L, active1, 0x8a000L);
-			case 79:
-			case 111:
-				return jjMoveStringLiteralDfa4_0(active0, 0L, active1, 0x20L);
-			case 82:
-			case 114:
-				return jjMoveStringLiteralDfa4_0(active0, 0xe000000100000000L, active1, 0x8L);
-			case 83:
-			case 115:
-				if ((active1 & 0x10000L) != 0L)
-					return jjStartNfaWithStates_0(3, 80, 37);
-				return jjMoveStringLiteralDfa4_0(active0, 0x20000000000L, active1, 0L);
-			case 84:
-			case 116:
-				if ((active0 & 0x8000000L) != 0L)
-					return jjStartNfaWithStates_0(3, 27, 37);
-				else if ((active1 & 0x4000L) != 0L)
-					return jjStartNfaWithStates_0(3, 78, 37);
-				return jjMoveStringLiteralDfa4_0(active0, 0x420000000000000L, active1, 0x1L);
-			case 85:
-			case 117:
-				return jjMoveStringLiteralDfa4_0(active0, 0x40000800000L, active1, 0L);
-			case 87:
-			case 119:
-				return jjMoveStringLiteralDfa4_0(active0, 0x4000000000L, active1, 0L);
-			case 89:
-			case 121:
-				return jjMoveStringLiteralDfa4_0(active0, 0x100000000000000L, active1, 0L);
-			default:
-				break;
-		}
-		return jjStartNfa_0(2, active0, active1);
-	}
-
-	private int jjMoveStringLiteralDfa4_0(long old0, long active0, long old1, long active1){
-		if (((active0 &= old0) | (active1 &= old1)) == 0L)
-			return jjStartNfa_0(2, old0, old1);
-		try{
-			curChar = input_stream.readChar();
-		}catch(java.io.IOException e){
-			jjStopStringLiteralDfa_0(3, active0, active1);
-			return 4;
-		}
-		switch(curChar){
-			case 48:
-				if ((active1 & 0x80L) != 0L)
-					return jjStartNfaWithStates_0(4, 71, 21);
-				break;
-			case 50:
-				if ((active1 & 0x80000L) != 0L)
-					return jjStartNfaWithStates_0(4, 83, 21);
-				break;
-			case 65:
-			case 97:
-				return jjMoveStringLiteralDfa5_0(active0, 0x400000000000000L, active1, 0x801L);
-			case 67:
-			case 99:
-				return jjMoveStringLiteralDfa5_0(active0, 0x40000L, active1, 0x8000L);
-			case 68:
-			case 100:
-				if ((active1 & 0x2000L) != 0L)
-					return jjStartNfaWithStates_0(4, 77, 37);
-				return jjMoveStringLiteralDfa5_0(active0, 0xe000000000000000L, active1, 0L);
-			case 69:
-			case 101:
-				if ((active0 & 0x100000000L) != 0L)
-					return jjStartNfaWithStates_0(4, 32, 37);
-				return jjMoveStringLiteralDfa5_0(active0, 0x4000000000L, active1, 0x8L);
-			case 71:
-			case 103:
-				if ((active0 & 0x80000000L) != 0L)
-					return jjStartNfaWithStates_0(4, 31, 37);
-				return jjMoveStringLiteralDfa5_0(active0, 0x100000000000000L, active1, 0L);
-			case 73:
-			case 105:
-				return jjMoveStringLiteralDfa5_0(active0, 0L, active1, 0x4L);
-			case 76:
-			case 108:
-				return jjMoveStringLiteralDfa5_0(active0, 0x40000000000000L, active1, 0L);
-			case 78:
-			case 110:
-				return jjMoveStringLiteralDfa5_0(active0, 0x80000000000L, active1, 0L);
-			case 79:
-			case 111:
-				return jjMoveStringLiteralDfa5_0(active0, 0x200000000000000L, active1, 0L);
-			case 80:
-			case 112:
-				return jjMoveStringLiteralDfa5_0(active0, 0x40000000000L, active1, 0L);
-			case 82:
-			case 114:
-				if ((active0 & 0x1000000L) != 0L)
-					return jjStartNfaWithStates_0(4, 24, 37);
-				else if ((active0 & 0x2000000L) != 0L)
-					return jjStartNfaWithStates_0(4, 25, 37);
-				else if ((active1 & 0x20L) != 0L)
-					return jjStartNfaWithStates_0(4, 69, 37);
-				else if ((active1 & 0x400L) != 0L)
-					return jjStartNfaWithStates_0(4, 74, 37);
-				return jjMoveStringLiteralDfa5_0(active0, 0x820100000800000L, active1, 0L);
-			case 84:
-			case 116:
-				if ((active0 & 0x4000000L) != 0L)
-					return jjStartNfaWithStates_0(4, 26, 37);
-				else if ((active0 & 0x8000000000000L) != 0L)
-					return jjStartNfaWithStates_0(4, 51, 37);
-				else if ((active0 & 0x80000000000000L) != 0L)
-					return jjStartNfaWithStates_0(4, 55, 37);
-				return jjMoveStringLiteralDfa5_0(active0, 0x20000000000L, active1, 0L);
-			default:
-				break;
-		}
-		return jjStartNfa_0(3, active0, active1);
-	}
-
-	private int jjMoveStringLiteralDfa5_0(long old0, long active0, long old1, long active1){
-		if (((active0 &= old0) | (active1 &= old1)) == 0L)
-			return jjStartNfa_0(3, old0, old1);
-		try{
-			curChar = input_stream.readChar();
-		}catch(java.io.IOException e){
-			jjStopStringLiteralDfa_0(4, active0, active1);
-			return 5;
-		}
-		switch(curChar){
-			case 32:
-				return jjMoveStringLiteralDfa6_0(active0, 0x140000000000L, active1, 0L);
-			case 49:
-				if ((active0 & 0x2000000000000000L) != 0L)
-					return jjStartNfaWithStates_0(5, 61, 21);
-				break;
-			case 50:
-				if ((active0 & 0x4000000000000000L) != 0L)
-					return jjStartNfaWithStates_0(5, 62, 21);
-				break;
-			case 65:
-			case 97:
-				return jjMoveStringLiteralDfa6_0(active0, 0x800000L, active1, 0x8000L);
-			case 69:
-			case 101:
-				if ((active0 & 0x40000000000000L) != 0L)
-					return jjStartNfaWithStates_0(5, 54, 37);
-				return jjMoveStringLiteralDfa6_0(active0, 0x4000000000L, active1, 0x8L);
-			case 71:
-			case 103:
-				if ((active0 & 0x80000000000L) != 0L)
-					return jjStartNfaWithStates_0(5, 43, 37);
-				break;
-			case 73:
-			case 105:
-				return jjMoveStringLiteralDfa6_0(active0, 0x400000000000000L, active1, 0L);
-			case 78:
-			case 110:
-				if ((active0 & 0x200000000000000L) != 0L)
-					return jjStartNfaWithStates_0(5, 57, 37);
-				return jjMoveStringLiteralDfa6_0(active0, 0L, active1, 0x805L);
-			case 79:
-			case 111:
-				return jjMoveStringLiteralDfa6_0(active0, 0x120000000000000L, active1, 0L);
-			case 83:
-			case 115:
-				if ((active0 & 0x20000000000L) != 0L)
-					return jjStartNfaWithStates_0(5, 41, 37);
-				return jjMoveStringLiteralDfa6_0(active0, 0x8800000000000000L, active1, 0L);
-			case 84:
-			case 116:
-				if ((active0 & 0x40000L) != 0L)
-					return jjStartNfaWithStates_0(5, 18, 37);
-				break;
-			default:
-				break;
-		}
-		return jjStartNfa_0(4, active0, active1);
-	}
-
-	private int jjMoveStringLiteralDfa6_0(long old0, long active0, long old1, long active1){
-		if (((active0 &= old0) | (active1 &= old1)) == 0L)
-			return jjStartNfa_0(4, old0, old1);
-		try{
-			curChar = input_stream.readChar();
-		}catch(java.io.IOException e){
-			jjStopStringLiteralDfa_0(5, active0, active1);
-			return 6;
-		}
-		switch(curChar){
-			case 66:
-			case 98:
-				return jjMoveStringLiteralDfa7_0(active0, 0x140000000000L, active1, 0L);
-			case 67:
-			case 99:
-				return jjMoveStringLiteralDfa7_0(active0, 0L, active1, 0x1L);
-			case 69:
-			case 101:
-				return jjMoveStringLiteralDfa7_0(active0, 0x800000000000000L, active1, 0L);
-			case 71:
-			case 103:
-				if ((active1 & 0x4L) != 0L)
-					return jjStartNfaWithStates_0(6, 66, 37);
-				break;
-			case 73:
-			case 105:
-				return jjMoveStringLiteralDfa7_0(active0, 0x20000000000000L, active1, 0L);
-			case 76:
-			case 108:
-				if ((active0 & 0x800000L) != 0L)
-					return jjStartNfaWithStates_0(6, 23, 37);
-				break;
-			case 78:
-			case 110:
-				if ((active0 & 0x4000000000L) != 0L)
-					return jjStartNfaWithStates_0(6, 38, 37);
-				else if ((active0 & 0x100000000000000L) != 0L)
-					return jjStartNfaWithStates_0(6, 56, 37);
-				return jjMoveStringLiteralDfa7_0(active0, 0x400000000000000L, active1, 0L);
-			case 83:
-			case 115:
-				if ((active1 & 0x8L) != 0L)
-					return jjStartNfaWithStates_0(6, 67, 37);
-				else if ((active1 & 0x800L) != 0L)
-					return jjStartNfaWithStates_0(6, 75, 37);
-				break;
-			case 84:
-			case 116:
-				return jjMoveStringLiteralDfa7_0(active0, 0L, active1, 0x8000L);
-			case 89:
-			case 121:
-				return jjMoveStringLiteralDfa7_0(active0, 0x8000000000000000L, active1, 0L);
-			default:
-				break;
-		}
-		return jjStartNfa_0(5, active0, active1);
-	}
+public class ADQLParserTokenManager implements ADQLParserConstants
+{
 
-	private int jjMoveStringLiteralDfa7_0(long old0, long active0, long old1, long active1){
-		if (((active0 &= old0) | (active1 &= old1)) == 0L)
-			return jjStartNfa_0(5, old0, old1);
-		try{
-			curChar = input_stream.readChar();
-		}catch(java.io.IOException e){
-			jjStopStringLiteralDfa_0(6, active0, active1);
-			return 7;
-		}
-		switch(curChar){
-			case 67:
-			case 99:
-				return jjMoveStringLiteralDfa8_0(active0, 0x800000000000000L, active1, 0L);
-			case 68:
-			case 100:
-				if ((active0 & 0x20000000000000L) != 0L)
-					return jjStartNfaWithStates_0(7, 53, 37);
-				break;
-			case 69:
-			case 101:
-				if ((active1 & 0x1L) != 0L)
-					return jjStartNfaWithStates_0(7, 64, 37);
-				else if ((active1 & 0x8000L) != 0L)
-					return jjStartNfaWithStates_0(7, 79, 37);
-				break;
-			case 83:
-			case 115:
-				if ((active0 & 0x400000000000000L) != 0L)
-					return jjStartNfaWithStates_0(7, 58, 37);
-				else if ((active0 & 0x8000000000000000L) != 0L)
-					return jjStartNfaWithStates_0(7, 63, 37);
-				break;
-			case 89:
-			case 121:
-				if ((active0 & 0x40000000000L) != 0L)
-					return jjStopAtPos(7, 42);
-				else if ((active0 & 0x100000000000L) != 0L)
-					return jjStopAtPos(7, 44);
-				break;
-			default:
-				break;
-		}
-		return jjStartNfa_0(6, active0, active1);
-	}
-
-	private int jjMoveStringLiteralDfa8_0(long old0, long active0, long old1, long active1){
-		if (((active0 &= old0) | (active1 &= old1)) == 0L)
-			return jjStartNfa_0(6, old0, old1);
-		try{
-			curChar = input_stream.readChar();
-		}catch(java.io.IOException e){
-			jjStopStringLiteralDfa_0(7, active0, 0L);
-			return 8;
-		}
-		switch(curChar){
-			case 84:
-			case 116:
-				return jjMoveStringLiteralDfa9_0(active0, 0x800000000000000L);
-			default:
-				break;
-		}
-		return jjStartNfa_0(7, active0, 0L);
-	}
-
-	private int jjMoveStringLiteralDfa9_0(long old0, long active0){
-		if (((active0 &= old0)) == 0L)
-			return jjStartNfa_0(7, old0, 0L);
-		try{
-			curChar = input_stream.readChar();
-		}catch(java.io.IOException e){
-			jjStopStringLiteralDfa_0(8, active0, 0L);
-			return 9;
-		}
-		switch(curChar){
-			case 83:
-			case 115:
-				if ((active0 & 0x800000000000000L) != 0L)
-					return jjStartNfaWithStates_0(9, 59, 37);
-				break;
-			default:
-				break;
-		}
-		return jjStartNfa_0(8, active0, 0L);
-	}
-
-	private int jjStartNfaWithStates_0(int pos, int kind, int state){
-		jjmatchedKind = kind;
-		jjmatchedPos = pos;
-		try{
-			curChar = input_stream.readChar();
-		}catch(java.io.IOException e){
-			return pos + 1;
-		}
-		return jjMoveNfa_0(state, pos + 1);
-	}
-
-	private int jjMoveNfa_0(int startState, int curPos){
-		int startsAt = 0;
-		jjnewStateCnt = 37;
-		int i = 1;
-		jjstateSet[0] = startState;
-		int kind = 0x7fffffff;
-		for(;;){
-			if (++jjround == 0x7fffffff)
-				ReInitRounds();
-			if (curChar < 64){
-				long l = 1L << curChar;
-				do{
-					switch(jjstateSet[--i]){
-						case 12:
-						case 21:
-							if ((0x3ff000000000000L & l) == 0L)
-								break;
-							if (kind > 97)
-								kind = 97;
-							jjCheckNAdd(21);
-							break;
-						case 38:
-							if ((0x3ff000000000000L & l) != 0L){
-								if (kind > 100)
-									kind = 100;
-								jjCheckNAdd(27);
-							}
-							if ((0x3ff000000000000L & l) != 0L)
-								jjCheckNAddTwoStates(23, 24);
-							break;
-						case 37:
-							if ((0x3ff000000000000L & l) == 0L)
-								break;
-							if (kind > 97)
-								kind = 97;
-							jjCheckNAdd(21);
-							break;
-						case 0:
-							if ((0x3ff000000000000L & l) != 0L){
-								if (kind > 101)
-									kind = 101;
-								jjCheckNAddStates(0, 6);
-							}else if ((0x100002600L & l) != 0L){
-								if (kind > 1)
-									kind = 1;
-							}else if (curChar == 46)
-								jjCheckNAddTwoStates(23, 27);
-							else if (curChar == 45)
-								jjCheckNAdd(19);
-							else if (curChar == 33)
-								jjstateSet[jjnewStateCnt++] = 5;
-							else if (curChar == 60)
-								jjstateSet[jjnewStateCnt++] = 3;
-							if (curChar == 13)
-								jjstateSet[jjnewStateCnt++] = 1;
-							break;
-						case 11:
-							if ((0x3ff000000000000L & l) == 0L)
-								break;
-							if (kind > 97)
-								kind = 97;
-							jjCheckNAdd(21);
-							break;
-						case 16:
-							if ((0x3ff000000000000L & l) == 0L)
-								break;
-							if (kind > 97)
-								kind = 97;
-							jjCheckNAdd(21);
-							break;
-						case 13:
-							if ((0x3ff000000000000L & l) == 0L)
-								break;
-							if (kind > 97)
-								kind = 97;
-							jjCheckNAdd(21);
-							break;
-						case 10:
-							if ((0x3ff000000000000L & l) == 0L)
-								break;
-							if (kind > 97)
-								kind = 97;
-							jjCheckNAdd(21);
-							break;
-						case 1:
-							if (curChar == 10 && kind > 1)
-								kind = 1;
-							break;
-						case 2:
-							if (curChar == 13)
-								jjstateSet[jjnewStateCnt++] = 1;
-							break;
-						case 3:
-							if (curChar == 62)
-								kind = 13;
-							break;
-						case 4:
-							if (curChar == 60)
-								jjstateSet[jjnewStateCnt++] = 3;
-							break;
-						case 5:
-							if (curChar == 61)
-								kind = 13;
-							break;
-						case 6:
-							if (curChar == 33)
-								jjstateSet[jjnewStateCnt++] = 5;
-							break;
-						case 18:
-							if (curChar == 45)
-								jjCheckNAdd(19);
-							break;
-						case 19:
-							if (curChar != 45)
-								break;
-							if (kind > 88)
-								kind = 88;
-							jjCheckNAdd(19);
-							break;
-						case 22:
-							if (curChar == 46)
-								jjCheckNAddTwoStates(23, 27);
-							break;
-						case 23:
-							if ((0x3ff000000000000L & l) != 0L)
-								jjCheckNAddTwoStates(23, 24);
-							break;
-						case 25:
-							if ((0x280000000000L & l) != 0L)
-								jjCheckNAdd(26);
-							break;
-						case 26:
-							if ((0x3ff000000000000L & l) == 0L)
-								break;
-							if (kind > 99)
-								kind = 99;
-							jjCheckNAdd(26);
-							break;
-						case 27:
-							if ((0x3ff000000000000L & l) == 0L)
-								break;
-							if (kind > 100)
-								kind = 100;
-							jjCheckNAdd(27);
-							break;
-						case 28:
-							if ((0x3ff000000000000L & l) == 0L)
-								break;
-							if (kind > 101)
-								kind = 101;
-							jjCheckNAddStates(0, 6);
-							break;
-						case 29:
-							if ((0x3ff000000000000L & l) != 0L)
-								jjCheckNAddTwoStates(29, 24);
-							break;
-						case 30:
-							if ((0x3ff000000000000L & l) != 0L)
-								jjCheckNAddTwoStates(30, 31);
-							break;
-						case 31:
-							if (curChar == 46)
-								jjCheckNAddTwoStates(32, 24);
-							break;
-						case 32:
-							if ((0x3ff000000000000L & l) != 0L)
-								jjCheckNAddTwoStates(32, 24);
-							break;
-						case 33:
-							if ((0x3ff000000000000L & l) != 0L)
-								jjCheckNAddTwoStates(33, 34);
-							break;
-						case 34:
-							if (curChar != 46)
-								break;
-							if (kind > 100)
-								kind = 100;
-							jjCheckNAdd(35);
-							break;
-						case 35:
-							if ((0x3ff000000000000L & l) == 0L)
-								break;
-							if (kind > 100)
-								kind = 100;
-							jjCheckNAdd(35);
-							break;
-						case 36:
-							if ((0x3ff000000000000L & l) == 0L)
-								break;
-							if (kind > 101)
-								kind = 101;
-							jjCheckNAdd(36);
-							break;
-						default:
-							break;
-					}
-				}while(i != startsAt);
-			}else if (curChar < 128){
-				long l = 1L << (curChar & 077);
-				do{
-					switch(jjstateSet[--i]){
-						case 12:
-							if ((0x7fffffe87fffffeL & l) != 0L){
-								if (kind > 97)
-									kind = 97;
-								jjCheckNAdd(21);
-							}
-							if ((0x7fffffe07fffffeL & l) != 0L){
-								if (kind > 97)
-									kind = 97;
-								jjCheckNAddTwoStates(20, 21);
-							}
-							if ((0x8000000080000L & l) != 0L)
-								jjstateSet[jjnewStateCnt++] = 11;
-							break;
-						case 37:
-							if ((0x7fffffe87fffffeL & l) != 0L){
-								if (kind > 97)
-									kind = 97;
-								jjCheckNAdd(21);
-							}
-							if ((0x7fffffe07fffffeL & l) != 0L){
-								if (kind > 97)
-									kind = 97;
-								jjCheckNAddTwoStates(20, 21);
-							}
-							break;
-						case 0:
-							if ((0x7fffffe07fffffeL & l) != 0L){
-								if (kind > 97)
-									kind = 97;
-								jjCheckNAddTwoStates(20, 21);
-							}
-							if ((0x200000002L & l) != 0L)
-								jjstateSet[jjnewStateCnt++] = 16;
-							else if ((0x1000000010L & l) != 0L)
-								jjstateSet[jjnewStateCnt++] = 13;
-							break;
-						case 11:
-							if ((0x7fffffe87fffffeL & l) != 0L){
-								if (kind > 97)
-									kind = 97;
-								jjCheckNAdd(21);
-							}
-							if ((0x7fffffe07fffffeL & l) != 0L){
-								if (kind > 97)
-									kind = 97;
-								jjCheckNAddTwoStates(20, 21);
-							}
-							if ((0x10000000100000L & l) != 0L)
-								jjstateSet[jjnewStateCnt++] = 10;
-							break;
-						case 16:
-							if ((0x7fffffe87fffffeL & l) != 0L){
-								if (kind > 97)
-									kind = 97;
-								jjCheckNAdd(21);
-							}
-							if ((0x7fffffe07fffffeL & l) != 0L){
-								if (kind > 97)
-									kind = 97;
-								jjCheckNAddTwoStates(20, 21);
-							}
-							if ((0x100000001000L & l) != 0L)
-								jjstateSet[jjnewStateCnt++] = 15;
-							break;
-						case 13:
-							if ((0x7fffffe87fffffeL & l) != 0L){
-								if (kind > 97)
-									kind = 97;
-								jjCheckNAdd(21);
-							}
-							if ((0x7fffffe07fffffeL & l) != 0L){
-								if (kind > 97)
-									kind = 97;
-								jjCheckNAddTwoStates(20, 21);
-							}
-							if ((0x20000000200L & l) != 0L)
-								jjstateSet[jjnewStateCnt++] = 12;
-							break;
-						case 10:
-							if ((0x7fffffe87fffffeL & l) != 0L){
-								if (kind > 97)
-									kind = 97;
-								jjCheckNAdd(21);
-							}
-							if ((0x7fffffe07fffffeL & l) != 0L){
-								if (kind > 97)
-									kind = 97;
-								jjCheckNAddTwoStates(20, 21);
-							}
-							if ((0x20000000200L & l) != 0L)
-								jjstateSet[jjnewStateCnt++] = 9;
-							break;
-						case 7:
-							if ((0x10000000100000L & l) != 0L && kind > 19)
-								kind = 19;
-							break;
-						case 8:
-							if ((0x800000008L & l) != 0L)
-								jjstateSet[jjnewStateCnt++] = 7;
-							break;
-						case 9:
-							if ((0x400000004000L & l) != 0L)
-								jjstateSet[jjnewStateCnt++] = 8;
-							break;
-						case 14:
-							if ((0x1000000010L & l) != 0L)
-								jjstateSet[jjnewStateCnt++] = 13;
-							break;
-						case 15:
-							if ((0x100000001000L & l) != 0L && kind > 19)
-								kind = 19;
-							break;
-						case 17:
-							if ((0x200000002L & l) != 0L)
-								jjstateSet[jjnewStateCnt++] = 16;
-							break;
-						case 20:
-							if ((0x7fffffe07fffffeL & l) == 0L)
-								break;
-							if (kind > 97)
-								kind = 97;
-							jjCheckNAddTwoStates(20, 21);
-							break;
-						case 21:
-							if ((0x7fffffe87fffffeL & l) == 0L)
-								break;
-							if (kind > 97)
-								kind = 97;
-							jjCheckNAdd(21);
-							break;
-						case 24:
-							if ((0x2000000020L & l) != 0L)
-								jjAddStates(7, 8);
-							break;
-						default:
-							break;
-					}
-				}while(i != startsAt);
-			}else{
-				int i2 = (curChar & 0xff) >> 6;
-				long l2 = 1L << (curChar & 077);
-				do{
-					switch(jjstateSet[--i]){
-						default:
-							break;
-					}
-				}while(i != startsAt);
-			}
-			if (kind != 0x7fffffff){
-				jjmatchedKind = kind;
-				jjmatchedPos = curPos;
-				kind = 0x7fffffff;
-			}
-			++curPos;
-			if ((i = jjnewStateCnt) == (startsAt = 37 - (jjnewStateCnt = startsAt)))
-				return curPos;
-			try{
-				curChar = input_stream.readChar();
-			}catch(java.io.IOException e){
-				return curPos;
-			}
-		}
-	}
-
-	private final int jjStopStringLiteralDfa_2(int pos, long active0, long active1){
-		switch(pos){
-			default:
-				return -1;
-		}
-	}
-
-	private final int jjStartNfa_2(int pos, long active0, long active1){
-		return jjMoveNfa_2(jjStopStringLiteralDfa_2(pos, active0, active1), pos + 1);
-	}
-
-	private int jjMoveStringLiteralDfa0_2(){
-		switch(curChar){
-			case 39:
-				return jjStartNfaWithStates_2(0, 93, 1);
-			default:
-				return jjMoveNfa_2(0, 0);
-		}
-	}
-
-	private int jjStartNfaWithStates_2(int pos, int kind, int state){
-		jjmatchedKind = kind;
-		jjmatchedPos = pos;
-		try{
-			curChar = input_stream.readChar();
-		}catch(java.io.IOException e){
-			return pos + 1;
-		}
-		return jjMoveNfa_2(state, pos + 1);
-	}
-
-	private int jjMoveNfa_2(int startState, int curPos){
-		int startsAt = 0;
-		jjnewStateCnt = 3;
-		int i = 1;
-		jjstateSet[0] = startState;
-		int kind = 0x7fffffff;
-		for(;;){
-			if (++jjround == 0x7fffffff)
-				ReInitRounds();
-			if (curChar < 64){
-				long l = 1L << curChar;
-				do{
-					switch(jjstateSet[--i]){
-						case 0:
-							if ((0xffffff7fffffffffL & l) != 0L){
-								if (kind > 92)
-									kind = 92;
-							}else if (curChar == 39)
-								jjstateSet[jjnewStateCnt++] = 1;
-							break;
-						case 1:
-							if (curChar == 39 && kind > 92)
-								kind = 92;
-							break;
-						case 2:
-							if (curChar == 39)
-								jjstateSet[jjnewStateCnt++] = 1;
-							break;
-						default:
-							break;
-					}
-				}while(i != startsAt);
-			}else if (curChar < 128){
-				long l = 1L << (curChar & 077);
-				do{
-					switch(jjstateSet[--i]){
-						case 0:
-							kind = 92;
-							break;
-						default:
-							break;
-					}
-				}while(i != startsAt);
-			}else{
-				int i2 = (curChar & 0xff) >> 6;
-				long l2 = 1L << (curChar & 077);
-				do{
-					switch(jjstateSet[--i]){
-						case 0:
-							if ((jjbitVec0[i2] & l2) != 0L && kind > 92)
-								kind = 92;
-							break;
-						default:
-							break;
-					}
-				}while(i != startsAt);
-			}
-			if (kind != 0x7fffffff){
-				jjmatchedKind = kind;
-				jjmatchedPos = curPos;
-				kind = 0x7fffffff;
-			}
-			++curPos;
-			if ((i = jjnewStateCnt) == (startsAt = 3 - (jjnewStateCnt = startsAt)))
-				return curPos;
-			try{
-				curChar = input_stream.readChar();
-			}catch(java.io.IOException e){
-				return curPos;
-			}
-		}
-	}
-
-	private int jjMoveStringLiteralDfa0_1(){
-		return jjMoveNfa_1(0, 0);
-	}
-
-	private int jjMoveNfa_1(int startState, int curPos){
-		int startsAt = 0;
-		jjnewStateCnt = 3;
-		int i = 1;
-		jjstateSet[0] = startState;
-		int kind = 0x7fffffff;
-		for(;;){
-			if (++jjround == 0x7fffffff)
-				ReInitRounds();
-			if (curChar < 64){
-				long l = 1L << curChar;
-				do{
-					switch(jjstateSet[--i]){
-						case 0:
-							if ((0x2400L & l) != 0L){
-								if (kind > 89)
-									kind = 89;
-							}
-							if (curChar == 13)
-								jjstateSet[jjnewStateCnt++] = 1;
-							break;
-						case 1:
-							if (curChar == 10 && kind > 89)
-								kind = 89;
-							break;
-						case 2:
-							if (curChar == 13)
-								jjstateSet[jjnewStateCnt++] = 1;
-							break;
-						default:
-							break;
-					}
-				}while(i != startsAt);
-			}else if (curChar < 128){
-				long l = 1L << (curChar & 077);
-				do{
-					switch(jjstateSet[--i]){
-						default:
-							break;
-					}
-				}while(i != startsAt);
-			}else{
-				int i2 = (curChar & 0xff) >> 6;
-				long l2 = 1L << (curChar & 077);
-				do{
-					switch(jjstateSet[--i]){
-						default:
-							break;
-					}
-				}while(i != startsAt);
-			}
-			if (kind != 0x7fffffff){
-				jjmatchedKind = kind;
-				jjmatchedPos = curPos;
-				kind = 0x7fffffff;
-			}
-			++curPos;
-			if ((i = jjnewStateCnt) == (startsAt = 3 - (jjnewStateCnt = startsAt)))
-				return curPos;
-			try{
-				curChar = input_stream.readChar();
-			}catch(java.io.IOException e){
-				return curPos;
-			}
-		}
-	}
-
-	static final int[] jjnextStates = {29,30,31,24,33,34,36,25,26,};
-
-	/** Token literal values. */
-	public static final String[] jjstrLiteralImages = {"",null,"\50","\51","\56","\54","\73","\174\174","\53","\55","\52","\57","\75",null,"\74","\74\75","\76","\76\75",null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,};
-
-	/** Lexer state names. */
-	public static final String[] lexStateNames = {"DEFAULT","WithinComment","WithinString","WithinDelimitedId",};
-
-	/** Lex State array. */
-	public static final int[] jjnewLexState = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,1,0,-1,2,-1,0,3,-1,0,-1,-1,-1,-1,-1,-1,};
-	static final long[] jjtoToken = {0xfffffffffffffffdL,0x3b20ffffffL,};
-	static final long[] jjtoSkip = {0x2L,0x2000000L,};
-	static final long[] jjtoMore = {0x0L,0xdd000000L,};
-	protected SimpleCharStream input_stream;
-	private final int[] jjrounds = new int[37];
-	private final int[] jjstateSet = new int[74];
-	protected char curChar;
-
-	/** Constructor. */
-	public ADQLParserTokenManager(SimpleCharStream stream){
-		if (SimpleCharStream.staticFlag)
-			throw new Error("ERROR: Cannot use a static CharStream class with a non-static lexical analyzer.");
-		input_stream = stream;
-	}
+  /** Debug output. */
+  public  java.io.PrintStream debugStream = System.out;
+  /** Set debug output. */
+  public  void setDebugStream(java.io.PrintStream ds) { debugStream = ds; }
+private final int jjStopStringLiteralDfa_3(int pos, long active0, long active1)
+{
+   switch (pos)
+   {
+      default :
+         return -1;
+   }
+}
+private final int jjStartNfa_3(int pos, long active0, long active1)
+{
+   return jjMoveNfa_3(jjStopStringLiteralDfa_3(pos, active0, active1), pos + 1);
+}
+private int jjStopAtPos(int pos, int kind)
+{
+   jjmatchedKind = kind;
+   jjmatchedPos = pos;
+   return pos + 1;
+}
+private int jjMoveStringLiteralDfa0_3()
+{
+   switch(curChar)
+   {
+      case 34:
+         return jjStartNfaWithStates_3(0, 96, 1);
+      default :
+         return jjMoveNfa_3(0, 0);
+   }
+}
+private int jjStartNfaWithStates_3(int pos, int kind, int state)
+{
+   jjmatchedKind = kind;
+   jjmatchedPos = pos;
+   try { curChar = input_stream.readChar(); }
+   catch(java.io.IOException e) { return pos + 1; }
+   return jjMoveNfa_3(state, pos + 1);
+}
+static final long[] jjbitVec0 = {
+   0x0L, 0x0L, 0xffffffffffffffffL, 0xffffffffffffffffL
+};
+private int jjMoveNfa_3(int startState, int curPos)
+{
+   int startsAt = 0;
+   jjnewStateCnt = 3;
+   int i = 1;
+   jjstateSet[0] = startState;
+   int kind = 0x7fffffff;
+   for (;;)
+   {
+      if (++jjround == 0x7fffffff)
+         ReInitRounds();
+      if (curChar < 64)
+      {
+         long l = 1L << curChar;
+         do
+         {
+            switch(jjstateSet[--i])
+            {
+               case 0:
+                  if ((0xfffffffbffffffffL & l) != 0L)
+                  {
+                     if (kind > 95)
+                        kind = 95;
+                  }
+                  else if (curChar == 34)
+                     jjstateSet[jjnewStateCnt++] = 1;
+                  break;
+               case 1:
+                  if (curChar == 34 && kind > 95)
+                     kind = 95;
+                  break;
+               case 2:
+                  if (curChar == 34)
+                     jjstateSet[jjnewStateCnt++] = 1;
+                  break;
+               default : break;
+            }
+         } while(i != startsAt);
+      }
+      else if (curChar < 128)
+      {
+         long l = 1L << (curChar & 077);
+         do
+         {
+            switch(jjstateSet[--i])
+            {
+               case 0:
+                  kind = 95;
+                  break;
+               default : break;
+            }
+         } while(i != startsAt);
+      }
+      else
+      {
+         int i2 = (curChar & 0xff) >> 6;
+         long l2 = 1L << (curChar & 077);
+         do
+         {
+            switch(jjstateSet[--i])
+            {
+               case 0:
+                  if ((jjbitVec0[i2] & l2) != 0L && kind > 95)
+                     kind = 95;
+                  break;
+               default : break;
+            }
+         } while(i != startsAt);
+      }
+      if (kind != 0x7fffffff)
+      {
+         jjmatchedKind = kind;
+         jjmatchedPos = curPos;
+         kind = 0x7fffffff;
+      }
+      ++curPos;
+      if ((i = jjnewStateCnt) == (startsAt = 3 - (jjnewStateCnt = startsAt)))
+         return curPos;
+      try { curChar = input_stream.readChar(); }
+      catch(java.io.IOException e) { return curPos; }
+   }
+}
+private final int jjStopStringLiteralDfa_0(int pos, long active0, long active1)
+{
+   switch (pos)
+   {
+      case 0:
+         if ((active0 & 0xefff1ffdffb40000L) != 0L || (active1 & 0xf0fff4L) != 0L)
+         {
+            jjmatchedKind = 97;
+            return 37;
+         }
+         if ((active0 & 0x1000a00200400000L) != 0L || (active1 & 0xf0002L) != 0L)
+         {
+            jjmatchedKind = 97;
+            return 16;
+         }
+         if ((active0 & 0x10L) != 0L)
+            return 38;
+         if ((active0 & 0x400000000000L) != 0L || (active1 & 0x9L) != 0L)
+         {
+            jjmatchedKind = 97;
+            return 13;
+         }
+         if ((active0 & 0xc000L) != 0L)
+            return 3;
+         if ((active0 & 0x200L) != 0L)
+            return 19;
+         return -1;
+      case 1:
+         if ((active0 & 0xf7ffceebbeb40000L) != 0L || (active1 & 0xfdfdfeL) != 0L)
+         {
+            if (jjmatchedPos != 1)
+            {
+               jjmatchedKind = 97;
+               jjmatchedPos = 1;
+            }
+            return 37;
+         }
+         if ((active0 & 0x800311441400000L) != 0L || (active1 & 0x20200L) != 0L)
+            return 37;
+         if ((active1 & 0x1L) != 0L)
+         {
+            if (jjmatchedPos != 1)
+            {
+               jjmatchedKind = 97;
+               jjmatchedPos = 1;
+            }
+            return 12;
+         }
+         return -1;
+      case 2:
+         if ((active1 & 0x1L) != 0L)
+         {
+            if (jjmatchedPos != 2)
+            {
+               jjmatchedKind = 97;
+               jjmatchedPos = 2;
+            }
+            return 11;
+         }
+         if ((active0 & 0x17a00a00100000L) != 0L || (active1 & 0xf001d2L) != 0L)
+            return 37;
+         if ((active0 & 0xffe85ee1bfa40000L) != 0L || (active1 & 0xffc2cL) != 0L)
+         {
+            if (jjmatchedPos != 2)
+            {
+               jjmatchedKind = 97;
+               jjmatchedPos = 2;
+            }
+            return 37;
+         }
+         return -1;
+      case 3:
+         if ((active0 & 0xefe81e4187840000L) != 0L || (active1 & 0xac2cL) != 0L)
+         {
+            if (jjmatchedPos != 3)
+            {
+               jjmatchedKind = 97;
+               jjmatchedPos = 3;
+            }
+            return 37;
+         }
+         if ((active0 & 0x100040a038200000L) != 0L || (active1 & 0xf5000L) != 0L)
+            return 37;
+         if ((active1 & 0x80L) != 0L)
+         {
+            if (jjmatchedPos != 3)
+            {
+               jjmatchedKind = 97;
+               jjmatchedPos = 3;
+            }
+            return 21;
+         }
+         if ((active1 & 0x1L) != 0L)
+         {
+            if (jjmatchedPos != 3)
+            {
+               jjmatchedKind = 97;
+               jjmatchedPos = 3;
+            }
+            return 10;
+         }
+         return -1;
+      case 4:
+         if ((active0 & 0xef601e4000840000L) != 0L || (active1 & 0x880dL) != 0L)
+         {
+            jjmatchedKind = 97;
+            jjmatchedPos = 4;
+            return 37;
+         }
+         if ((active0 & 0x88000187000000L) != 0L || (active1 & 0x2420L) != 0L)
+            return 37;
+         if ((active1 & 0x80080L) != 0L)
+            return 21;
+         return -1;
+      case 5:
+         if ((active0 & 0x2400a0000040000L) != 0L)
+            return 37;
+         if ((active0 & 0x6000000000000000L) != 0L)
+            return 21;
+         if ((active0 & 0x8d20004000800000L) != 0L || (active1 & 0x880dL) != 0L)
+         {
+            jjmatchedKind = 97;
+            jjmatchedPos = 5;
+            return 37;
+         }
+         if ((active0 & 0x140000000000L) != 0L)
+         {
+            if (jjmatchedPos < 4)
+            {
+               jjmatchedKind = 97;
+               jjmatchedPos = 4;
+            }
+            return -1;
+         }
+         return -1;
+      case 6:
+         if ((active0 & 0x8c20000000000000L) != 0L || (active1 & 0x8001L) != 0L)
+         {
+            jjmatchedKind = 97;
+            jjmatchedPos = 6;
+            return 37;
+         }
+         if ((active0 & 0x100004000800000L) != 0L || (active1 & 0x80cL) != 0L)
+            return 37;
+         if ((active0 & 0x140000000000L) != 0L)
+         {
+            if (jjmatchedPos < 4)
+            {
+               jjmatchedKind = 97;
+               jjmatchedPos = 4;
+            }
+            return -1;
+         }
+         return -1;
+      case 7:
+         if ((active0 & 0x800000000000000L) != 0L)
+         {
+            jjmatchedKind = 97;
+            jjmatchedPos = 7;
+            return 37;
+         }
+         if ((active0 & 0x8420000000000000L) != 0L || (active1 & 0x8001L) != 0L)
+            return 37;
+         if ((active0 & 0x140000000000L) != 0L)
+         {
+            if (jjmatchedPos < 4)
+            {
+               jjmatchedKind = 97;
+               jjmatchedPos = 4;
+            }
+            return -1;
+         }
+         return -1;
+      case 8:
+         if ((active0 & 0x800000000000000L) != 0L)
+         {
+            jjmatchedKind = 97;
+            jjmatchedPos = 8;
+            return 37;
+         }
+         return -1;
+      default :
+         return -1;
+   }
+}
+private final int jjStartNfa_0(int pos, long active0, long active1)
+{
+   return jjMoveNfa_0(jjStopStringLiteralDfa_0(pos, active0, active1), pos + 1);
+}
+private int jjMoveStringLiteralDfa0_0()
+{
+   switch(curChar)
+   {
+      case 34:
+         return jjStopAtPos(0, 94);
+      case 39:
+         return jjStopAtPos(0, 91);
+      case 40:
+         return jjStopAtPos(0, 2);
+      case 41:
+         return jjStopAtPos(0, 3);
+      case 42:
+         return jjStopAtPos(0, 10);
+      case 43:
+         return jjStopAtPos(0, 8);
+      case 44:
+         return jjStopAtPos(0, 5);
+      case 45:
+         return jjStartNfaWithStates_0(0, 9, 19);
+      case 46:
+         return jjStartNfaWithStates_0(0, 4, 38);
+      case 47:
+         return jjStopAtPos(0, 11);
+      case 59:
+         return jjStopAtPos(0, 6);
+      case 60:
+         jjmatchedKind = 14;
+         return jjMoveStringLiteralDfa1_0(0x8000L, 0x0L);
+      case 61:
+         return jjStopAtPos(0, 12);
+      case 62:
+         jjmatchedKind = 16;
+         return jjMoveStringLiteralDfa1_0(0x20000L, 0x0L);
+      case 65:
+      case 97:
+         return jjMoveStringLiteralDfa1_0(0x1000a00200400000L, 0xf0002L);
+      case 66:
+      case 98:
+         return jjMoveStringLiteralDfa1_0(0x10004000000000L, 0x0L);
+      case 67:
+      case 99:
+         return jjMoveStringLiteralDfa1_0(0xe468000000000000L, 0x300004L);
+      case 68:
+      case 100:
+         return jjMoveStringLiteralDfa1_0(0x400000000000L, 0x9L);
+      case 69:
+      case 101:
+         return jjMoveStringLiteralDfa1_0(0x20000000000L, 0x10L);
+      case 70:
+      case 102:
+         return jjMoveStringLiteralDfa1_0(0x10200000L, 0x20L);
+      case 71:
+      case 103:
+         return jjMoveStringLiteralDfa1_0(0x40000000000L, 0x0L);
+      case 72:
+      case 104:
+         return jjMoveStringLiteralDfa1_0(0x80000000000L, 0x0L);
+      case 73:
+      case 105:
+         return jjMoveStringLiteralDfa1_0(0x800011001000000L, 0x0L);
+      case 74:
+      case 106:
+         return jjMoveStringLiteralDfa1_0(0x20000000L, 0x0L);
+      case 76:
+      case 108:
+         return jjMoveStringLiteralDfa1_0(0x8008000000L, 0xc0L);
+      case 77:
+      case 109:
+         return jjMoveStringLiteralDfa1_0(0x3000000000000L, 0x100L);
+      case 78:
+      case 110:
+         return jjMoveStringLiteralDfa1_0(0x2800800000L, 0x0L);
+      case 79:
+      case 111:
+         return jjMoveStringLiteralDfa1_0(0x100442000000L, 0x0L);
+      case 80:
+      case 112:
+         return jjMoveStringLiteralDfa1_0(0x180000000000000L, 0x600L);
+      case 82:
+      case 114:
+         return jjMoveStringLiteralDfa1_0(0x200000004000000L, 0x3800L);
+      case 83:
+      case 115:
+         return jjMoveStringLiteralDfa1_0(0x4000000040000L, 0x404000L);
+      case 84:
+      case 116:
+         return jjMoveStringLiteralDfa1_0(0x100000L, 0x808000L);
+      case 85:
+      case 117:
+         return jjMoveStringLiteralDfa1_0(0x80000000L, 0x0L);
+      case 87:
+      case 119:
+         return jjMoveStringLiteralDfa1_0(0x100000000L, 0x0L);
+      case 124:
+         return jjMoveStringLiteralDfa1_0(0x80L, 0x0L);
+      default :
+         return jjMoveNfa_0(0, 0);
+   }
+}
+private int jjMoveStringLiteralDfa1_0(long active0, long active1)
+{
+   try { curChar = input_stream.readChar(); }
+   catch(java.io.IOException e) {
+      jjStopStringLiteralDfa_0(0, active0, active1);
+      return 1;
+   }
+   switch(curChar)
+   {
+      case 61:
+         if ((active0 & 0x8000L) != 0L)
+            return jjStopAtPos(1, 15);
+         else if ((active0 & 0x20000L) != 0L)
+            return jjStopAtPos(1, 17);
+         break;
+      case 65:
+      case 97:
+         return jjMoveStringLiteralDfa2_0(active0, 0x1080000800000L, active1, 0x801800L);
+      case 66:
+      case 98:
+         return jjMoveStringLiteralDfa2_0(active0, 0L, active1, 0x2L);
+      case 67:
+      case 99:
+         return jjMoveStringLiteralDfa2_0(active0, 0L, active1, 0x10000L);
+      case 69:
+      case 101:
+         return jjMoveStringLiteralDfa2_0(active0, 0x220404008040000L, active1, 0xcL);
+      case 72:
+      case 104:
+         return jjMoveStringLiteralDfa2_0(active0, 0x100000000L, active1, 0L);
+      case 73:
+      case 105:
+         if ((active1 & 0x200L) != 0L)
+            return jjStartNfaWithStates_0(1, 73, 37);
+         return jjMoveStringLiteralDfa2_0(active0, 0x42008004000000L, active1, 0x400001L);
+      case 76:
+      case 108:
+         return jjMoveStringLiteralDfa2_0(active0, 0L, active1, 0x20L);
+      case 78:
+      case 110:
+         if ((active0 & 0x40000000L) != 0L)
+            return jjStartNfaWithStates_0(1, 30, 37);
+         else if ((active0 & 0x10000000000L) != 0L)
+         {
+            jjmatchedKind = 40;
+            jjmatchedPos = 1;
+         }
+         return jjMoveStringLiteralDfa2_0(active0, 0x800000201000000L, active1, 0L);
+      case 79:
+      case 111:
+         return jjMoveStringLiteralDfa2_0(active0, 0xe598000820100000L, active1, 0x3025c0L);
+      case 81:
+      case 113:
+         return jjMoveStringLiteralDfa2_0(active0, 0L, active1, 0x4000L);
+      case 82:
+      case 114:
+         if ((active0 & 0x400000000L) != 0L)
+         {
+            jjmatchedKind = 34;
+            jjmatchedPos = 1;
+         }
+         return jjMoveStringLiteralDfa2_0(active0, 0x1000140000200000L, active1, 0x8000L);
+      case 83:
+      case 115:
+         if ((active0 & 0x400000L) != 0L)
+         {
+            jjmatchedKind = 22;
+            jjmatchedPos = 1;
+         }
+         else if ((active0 & 0x1000000000L) != 0L)
+            return jjStartNfaWithStates_0(1, 36, 37);
+         return jjMoveStringLiteralDfa2_0(active0, 0x200080000000L, active1, 0x20000L);
+      case 84:
+      case 116:
+         return jjMoveStringLiteralDfa2_0(active0, 0L, active1, 0xc0000L);
+      case 85:
+      case 117:
+         return jjMoveStringLiteralDfa2_0(active0, 0x4002012000000L, active1, 0L);
+      case 86:
+      case 118:
+         return jjMoveStringLiteralDfa2_0(active0, 0x800000000000L, active1, 0L);
+      case 88:
+      case 120:
+         return jjMoveStringLiteralDfa2_0(active0, 0x20000000000L, active1, 0x10L);
+      case 124:
+         if ((active0 & 0x80L) != 0L)
+            return jjStopAtPos(1, 7);
+         break;
+      default :
+         break;
+   }
+   return jjStartNfa_0(0, active0, active1);
+}
+private int jjMoveStringLiteralDfa2_0(long old0, long active0, long old1, long active1)
+{
+   if (((active0 &= old0) | (active1 &= old1)) == 0L)
+      return jjStartNfa_0(0, old0, old1);
+   try { curChar = input_stream.readChar(); }
+   catch(java.io.IOException e) {
+      jjStopStringLiteralDfa_0(1, active0, active1);
+      return 2;
+   }
+   switch(curChar)
+   {
+      case 65:
+      case 97:
+         return jjMoveStringLiteralDfa3_0(active0, 0L, active1, 0xc0000L);
+      case 67:
+      case 99:
+         if ((active0 & 0x200000000000L) != 0L)
+            return jjStartNfaWithStates_0(2, 45, 37);
+         break;
+      case 68:
+      case 100:
+         if ((active0 & 0x200000000L) != 0L)
+            return jjStartNfaWithStates_0(2, 33, 37);
+         else if ((active1 & 0x100L) != 0L)
+            return jjStartNfaWithStates_0(2, 72, 37);
+         return jjMoveStringLiteralDfa3_0(active0, 0x100000000000L, active1, 0x800L);
+      case 69:
+      case 101:
+         return jjMoveStringLiteralDfa3_0(active0, 0x1000000100000000L, active1, 0L);
+      case 70:
+      case 102:
+         return jjMoveStringLiteralDfa3_0(active0, 0x8000000L, active1, 0L);
+      case 71:
+      case 103:
+         if ((active0 & 0x800000000000L) != 0L)
+            return jjStartNfaWithStates_0(2, 47, 37);
+         else if ((active1 & 0x40L) != 0L)
+         {
+            jjmatchedKind = 70;
+            jjmatchedPos = 2;
+         }
+         return jjMoveStringLiteralDfa3_0(active0, 0x200000004000000L, active1, 0x88L);
+      case 73:
+      case 105:
+         return jjMoveStringLiteralDfa3_0(active0, 0x800200a0000000L, active1, 0x20004L);
+      case 75:
+      case 107:
+         return jjMoveStringLiteralDfa3_0(active0, 0x8000000000L, active1, 0L);
+      case 76:
+      case 108:
+         return jjMoveStringLiteralDfa3_0(active0, 0x100002010040000L, active1, 0L);
+      case 77:
+      case 109:
+         if ((active0 & 0x4000000000000L) != 0L)
+            return jjStartNfaWithStates_0(2, 50, 37);
+         break;
+      case 78:
+      case 110:
+         if ((active0 & 0x2000000000000L) != 0L)
+            return jjStartNfaWithStates_0(2, 49, 37);
+         else if ((active1 & 0x400000L) != 0L)
+            return jjStartNfaWithStates_0(2, 86, 37);
+         else if ((active1 & 0x800000L) != 0L)
+            return jjStartNfaWithStates_0(2, 87, 37);
+         return jjMoveStringLiteralDfa3_0(active0, 0x420000001000000L, active1, 0x1000L);
+      case 79:
+      case 111:
+         return jjMoveStringLiteralDfa3_0(active0, 0xe000040000200000L, active1, 0x10020L);
+      case 80:
+      case 112:
+         if ((active0 & 0x100000L) != 0L)
+            return jjStartNfaWithStates_0(2, 20, 37);
+         else if ((active1 & 0x10L) != 0L)
+            return jjStartNfaWithStates_0(2, 68, 37);
+         break;
+      case 82:
+      case 114:
+         return jjMoveStringLiteralDfa3_0(active0, 0x40000000000000L, active1, 0x4000L);
+      case 83:
+      case 115:
+         if ((active1 & 0x2L) != 0L)
+            return jjStartNfaWithStates_0(2, 65, 37);
+         else if ((active1 & 0x100000L) != 0L)
+            return jjStartNfaWithStates_0(2, 84, 37);
+         return jjMoveStringLiteralDfa3_0(active0, 0x400000000000L, active1, 0x1L);
+      case 84:
+      case 116:
+         if ((active0 & 0x800000000L) != 0L)
+            return jjStartNfaWithStates_0(2, 35, 37);
+         else if ((active1 & 0x200000L) != 0L)
+            return jjStartNfaWithStates_0(2, 85, 37);
+         return jjMoveStringLiteralDfa3_0(active0, 0x800004002800000L, active1, 0L);
+      case 85:
+      case 117:
+         return jjMoveStringLiteralDfa3_0(active0, 0x8000000000000L, active1, 0xa000L);
+      case 86:
+      case 118:
+         return jjMoveStringLiteralDfa3_0(active0, 0x80000000000L, active1, 0L);
+      case 87:
+      case 119:
+         return jjMoveStringLiteralDfa3_0(active0, 0L, active1, 0x400L);
+      case 88:
+      case 120:
+         if ((active0 & 0x1000000000000L) != 0L)
+            return jjStartNfaWithStates_0(2, 48, 37);
+         else if ((active0 & 0x10000000000000L) != 0L)
+            return jjStartNfaWithStates_0(2, 52, 37);
+         break;
+      default :
+         break;
+   }
+   return jjStartNfa_0(1, active0, active1);
+}
+private int jjMoveStringLiteralDfa3_0(long old0, long active0, long old1, long active1)
+{
+   if (((active0 &= old0) | (active1 &= old1)) == 0L)
+      return jjStartNfa_0(1, old0, old1);
+   try { curChar = input_stream.readChar(); }
+   catch(java.io.IOException e) {
+      jjStopStringLiteralDfa_0(2, active0, active1);
+      return 3;
+   }
+   switch(curChar)
+   {
+      case 49:
+         return jjMoveStringLiteralDfa4_0(active0, 0L, active1, 0x80L);
+      case 65:
+      case 97:
+         if ((active0 & 0x1000000000000000L) != 0L)
+            return jjStartNfaWithStates_0(3, 60, 37);
+         break;
+      case 67:
+      case 99:
+         if ((active0 & 0x400000000000L) != 0L)
+            return jjStartNfaWithStates_0(3, 46, 37);
+         return jjMoveStringLiteralDfa4_0(active0, 0x40000000000000L, active1, 0L);
+      case 68:
+      case 100:
+         if ((active1 & 0x1000L) != 0L)
+            return jjStartNfaWithStates_0(3, 76, 37);
+         break;
+      case 69:
+      case 101:
+         if ((active0 & 0x8000000000L) != 0L)
+            return jjStartNfaWithStates_0(3, 39, 37);
+         return jjMoveStringLiteralDfa4_0(active0, 0x800100003040000L, active1, 0x400L);
+      case 72:
+      case 104:
+         return jjMoveStringLiteralDfa4_0(active0, 0x4000000L, active1, 0L);
+      case 73:
+      case 105:
+         return jjMoveStringLiteralDfa4_0(active0, 0x200080000000000L, active1, 0x800L);
+      case 76:
+      case 108:
+         if ((active0 & 0x10000000L) != 0L)
+            return jjStartNfaWithStates_0(3, 28, 37);
+         else if ((active0 & 0x2000000000L) != 0L)
+            return jjStartNfaWithStates_0(3, 37, 37);
+         return jjMoveStringLiteralDfa4_0(active0, 0L, active1, 0x4L);
+      case 77:
+      case 109:
+         if ((active0 & 0x200000L) != 0L)
+            return jjStartNfaWithStates_0(3, 21, 37);
+         break;
+      case 78:
+      case 110:
+         if ((active0 & 0x20000000L) != 0L)
+            return jjStartNfaWithStates_0(3, 29, 37);
+         else if ((active1 & 0x20000L) != 0L)
+            return jjStartNfaWithStates_0(3, 81, 37);
+         else if ((active1 & 0x40000L) != 0L)
+         {
+            jjmatchedKind = 82;
+            jjmatchedPos = 3;
+         }
+         return jjMoveStringLiteralDfa4_0(active0, 0x88000080000000L, active1, 0x8a000L);
+      case 79:
+      case 111:
+         return jjMoveStringLiteralDfa4_0(active0, 0L, active1, 0x20L);
+      case 82:
+      case 114:
+         return jjMoveStringLiteralDfa4_0(active0, 0xe000000100000000L, active1, 0x8L);
+      case 83:
+      case 115:
+         if ((active1 & 0x10000L) != 0L)
+            return jjStartNfaWithStates_0(3, 80, 37);
+         return jjMoveStringLiteralDfa4_0(active0, 0x20000000000L, active1, 0L);
+      case 84:
+      case 116:
+         if ((active0 & 0x8000000L) != 0L)
+            return jjStartNfaWithStates_0(3, 27, 37);
+         else if ((active1 & 0x4000L) != 0L)
+            return jjStartNfaWithStates_0(3, 78, 37);
+         return jjMoveStringLiteralDfa4_0(active0, 0x420000000000000L, active1, 0x1L);
+      case 85:
+      case 117:
+         return jjMoveStringLiteralDfa4_0(active0, 0x40000800000L, active1, 0L);
+      case 87:
+      case 119:
+         return jjMoveStringLiteralDfa4_0(active0, 0x4000000000L, active1, 0L);
+      case 89:
+      case 121:
+         return jjMoveStringLiteralDfa4_0(active0, 0x100000000000000L, active1, 0L);
+      default :
+         break;
+   }
+   return jjStartNfa_0(2, active0, active1);
+}
+private int jjMoveStringLiteralDfa4_0(long old0, long active0, long old1, long active1)
+{
+   if (((active0 &= old0) | (active1 &= old1)) == 0L)
+      return jjStartNfa_0(2, old0, old1);
+   try { curChar = input_stream.readChar(); }
+   catch(java.io.IOException e) {
+      jjStopStringLiteralDfa_0(3, active0, active1);
+      return 4;
+   }
+   switch(curChar)
+   {
+      case 48:
+         if ((active1 & 0x80L) != 0L)
+            return jjStartNfaWithStates_0(4, 71, 21);
+         break;
+      case 50:
+         if ((active1 & 0x80000L) != 0L)
+            return jjStartNfaWithStates_0(4, 83, 21);
+         break;
+      case 65:
+      case 97:
+         return jjMoveStringLiteralDfa5_0(active0, 0x400000000000000L, active1, 0x801L);
+      case 67:
+      case 99:
+         return jjMoveStringLiteralDfa5_0(active0, 0x40000L, active1, 0x8000L);
+      case 68:
+      case 100:
+         if ((active1 & 0x2000L) != 0L)
+            return jjStartNfaWithStates_0(4, 77, 37);
+         return jjMoveStringLiteralDfa5_0(active0, 0xe000000000000000L, active1, 0L);
+      case 69:
+      case 101:
+         if ((active0 & 0x100000000L) != 0L)
+            return jjStartNfaWithStates_0(4, 32, 37);
+         return jjMoveStringLiteralDfa5_0(active0, 0x4000000000L, active1, 0x8L);
+      case 71:
+      case 103:
+         if ((active0 & 0x80000000L) != 0L)
+            return jjStartNfaWithStates_0(4, 31, 37);
+         return jjMoveStringLiteralDfa5_0(active0, 0x100000000000000L, active1, 0L);
+      case 73:
+      case 105:
+         return jjMoveStringLiteralDfa5_0(active0, 0L, active1, 0x4L);
+      case 76:
+      case 108:
+         return jjMoveStringLiteralDfa5_0(active0, 0x40000000000000L, active1, 0L);
+      case 78:
+      case 110:
+         return jjMoveStringLiteralDfa5_0(active0, 0x80000000000L, active1, 0L);
+      case 79:
+      case 111:
+         return jjMoveStringLiteralDfa5_0(active0, 0x200000000000000L, active1, 0L);
+      case 80:
+      case 112:
+         return jjMoveStringLiteralDfa5_0(active0, 0x40000000000L, active1, 0L);
+      case 82:
+      case 114:
+         if ((active0 & 0x1000000L) != 0L)
+            return jjStartNfaWithStates_0(4, 24, 37);
+         else if ((active0 & 0x2000000L) != 0L)
+            return jjStartNfaWithStates_0(4, 25, 37);
+         else if ((active1 & 0x20L) != 0L)
+            return jjStartNfaWithStates_0(4, 69, 37);
+         else if ((active1 & 0x400L) != 0L)
+            return jjStartNfaWithStates_0(4, 74, 37);
+         return jjMoveStringLiteralDfa5_0(active0, 0x820100000800000L, active1, 0L);
+      case 84:
+      case 116:
+         if ((active0 & 0x4000000L) != 0L)
+            return jjStartNfaWithStates_0(4, 26, 37);
+         else if ((active0 & 0x8000000000000L) != 0L)
+            return jjStartNfaWithStates_0(4, 51, 37);
+         else if ((active0 & 0x80000000000000L) != 0L)
+            return jjStartNfaWithStates_0(4, 55, 37);
+         return jjMoveStringLiteralDfa5_0(active0, 0x20000000000L, active1, 0L);
+      default :
+         break;
+   }
+   return jjStartNfa_0(3, active0, active1);
+}
+private int jjMoveStringLiteralDfa5_0(long old0, long active0, long old1, long active1)
+{
+   if (((active0 &= old0) | (active1 &= old1)) == 0L)
+      return jjStartNfa_0(3, old0, old1);
+   try { curChar = input_stream.readChar(); }
+   catch(java.io.IOException e) {
+      jjStopStringLiteralDfa_0(4, active0, active1);
+      return 5;
+   }
+   switch(curChar)
+   {
+      case 32:
+         return jjMoveStringLiteralDfa6_0(active0, 0x140000000000L, active1, 0L);
+      case 49:
+         if ((active0 & 0x2000000000000000L) != 0L)
+            return jjStartNfaWithStates_0(5, 61, 21);
+         break;
+      case 50:
+         if ((active0 & 0x4000000000000000L) != 0L)
+            return jjStartNfaWithStates_0(5, 62, 21);
+         break;
+      case 65:
+      case 97:
+         return jjMoveStringLiteralDfa6_0(active0, 0x800000L, active1, 0x8000L);
+      case 69:
+      case 101:
+         if ((active0 & 0x40000000000000L) != 0L)
+            return jjStartNfaWithStates_0(5, 54, 37);
+         return jjMoveStringLiteralDfa6_0(active0, 0x4000000000L, active1, 0x8L);
+      case 71:
+      case 103:
+         if ((active0 & 0x80000000000L) != 0L)
+            return jjStartNfaWithStates_0(5, 43, 37);
+         break;
+      case 73:
+      case 105:
+         return jjMoveStringLiteralDfa6_0(active0, 0x400000000000000L, active1, 0L);
+      case 78:
+      case 110:
+         if ((active0 & 0x200000000000000L) != 0L)
+            return jjStartNfaWithStates_0(5, 57, 37);
+         return jjMoveStringLiteralDfa6_0(active0, 0L, active1, 0x805L);
+      case 79:
+      case 111:
+         return jjMoveStringLiteralDfa6_0(active0, 0x120000000000000L, active1, 0L);
+      case 83:
+      case 115:
+         if ((active0 & 0x20000000000L) != 0L)
+            return jjStartNfaWithStates_0(5, 41, 37);
+         return jjMoveStringLiteralDfa6_0(active0, 0x8800000000000000L, active1, 0L);
+      case 84:
+      case 116:
+         if ((active0 & 0x40000L) != 0L)
+            return jjStartNfaWithStates_0(5, 18, 37);
+         break;
+      default :
+         break;
+   }
+   return jjStartNfa_0(4, active0, active1);
+}
+private int jjMoveStringLiteralDfa6_0(long old0, long active0, long old1, long active1)
+{
+   if (((active0 &= old0) | (active1 &= old1)) == 0L)
+      return jjStartNfa_0(4, old0, old1);
+   try { curChar = input_stream.readChar(); }
+   catch(java.io.IOException e) {
+      jjStopStringLiteralDfa_0(5, active0, active1);
+      return 6;
+   }
+   switch(curChar)
+   {
+      case 66:
+      case 98:
+         return jjMoveStringLiteralDfa7_0(active0, 0x140000000000L, active1, 0L);
+      case 67:
+      case 99:
+         return jjMoveStringLiteralDfa7_0(active0, 0L, active1, 0x1L);
+      case 69:
+      case 101:
+         return jjMoveStringLiteralDfa7_0(active0, 0x800000000000000L, active1, 0L);
+      case 71:
+      case 103:
+         if ((active1 & 0x4L) != 0L)
+            return jjStartNfaWithStates_0(6, 66, 37);
+         break;
+      case 73:
+      case 105:
+         return jjMoveStringLiteralDfa7_0(active0, 0x20000000000000L, active1, 0L);
+      case 76:
+      case 108:
+         if ((active0 & 0x800000L) != 0L)
+            return jjStartNfaWithStates_0(6, 23, 37);
+         break;
+      case 78:
+      case 110:
+         if ((active0 & 0x4000000000L) != 0L)
+            return jjStartNfaWithStates_0(6, 38, 37);
+         else if ((active0 & 0x100000000000000L) != 0L)
+            return jjStartNfaWithStates_0(6, 56, 37);
+         return jjMoveStringLiteralDfa7_0(active0, 0x400000000000000L, active1, 0L);
+      case 83:
+      case 115:
+         if ((active1 & 0x8L) != 0L)
+            return jjStartNfaWithStates_0(6, 67, 37);
+         else if ((active1 & 0x800L) != 0L)
+            return jjStartNfaWithStates_0(6, 75, 37);
+         break;
+      case 84:
+      case 116:
+         return jjMoveStringLiteralDfa7_0(active0, 0L, active1, 0x8000L);
+      case 89:
+      case 121:
+         return jjMoveStringLiteralDfa7_0(active0, 0x8000000000000000L, active1, 0L);
+      default :
+         break;
+   }
+   return jjStartNfa_0(5, active0, active1);
+}
+private int jjMoveStringLiteralDfa7_0(long old0, long active0, long old1, long active1)
+{
+   if (((active0 &= old0) | (active1 &= old1)) == 0L)
+      return jjStartNfa_0(5, old0, old1);
+   try { curChar = input_stream.readChar(); }
+   catch(java.io.IOException e) {
+      jjStopStringLiteralDfa_0(6, active0, active1);
+      return 7;
+   }
+   switch(curChar)
+   {
+      case 67:
+      case 99:
+         return jjMoveStringLiteralDfa8_0(active0, 0x800000000000000L, active1, 0L);
+      case 68:
+      case 100:
+         if ((active0 & 0x20000000000000L) != 0L)
+            return jjStartNfaWithStates_0(7, 53, 37);
+         break;
+      case 69:
+      case 101:
+         if ((active1 & 0x1L) != 0L)
+            return jjStartNfaWithStates_0(7, 64, 37);
+         else if ((active1 & 0x8000L) != 0L)
+            return jjStartNfaWithStates_0(7, 79, 37);
+         break;
+      case 83:
+      case 115:
+         if ((active0 & 0x400000000000000L) != 0L)
+            return jjStartNfaWithStates_0(7, 58, 37);
+         else if ((active0 & 0x8000000000000000L) != 0L)
+            return jjStartNfaWithStates_0(7, 63, 37);
+         break;
+      case 89:
+      case 121:
+         if ((active0 & 0x40000000000L) != 0L)
+            return jjStopAtPos(7, 42);
+         else if ((active0 & 0x100000000000L) != 0L)
+            return jjStopAtPos(7, 44);
+         break;
+      default :
+         break;
+   }
+   return jjStartNfa_0(6, active0, active1);
+}
+private int jjMoveStringLiteralDfa8_0(long old0, long active0, long old1, long active1)
+{
+   if (((active0 &= old0) | (active1 &= old1)) == 0L)
+      return jjStartNfa_0(6, old0, old1);
+   try { curChar = input_stream.readChar(); }
+   catch(java.io.IOException e) {
+      jjStopStringLiteralDfa_0(7, active0, 0L);
+      return 8;
+   }
+   switch(curChar)
+   {
+      case 84:
+      case 116:
+         return jjMoveStringLiteralDfa9_0(active0, 0x800000000000000L);
+      default :
+         break;
+   }
+   return jjStartNfa_0(7, active0, 0L);
+}
+private int jjMoveStringLiteralDfa9_0(long old0, long active0)
+{
+   if (((active0 &= old0)) == 0L)
+      return jjStartNfa_0(7, old0, 0L);
+   try { curChar = input_stream.readChar(); }
+   catch(java.io.IOException e) {
+      jjStopStringLiteralDfa_0(8, active0, 0L);
+      return 9;
+   }
+   switch(curChar)
+   {
+      case 83:
+      case 115:
+         if ((active0 & 0x800000000000000L) != 0L)
+            return jjStartNfaWithStates_0(9, 59, 37);
+         break;
+      default :
+         break;
+   }
+   return jjStartNfa_0(8, active0, 0L);
+}
+private int jjStartNfaWithStates_0(int pos, int kind, int state)
+{
+   jjmatchedKind = kind;
+   jjmatchedPos = pos;
+   try { curChar = input_stream.readChar(); }
+   catch(java.io.IOException e) { return pos + 1; }
+   return jjMoveNfa_0(state, pos + 1);
+}
+private int jjMoveNfa_0(int startState, int curPos)
+{
+   int startsAt = 0;
+   jjnewStateCnt = 37;
+   int i = 1;
+   jjstateSet[0] = startState;
+   int kind = 0x7fffffff;
+   for (;;)
+   {
+      if (++jjround == 0x7fffffff)
+         ReInitRounds();
+      if (curChar < 64)
+      {
+         long l = 1L << curChar;
+         do
+         {
+            switch(jjstateSet[--i])
+            {
+               case 12:
+               case 21:
+                  if ((0x3ff000000000000L & l) == 0L)
+                     break;
+                  if (kind > 97)
+                     kind = 97;
+                  jjCheckNAdd(21);
+                  break;
+               case 38:
+                  if ((0x3ff000000000000L & l) != 0L)
+                  {
+                     if (kind > 100)
+                        kind = 100;
+                     jjCheckNAdd(27);
+                  }
+                  if ((0x3ff000000000000L & l) != 0L)
+                     jjCheckNAddTwoStates(23, 24);
+                  break;
+               case 37:
+                  if ((0x3ff000000000000L & l) == 0L)
+                     break;
+                  if (kind > 97)
+                     kind = 97;
+                  jjCheckNAdd(21);
+                  break;
+               case 0:
+                  if ((0x3ff000000000000L & l) != 0L)
+                  {
+                     if (kind > 101)
+                        kind = 101;
+                     jjCheckNAddStates(0, 6);
+                  }
+                  else if ((0x100002600L & l) != 0L)
+                  {
+                     if (kind > 1)
+                        kind = 1;
+                  }
+                  else if (curChar == 46)
+                     jjCheckNAddTwoStates(23, 27);
+                  else if (curChar == 45)
+                     jjCheckNAdd(19);
+                  else if (curChar == 33)
+                     jjstateSet[jjnewStateCnt++] = 5;
+                  else if (curChar == 60)
+                     jjstateSet[jjnewStateCnt++] = 3;
+                  if (curChar == 13)
+                     jjstateSet[jjnewStateCnt++] = 1;
+                  break;
+               case 11:
+                  if ((0x3ff000000000000L & l) == 0L)
+                     break;
+                  if (kind > 97)
+                     kind = 97;
+                  jjCheckNAdd(21);
+                  break;
+               case 16:
+                  if ((0x3ff000000000000L & l) == 0L)
+                     break;
+                  if (kind > 97)
+                     kind = 97;
+                  jjCheckNAdd(21);
+                  break;
+               case 13:
+                  if ((0x3ff000000000000L & l) == 0L)
+                     break;
+                  if (kind > 97)
+                     kind = 97;
+                  jjCheckNAdd(21);
+                  break;
+               case 10:
+                  if ((0x3ff000000000000L & l) == 0L)
+                     break;
+                  if (kind > 97)
+                     kind = 97;
+                  jjCheckNAdd(21);
+                  break;
+               case 1:
+                  if (curChar == 10 && kind > 1)
+                     kind = 1;
+                  break;
+               case 2:
+                  if (curChar == 13)
+                     jjstateSet[jjnewStateCnt++] = 1;
+                  break;
+               case 3:
+                  if (curChar == 62)
+                     kind = 13;
+                  break;
+               case 4:
+                  if (curChar == 60)
+                     jjstateSet[jjnewStateCnt++] = 3;
+                  break;
+               case 5:
+                  if (curChar == 61)
+                     kind = 13;
+                  break;
+               case 6:
+                  if (curChar == 33)
+                     jjstateSet[jjnewStateCnt++] = 5;
+                  break;
+               case 18:
+                  if (curChar == 45)
+                     jjCheckNAdd(19);
+                  break;
+               case 19:
+                  if (curChar != 45)
+                     break;
+                  if (kind > 88)
+                     kind = 88;
+                  jjCheckNAdd(19);
+                  break;
+               case 22:
+                  if (curChar == 46)
+                     jjCheckNAddTwoStates(23, 27);
+                  break;
+               case 23:
+                  if ((0x3ff000000000000L & l) != 0L)
+                     jjCheckNAddTwoStates(23, 24);
+                  break;
+               case 25:
+                  if ((0x280000000000L & l) != 0L)
+                     jjCheckNAdd(26);
+                  break;
+               case 26:
+                  if ((0x3ff000000000000L & l) == 0L)
+                     break;
+                  if (kind > 99)
+                     kind = 99;
+                  jjCheckNAdd(26);
+                  break;
+               case 27:
+                  if ((0x3ff000000000000L & l) == 0L)
+                     break;
+                  if (kind > 100)
+                     kind = 100;
+                  jjCheckNAdd(27);
+                  break;
+               case 28:
+                  if ((0x3ff000000000000L & l) == 0L)
+                     break;
+                  if (kind > 101)
+                     kind = 101;
+                  jjCheckNAddStates(0, 6);
+                  break;
+               case 29:
+                  if ((0x3ff000000000000L & l) != 0L)
+                     jjCheckNAddTwoStates(29, 24);
+                  break;
+               case 30:
+                  if ((0x3ff000000000000L & l) != 0L)
+                     jjCheckNAddTwoStates(30, 31);
+                  break;
+               case 31:
+                  if (curChar == 46)
+                     jjCheckNAddTwoStates(32, 24);
+                  break;
+               case 32:
+                  if ((0x3ff000000000000L & l) != 0L)
+                     jjCheckNAddTwoStates(32, 24);
+                  break;
+               case 33:
+                  if ((0x3ff000000000000L & l) != 0L)
+                     jjCheckNAddTwoStates(33, 34);
+                  break;
+               case 34:
+                  if (curChar != 46)
+                     break;
+                  if (kind > 100)
+                     kind = 100;
+                  jjCheckNAdd(35);
+                  break;
+               case 35:
+                  if ((0x3ff000000000000L & l) == 0L)
+                     break;
+                  if (kind > 100)
+                     kind = 100;
+                  jjCheckNAdd(35);
+                  break;
+               case 36:
+                  if ((0x3ff000000000000L & l) == 0L)
+                     break;
+                  if (kind > 101)
+                     kind = 101;
+                  jjCheckNAdd(36);
+                  break;
+               default : break;
+            }
+         } while(i != startsAt);
+      }
+      else if (curChar < 128)
+      {
+         long l = 1L << (curChar & 077);
+         do
+         {
+            switch(jjstateSet[--i])
+            {
+               case 12:
+                  if ((0x7fffffe87fffffeL & l) != 0L)
+                  {
+                     if (kind > 97)
+                        kind = 97;
+                     jjCheckNAdd(21);
+                  }
+                  if ((0x7fffffe07fffffeL & l) != 0L)
+                  {
+                     if (kind > 97)
+                        kind = 97;
+                     jjCheckNAddTwoStates(20, 21);
+                  }
+                  if ((0x8000000080000L & l) != 0L)
+                     jjstateSet[jjnewStateCnt++] = 11;
+                  break;
+               case 37:
+                  if ((0x7fffffe87fffffeL & l) != 0L)
+                  {
+                     if (kind > 97)
+                        kind = 97;
+                     jjCheckNAdd(21);
+                  }
+                  if ((0x7fffffe07fffffeL & l) != 0L)
+                  {
+                     if (kind > 97)
+                        kind = 97;
+                     jjCheckNAddTwoStates(20, 21);
+                  }
+                  break;
+               case 0:
+                  if ((0x7fffffe07fffffeL & l) != 0L)
+                  {
+                     if (kind > 97)
+                        kind = 97;
+                     jjCheckNAddTwoStates(20, 21);
+                  }
+                  if ((0x200000002L & l) != 0L)
+                     jjstateSet[jjnewStateCnt++] = 16;
+                  else if ((0x1000000010L & l) != 0L)
+                     jjstateSet[jjnewStateCnt++] = 13;
+                  break;
+               case 11:
+                  if ((0x7fffffe87fffffeL & l) != 0L)
+                  {
+                     if (kind > 97)
+                        kind = 97;
+                     jjCheckNAdd(21);
+                  }
+                  if ((0x7fffffe07fffffeL & l) != 0L)
+                  {
+                     if (kind > 97)
+                        kind = 97;
+                     jjCheckNAddTwoStates(20, 21);
+                  }
+                  if ((0x10000000100000L & l) != 0L)
+                     jjstateSet[jjnewStateCnt++] = 10;
+                  break;
+               case 16:
+                  if ((0x7fffffe87fffffeL & l) != 0L)
+                  {
+                     if (kind > 97)
+                        kind = 97;
+                     jjCheckNAdd(21);
+                  }
+                  if ((0x7fffffe07fffffeL & l) != 0L)
+                  {
+                     if (kind > 97)
+                        kind = 97;
+                     jjCheckNAddTwoStates(20, 21);
+                  }
+                  if ((0x100000001000L & l) != 0L)
+                     jjstateSet[jjnewStateCnt++] = 15;
+                  break;
+               case 13:
+                  if ((0x7fffffe87fffffeL & l) != 0L)
+                  {
+                     if (kind > 97)
+                        kind = 97;
+                     jjCheckNAdd(21);
+                  }
+                  if ((0x7fffffe07fffffeL & l) != 0L)
+                  {
+                     if (kind > 97)
+                        kind = 97;
+                     jjCheckNAddTwoStates(20, 21);
+                  }
+                  if ((0x20000000200L & l) != 0L)
+                     jjstateSet[jjnewStateCnt++] = 12;
+                  break;
+               case 10:
+                  if ((0x7fffffe87fffffeL & l) != 0L)
+                  {
+                     if (kind > 97)
+                        kind = 97;
+                     jjCheckNAdd(21);
+                  }
+                  if ((0x7fffffe07fffffeL & l) != 0L)
+                  {
+                     if (kind > 97)
+                        kind = 97;
+                     jjCheckNAddTwoStates(20, 21);
+                  }
+                  if ((0x20000000200L & l) != 0L)
+                     jjstateSet[jjnewStateCnt++] = 9;
+                  break;
+               case 7:
+                  if ((0x10000000100000L & l) != 0L && kind > 19)
+                     kind = 19;
+                  break;
+               case 8:
+                  if ((0x800000008L & l) != 0L)
+                     jjstateSet[jjnewStateCnt++] = 7;
+                  break;
+               case 9:
+                  if ((0x400000004000L & l) != 0L)
+                     jjstateSet[jjnewStateCnt++] = 8;
+                  break;
+               case 14:
+                  if ((0x1000000010L & l) != 0L)
+                     jjstateSet[jjnewStateCnt++] = 13;
+                  break;
+               case 15:
+                  if ((0x100000001000L & l) != 0L && kind > 19)
+                     kind = 19;
+                  break;
+               case 17:
+                  if ((0x200000002L & l) != 0L)
+                     jjstateSet[jjnewStateCnt++] = 16;
+                  break;
+               case 20:
+                  if ((0x7fffffe07fffffeL & l) == 0L)
+                     break;
+                  if (kind > 97)
+                     kind = 97;
+                  jjCheckNAddTwoStates(20, 21);
+                  break;
+               case 21:
+                  if ((0x7fffffe87fffffeL & l) == 0L)
+                     break;
+                  if (kind > 97)
+                     kind = 97;
+                  jjCheckNAdd(21);
+                  break;
+               case 24:
+                  if ((0x2000000020L & l) != 0L)
+                     jjAddStates(7, 8);
+                  break;
+               default : break;
+            }
+         } while(i != startsAt);
+      }
+      else
+      {
+         int i2 = (curChar & 0xff) >> 6;
+         long l2 = 1L << (curChar & 077);
+         do
+         {
+            switch(jjstateSet[--i])
+            {
+               default : break;
+            }
+         } while(i != startsAt);
+      }
+      if (kind != 0x7fffffff)
+      {
+         jjmatchedKind = kind;
+         jjmatchedPos = curPos;
+         kind = 0x7fffffff;
+      }
+      ++curPos;
+      if ((i = jjnewStateCnt) == (startsAt = 37 - (jjnewStateCnt = startsAt)))
+         return curPos;
+      try { curChar = input_stream.readChar(); }
+      catch(java.io.IOException e) { return curPos; }
+   }
+}
+private final int jjStopStringLiteralDfa_2(int pos, long active0, long active1)
+{
+   switch (pos)
+   {
+      default :
+         return -1;
+   }
+}
+private final int jjStartNfa_2(int pos, long active0, long active1)
+{
+   return jjMoveNfa_2(jjStopStringLiteralDfa_2(pos, active0, active1), pos + 1);
+}
+private int jjMoveStringLiteralDfa0_2()
+{
+   switch(curChar)
+   {
+      case 39:
+         return jjStartNfaWithStates_2(0, 93, 1);
+      default :
+         return jjMoveNfa_2(0, 0);
+   }
+}
+private int jjStartNfaWithStates_2(int pos, int kind, int state)
+{
+   jjmatchedKind = kind;
+   jjmatchedPos = pos;
+   try { curChar = input_stream.readChar(); }
+   catch(java.io.IOException e) { return pos + 1; }
+   return jjMoveNfa_2(state, pos + 1);
+}
+private int jjMoveNfa_2(int startState, int curPos)
+{
+   int startsAt = 0;
+   jjnewStateCnt = 3;
+   int i = 1;
+   jjstateSet[0] = startState;
+   int kind = 0x7fffffff;
+   for (;;)
+   {
+      if (++jjround == 0x7fffffff)
+         ReInitRounds();
+      if (curChar < 64)
+      {
+         long l = 1L << curChar;
+         do
+         {
+            switch(jjstateSet[--i])
+            {
+               case 0:
+                  if ((0xffffff7fffffffffL & l) != 0L)
+                  {
+                     if (kind > 92)
+                        kind = 92;
+                  }
+                  else if (curChar == 39)
+                     jjstateSet[jjnewStateCnt++] = 1;
+                  break;
+               case 1:
+                  if (curChar == 39 && kind > 92)
+                     kind = 92;
+                  break;
+               case 2:
+                  if (curChar == 39)
+                     jjstateSet[jjnewStateCnt++] = 1;
+                  break;
+               default : break;
+            }
+         } while(i != startsAt);
+      }
+      else if (curChar < 128)
+      {
+         long l = 1L << (curChar & 077);
+         do
+         {
+            switch(jjstateSet[--i])
+            {
+               case 0:
+                  kind = 92;
+                  break;
+               default : break;
+            }
+         } while(i != startsAt);
+      }
+      else
+      {
+         int i2 = (curChar & 0xff) >> 6;
+         long l2 = 1L << (curChar & 077);
+         do
+         {
+            switch(jjstateSet[--i])
+            {
+               case 0:
+                  if ((jjbitVec0[i2] & l2) != 0L && kind > 92)
+                     kind = 92;
+                  break;
+               default : break;
+            }
+         } while(i != startsAt);
+      }
+      if (kind != 0x7fffffff)
+      {
+         jjmatchedKind = kind;
+         jjmatchedPos = curPos;
+         kind = 0x7fffffff;
+      }
+      ++curPos;
+      if ((i = jjnewStateCnt) == (startsAt = 3 - (jjnewStateCnt = startsAt)))
+         return curPos;
+      try { curChar = input_stream.readChar(); }
+      catch(java.io.IOException e) { return curPos; }
+   }
+}
+private int jjMoveStringLiteralDfa0_1()
+{
+   return jjMoveNfa_1(0, 0);
+}
+private int jjMoveNfa_1(int startState, int curPos)
+{
+   int startsAt = 0;
+   jjnewStateCnt = 3;
+   int i = 1;
+   jjstateSet[0] = startState;
+   int kind = 0x7fffffff;
+   for (;;)
+   {
+      if (++jjround == 0x7fffffff)
+         ReInitRounds();
+      if (curChar < 64)
+      {
+         long l = 1L << curChar;
+         do
+         {
+            switch(jjstateSet[--i])
+            {
+               case 0:
+                  if ((0x2400L & l) != 0L)
+                  {
+                     if (kind > 89)
+                        kind = 89;
+                  }
+                  if (curChar == 13)
+                     jjstateSet[jjnewStateCnt++] = 1;
+                  break;
+               case 1:
+                  if (curChar == 10 && kind > 89)
+                     kind = 89;
+                  break;
+               case 2:
+                  if (curChar == 13)
+                     jjstateSet[jjnewStateCnt++] = 1;
+                  break;
+               default : break;
+            }
+         } while(i != startsAt);
+      }
+      else if (curChar < 128)
+      {
+         long l = 1L << (curChar & 077);
+         do
+         {
+            switch(jjstateSet[--i])
+            {
+               default : break;
+            }
+         } while(i != startsAt);
+      }
+      else
+      {
+         int i2 = (curChar & 0xff) >> 6;
+         long l2 = 1L << (curChar & 077);
+         do
+         {
+            switch(jjstateSet[--i])
+            {
+               default : break;
+            }
+         } while(i != startsAt);
+      }
+      if (kind != 0x7fffffff)
+      {
+         jjmatchedKind = kind;
+         jjmatchedPos = curPos;
+         kind = 0x7fffffff;
+      }
+      ++curPos;
+      if ((i = jjnewStateCnt) == (startsAt = 3 - (jjnewStateCnt = startsAt)))
+         return curPos;
+      try { curChar = input_stream.readChar(); }
+      catch(java.io.IOException e) { return curPos; }
+   }
+}
+static final int[] jjnextStates = {
+   29, 30, 31, 24, 33, 34, 36, 25, 26, 
+};
 
-	/** Constructor. */
-	public ADQLParserTokenManager(SimpleCharStream stream, int lexState){
-		this(stream);
-		SwitchTo(lexState);
-	}
+/** Token literal values. */
+public static final String[] jjstrLiteralImages = {
+"", null, "\50", "\51", "\56", "\54", "\73", "\174\174", "\53", "\55", "\52", 
+"\57", "\75", null, "\74", "\74\75", "\76", "\76\75", null, null, null, null, null, 
+null, null, null, null, null, null, null, null, null, null, null, null, null, null, 
+null, null, null, null, null, null, null, null, null, null, null, null, null, null, 
+null, null, null, null, null, null, null, null, null, null, null, null, null, null, 
+null, null, null, null, null, null, null, null, null, null, null, null, null, null, 
+null, null, null, null, null, null, null, null, null, null, null, null, null, null, 
+null, null, null, null, null, null, null, null, null, null, };
 
-	/** Reinitialise parser. */
-	public void ReInit(SimpleCharStream stream){
-		jjmatchedPos = jjnewStateCnt = 0;
-		curLexState = defaultLexState;
-		input_stream = stream;
-		ReInitRounds();
-	}
+/** Lexer state names. */
+public static final String[] lexStateNames = {
+   "DEFAULT",
+   "WithinComment",
+   "WithinString",
+   "WithinDelimitedId",
+};
 
-	private void ReInitRounds(){
-		int i;
-		jjround = 0x80000001;
-		for(i = 37; i-- > 0;)
-			jjrounds[i] = 0x80000000;
-	}
+/** Lex State array. */
+public static final int[] jjnewLexState = {
+   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
+   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
+   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
+   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 0, -1, 2, -1, 0, 3, -1, 0, -1, -1, -1, 
+   -1, -1, -1, 
+};
+static final long[] jjtoToken = {
+   0xfffffffffffffffdL, 0x3b20ffffffL, 
+};
+static final long[] jjtoSkip = {
+   0x2L, 0x2000000L, 
+};
+static final long[] jjtoMore = {
+   0x0L, 0xdd000000L, 
+};
+protected SimpleCharStream input_stream;
+private final int[] jjrounds = new int[37];
+private final int[] jjstateSet = new int[74];
+protected char curChar;
+/** Constructor. */
+public ADQLParserTokenManager(SimpleCharStream stream){
+   if (SimpleCharStream.staticFlag)
+      throw new Error("ERROR: Cannot use a static CharStream class with a non-static lexical analyzer.");
+   input_stream = stream;
+}
 
-	/** Reinitialise parser. */
-	public void ReInit(SimpleCharStream stream, int lexState){
-		ReInit(stream);
-		SwitchTo(lexState);
-	}
+/** Constructor. */
+public ADQLParserTokenManager(SimpleCharStream stream, int lexState){
+   this(stream);
+   SwitchTo(lexState);
+}
 
-	/** Switch to specified lex state. */
-	public void SwitchTo(int lexState){
-		if (lexState >= 4 || lexState < 0)
-			throw new TokenMgrError("Error: Ignoring invalid lexical state : " + lexState + ". State unchanged.", TokenMgrError.INVALID_LEXICAL_STATE);
-		else
-			curLexState = lexState;
-	}
+/** Reinitialise parser. */
+public void ReInit(SimpleCharStream stream)
+{
+   jjmatchedPos = jjnewStateCnt = 0;
+   curLexState = defaultLexState;
+   input_stream = stream;
+   ReInitRounds();
+}
+private void ReInitRounds()
+{
+   int i;
+   jjround = 0x80000001;
+   for (i = 37; i-- > 0;)
+      jjrounds[i] = 0x80000000;
+}
 
-	protected Token jjFillToken(){
-		final Token t;
-		final String curTokenImage;
-		final int beginLine;
-		final int endLine;
-		final int beginColumn;
-		final int endColumn;
-		String im = jjstrLiteralImages[jjmatchedKind];
-		curTokenImage = (im == null) ? input_stream.GetImage() : im;
-		beginLine = input_stream.getBeginLine();
-		beginColumn = input_stream.getBeginColumn();
-		endLine = input_stream.getEndLine();
-		endColumn = input_stream.getEndColumn();
-		t = Token.newToken(jjmatchedKind, curTokenImage);
+/** Reinitialise parser. */
+public void ReInit(SimpleCharStream stream, int lexState)
+{
+   ReInit(stream);
+   SwitchTo(lexState);
+}
 
-		t.beginLine = beginLine;
-		t.endLine = endLine;
-		t.beginColumn = beginColumn;
-		t.endColumn = endColumn;
+/** Switch to specified lex state. */
+public void SwitchTo(int lexState)
+{
+   if (lexState >= 4 || lexState < 0)
+      throw new TokenMgrError("Error: Ignoring invalid lexical state : " + lexState + ". State unchanged.", TokenMgrError.INVALID_LEXICAL_STATE);
+   else
+      curLexState = lexState;
+}
 
-		return t;
-	}
+protected Token jjFillToken()
+{
+   final Token t;
+   final String curTokenImage;
+   final int beginLine;
+   final int endLine;
+   final int beginColumn;
+   final int endColumn;
+   String im = jjstrLiteralImages[jjmatchedKind];
+   curTokenImage = (im == null) ? input_stream.GetImage() : im;
+   beginLine = input_stream.getBeginLine();
+   beginColumn = input_stream.getBeginColumn();
+   endLine = input_stream.getEndLine();
+   endColumn = input_stream.getEndColumn();
+   t = Token.newToken(jjmatchedKind, curTokenImage);
 
-	int curLexState = 0;
-	int defaultLexState = 0;
-	int jjnewStateCnt;
-	int jjround;
-	int jjmatchedPos;
-	int jjmatchedKind;
+   t.beginLine = beginLine;
+   t.endLine = endLine;
+   t.beginColumn = beginColumn;
+   t.endColumn = endColumn;
 
-	/** Get the next Token. */
-	public Token getNextToken(){
-		Token matchedToken;
-		int curPos = 0;
+   return t;
+}
 
-		EOFLoop: for(;;){
-			try{
-				curChar = input_stream.BeginToken();
-			}catch(java.io.IOException e){
-				jjmatchedKind = 0;
-				matchedToken = jjFillToken();
-				return matchedToken;
-			}
+int curLexState = 0;
+int defaultLexState = 0;
+int jjnewStateCnt;
+int jjround;
+int jjmatchedPos;
+int jjmatchedKind;
 
-			for(;;){
-				switch(curLexState){
-					case 0:
-						jjmatchedKind = 0x7fffffff;
-						jjmatchedPos = 0;
-						curPos = jjMoveStringLiteralDfa0_0();
-						break;
-					case 1:
-						jjmatchedKind = 0x7fffffff;
-						jjmatchedPos = 0;
-						curPos = jjMoveStringLiteralDfa0_1();
-						if (jjmatchedPos == 0 && jjmatchedKind > 90){
-							jjmatchedKind = 90;
-						}
-						break;
-					case 2:
-						jjmatchedKind = 0x7fffffff;
-						jjmatchedPos = 0;
-						curPos = jjMoveStringLiteralDfa0_2();
-						break;
-					case 3:
-						jjmatchedKind = 0x7fffffff;
-						jjmatchedPos = 0;
-						curPos = jjMoveStringLiteralDfa0_3();
-						break;
-				}
-				if (jjmatchedKind != 0x7fffffff){
-					if (jjmatchedPos + 1 < curPos)
-						input_stream.backup(curPos - jjmatchedPos - 1);
-					if ((jjtoToken[jjmatchedKind >> 6] & (1L << (jjmatchedKind & 077))) != 0L){
-						matchedToken = jjFillToken();
-						if (jjnewLexState[jjmatchedKind] != -1)
-							curLexState = jjnewLexState[jjmatchedKind];
-						return matchedToken;
-					}else if ((jjtoSkip[jjmatchedKind >> 6] & (1L << (jjmatchedKind & 077))) != 0L){
-						if (jjnewLexState[jjmatchedKind] != -1)
-							curLexState = jjnewLexState[jjmatchedKind];
-						continue EOFLoop;
-					}
-					if (jjnewLexState[jjmatchedKind] != -1)
-						curLexState = jjnewLexState[jjmatchedKind];
-					curPos = 0;
-					jjmatchedKind = 0x7fffffff;
-					try{
-						curChar = input_stream.readChar();
-						continue;
-					}catch(java.io.IOException e1){}
-				}
-				int error_line = input_stream.getEndLine();
-				int error_column = input_stream.getEndColumn();
-				String error_after = null;
-				boolean EOFSeen = false;
-				try{
-					input_stream.readChar();
-					input_stream.backup(1);
-				}catch(java.io.IOException e1){
-					EOFSeen = true;
-					error_after = curPos <= 1 ? "" : input_stream.GetImage();
-					if (curChar == '\n' || curChar == '\r'){
-						error_line++;
-						error_column = 0;
-					}else
-						error_column++;
-				}
-				if (!EOFSeen){
-					input_stream.backup(1);
-					error_after = curPos <= 1 ? "" : input_stream.GetImage();
-				}
-				throw new TokenMgrError(EOFSeen, curLexState, error_line, error_column, error_after, curChar, TokenMgrError.LEXICAL_ERROR);
-			}
-		}
-	}
+/** Get the next Token. */
+public Token getNextToken() 
+{
+  Token matchedToken;
+  int curPos = 0;
 
-	private void jjCheckNAdd(int state){
-		if (jjrounds[state] != jjround){
-			jjstateSet[jjnewStateCnt++] = state;
-			jjrounds[state] = jjround;
-		}
-	}
+  EOFLoop :
+  for (;;)
+  {
+   try
+   {
+      curChar = input_stream.BeginToken();
+   }
+   catch(java.io.IOException e)
+   {
+      jjmatchedKind = 0;
+      matchedToken = jjFillToken();
+      return matchedToken;
+   }
 
-	private void jjAddStates(int start, int end){
-		do{
-			jjstateSet[jjnewStateCnt++] = jjnextStates[start];
-		}while(start++ != end);
-	}
+   for (;;)
+   {
+     switch(curLexState)
+     {
+       case 0:
+         jjmatchedKind = 0x7fffffff;
+         jjmatchedPos = 0;
+         curPos = jjMoveStringLiteralDfa0_0();
+         break;
+       case 1:
+         jjmatchedKind = 0x7fffffff;
+         jjmatchedPos = 0;
+         curPos = jjMoveStringLiteralDfa0_1();
+         if (jjmatchedPos == 0 && jjmatchedKind > 90)
+         {
+            jjmatchedKind = 90;
+         }
+         break;
+       case 2:
+         jjmatchedKind = 0x7fffffff;
+         jjmatchedPos = 0;
+         curPos = jjMoveStringLiteralDfa0_2();
+         break;
+       case 3:
+         jjmatchedKind = 0x7fffffff;
+         jjmatchedPos = 0;
+         curPos = jjMoveStringLiteralDfa0_3();
+         break;
+     }
+     if (jjmatchedKind != 0x7fffffff)
+     {
+        if (jjmatchedPos + 1 < curPos)
+           input_stream.backup(curPos - jjmatchedPos - 1);
+        if ((jjtoToken[jjmatchedKind >> 6] & (1L << (jjmatchedKind & 077))) != 0L)
+        {
+           matchedToken = jjFillToken();
+       if (jjnewLexState[jjmatchedKind] != -1)
+         curLexState = jjnewLexState[jjmatchedKind];
+           return matchedToken;
+        }
+        else if ((jjtoSkip[jjmatchedKind >> 6] & (1L << (jjmatchedKind & 077))) != 0L)
+        {
+         if (jjnewLexState[jjmatchedKind] != -1)
+           curLexState = jjnewLexState[jjmatchedKind];
+           continue EOFLoop;
+        }
+      if (jjnewLexState[jjmatchedKind] != -1)
+        curLexState = jjnewLexState[jjmatchedKind];
+        curPos = 0;
+        jjmatchedKind = 0x7fffffff;
+        try {
+           curChar = input_stream.readChar();
+           continue;
+        }
+        catch (java.io.IOException e1) { }
+     }
+     int error_line = input_stream.getEndLine();
+     int error_column = input_stream.getEndColumn();
+     String error_after = null;
+     boolean EOFSeen = false;
+     try { input_stream.readChar(); input_stream.backup(1); }
+     catch (java.io.IOException e1) {
+        EOFSeen = true;
+        error_after = curPos <= 1 ? "" : input_stream.GetImage();
+        if (curChar == '\n' || curChar == '\r') {
+           error_line++;
+           error_column = 0;
+        }
+        else
+           error_column++;
+     }
+     if (!EOFSeen) {
+        input_stream.backup(1);
+        error_after = curPos <= 1 ? "" : input_stream.GetImage();
+     }
+     throw new TokenMgrError(EOFSeen, curLexState, error_line, error_column, error_after, curChar, TokenMgrError.LEXICAL_ERROR);
+   }
+  }
+}
 
-	private void jjCheckNAddTwoStates(int state1, int state2){
-		jjCheckNAdd(state1);
-		jjCheckNAdd(state2);
-	}
+private void jjCheckNAdd(int state)
+{
+   if (jjrounds[state] != jjround)
+   {
+      jjstateSet[jjnewStateCnt++] = state;
+      jjrounds[state] = jjround;
+   }
+}
+private void jjAddStates(int start, int end)
+{
+   do {
+      jjstateSet[jjnewStateCnt++] = jjnextStates[start];
+   } while (start++ != end);
+}
+private void jjCheckNAddTwoStates(int state1, int state2)
+{
+   jjCheckNAdd(state1);
+   jjCheckNAdd(state2);
+}
 
-	private void jjCheckNAddStates(int start, int end){
-		do{
-			jjCheckNAdd(jjnextStates[start]);
-		}while(start++ != end);
-	}
+private void jjCheckNAddStates(int start, int end)
+{
+   do {
+      jjCheckNAdd(jjnextStates[start]);
+   } while (start++ != end);
+}
 
 }
diff --git a/src/adql/parser/ADQLQueryFactory.java b/src/adql/parser/ADQLQueryFactory.java
index daef3dc..4543372 100644
--- a/src/adql/parser/ADQLQueryFactory.java
+++ b/src/adql/parser/ADQLQueryFactory.java
@@ -16,12 +16,13 @@ package adql.parser;
  * 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 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import java.util.Collection;
-import java.util.Vector;
 
+import adql.db.FunctionDef;
 import adql.parser.IdentifierItems.IdentifierItem;
 import adql.query.ADQLOrder;
 import adql.query.ADQLQuery;
@@ -30,26 +31,24 @@ import adql.query.ColumnReference;
 import adql.query.IdentifierField;
 import adql.query.SelectItem;
 import adql.query.TextPosition;
+import adql.query.constraint.ADQLConstraint;
 import adql.query.constraint.Between;
 import adql.query.constraint.Comparison;
-import adql.query.constraint.ADQLConstraint;
-import adql.query.constraint.ConstraintsGroup;
 import adql.query.constraint.ComparisonOperator;
+import adql.query.constraint.ConstraintsGroup;
 import adql.query.constraint.Exists;
 import adql.query.constraint.In;
 import adql.query.constraint.IsNull;
 import adql.query.constraint.NotConstraint;
-
 import adql.query.from.ADQLJoin;
 import adql.query.from.ADQLTable;
-import adql.query.from.FromContent;
 import adql.query.from.CrossJoin;
+import adql.query.from.FromContent;
 import adql.query.from.InnerJoin;
 import adql.query.from.OuterJoin;
 import adql.query.from.OuterJoin.OuterType;
-
-import adql.query.operand.ADQLOperand;
 import adql.query.operand.ADQLColumn;
+import adql.query.operand.ADQLOperand;
 import adql.query.operand.Concatenation;
 import adql.query.operand.NegativeOperand;
 import adql.query.operand.NumericConstant;
@@ -57,14 +56,12 @@ import adql.query.operand.Operation;
 import adql.query.operand.OperationType;
 import adql.query.operand.StringConstant;
 import adql.query.operand.WrappedOperand;
-
 import adql.query.operand.function.DefaultUDF;
 import adql.query.operand.function.MathFunction;
 import adql.query.operand.function.MathFunctionType;
 import adql.query.operand.function.SQLFunction;
 import adql.query.operand.function.SQLFunctionType;
 import adql.query.operand.function.UserDefinedFunction;
-import adql.query.operand.function.geometry.GeometryFunction.GeometryValue;
 import adql.query.operand.function.geometry.AreaFunction;
 import adql.query.operand.function.geometry.BoxFunction;
 import adql.query.operand.function.geometry.CentroidFunction;
@@ -74,6 +71,7 @@ import adql.query.operand.function.geometry.DistanceFunction;
 import adql.query.operand.function.geometry.ExtractCoord;
 import adql.query.operand.function.geometry.ExtractCoordSys;
 import adql.query.operand.function.geometry.GeometryFunction;
+import adql.query.operand.function.geometry.GeometryFunction.GeometryValue;
 import adql.query.operand.function.geometry.IntersectsFunction;
 import adql.query.operand.function.geometry.PointFunction;
 import adql.query.operand.function.geometry.PolygonFunction;
@@ -84,27 +82,30 @@ import adql.query.operand.function.geometry.RegionFunction;
  * 
  * <p>To customize the object representation you merely have to extends the appropriate functions of this class.</p>
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 08/2011
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 1.3 (10/2014)
  * 
  * @see ADQLParser
  */
 public class ADQLQueryFactory {
 
-	protected boolean allowUnknownFunctions = false;
-
+	/**
+	 * Type of table JOIN.
+	 * 
+	 * @author Gr&eacute;gory Mantelet (CDS)
+	 * @version 1.0 (08/2011)
+	 */
 	public static enum JoinType{
 		CROSS, INNER, OUTER_LEFT, OUTER_RIGHT, OUTER_FULL;
 	}
 
+	/**
+	 * Create a query factory.
+	 */
 	public ADQLQueryFactory(){
 		;
 	}
 
-	public ADQLQueryFactory(boolean allowUnknownFunctions){
-		this.allowUnknownFunctions = allowUnknownFunctions;
-	}
-
 	public ADQLQuery createQuery() throws Exception{
 		return new ADQLQuery();
 	}
@@ -273,21 +274,29 @@ public class ADQLQueryFactory {
 
 	/**
 	 * <p>Creates the user defined functions called as the given name and with the given parameters.</p>
-	 * <p><b>IMPORTANT: This function must be overridden if some user defined functions are available.</b></p>
+	 * 
+	 * <p>
+	 * 	By default, this function returns a {@link DefaultUDF} instance. It is generic enough to cover every kind of functions.
+	 * 	But you can of course override this function in order to return your own instance of {@link UserDefinedFunction}.
+	 * 	In this case, you may not forget to call the super function (super.createUserDefinedFunction(name, params)) so that
+	 * 	all other unknown functions are still returned as {@link DefaultUDF} instances.
+	 * </p>
+	 * 
+	 * <p><i><b>IMPORTANT:</b>
+	 * 	The tests done to check whether a user defined function is allowed/managed in this implementation, is done later by the parser.
+	 * 	Only declared UDF will pass the test of the parser. For that, you should give it a list of allowed UDFs (each UDF will be then
+	 * 	represented by a {@link FunctionDef} object). 
+	 * </i></p>
 	 * 
 	 * @param name			Name of the user defined function to create.
 	 * @param params		Parameters of the user defined function to create.
 	 * 
-	 * @return				The corresponding user defined function.
+	 * @return				The corresponding user defined function (by default an instance of {@link DefaultUDF}).
 	 * 
-	 * @throws Exception	An {@link UnsupportedOperationException} by default, otherwise any other type of error may be
-	 * 						thrown if there is a problem while creating the function.
+	 * @throws Exception	If there is a problem while creating the function.
 	 */
 	public UserDefinedFunction createUserDefinedFunction(String name, ADQLOperand[] params) throws Exception{
-		if (allowUnknownFunctions)
-			return new DefaultUDF(name, params);
-		else
-			throw new UnsupportedOperationException("No ADQL function called \"" + name + "\" !");
+		return new DefaultUDF(name, params);
 	}
 
 	public DistanceFunction createDistance(PointFunction point1, PointFunction point2) throws Exception{
@@ -322,7 +331,7 @@ public class ADQLQueryFactory {
 		return new RegionFunction(param);
 	}
 
-	public PolygonFunction createPolygon(ADQLOperand coordSys, Vector<ADQLOperand> coords) throws Exception{
+	public PolygonFunction createPolygon(ADQLOperand coordSys, Collection<? extends ADQLOperand> coords) throws Exception{
 		return new PolygonFunction(coordSys, coords);
 	}
 
diff --git a/src/adql/parser/adqlGrammar.jj b/src/adql/parser/adqlGrammar.jj
index bfac21d..52e6ded 100644
--- a/src/adql/parser/adqlGrammar.jj
+++ b/src/adql/parser/adqlGrammar.jj
@@ -14,7 +14,7 @@
  * 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-2014 - UDS/Centre de DonnM-CM-)es astronomiques de Strasbourg (CDS),
+ * Copyright 2012-2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
  *                       Astronomisches Rechen Institute (ARI)
  */
 
@@ -26,7 +26,7 @@
 *  If the syntax is not conform to the ADQL definition an error message is printed else it will be the message "Correct syntax".
 *
 *  Author:  Gr&eacute;gory Mantelet (CDS;ARI) - gmantele@ari.uni-heidelberg.de
-*  Version: 1.2 (03/2014)
+*  Version: 1.3 (10/2014)
 */
 
 							/* ########### */
@@ -35,7 +35,7 @@
 options {
 	STATIC = false;
 	IGNORE_CASE = true;
-	DEBUG_PARSER = true;
+	DEBUG_PARSER = false;
 }
 
 							/* ########## */
@@ -89,7 +89,7 @@ import adql.translator.TranslationException;
 * @see ADQLQueryFactory
 *
 * @author Gr&eacute;gory Mantelet (CDS;ARI) - gmantele@ari.uni-heidelberg.de
-* @version 1.2 (03/2014)
+* @version 1.3 (10/2014)
 */
 public class ADQLParser {
 	
@@ -108,9 +108,6 @@ public class ADQLParser {
 	/** The first token of a table/column name. This token is extracted by {@link #Identifier()}. */
 	private Token currentIdentifierToken = null;
 	
-	/** List of all allowed coordinate systems. */
-	private ArrayList<String> allowedCoordSys = new ArrayList<String>();
-	
 	/**
 	* Builds an ADQL parser without a query to parse.
 	*/
@@ -348,24 +345,6 @@ public class ADQLParser {
 		return Query();
 	}
 	
-	public final void addCoordinateSystem(final String coordSys){
-		allowedCoordSys.add(coordSys);
-	}
-	
-	public final void setCoordinateSystems(final Collection<String> coordSys){
-		allowedCoordSys.clear();
-		if (coordSys != null)
-			allowedCoordSys.addAll(coordSys);
-	}
-	
-	public final boolean isAllowedCoordSys(final String coordSys) {
-		for(String cs : allowedCoordSys){
-			if (cs.equalsIgnoreCase(coordSys))
-				return true;
-		}
-		return false;
-	}
-	
 	public final void setDebug(boolean debug){
 		if (debug) enable_tracing();
 		else       disable_tracing();
@@ -983,8 +962,8 @@ ADQLJoin JoinSpecification(FromContent leftTable): { boolean natural = false; Jo
 /* STRING */
 /* ****** */
 String String(): {Token t; String str="";} {
-	(t=<STRING_LITERAL> {str += t.image;})+
-	{return (str!=null)?str.substring(1,str.length()-1):str;}
+	(t=<STRING_LITERAL> {str += t.image.substring(1,t.image.length()-1).replaceAll("''", "'");})+
+	{return str;}
 }
 
 /* ************* */
@@ -1011,30 +990,48 @@ String SignedInteger(): {Token sign=null, number;} {
 /* *********** */
 /* EXPRESSIONS */
 /* *********** */
-ADQLOperand ValueExpressionPrimary(): {String expr; ADQLColumn column; ADQLOperand op;} {
+ADQLOperand NumericValueExpressionPrimary(): {String expr; ADQLColumn column; ADQLOperand op;} {
 	try{
 		(// unsigned_value_specification
 		  expr=UnsignedNumeric() {return queryFactory.createNumericConstant(expr);}
-		// string
-		| expr=String() {return queryFactory.createStringConstant(expr);}
 		// column_reference
-		| column=Column() {return column;}
+		| column=Column() {column.setExpectedType('N'); return column;}
 		// set_function_specification
 		| op=SqlFunction() {return op;}
 		// LEFT_PAR value_expression RIGHT_PAR
-		| (<LEFT_PAR> op=ValueExpression() <RIGHT_PAR>) {return queryFactory.createWrappedOperand(op);})
+		| (<LEFT_PAR> op=NumericExpression() <RIGHT_PAR>) {return queryFactory.createWrappedOperand(op);})
+	}catch(Exception ex){
+		throw generateParseException(ex);
+	}
+}
+
+ADQLOperand StringValueExpressionPrimary(): {String expr; ADQLColumn column; ADQLOperand op;} {
+	try{
+		(// string
+		  expr=String() {return queryFactory.createStringConstant(expr);}
+		// column_reference
+		| column=Column() {column.setExpectedType('S'); return column;}
+		// LEFT_PAR value_expression RIGHT_PAR
+		| (<LEFT_PAR> (op=StringExpression()) <RIGHT_PAR>) {return queryFactory.createWrappedOperand(op);})
 	}catch(Exception ex){
 		throw generateParseException(ex);
 	}
 }
 
 ADQLOperand ValueExpression(): {ADQLOperand valueExpr = null; } {
-	(valueExpr=GeometryValueFunction()
-	| LOOKAHEAD(<PLUS> | <MINUS>) valueExpr=NumericExpression()
-	| LOOKAHEAD(<COORDSYS>) valueExpr=StringExpression()
-	| LOOKAHEAD(StringFactor() <CONCAT>) valueExpr=StringExpression()
-	| valueExpr=NumericExpression())
-	{return valueExpr;}
+	try{
+		(LOOKAHEAD((<PLUS>|<MINUS>) | (Factor() (<PLUS>|<MINUS>|<ASTERISK>|<DIVIDE>))) valueExpr=NumericExpression()
+		| LOOKAHEAD(<COORDSYS> | (StringFactor() <CONCAT>)) valueExpr=StringExpression()
+		| LOOKAHEAD(<LEFT_PAR>) <LEFT_PAR> valueExpr=ValueExpression() <RIGHT_PAR> { valueExpr = queryFactory.createWrappedOperand(valueExpr); }
+		| LOOKAHEAD(<REGULAR_IDENTIFIER> <LEFT_PAR>) valueExpr=UserDefinedFunction()
+		| valueExpr=GeometryValueFunction()
+		| LOOKAHEAD(Column()) valueExpr=Column()
+		| LOOKAHEAD(String()) valueExpr=StringFactor()
+		| valueExpr=Factor())
+		{return valueExpr;}
+	}catch(Exception ex){
+		throw generateParseException(ex);
+	}
 }
 
 ADQLOperand NumericExpression(): {Token sign=null; ADQLOperand leftOp, rightOp=null;} {
@@ -1070,7 +1067,7 @@ ADQLOperand NumericTerm(): {Token sign=null; ADQLOperand leftOp, rightOp=null;}
 ADQLOperand Factor(): {boolean negative = false;; ADQLOperand op;} {
 	(
 		(<PLUS> | (<MINUS> {negative = true;}))?
-		(LOOKAHEAD(2) op=NumericFunction() | op=ValueExpressionPrimary())
+		(LOOKAHEAD(2) op=NumericFunction() | op=NumericValueExpressionPrimary())
 	)
 	
 	{
@@ -1109,17 +1106,17 @@ ADQLOperand StringExpression(): {ADQLOperand leftOp; ADQLOperand rightOp = null;
 
 ADQLOperand StringFactor(): {ADQLOperand op;} {
 	(op=ExtractCoordSys()
-	| LOOKAHEAD(2) op=UserDefinedFunction()
-	| op=ValueExpressionPrimary())
+	| LOOKAHEAD(2) op=UserDefinedFunction() { ((UserDefinedFunction)op).setExpectedType('S'); }
+	| op=StringValueExpressionPrimary())
 	{return op;}
 }
 
 GeometryValue<GeometryFunction> GeometryExpression(): {ADQLColumn col = null; GeometryFunction gf = null;} {
 	(col=Column() | gf=GeometryValueFunction())
 	{
-		if (col != null)
+		if (col != null){
		  	col.setExpectedType('G');
 			return new GeometryValue<GeometryFunction>(col);
-		else
+		}else
 			return new GeometryValue<GeometryFunction>(gf);
 	}
 }
@@ -1282,24 +1279,26 @@ GeometryFunction GeometryFunction(): {Token t=null; GeometryValue<GeometryFuncti
 			})
 		// non_predicate_geometry_function
 		|	(<AREA> <LEFT_PAR> gvf1=GeometryExpression() <RIGHT_PAR>) {gf = queryFactory.createArea(gvf1);}
-		|	(<COORD1> <LEFT_PAR> (p1=Point() {gf = queryFactory.createCoord1(p1);} | col1=Column() {gf = queryFactory.createCoord1(col1);}) <RIGHT_PAR>)
-		|	(<COORD2> <LEFT_PAR> (p1=Point() {gf = queryFactory.createCoord2(p1);} | col1=Column() {gf = queryFactory.createCoord2(col1);}) <RIGHT_PAR>)
+		|	(<COORD1> <LEFT_PAR> (p1=Point() {gf = queryFactory.createCoord1(p1);} | col1=Column() {col1.setExpectedType('G'); gf = queryFactory.createCoord1(col1);}) <RIGHT_PAR>)
+		|	(<COORD2> <LEFT_PAR> (p1=Point() {gf = queryFactory.createCoord2(p1);} | col1=Column() {col1.setExpectedType('G'); gf = queryFactory.createCoord2(col1);}) <RIGHT_PAR>)
 		|	(<DISTANCE>
 				<LEFT_PAR>
 				(p1=Point()|col1=Column()) 
 				{
 					if (p1 != null)
 						gvp1 = new GeometryValue<PointFunction>(p1);
-					else
+					else{
						col1.setExpectedType('G');
 						gvp1 = new GeometryValue<PointFunction>(col1);
+					}
 				}
 				<COMMA>
 				(p2=Point()|col2=Column())
 				{
 					if (p2 != null)
 						gvp2 = new GeometryValue<PointFunction>(p2);
-					else
+					else{
						col2.setExpectedType('G');
 						gvp2 = new GeometryValue<PointFunction>(col2);
+					}
 				} 
 				<RIGHT_PAR>
 				{gf = queryFactory.createDistance(gvp1, gvp2);}
@@ -1312,19 +1311,9 @@ GeometryFunction GeometryFunction(): {Token t=null; GeometryValue<GeometryFuncti
 	{ return gf; }
 }
 
-ADQLOperand CoordinateSystem(): { Token oldToken = token; ADQLOperand coordSys=null;}{
+ADQLOperand CoordinateSystem(): { ADQLOperand coordSys=null;}{
 	coordSys=StringExpression()
-	{
-		if (allowedCoordSys.size() > 0){
-			TextPosition position = new TextPosition(oldToken.next, token);
-			if (coordSys == null)
-				throw new ParseException("A coordinate system must always be provided !", position);
-			if (coordSys instanceof StringConstant && !isAllowedCoordSys(((StringConstant)coordSys).getValue()))
-				throw new ParseException("\""+coordSys.toADQL()+"\" is not an allowed coordinate systems !", position);
-		}
-			
-		return coordSys;
-	}
+	{ return coordSys; }
 }
 
 GeometryFunction GeometryValueFunction(): {ADQLOperand coordSys; ADQLOperand width, height; ADQLOperand[] coords, tmp; Vector<ADQLOperand> vCoords; ADQLOperand op=null; GeometryValue<GeometryFunction> gvf = null; GeometryFunction gf = null;} {
@@ -1396,7 +1385,7 @@ ADQLFunction NumericFunction(): {ADQLFunction fct;} {
 	(fct=MathFunction()
 	| fct=TrigFunction()
 	| fct=GeometryFunction()
-	| fct=UserDefinedFunction())
+	| fct=UserDefinedFunction() { ((UserDefinedFunction)fct).setExpectedType('N'); })
 	{return fct;}
 }
 
@@ -1449,17 +1438,22 @@ MathFunction TrigFunction(): {Token fct=null; ADQLOperand param1=null, param2=nu
 	}
 }
 
-/* /!\ WARNING: The function name may be prefixed by "udf_" but there is no way to check it here ! */
 UserDefinedFunction UserDefinedFunction(): {Token fct; Vector<ADQLOperand> params = new Vector<ADQLOperand>(); ADQLOperand op;} {
 	fct=<REGULAR_IDENTIFIER> <LEFT_PAR> (op=ValueExpression() {params.add(op);} (<COMMA> op=ValueExpression() {params.add(op);})*)? <RIGHT_PAR>
 	{
 		//System.out.println("INFO [ADQLParser]: \""+fct.image+"\" (from line "+fct.beginLine+" and column "+fct.beginColumn+" to line "+token.endLine+" and column "+(token.endColumn+1)+") is considered as an user defined function !");
 		try{
+			//  Build the parameters list:
 			ADQLOperand[] parameters = new ADQLOperand[params.size()];
 			for(int i=0; i<params.size(); i++)
 				parameters[i] = params.get(i);
+
+			// Create the UDF function:
 			return queryFactory.createUserDefinedFunction(fct.image, parameters);
 		}catch(UnsupportedOperationException uoe){
+		  	/* This catch clause is just for backward compatibility:
+		  	 * if the createUserDefinedFunction(...) is overridden and
+		  	 * the function can not be identified a such exception may be thrown). */
 			throw new ParseException(uoe.getMessage(), new TextPosition(fct, token));
 		}catch(Exception ex){
 			throw generateParseException(ex);
diff --git a/src/adql/query/ADQLList.java b/src/adql/query/ADQLList.java
index 25d5933..68c93a1 100644
--- a/src/adql/query/ADQLList.java
+++ b/src/adql/query/ADQLList.java
@@ -16,7 +16,8 @@ package adql.query;
  * 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 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import java.util.Iterator;
@@ -27,8 +28,8 @@ import java.util.Vector;
  * 
  * <p>Since it is a list, it is possible to add, remove, modify and iterate on a such object.</p>
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 06/2011
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 2.2 (10/2014)
  * 
  * @see ClauseADQL
  * @see ClauseConstraints
@@ -164,23 +165,30 @@ public abstract class ADQLList< T extends ADQLObject > implements ADQLObject, It
 		return list.isEmpty();
 	}
 
+	@Override
 	public String getName(){
 		return name;
 	}
 
+	@Override
 	public String toADQL(){
-		String adql = (getName() == null) ? "" : (getName() + " ");
+		StringBuffer adql = new StringBuffer((getName() == null) ? "" : (getName() + " "));
 
-		for(int i = 0; i < size(); i++)
-			adql += ((i == 0) ? "" : (" " + getSeparator(i) + " ")) + get(i).toADQL();
+		for(int i = 0; i < size(); i++){
+			if (i > 0)
+				adql.append(" " + getSeparator(i) + " ");
+			adql.append(get(i).toADQL());
+		}
 
-		return adql;
+		return adql.toString();
 	}
 
+	@Override
 	public Iterator<T> iterator(){
 		return list.iterator();
 	}
 
+	@Override
 	public ADQLIterator adqlIterator(){
 		return new ADQLListIterator(this);
 	}
@@ -201,6 +209,7 @@ public abstract class ADQLList< T extends ADQLObject > implements ADQLObject, It
 	 */
 	public abstract String getSeparator(int index) throws ArrayIndexOutOfBoundsException;
 
+	@Override
 	public abstract ADQLObject getCopy() throws Exception;
 
 	/**
@@ -219,15 +228,18 @@ public abstract class ADQLList< T extends ADQLObject > implements ADQLObject, It
 			list = (ADQLList<ADQLObject>)lst;
 		}
 
+		@Override
 		public boolean hasNext(){
 			return index + 1 < list.size();
 		}
 
+		@Override
 		public ADQLObject next(){
 			removed = false;
 			return list.get(++index);
 		}
 
+		@Override
 		public void replace(ADQLObject replacer) throws UnsupportedOperationException, IllegalStateException{
 			if (index <= -1)
 				throw new IllegalStateException("replace(ADQLObject) impossible: next() has not yet been called !");
@@ -241,6 +253,7 @@ public abstract class ADQLList< T extends ADQLObject > implements ADQLObject, It
 				list.set(index, replacer);
 		}
 
+		@Override
 		public void remove(){
 			if (index <= -1)
 				throw new IllegalStateException("remove() impossible: next() has not yet been called !");
diff --git a/src/adql/query/ADQLQuery.java b/src/adql/query/ADQLQuery.java
index 75f3a64..95725e4 100644
--- a/src/adql/query/ADQLQuery.java
+++ b/src/adql/query/ADQLQuery.java
@@ -17,7 +17,7 @@ package adql.query;
  * along with ADQLLibrary.  If not, see <http://www.gnu.org/licenses/>.
  * 
  * Copyright 2012-2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
- *                       Astronomishes Rechen Institut (ARI)
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import java.util.ArrayList;
diff --git a/src/adql/query/from/ADQLJoin.java b/src/adql/query/from/ADQLJoin.java
index c719933..bbc7482 100644
--- a/src/adql/query/from/ADQLJoin.java
+++ b/src/adql/query/from/ADQLJoin.java
@@ -17,7 +17,7 @@ package adql.query.from;
  * along with ADQLLibrary.  If not, see <http://www.gnu.org/licenses/>.
  * 
  * Copyright 2012-2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
- *                       Astronomishes Rechen Institut (ARI)
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import java.util.ArrayList;
diff --git a/src/adql/query/from/FromContent.java b/src/adql/query/from/FromContent.java
index 64da1e5..1660ebf 100644
--- a/src/adql/query/from/FromContent.java
+++ b/src/adql/query/from/FromContent.java
@@ -17,7 +17,7 @@ package adql.query.from;
  * along with ADQLLibrary.  If not, see <http://www.gnu.org/licenses/>.
  * 
  * Copyright 2012-2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
- *                       Astronomishes Rechen Institut (ARI)
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import java.util.ArrayList;
diff --git a/src/adql/query/operand/ADQLColumn.java b/src/adql/query/operand/ADQLColumn.java
index d148bd7..57b30b5 100644
--- a/src/adql/query/operand/ADQLColumn.java
+++ b/src/adql/query/operand/ADQLColumn.java
@@ -16,11 +16,11 @@ package adql.query.operand;
  * 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 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - 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;
@@ -31,10 +31,10 @@ import adql.query.from.ADQLTable;
 /**
  * Represents the complete (literal) reference to a column ({schema(s)}.{table}.{column}).
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 07/2011
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 1.3 (10/2014)
  */
-public class ADQLColumn implements ADQLOperand {
+public class ADQLColumn implements ADQLOperand, UnknownType {
 
 	/** Position in the original ADQL query string. */
 	private TextPosition position = null;
@@ -60,6 +60,10 @@ public class ADQLColumn implements ADQLOperand {
 	/** 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 */
 	/* ************ */
@@ -457,26 +461,47 @@ public class ADQLColumn implements ADQLOperand {
 	/* ***************** */
 	/* INHERITED METHODS */
 	/* ***************** */
+	@Override
+	public char getExpectedType(){
+		return expectedType;
+	}
+
+	@Override
+	public void setExpectedType(final char c){
+		expectedType = c;
+	}
+
+	@Override
 	public boolean isNumeric(){
-		return true;
+		return (dbLink == null || dbLink.getDatatype() == null || dbLink.getDatatype().isNumeric());
 	}
 
+	@Override
 	public boolean isString(){
-		return true;
+		return (dbLink == null || dbLink.getDatatype() == null || dbLink.getDatatype().isString());
+	}
+
+	@Override
+	public boolean isGeometry(){
+		return (dbLink == null || dbLink.getDatatype() == null || dbLink.getDatatype().isGeometry());
 	}
 
+	@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();
 	}
diff --git a/src/adql/query/operand/ADQLOperand.java b/src/adql/query/operand/ADQLOperand.java
index 61dc647..413546b 100644
--- a/src/adql/query/operand/ADQLOperand.java
+++ b/src/adql/query/operand/ADQLOperand.java
@@ -16,22 +16,42 @@ package adql.query.operand;
  * 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 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import adql.query.ADQLObject;
 
 /**
  * <p>Any ADQL operand (an operation, a constant, a column name, a function, ...) must implement this interface
- * and indicates whether it corresponds to a numeric or a string value.</p>
+ * and indicates whether it corresponds to a numeric, a string or a geometrical region value.</p>
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 11/2010
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 1.3 (10/2014)
  */
 public interface ADQLOperand extends ADQLObject {
 
+	/**
+	 * Tell whether this operand is numeric or not.
+	 * 
+	 * @return	<i>true</i> if this operand is numeric, <i>false</i> otherwise.
+	 */
 	public boolean isNumeric();
 
+	/**
+	 * Tell whether this operand is a string or not.
+	 * 
+	 * @return	<i>true</i> if this operand is a string, <i>false</i> otherwise.
+	 */
 	public boolean isString();
 
+	/**
+	 * Tell whether this operand is a geometrical region or not.
+	 * 
+	 * @return	<i>true</i> if this operand is a geometry, <i>false</i> otherwise.
+	 * 
+	 * @since 1.3
+	 */
+	public boolean isGeometry();
+
 }
diff --git a/src/adql/query/operand/Concatenation.java b/src/adql/query/operand/Concatenation.java
index a09b790..24536c7 100644
--- a/src/adql/query/operand/Concatenation.java
+++ b/src/adql/query/operand/Concatenation.java
@@ -16,7 +16,8 @@ package adql.query.operand;
  * 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 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import adql.query.ADQLList;
@@ -25,8 +26,8 @@ import adql.query.ADQLObject;
 /**
  * Represents a concatenation in ADQL (ex: <i>"_s_ra" || ':' || "_s_dec"</i>).
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 11/2010
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 1.3 (10/2014)
  */
 public final class Concatenation extends ADQLList<ADQLOperand> implements ADQLOperand {
 
@@ -65,12 +66,19 @@ public final class Concatenation extends ADQLList<ADQLOperand> implements ADQLOp
 		return "||";
 	}
 
+	@Override
 	public final boolean isNumeric(){
 		return false;
 	}
 
+	@Override
 	public final boolean isString(){
 		return true;
 	}
 
+	@Override
+	public final boolean isGeometry(){
+		return false;
+	}
+
 }
\ No newline at end of file
diff --git a/src/adql/query/operand/NegativeOperand.java b/src/adql/query/operand/NegativeOperand.java
index 82fad6b..b6e4dd7 100644
--- a/src/adql/query/operand/NegativeOperand.java
+++ b/src/adql/query/operand/NegativeOperand.java
@@ -16,7 +16,8 @@ package adql.query.operand;
  * 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 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import java.util.NoSuchElementException;
@@ -27,8 +28,8 @@ import adql.query.ADQLObject;
 /**
  * Lets putting a minus sign in front of any numeric operand.
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 06/2011
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 1.3 (10/2014)
  */
 public final class NegativeOperand implements ADQLOperand {
 
@@ -66,6 +67,7 @@ public final class NegativeOperand implements ADQLOperand {
 	/** Always returns <i>true</i>.
 	 * @see adql.query.operand.ADQLOperand#isNumeric()
 	 */
+	@Override
 	public final boolean isNumeric(){
 		return true;
 	}
@@ -73,24 +75,37 @@ public final class NegativeOperand implements ADQLOperand {
 	/** Always returns <i>false</i>.
 	 * @see adql.query.operand.ADQLOperand#isString()
 	 */
+	@Override
 	public final boolean isString(){
 		return false;
 	}
 
+	/** Always returns <i>false</i>.
+	 * @see adql.query.operand.ADQLOperand#isGeometry()
+	 */
+	@Override
+	public final boolean isGeometry(){
+		return false;
+	}
+
+	@Override
 	public ADQLObject getCopy() throws Exception{
 		NegativeOperand copy = new NegativeOperand((ADQLOperand)operand.getCopy());
 		return copy;
 	}
 
+	@Override
 	public String getName(){
 		return "-" + operand.getName();
 	}
 
+	@Override
 	public ADQLIterator adqlIterator(){
 		return new ADQLIterator(){
 
 			private boolean operandGot = (operand == null);
 
+			@Override
 			public ADQLObject next(){
 				if (operandGot)
 					throw new NoSuchElementException();
@@ -98,10 +113,12 @@ public final class NegativeOperand implements ADQLOperand {
 				return operand;
 			}
 
+			@Override
 			public boolean hasNext(){
 				return !operandGot;
 			}
 
+			@Override
 			public void replace(ADQLObject replacer) throws UnsupportedOperationException, IllegalStateException{
 				if (!operandGot)
 					throw new IllegalStateException("replace(ADQLObject) impossible: next() has not yet been called !");
@@ -114,6 +131,7 @@ public final class NegativeOperand implements ADQLOperand {
 					throw new UnsupportedOperationException("Impossible to replace the operand \"" + operand.toADQL() + "\" by \"" + replacer.toADQL() + "\" in the NegativeOperand \"" + toADQL() + "\" because the replacer is not an ADQLOperand or is not numeric !");
 			}
 
+			@Override
 			public void remove(){
 				if (!operandGot)
 					throw new IllegalStateException("remove() impossible: next() has not yet been called !");
@@ -123,6 +141,7 @@ public final class NegativeOperand implements ADQLOperand {
 		};
 	}
 
+	@Override
 	public String toADQL(){
 		return "-" + operand.toADQL();
 	}
diff --git a/src/adql/query/operand/NumericConstant.java b/src/adql/query/operand/NumericConstant.java
index 2baa6d2..8baa697 100644
--- a/src/adql/query/operand/NumericConstant.java
+++ b/src/adql/query/operand/NumericConstant.java
@@ -16,7 +16,8 @@ package adql.query.operand;
  * 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 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import adql.query.ADQLIterator;
@@ -26,8 +27,8 @@ import adql.query.NullADQLIterator;
 /**
  * A numeric (integer, double, ...) constant.
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 06/2011
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 1.3 (10/2014)
  */
 public final class NumericConstant implements ADQLOperand {
 
@@ -149,6 +150,7 @@ public final class NumericConstant implements ADQLOperand {
 	/** Always returns <i>true</i>.
 	 * @see adql.query.operand.ADQLOperand#isNumeric()
 	 */
+	@Override
 	public final boolean isNumeric(){
 		return true;
 	}
@@ -156,22 +158,35 @@ public final class NumericConstant implements ADQLOperand {
 	/** Always returns <i>false</i>.
 	 * @see adql.query.operand.ADQLOperand#isString()
 	 */
+	@Override
 	public final boolean isString(){
 		return false;
 	}
 
+	/** Always returns <i>false</i>.
+	 * @see adql.query.operand.ADQLOperand#isGeometry()
+	 */
+	@Override
+	public final boolean isGeometry(){
+		return false;
+	}
+
+	@Override
 	public ADQLObject getCopy(){
 		return new NumericConstant(this);
 	}
 
+	@Override
 	public String getName(){
 		return value;
 	}
 
+	@Override
 	public ADQLIterator adqlIterator(){
 		return new NullADQLIterator();
 	}
 
+	@Override
 	public String toADQL(){
 		return value;
 	}
diff --git a/src/adql/query/operand/Operation.java b/src/adql/query/operand/Operation.java
index b5398ff..d890770 100644
--- a/src/adql/query/operand/Operation.java
+++ b/src/adql/query/operand/Operation.java
@@ -16,7 +16,8 @@ package adql.query.operand;
  * 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 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import java.util.NoSuchElementException;
@@ -27,8 +28,8 @@ import adql.query.ADQLObject;
 /**
  * It represents a simple numeric operation (sum, difference, multiplication and division).
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 06/2011
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 1.3 (10/2014)
  * 
  * @see OperationType
  */
@@ -160,6 +161,7 @@ public class Operation implements ADQLOperand {
 	/** Always returns <i>true</i>.
 	 * @see adql.query.operand.ADQLOperand#isNumeric()
 	 */
+	@Override
 	public final boolean isNumeric(){
 		return true;
 	}
@@ -167,24 +169,37 @@ public class Operation implements ADQLOperand {
 	/** Always returns <i>false</i>.
 	 * @see adql.query.operand.ADQLOperand#isString()
 	 */
+	@Override
 	public final boolean isString(){
 		return false;
 	}
 
+	/** Always returns <i>false</i>.
+	 * @see adql.query.operand.ADQLOperand#isGeometry()
+	 */
+	@Override
+	public final boolean isGeometry(){
+		return false;
+	}
+
+	@Override
 	public ADQLObject getCopy() throws Exception{
 		return new Operation(this);
 	}
 
+	@Override
 	public String getName(){
 		return operation.toString();
 	}
 
+	@Override
 	public ADQLIterator adqlIterator(){
 		return new ADQLIterator(){
 
 			private int index = -1;
 			private ADQLOperand operand = null;
 
+			@Override
 			public ADQLObject next(){
 				index++;
 
@@ -197,10 +212,12 @@ public class Operation implements ADQLOperand {
 				return operand;
 			}
 
+			@Override
 			public boolean hasNext(){
 				return index + 1 < 2;
 			}
 
+			@Override
 			public void replace(ADQLObject replacer) throws UnsupportedOperationException, IllegalStateException{
 				if (index <= -1)
 					throw new IllegalStateException("replace(ADQLObject) impossible: next() has not yet been called !");
@@ -218,6 +235,7 @@ public class Operation implements ADQLOperand {
 				}
 			}
 
+			@Override
 			public void remove(){
 				if (index <= -1)
 					throw new IllegalStateException("remove() impossible: next() has not yet been called !");
@@ -227,6 +245,7 @@ public class Operation implements ADQLOperand {
 		};
 	}
 
+	@Override
 	public String toADQL(){
 		return leftOperand.toADQL() + operation.toADQL() + rightOperand.toADQL();
 	}
diff --git a/src/adql/query/operand/StringConstant.java b/src/adql/query/operand/StringConstant.java
index 2e2b0d4..6c115e7 100644
--- a/src/adql/query/operand/StringConstant.java
+++ b/src/adql/query/operand/StringConstant.java
@@ -16,7 +16,8 @@ package adql.query.operand;
  * 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 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import adql.query.ADQLIterator;
@@ -26,8 +27,8 @@ import adql.query.NullADQLIterator;
 /**
  * A string constant.
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 06/2011
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 1.3 (10/2014)
  */
 public final class StringConstant implements ADQLOperand {
 
@@ -49,28 +50,39 @@ public final class StringConstant implements ADQLOperand {
 		this.value = value;
 	}
 
+	@Override
 	public final boolean isNumeric(){
 		return false;
 	}
 
+	@Override
 	public final boolean isString(){
 		return true;
 	}
 
+	@Override
+	public final boolean isGeometry(){
+		return false;
+	}
+
+	@Override
 	public ADQLObject getCopy(){
 		return new StringConstant(this);
 	}
 
+	@Override
 	public String getName(){
-		return "'" + value + "'";
+		return toADQL();
 	}
 
+	@Override
 	public ADQLIterator adqlIterator(){
 		return new NullADQLIterator();
 	}
 
+	@Override
 	public String toADQL(){
-		return "'" + value + "'";
+		return "'" + value.replaceAll("'", "''") + "'";
 	}
 
 }
diff --git a/src/adql/query/operand/UnknownType.java b/src/adql/query/operand/UnknownType.java
new file mode 100644
index 0000000..bae9d9b
--- /dev/null
+++ b/src/adql/query/operand/UnknownType.java
@@ -0,0 +1,52 @@
+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 2014 - Astronomisches Rechen Institut (ARI)
+ */
+
+import adql.query.operand.function.UserDefinedFunction;
+
+/**
+ * <p>Operand whose the type can not be known at the parsing time.
+ * A post-parsing step with column metadata is needed to resolved their types.</p>
+ * 
+ * <p><i>Note:
+ * 	For the moment, only two operands are concerned: columns ({@link ADQLColumn}) and user defined functions ({@link UserDefinedFunction}).
+ * </i></p>
+ * 
+ * @author Gr&eacute;gory Mantelet (ARI)
+ * @version 1.3 (10/2014)
+ * @since 1.3
+ */
+public interface UnknownType extends ADQLOperand {
+
+	/**
+	 * Get the type expected by the syntactic parser according to the context.
+	 * 
+	 * @return	Expected type: 'n' or 'N' for numeric, 's' or 'S' for string, 'g' or 'G' for geometry.
+	 */
+	public char getExpectedType();
+
+	/**
+	 * Set the type expected for this operand.
+	 * 
+	 * @param c	Expected type: 'n' or 'N' for numeric, 's' or 'S' for string, 'g' or 'G' for geometry.
+	 */
+	public void setExpectedType(final char c);
+
+}
diff --git a/src/adql/query/operand/WrappedOperand.java b/src/adql/query/operand/WrappedOperand.java
index 2ffae9b..98fde1d 100644
--- a/src/adql/query/operand/WrappedOperand.java
+++ b/src/adql/query/operand/WrappedOperand.java
@@ -16,7 +16,8 @@ package adql.query.operand;
  * 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 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import java.util.NoSuchElementException;
@@ -27,8 +28,8 @@ import adql.query.ADQLObject;
 /**
  * Lets wrapping an operand by parenthesis.
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 06/2011
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 1.3 (10/2014)
  */
 public class WrappedOperand implements ADQLOperand {
 
@@ -57,27 +58,38 @@ public class WrappedOperand implements ADQLOperand {
 		return operand;
 	}
 
+	@Override
 	public final boolean isNumeric(){
 		return operand.isNumeric();
 	}
 
+	@Override
 	public final boolean isString(){
 		return operand.isString();
 	}
 
+	@Override
+	public final boolean isGeometry(){
+		return operand.isGeometry();
+	}
+
+	@Override
 	public ADQLObject getCopy() throws Exception{
 		return new WrappedOperand((ADQLOperand)operand.getCopy());
 	}
 
+	@Override
 	public String getName(){
 		return "(" + operand.getName() + ")";
 	}
 
+	@Override
 	public ADQLIterator adqlIterator(){
 		return new ADQLIterator(){
 
 			private boolean operandGot = (operand == null);
 
+			@Override
 			public ADQLObject next(){
 				if (operandGot)
 					throw new NoSuchElementException();
@@ -85,10 +97,12 @@ public class WrappedOperand implements ADQLOperand {
 				return operand;
 			}
 
+			@Override
 			public boolean hasNext(){
 				return !operandGot;
 			}
 
+			@Override
 			public void replace(ADQLObject replacer) throws UnsupportedOperationException, IllegalStateException{
 				if (!operandGot)
 					throw new IllegalStateException("replace(ADQLObject) impossible: next() has not yet been called !");
@@ -101,6 +115,7 @@ public class WrappedOperand implements ADQLOperand {
 					throw new UnsupportedOperationException("Impossible to replace an ADQLOperand (\"" + operand + "\") by a " + replacer.getClass().getName() + " (\"" + replacer.toADQL() + "\") !");
 			}
 
+			@Override
 			public void remove(){
 				if (!operandGot)
 					throw new IllegalStateException("remove() impossible: next() has not yet been called !");
@@ -110,6 +125,7 @@ public class WrappedOperand implements ADQLOperand {
 		};
 	}
 
+	@Override
 	public String toADQL(){
 		return "(" + operand.toADQL() + ")";
 	}
diff --git a/src/adql/query/operand/function/DefaultUDF.java b/src/adql/query/operand/function/DefaultUDF.java
index c084365..dd0c615 100644
--- a/src/adql/query/operand/function/DefaultUDF.java
+++ b/src/adql/query/operand/function/DefaultUDF.java
@@ -16,9 +16,11 @@ package adql.query.operand.function;
  * 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-2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS), Astronomisches Rechen Institut (ARI)
+ * Copyright 2012-2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
+import adql.db.FunctionDef;
 import adql.query.ADQLList;
 import adql.query.ADQLObject;
 import adql.query.ClauseADQL;
@@ -28,13 +30,18 @@ import adql.query.operand.ADQLOperand;
  * It represents any function which is not managed by ADQL.
  * 
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 1.2 (04/2014)
+ * @version 1.3 (10/2014)
  */
 public final class DefaultUDF extends UserDefinedFunction {
 
-	/** Its parameters. */
+	/** Define/Describe this user defined function.
+	 * This object gives the return type and the number and type of all parameters. */
+	protected FunctionDef definition = null;
+
+	/** Its parsed parameters. */
 	protected final ADQLList<ADQLOperand> parameters;
 
+	/** Parsed name of this UDF. */
 	protected final String functionName;
 
 	/**
@@ -62,19 +69,56 @@ public final class DefaultUDF extends UserDefinedFunction {
 		parameters = (ADQLList<ADQLOperand>)(toCopy.parameters.getCopy());
 	}
 
+	/**
+	 * Get the signature/definition/description of this user defined function.
+	 * The returned object provides information on the return type and the number and type of parameters. 
+	 * 
+	 * @return	Definition of this function. (MAY be NULL)
+	 */
+	public final FunctionDef getDefinition(){
+		return definition;
+	}
+
+	/**
+	 * <p>Let set the signature/definition/description of this user defined function.</p>
+	 * 
+	 * <p><i><b>IMPORTANT:</b>
+	 * 	No particular checks are done here except on the function name which MUST
+	 * 	be the same (case insensitive) as the name of the given definition.
+	 * 	Advanced checks must have been done before calling this setter.
+	 * </i></p>
+	 * 
+	 * @param def	The definition applying to this parsed UDF, or NULL if none has been found.
+	 * 
+	 * @throws IllegalArgumentException	If the name in the given definition does not match the name of this parsed function.
+	 */
+	public final void setDefinition(final FunctionDef def) throws IllegalArgumentException{
+		if (def != null && (def.name == null || !functionName.equalsIgnoreCase(def.name)))
+			throw new IllegalArgumentException("The parsed function name (" + functionName + ") does not match to the name of the given UDF definition (" + def.name + ").");
+
+		this.definition = def;
+	}
+
 	@Override
 	public final boolean isNumeric(){
-		return true;
+		return (definition == null || definition.isNumeric());
 	}
 
 	@Override
 	public final boolean isString(){
-		return true;
+		return (definition == null || definition.isString());
+	}
+
+	@Override
+	public final boolean isGeometry(){
+		return (definition == null || definition.isGeometry());
 	}
 
 	@Override
 	public ADQLObject getCopy() throws Exception{
-		return new DefaultUDF(this);
+		DefaultUDF copy = new DefaultUDF(this);
+		copy.setDefinition(definition);
+		return copy;
 	}
 
 	@Override
diff --git a/src/adql/query/operand/function/MathFunction.java b/src/adql/query/operand/function/MathFunction.java
index 15acf0a..c29d64a 100644
--- a/src/adql/query/operand/function/MathFunction.java
+++ b/src/adql/query/operand/function/MathFunction.java
@@ -27,7 +27,7 @@ import adql.query.operand.ADQLOperand;
  * It represents any basic mathematical function.
  * 
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 1.2 (03/2014)
+ * @version 1.3 (10/2014)
  * 
  * @see MathFunctionType
  */
@@ -149,6 +149,11 @@ public class MathFunction extends ADQLFunction {
 		return false;
 	}
 
+	@Override
+	public final boolean isGeometry(){
+		return false;
+	}
+
 	@Override
 	public ADQLOperand[] getParameters(){
 		switch(getNbParameters()){
diff --git a/src/adql/query/operand/function/SQLFunction.java b/src/adql/query/operand/function/SQLFunction.java
index 3442aea..6603fd9 100644
--- a/src/adql/query/operand/function/SQLFunction.java
+++ b/src/adql/query/operand/function/SQLFunction.java
@@ -16,18 +16,18 @@ package adql.query.operand.function;
  * 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 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import adql.query.ADQLObject;
-
 import adql.query.operand.ADQLOperand;
 
 /**
  * It represents any SQL function (COUNT, MAX, MIN, AVG, SUM, etc...).
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 06/2011
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 1.3 (10/2014)
  * 
  * @see SQLFunctionType
  */
@@ -116,22 +116,31 @@ public class SQLFunction extends ADQLFunction {
 		return type;
 	}
 
+	@Override
 	public ADQLObject getCopy() throws Exception{
 		return new SQLFunction(this);
 	}
 
+	@Override
 	public String getName(){
 		return type.name();
 	}
 
+	@Override
 	public final boolean isNumeric(){
 		return true;
 	}
 
+	@Override
 	public final boolean isString(){
 		return false;
 	}
 
+	@Override
+	public final boolean isGeometry(){
+		return false;
+	}
+
 	@Override
 	public ADQLOperand[] getParameters(){
 		if (param != null)
diff --git a/src/adql/query/operand/function/UserDefinedFunction.java b/src/adql/query/operand/function/UserDefinedFunction.java
index 39d8778..78026e7 100644
--- a/src/adql/query/operand/function/UserDefinedFunction.java
+++ b/src/adql/query/operand/function/UserDefinedFunction.java
@@ -1,5 +1,7 @@
 package adql.query.operand.function;
 
+import adql.query.operand.UnknownType;
+
 /*
  * This file is part of ADQLLibrary.
  * 
@@ -16,17 +18,32 @@ package adql.query.operand.function;
  * 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 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 /**
  * Function defined by the user (i.e. PSQL functions).
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 01/2012
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 1.3 (10/2014)
  * 
  * @see DefaultUDF
  */
-public abstract class UserDefinedFunction extends ADQLFunction {
+public abstract class UserDefinedFunction extends ADQLFunction implements UnknownType {
+
+	/** Type expected by the parser.
+	 * @since 1.3 */
+	private char expectedType = '?';
+
+	@Override
+	public char getExpectedType(){
+		return expectedType;
+	}
+
+	@Override
+	public void setExpectedType(final char c){
+		expectedType = c;
+	}
 
 }
diff --git a/src/adql/query/operand/function/geometry/AreaFunction.java b/src/adql/query/operand/function/geometry/AreaFunction.java
index 42906c9..6b163ac 100644
--- a/src/adql/query/operand/function/geometry/AreaFunction.java
+++ b/src/adql/query/operand/function/geometry/AreaFunction.java
@@ -16,13 +16,13 @@ package adql.query.operand.function.geometry;
  * 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 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import adql.query.ADQLObject;
-
-import adql.query.operand.ADQLOperand;
 import adql.query.operand.ADQLColumn;
+import adql.query.operand.ADQLOperand;
 
 /**
  * <p>It represents the AREA function of ADQL.</p>
@@ -33,8 +33,8 @@ import adql.query.operand.ADQLColumn;
  * 
  * <p>Inappropriate geometries for this construct (e.g. POINT) SHOULD either return zero or throw an error message. <b>This choice must be done in an extended class of {@link AreaFunction}</b>.</p>
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 06/2011
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 1.3 (10/2014)
  */
 public class AreaFunction extends GeometryFunction {
 
@@ -47,7 +47,6 @@ public class AreaFunction extends GeometryFunction {
 	 * @param param					Parameter of AREA.
 	 * @throws NullPointerException	If the given operand is <i>null</i> or if it's not a {@link GeometryFunction}.
 	 */
-	@SuppressWarnings("unchecked")
 	public AreaFunction(GeometryValue<GeometryFunction> param) throws NullPointerException{
 		super();
 		if (param == null)
@@ -88,22 +87,31 @@ public class AreaFunction extends GeometryFunction {
 		this.parameter = parameter;
 	}
 
+	@Override
 	public ADQLObject getCopy() throws Exception{
 		return new AreaFunction(this);
 	}
 
+	@Override
 	public String getName(){
 		return "AREA";
 	}
 
+	@Override
 	public boolean isNumeric(){
 		return true;
 	}
 
+	@Override
 	public boolean isString(){
 		return false;
 	}
 
+	@Override
+	public boolean isGeometry(){
+		return false;
+	}
+
 	@Override
 	public ADQLOperand[] getParameters(){
 		return new ADQLOperand[]{parameter.getValue()};
diff --git a/src/adql/query/operand/function/geometry/BoxFunction.java b/src/adql/query/operand/function/geometry/BoxFunction.java
index e907133..7e064a4 100644
--- a/src/adql/query/operand/function/geometry/BoxFunction.java
+++ b/src/adql/query/operand/function/geometry/BoxFunction.java
@@ -16,11 +16,11 @@ package adql.query.operand.function.geometry;
  * 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 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import adql.query.ADQLObject;
-
 import adql.query.operand.ADQLOperand;
 
 /**
@@ -38,9 +38,8 @@ import adql.query.operand.ADQLOperand;
  * In this second example the coordinates of the center position are extracted from a coordinate's column reference.
  * </i></p>
  * 
- * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 06/2011
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 1.3 (10/2014)
  */
 public class BoxFunction extends GeometryFunction {
 
@@ -93,19 +92,28 @@ public class BoxFunction extends GeometryFunction {
 		height = (ADQLOperand)(toCopy.height.getCopy());
 	}
 
+	@Override
 	public ADQLObject getCopy() throws Exception{
 		return new BoxFunction(this);
 	}
 
+	@Override
 	public String getName(){
 		return "BOX";
 	}
 
+	@Override
 	public boolean isNumeric(){
 		return false;
 	}
 
+	@Override
 	public boolean isString(){
+		return false;
+	}
+
+	@Override
+	public boolean isGeometry(){
 		return true;
 	}
 
diff --git a/src/adql/query/operand/function/geometry/CentroidFunction.java b/src/adql/query/operand/function/geometry/CentroidFunction.java
index 88f80e3..3b3b014 100644
--- a/src/adql/query/operand/function/geometry/CentroidFunction.java
+++ b/src/adql/query/operand/function/geometry/CentroidFunction.java
@@ -16,13 +16,13 @@ package adql.query.operand.function.geometry;
  * 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 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import adql.query.ADQLObject;
-
-import adql.query.operand.ADQLOperand;
 import adql.query.operand.ADQLColumn;
+import adql.query.operand.ADQLOperand;
 
 /**
  * <p>It represents the CENTROID function of the ADQL language.</p>
@@ -35,8 +35,8 @@ import adql.query.operand.ADQLColumn;
  * in a position of (25.4,-20.0) degrees and defined according to the ICRS coordinate system with GEOCENTER reference position.
  * </i></p>
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 06/2011
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 1.3 (10/2014)
  */
 public class CentroidFunction extends GeometryFunction {
 
@@ -68,22 +68,31 @@ public class CentroidFunction extends GeometryFunction {
 		parameter = (GeometryValue<GeometryFunction>)(toCopy.parameter.getCopy());
 	}
 
+	@Override
 	public ADQLObject getCopy() throws Exception{
 		return new CentroidFunction(this);
 	}
 
+	@Override
 	public String getName(){
 		return "CENTROID";
 	}
 
+	@Override
 	public boolean isNumeric(){
 		return true;
 	}
 
+	@Override
 	public boolean isString(){
 		return false;
 	}
 
+	@Override
+	public boolean isGeometry(){
+		return false;
+	}
+
 	@Override
 	public ADQLOperand[] getParameters(){
 		return new ADQLOperand[]{parameter.getValue()};
diff --git a/src/adql/query/operand/function/geometry/CircleFunction.java b/src/adql/query/operand/function/geometry/CircleFunction.java
index aff59d8..f979d38 100644
--- a/src/adql/query/operand/function/geometry/CircleFunction.java
+++ b/src/adql/query/operand/function/geometry/CircleFunction.java
@@ -16,11 +16,11 @@ package adql.query.operand.function.geometry;
  * 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 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import adql.query.ADQLObject;
-
 import adql.query.operand.ADQLOperand;
 
 /**
@@ -35,8 +35,8 @@ import adql.query.operand.ADQLOperand;
  * according to the ICRS coordinate system with GEOCENTER reference position.
  * </i></p>
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 06/2011
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 1.3 (10/2014)
  */
 public class CircleFunction extends GeometryFunction {
 
@@ -83,19 +83,28 @@ public class CircleFunction extends GeometryFunction {
 		radius = (ADQLOperand)(toCopy.radius.getCopy());
 	}
 
+	@Override
 	public ADQLObject getCopy() throws Exception{
 		return new CircleFunction(this);
 	}
 
+	@Override
 	public String getName(){
 		return "CIRCLE";
 	}
 
+	@Override
 	public boolean isNumeric(){
 		return false;
 	}
 
+	@Override
 	public boolean isString(){
+		return false;
+	}
+
+	@Override
+	public boolean isGeometry(){
 		return true;
 	}
 
diff --git a/src/adql/query/operand/function/geometry/ContainsFunction.java b/src/adql/query/operand/function/geometry/ContainsFunction.java
index f97a9c7..4cb4aad 100644
--- a/src/adql/query/operand/function/geometry/ContainsFunction.java
+++ b/src/adql/query/operand/function/geometry/ContainsFunction.java
@@ -16,13 +16,13 @@ package adql.query.operand.function.geometry;
  * 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 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import adql.query.ADQLObject;
-
-import adql.query.operand.ADQLOperand;
 import adql.query.operand.ADQLColumn;
+import adql.query.operand.ADQLOperand;
 
 /**
  * <p>It represents the CONTAINS function of the ADQL language.</p>
@@ -41,8 +41,8 @@ import adql.query.operand.ADQLColumn;
  * If it can not do so, it SHOULD throw an error message, to be defined by the service making use of ADQL.</li></ul>
  * </b></p>
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 06/2011
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 1.3 (10/2014)
  */
 public class ContainsFunction extends GeometryFunction {
 
@@ -81,22 +81,31 @@ public class ContainsFunction extends GeometryFunction {
 		rightParam = (GeometryValue<GeometryFunction>)(toCopy.rightParam.getCopy());
 	}
 
+	@Override
 	public ADQLObject getCopy() throws Exception{
 		return new ContainsFunction(this);
 	}
 
+	@Override
 	public String getName(){
 		return "CONTAINS";
 	}
 
+	@Override
 	public boolean isNumeric(){
 		return true;
 	}
 
+	@Override
 	public boolean isString(){
 		return false;
 	}
 
+	@Override
+	public boolean isGeometry(){
+		return false;
+	}
+
 	/**
 	 * @return The leftParam.
 	 */
diff --git a/src/adql/query/operand/function/geometry/DistanceFunction.java b/src/adql/query/operand/function/geometry/DistanceFunction.java
index d942967..ead9b4b 100644
--- a/src/adql/query/operand/function/geometry/DistanceFunction.java
+++ b/src/adql/query/operand/function/geometry/DistanceFunction.java
@@ -16,13 +16,13 @@ package adql.query.operand.function.geometry;
  * 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 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import adql.query.ADQLObject;
-
-import adql.query.operand.ADQLOperand;
 import adql.query.operand.ADQLColumn;
+import adql.query.operand.ADQLOperand;
 
 /**
  * <p>It represents the DISTANCE function of the ADQL language.</p>
@@ -34,8 +34,8 @@ import adql.query.operand.ADQLColumn;
  * In this example the function computes the distance between two points of coordinates (25, -19.5) and (25.4, -20) both expressed according to the ICRS
  * coordinate system with GEOCENTER reference position.</i></p>
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 06/2011
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 1.3 (10/2014)
  */
 public class DistanceFunction extends GeometryFunction {
 
@@ -79,22 +79,31 @@ public class DistanceFunction extends GeometryFunction {
 		throw new UnsupportedOperationException("A DISTANCE function is not associated to a coordinate system !");
 	}
 
+	@Override
 	public ADQLObject getCopy() throws Exception{
 		return new DistanceFunction(this);
 	}
 
+	@Override
 	public String getName(){
 		return "DISTANCE";
 	}
 
+	@Override
 	public boolean isNumeric(){
 		return true;
 	}
 
+	@Override
 	public boolean isString(){
 		return false;
 	}
 
+	@Override
+	public boolean isGeometry(){
+		return false;
+	}
+
 	/**
 	 * Gets the first point.
 	 * 
diff --git a/src/adql/query/operand/function/geometry/ExtractCoord.java b/src/adql/query/operand/function/geometry/ExtractCoord.java
index 6f016c9..2fa17cd 100644
--- a/src/adql/query/operand/function/geometry/ExtractCoord.java
+++ b/src/adql/query/operand/function/geometry/ExtractCoord.java
@@ -16,13 +16,13 @@ package adql.query.operand.function.geometry;
  * 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 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import adql.query.ADQLObject;
-
-import adql.query.operand.ADQLOperand;
 import adql.query.operand.ADQLColumn;
+import adql.query.operand.ADQLOperand;
 
 /**
  * <p>It represents the COORD1 and the COORD2 functions of the ADQL language.</p>
@@ -35,8 +35,8 @@ import adql.query.operand.ADQLColumn;
  * system with GEOCENTER reference position.
  * </i></p>
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 06/2011
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 1.3 (10/2014)
  */
 public class ExtractCoord extends GeometryFunction {
 
@@ -78,22 +78,31 @@ public class ExtractCoord extends GeometryFunction {
 		point = (GeometryValue<PointFunction>)(toCopy.point.getCopy());
 	}
 
+	@Override
 	public String getName(){
 		return "COORD" + indCoord;
 	}
 
+	@Override
 	public ADQLObject getCopy() throws Exception{
 		return new ExtractCoord(this);
 	}
 
+	@Override
 	public boolean isNumeric(){
 		return true;
 	}
 
+	@Override
 	public boolean isString(){
 		return false;
 	}
 
+	@Override
+	public boolean isGeometry(){
+		return false;
+	}
+
 	@Override
 	public ADQLOperand[] getParameters(){
 		return new ADQLOperand[]{point.getValue()};
diff --git a/src/adql/query/operand/function/geometry/ExtractCoordSys.java b/src/adql/query/operand/function/geometry/ExtractCoordSys.java
index 5337f49..dcd8e54 100644
--- a/src/adql/query/operand/function/geometry/ExtractCoordSys.java
+++ b/src/adql/query/operand/function/geometry/ExtractCoordSys.java
@@ -16,13 +16,13 @@ package adql.query.operand.function.geometry;
  * 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 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import adql.query.ADQLObject;
-
-import adql.query.operand.ADQLOperand;
 import adql.query.operand.ADQLColumn;
+import adql.query.operand.ADQLOperand;
 
 /**
  * <p>It represents the COORDSYS function the ADQL language.</p>
@@ -35,8 +35,8 @@ import adql.query.operand.ADQLColumn;
  * system with GEOCENTER reference position.
  * </i></p>
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 06/2011
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 1.3 (10/2014)
  */
 public class ExtractCoordSys extends GeometryFunction {
 
@@ -65,22 +65,31 @@ public class ExtractCoordSys extends GeometryFunction {
 		geomExpr = (GeometryValue<GeometryFunction>)(toCopy.geomExpr.getCopy());
 	}
 
+	@Override
 	public ADQLObject getCopy() throws Exception{
 		return new ExtractCoordSys(this);
 	}
 
+	@Override
 	public String getName(){
 		return "COORDSYS";
 	}
 
+	@Override
 	public boolean isNumeric(){
 		return false;
 	}
 
+	@Override
 	public boolean isString(){
 		return true;
 	}
 
+	@Override
+	public boolean isGeometry(){
+		return false;
+	}
+
 	@Override
 	public ADQLOperand[] getParameters(){
 		return new ADQLOperand[]{geomExpr.getValue()};
diff --git a/src/adql/query/operand/function/geometry/GeometryFunction.java b/src/adql/query/operand/function/geometry/GeometryFunction.java
index 4270eb1..e6e10ec 100644
--- a/src/adql/query/operand/function/geometry/GeometryFunction.java
+++ b/src/adql/query/operand/function/geometry/GeometryFunction.java
@@ -16,20 +16,23 @@ package adql.query.operand.function.geometry;
  * 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 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
+import adql.parser.ParseException;
 import adql.query.ADQLIterator;
 import adql.query.ADQLObject;
-import adql.query.operand.ADQLOperand;
 import adql.query.operand.ADQLColumn;
+import adql.query.operand.ADQLOperand;
+import adql.query.operand.StringConstant;
 import adql.query.operand.function.ADQLFunction;
 
 /**
  * <p>It represents any geometric function of ADQL.</p>
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 06/2011
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 1.3 (10/2014)
  */
 public abstract class GeometryFunction extends ADQLFunction {
 
@@ -80,13 +83,13 @@ public abstract class GeometryFunction extends ADQLFunction {
 	 * @param coordSys							Its new coordinate system.
 	 * @throws UnsupportedOperationException	If this function is not associated with a coordinate system.
 	 * @throws NullPointerException				If the given operand is <i>null</i>.
-	 * @throws Exception						If the given operand is not a string.
+	 * @throws ParseException					If the given operand is not a string.
 	 */
-	public void setCoordinateSystem(ADQLOperand coordSys) throws UnsupportedOperationException, NullPointerException, Exception{
+	public void setCoordinateSystem(ADQLOperand coordSys) throws UnsupportedOperationException, NullPointerException, ParseException{
 		if (coordSys == null)
-			throw new NullPointerException("");
+			this.coordSys = new StringConstant("");
 		else if (!coordSys.isString())
-			throw new Exception("A coordinate system must be a string literal: \"" + coordSys.toADQL() + "\" is not a string operand !");
+			throw new ParseException("A coordinate system must be a string literal: \"" + coordSys.toADQL() + "\" is not a string operand!");
 		else
 			this.coordSys = coordSys;
 	}
@@ -95,8 +98,8 @@ public abstract class GeometryFunction extends ADQLFunction {
 	 * This class represents a parameter of a geometry function
 	 * which, in general, is either a GeometryFunction or a Column.
 	 * 
-	 * @author Gr&eacute;gory Mantelet (CDS)
-	 * @version 06/2011
+	 * @author Gr&eacute;gory Mantelet (CDS;ARI)
+	 * @version 1.3 (10/2014)
 	 */
 	public static final class GeometryValue< F extends GeometryFunction > implements ADQLOperand {
 		private ADQLColumn column;
@@ -142,26 +145,37 @@ public abstract class GeometryFunction extends ADQLFunction {
 			return column != null;
 		}
 
+		@Override
 		public boolean isNumeric(){
 			return getValue().isNumeric();
 		}
 
+		@Override
 		public boolean isString(){
 			return getValue().isString();
 		}
 
+		@Override
+		public boolean isGeometry(){
+			return getValue().isGeometry();
+		}
+
+		@Override
 		public ADQLObject getCopy() throws Exception{
 			return new GeometryValue<F>(this);
 		}
 
+		@Override
 		public String getName(){
 			return getValue().getName();
 		}
 
+		@Override
 		public ADQLIterator adqlIterator(){
 			return getValue().adqlIterator();
 		}
 
+		@Override
 		public String toADQL(){
 			return getValue().toADQL();
 		}
diff --git a/src/adql/query/operand/function/geometry/IntersectsFunction.java b/src/adql/query/operand/function/geometry/IntersectsFunction.java
index 0a37597..29bc881 100644
--- a/src/adql/query/operand/function/geometry/IntersectsFunction.java
+++ b/src/adql/query/operand/function/geometry/IntersectsFunction.java
@@ -16,13 +16,13 @@ package adql.query.operand.function.geometry;
  * 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 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import adql.query.ADQLObject;
-
-import adql.query.operand.ADQLOperand;
 import adql.query.operand.ADQLColumn;
+import adql.query.operand.ADQLOperand;
 
 /**
  * <p>It represents the INTERSECTS function of the ADQL language.</p>
@@ -42,8 +42,8 @@ import adql.query.operand.ADQLColumn;
  * If it can not do so, it SHOULD throw an error message, to be defined by the service making use of ADQL.</li></ul>
  * </b></p>
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 06/2011
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 1.3 (10/2014)
  */
 public class IntersectsFunction extends GeometryFunction {
 
@@ -82,22 +82,31 @@ public class IntersectsFunction extends GeometryFunction {
 		rightParam = (GeometryValue<GeometryFunction>)(toCopy.rightParam.getCopy());
 	}
 
+	@Override
 	public ADQLObject getCopy() throws Exception{
 		return new IntersectsFunction(this);
 	}
 
+	@Override
 	public String getName(){
 		return "INTERSECTS";
 	}
 
+	@Override
 	public boolean isNumeric(){
 		return true;
 	}
 
+	@Override
 	public boolean isString(){
 		return false;
 	}
 
+	@Override
+	public boolean isGeometry(){
+		return false;
+	}
+
 	/**
 	 * @return The leftParam.
 	 */
diff --git a/src/adql/query/operand/function/geometry/PointFunction.java b/src/adql/query/operand/function/geometry/PointFunction.java
index 626e34f..1b736ff 100644
--- a/src/adql/query/operand/function/geometry/PointFunction.java
+++ b/src/adql/query/operand/function/geometry/PointFunction.java
@@ -16,11 +16,11 @@ package adql.query.operand.function.geometry;
  * 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 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import adql.query.ADQLObject;
-
 import adql.query.operand.ADQLOperand;
 
 /**
@@ -34,8 +34,8 @@ import adql.query.operand.ADQLOperand;
  * In this example the function expresses a point with right ascension of 25 degrees and declination of  -19.5 degrees according
  * to the ICRS coordinate system with GEOCENTER reference position.</i></p>
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 06/2011
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 1.3 (10/2014)
  */
 public class PointFunction extends GeometryFunction {
 
@@ -59,7 +59,7 @@ public class PointFunction extends GeometryFunction {
 		super(coordinateSystem);
 
 		if (firstCoord == null || secondCoord == null)
-			throw new NullPointerException("The POINT function must have non-null coordinates !");
+			throw new NullPointerException("The POINT function must have non-null coordinates!");
 
 		coord1 = firstCoord;
 		coord2 = secondCoord;
@@ -127,19 +127,28 @@ public class PointFunction extends GeometryFunction {
 			this.coord2 = coord2;
 	}
 
+	@Override
 	public ADQLObject getCopy() throws Exception{
 		return new PointFunction(this);
 	}
 
+	@Override
 	public String getName(){
 		return "POINT";
 	}
 
+	@Override
 	public boolean isNumeric(){
 		return false;
 	}
 
+	@Override
 	public boolean isString(){
+		return false;
+	}
+
+	@Override
+	public boolean isGeometry(){
 		return true;
 	}
 
diff --git a/src/adql/query/operand/function/geometry/PolygonFunction.java b/src/adql/query/operand/function/geometry/PolygonFunction.java
index 07e47ba..33dd2a6 100644
--- a/src/adql/query/operand/function/geometry/PolygonFunction.java
+++ b/src/adql/query/operand/function/geometry/PolygonFunction.java
@@ -16,13 +16,14 @@ package adql.query.operand.function.geometry;
  * 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 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
+import java.util.Collection;
 import java.util.Vector;
 
 import adql.query.ADQLObject;
-
 import adql.query.operand.ADQLOperand;
 
 /**
@@ -40,8 +41,8 @@ import adql.query.operand.ADQLOperand;
  * In this example the function expresses a triangle, whose vertices are (10.0, -10.5), (20.0, 20.5) and (30.0, 30.5) in degrees
  * according to the STC coordinate system with GEOCENTER reference position.</i></p>
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 06/2011
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 1.3 (10/2014)
  */
 public class PolygonFunction extends GeometryFunction {
 
@@ -79,7 +80,7 @@ public class PolygonFunction extends GeometryFunction {
 	 * @throws NullPointerException				If one of the parameters is <i>null</i>.
 	 * @throws Exception						If there is another error.
 	 */
-	public PolygonFunction(ADQLOperand coordSystem, Vector<ADQLOperand> coords) throws UnsupportedOperationException, NullPointerException, Exception{
+	public PolygonFunction(ADQLOperand coordSystem, Collection<? extends ADQLOperand> coords) throws UnsupportedOperationException, NullPointerException, Exception{
 		super(coordSystem);
 		if (coords == null || coords.size() < 6)
 			throw new NullPointerException("A POLYGON function must have at least 3 2-D coordinates !");
@@ -102,19 +103,28 @@ public class PolygonFunction extends GeometryFunction {
 			coordinates.add((ADQLOperand)(item.getCopy()));
 	}
 
+	@Override
 	public ADQLObject getCopy() throws Exception{
 		return new PolygonFunction(this);
 	}
 
+	@Override
 	public String getName(){
 		return "POLYGON";
 	}
 
+	@Override
 	public boolean isNumeric(){
 		return false;
 	}
 
+	@Override
 	public boolean isString(){
+		return false;
+	}
+
+	@Override
+	public boolean isGeometry(){
 		return true;
 	}
 
diff --git a/src/adql/query/operand/function/geometry/RegionFunction.java b/src/adql/query/operand/function/geometry/RegionFunction.java
index dea04a2..998d4a5 100644
--- a/src/adql/query/operand/function/geometry/RegionFunction.java
+++ b/src/adql/query/operand/function/geometry/RegionFunction.java
@@ -38,7 +38,7 @@ import adql.query.operand.ADQLOperand;
  * Inappropriate geometries for this construct SHOULD throw an error message, to be defined by the service making use of ADQL.</b></p>
  * 
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 1.2 (02/2014)
+ * @version 1.3 (10/2014)
  */
 public class RegionFunction extends GeometryFunction {
 
@@ -88,6 +88,11 @@ public class RegionFunction extends GeometryFunction {
 
 	@Override
 	public boolean isString(){
+		return false;
+	}
+
+	@Override
+	public boolean isGeometry(){
 		return true;
 	}
 
diff --git a/src/tap/ADQLExecutor.java b/src/tap/ADQLExecutor.java
index fc9c6c4..43b18fb 100644
--- a/src/tap/ADQLExecutor.java
+++ b/src/tap/ADQLExecutor.java
@@ -496,8 +496,6 @@ public class ADQLExecutor {
 			parser = new ADQLParser(queryChecker);
 		else
 			parser = new ADQLParser(queryChecker, queryFactory);
-		parser.setCoordinateSystems(service.getCoordinateSystems());
-		parser.setDebug(false);
 		ADQLQuery query = parser.parseQuery(tapParams.getQuery());
 
 		// Set or check the row limit:
diff --git a/src/tap/AbstractTAPFactory.java b/src/tap/AbstractTAPFactory.java
index 642aa10..36984fa 100644
--- a/src/tap/AbstractTAPFactory.java
+++ b/src/tap/AbstractTAPFactory.java
@@ -44,6 +44,7 @@ import uws.service.backup.UWSBackupManager;
 import uws.service.error.ServiceErrorWriter;
 import adql.db.DBChecker;
 import adql.parser.ADQLQueryFactory;
+import adql.parser.ParseException;
 import adql.parser.QueryChecker;
 import adql.query.ADQLQuery;
 
@@ -52,7 +53,7 @@ import adql.query.ADQLQuery;
  * Only the functions related with the database connection stay abstract.
  * 
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 2.0 (09/2014)
+ * @version 2.0 (10/2014)
  */
 public abstract class AbstractTAPFactory extends TAPFactory {
 
@@ -130,7 +131,7 @@ public abstract class AbstractTAPFactory extends TAPFactory {
 	 * </p>
 	 * 
 	 * <p><i>Note:
-	 * 	This function can not be overrided, but {@link #createQueryChecker(Collection)} can be.
+	 * 	This function can not be overridded, but {@link #createQueryChecker(Collection)} can be.
 	 * </i></p>
 	 */
 	@Override
@@ -172,7 +173,11 @@ public abstract class AbstractTAPFactory extends TAPFactory {
 	 * @throws TAPException	If any error occurs while creating the query checker.
 	 */
 	protected QueryChecker createQueryChecker(final Collection<TAPTable> tables) throws TAPException{
-		return new DBChecker(tables);
+		try{
+			return new DBChecker(tables, service.getUDFs(), service.getGeometries(), service.getCoordinateSystems());
+		}catch(ParseException e){
+			throw new TAPException("Unable to build a DBChecker instance! " + e.getMessage(), e, UWSException.INTERNAL_SERVER_ERROR);
+		}
 	}
 
 	/* ****** */
diff --git a/src/tap/ServiceConnection.java b/src/tap/ServiceConnection.java
index 360fcec..f849d82 100644
--- a/src/tap/ServiceConnection.java
+++ b/src/tap/ServiceConnection.java
@@ -23,64 +23,420 @@ package tap;
 import java.util.Collection;
 import java.util.Iterator;
 
+import tap.file.LocalTAPFileManager;
 import tap.file.TAPFileManager;
 import tap.formatter.OutputFormat;
+import tap.log.DefaultTAPLog;
 import tap.log.TAPLog;
 import tap.metadata.TAPMetadata;
 import uws.service.UserIdentifier;
+import adql.db.FunctionDef;
 
 /**
- * TODO JAVADOC OF THE WHOLE CLASS!
+ * <p>Description and parameters list of a TAP service.</p>
+ * 
+ * <p>
+ * 	Through this object, it is possible to configure the different limits and formats,
+ * 	but also to list all available tables and columns, to declare geometry features as all allowed user defined functions
+ * 	and to say where log and other kinds of files must be stored.
+ * </p>
  * 
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 2.0 (09/2014)
+ * @version 2.0 (10/2014)
  */
 public interface ServiceConnection {
 
+	/**
+	 * List of possible limit units.
+	 * 
+	 * @author Gr&eacute;gory Mantelet (CDS;ARI)
+	 * @version 2.0 (10/2014)
+	 */
 	public static enum LimitUnit{
-		rows("row"),
-		bytes("byte");
-		
+		rows("row"), bytes("byte");
+
 		private final String str;
+
 		private LimitUnit(final String str){
 			this.str = str;
 		}
+
+		@Override
 		public String toString(){
 			return str;
 		}
 	}
 
+	/**
+	 * <i>[OPTIONAL]</i>
+	 * <p>Name of the service provider ; it can be an organization as an individual person.</p>
+	 * 
+	 * <p>There is no restriction on the syntax or on the label to use ; this information is totally free</p>
+	 * 
+	 * <p>It will be used as additional information (INFO tag) in any VOTable and HTML output.</p>
+	 * 
+	 * @return	The TAP service provider or NULL to leave this field blank.
+	 */
 	public String getProviderName();
 
+	/**
+	 * <i>[OPTIONAL]</i>
+	 * <p>Description of the service provider.</p>
+	 * 
+	 * <p>It will be used as additional information (INFO tag) in any VOTable output.</p>
+	 * 
+	 * @return	The TAP service description or NULL to leave this field blank.
+	 */
 	public String getProviderDescription();
 
+	/**
+	 * <i><b>[MANDATORY]</b></i>
+	 * <p>This function controls the state of the whole TAP service.</p>
+	 * 
+	 * <p>
+	 * 	A message explaining the current state of the TAP service could be provided thanks to {@link #getAvailability()}.
+	 * </p>
+	 * 
+	 * @return	<i>true</i> to enable all TAP resources, <i>false</i> to disable all of them (except /availability).
+	 */
 	public boolean isAvailable();
 
+	/**
+	 * <i>[OPTIONAL]</i>
+	 * <p>Get an explanation about the current TAP service state (working or not).
+	 * This message aims to provide more details to the users about the availability of this service,
+	 * or more particularly about its unavailability.</p>
+	 * 
+	 * @return	Explanation about the TAP service state.
+	 */
 	public String getAvailability();
 
+	/**
+	 * <i>[OPTIONAL]</i>
+	 * <p>Get the limit of the retention period.</p>
+	 * 
+	 * <p>
+	 * 	It is the maximum period while an asynchronous job can leave in the jobs list
+	 * 	and so can stay on the server.
+	 * </p>
+	 * 
+	 * <p><b>Important notes:</b></p>
+	 * <ul>
+	 * 	<li><b>Exactly 2 values or a NULL object is expected here.</b></li>
+	 * 	<li><b>If NULL</b>, the retention period is not limited and jobs will
+	 * 	    theoretically stay infinitely on the server.</li>
+	 * 	<li><b>If not NULL</b>, the 2 values must correspond to the default retention period
+	 * 	    and the maximum retention period.</li>
+	 * 	<li><b>The default value</b> is used to set the retention period when a job is created with no user defined retention period.</li>
+	 * 	<li><b>The maximum value</b> is used to limit the retention period when specified by the user while creating a job.</li>
+	 * 	<li><b>The default value</b> MUST be less or equals the maximum value.</li>
+	 * 	<li><b>Both values must be positive</b>. If a negative value is given it will be interpreted as "no limit".</li>
+	 * </ul>
+	 * 
+	 * @return NULL if no limit must be set, or a two-items array ([0]: default value, [1]: maximum value).
+	 */
 	public int[] getRetentionPeriod();
 
+	/**
+	 * <i>[OPTIONAL]</i>
+	 * <p>Get the limit of the job execution duration.</p>
+	 * 
+	 * <p>
+	 * 	It is the duration of a running job (including the query execution).
+	 * 	This duration is used for synchronous AND asynchronous jobs.
+	 * </p>
+	 * 
+	 * <p><b>Important notes:</b></p>
+	 * <ul>
+	 * 	<li><b>Exactly 2 values or a NULL object is expected here.</b></li>
+	 * 	<li><b>If NULL</b>, the execution duration is not limited and jobs could
+	 * 	    theoretically run infinitely.</li>
+	 * 	<li><b>If not NULL</b>, the 2 values must correspond to the default execution duration
+	 * 	    and the maximum execution duration.</li>
+	 * 	<li><b>The default value</b> is used to set the execution duration when a job is created with no user defined execution duration.</li>
+	 * 	<li><b>The maximum value</b> is used to limit the execution duration when specified by the user while creating a job.</li>
+	 * 	<li><b>The default value</b> MUST be less or equals the maximum value.</li>
+	 * 	<li><b>Both values must be positive</b>. If a negative value is given it will be interpreted as "no limit".</li>
+	 * </ul>
+	 * 
+	 * @return	NULL if no limit must be set, or a two-items array ([0]: default value, [1]: maximum value).
+	 */
 	public int[] getExecutionDuration();
 
+	/**
+	 * <i>[OPTIONAL]</i>
+	 * <p>Get the limit of the job execution result.</p>
+	 * 
+	 * <p>
+	 * 	This value will limit the size of the query results, either in rows or in bytes.
+	 * 	The type of limit is defined by the function {@link #getOutputLimitType()}.
+	 * </p>
+	 * 
+	 * <p><b>Important notes:</b></p>
+	 * <ul>
+	 * 	<li><b>Exactly 2 values or a NULL object is expected here.</b></li>
+	 * 	<li><b>If NULL</b>, the output limit is not limited and jobs could theoretically
+	 * 	    return very big files.</li>
+	 * 	<li><b>If not NULL</b>, the 2 values must correspond to the default output limit
+	 * 	    and the maximum output limit.</li>
+	 * 	<li><b>The default value</b> is used to set the output limit when a job is created with no user defined output limit.</li>
+	 * 	<li><b>The maximum value</b> is used to limit the output limit when specified by the user while creating a job.</li>
+	 * 	<li><b>The structure of the object</b> returned by this function MUST be the same as the object returned by {@link #getOutputLimitType()}.
+	 * 	    Particularly, the type given by the N-th item of {@link #getOutputLimitType()} must correspond to the N-th limit returned by this function.</li>
+	 * 	<li><b>The default value</b> MUST be less or equals the maximum value.</li>
+	 * 	<li><b>Both values must be positive</b>. If a negative value is given it will be interpreted as "no limit".</li>
+	 * </ul>
+	 * 
+	 * <p><i><b>Important note:</b>
+	 * 	To save performances, it is strongly recommended to use ROWS limit rather than in bytes. Indeed, the rows limit can be taken
+	 * 	into account at the effective execution of the query (so before getting the result), on the contrary of the bytes limit which
+	 * 	will be applied on the query result.
+	 * </i></p>
+	 * 
+	 * @return	NULL if no limit must be set, or a two-items array ([0]: default value, [1]: maximum value).
+	 * 
+	 * @see #getOutputLimitType()
+	 */
 	public int[] getOutputLimit();
 
+	/**
+	 * <i>[OPTIONAL]</i>
+	 * <p>Get the type of each output limit set by this service connection (and accessible with {@link #getOutputLimit()}).</p>
+	 * 
+	 * <p><b>Important notes:</b></p>
+	 * <ul>
+	 * 	<li><b>Exactly 2 values or a NULL object is expected here.</b></li>
+	 * 	<li><b>If NULL</b>, the output limit will be considered as expressed in ROWS.</li>
+	 * 	<li><b>The structure of the object</b> returned by this function MUST be the same as the object returned by {@link #getOutputLimit()}.
+	 * 	    Particularly, the type given by the N-th item of this function must correspond to the N-th limit returned by {@link #getOutputLimit()}.</li>
+	 * </ul>
+	 * 
+	 * <p><i><b>Important note:</b>
+	 * 	To save performances, it is strongly recommended to use ROWS limit rather than in bytes. Indeed, the rows limit can be taken
+	 * 	into account at the effective execution of the query (so before getting the result), on the contrary of the bytes limit which
+	 * 	will be applied on the query result.
+	 * </i></p>
+	 * 
+	 * @return	NULL if limits should be expressed in ROWS, or a two-items array ([0]: type of getOutputLimit()[0], [1]: type of getOutputLimit()[1]). 
+	 * 
+	 * @see #getOutputLimit()
+	 */
 	public LimitUnit[] getOutputLimitType();
 
+	/**
+	 * <i>[OPTIONAL]</i>
+	 * <p>Get the object to use in order to identify users at the origin of requests.</p>
+	 * 
+	 * @return	NULL if no user identification should be done, a {@link UserIdentifier} instance otherwise.
+	 */
 	public UserIdentifier getUserIdentifier();
 
+	/**
+	 * <i><b>[MANDATORY]</b></i>
+	 * <p>This function let enable or disable the upload capability of this TAP service.</p>
+	 * 
+	 * <p><i>Note:
+	 * 	If the upload is disabled, the request is aborted and an HTTP-400 error is thrown each time some tables are uploaded.
+	 * </i></p>
+	 * 
+	 * @return	<i>true</i> to enable the upload capability, <i>false</i> to disable it.
+	 */
 	public boolean uploadEnabled();
 
+	/**
+	 * <i>[OPTIONAL]</i>
+	 * <p>Get the maximum size of EACH uploaded table.</p>
+	 * 
+	 * <p>
+	 * 	This value is expressed either in rows or in bytes.
+	 * 	The unit limit is defined by the function {@link #getUploadLimitType()}.
+	 * </p>
+	 * 
+	 * <p><b>Important notes:</b></p>
+	 * <ul>
+	 * 	<li><b>Exactly 2 values or a NULL object is expected here.</b></li>
+	 * 	<li><b>If NULL</b>, the upload limit is not limited and uploads could be
+	 * 	    theoretically unlimited.</li>
+	 * 	<li><b>If not NULL</b>, the 2 values must correspond to the default upload limit
+	 * 	    and the maximum upload limit.</li>
+	 * 	<li><b>The default value</b> is used inform the user about the server wishes.</li>
+	 * 	<li><b>The maximum value</b> is used to really limit the upload limit.</li>
+	 * 	<li><b>The structure of the object</b> returned by this function MUST be the same as the object returned by {@link #getUploadLimitType()}.
+	 * 	    Particularly, the type given by the N-th item of {@link #getUploadLimitType()} must correspond to the N-th limit returned by this function.</li>
+	 * 	<li><b>The default value</b> MUST be less or equals the maximum value.</li>
+	 * 	<li><b>Both values must be positive</b>. If a negative value is given it will be interpreted as "no limit".</li>
+	 * </ul>
+	 * 
+	 * <p><i><b>Important note:</b>
+	 * 	To save performances, it is recommended to use BYTES limit rather than in rows. Indeed, the bytes limit can be taken
+	 * 	into account at directly when reading the bytes of the request, on the contrary of the rows limit which
+	 * 	requires to parse the uploaded tables.
+	 * </i></p>
+	 * 
+	 * @return	NULL if no limit must be set, or a two-items array ([0]: default value, [1]: maximum value).
+	 * 
+	 * @see #getUploadLimitType()
+	 */
 	public int[] getUploadLimit();
 
+	/**
+	 * <i>[OPTIONAL]</i>
+	 * <p>Get the type of each upload limit set by this service connection (and accessible with {@link #getUploadLimit()}).</p>
+	 * 
+	 * <p><b>Important notes:</b></p>
+	 * <ul>
+	 * 	<li><b>Exactly 2 values or a NULL object is expected here.</b></li>
+	 * 	<li><b>If NULL</b>, the upload limit will be considered as expressed in ROWS.</li>
+	 * 	<li><b>The structure of the object</b> returned by this function MUST be the same as the object returned by {@link #getUploadLimit()}.
+	 * 	    Particularly, the type given by the N-th item of this function must correspond to the N-th limit returned by {@link #getUploadLimit()}.</li>
+	 * </ul>
+	 * 
+	 * <p><i><b>Important note:</b>
+	 * 	To save performances, it is recommended to use BYTES limit rather than in rows. Indeed, the bytes limit can be taken
+	 * 	into account at directly when reading the bytes of the request, on the contrary of the rows limit which
+	 * 	requires to parse the uploaded tables.
+	 * </i></p>
+	 * 
+	 * @return	NULL if limits should be expressed in ROWS, or a two-items array ([0]: type of getUploadLimit()[0], [1]: type of getUploadLimit()[1]). 
+	 * 
+	 * @see #getUploadLimit()
+	 */
 	public LimitUnit[] getUploadLimitType();
 
+	/**
+	 * <i>[OPTIONAL]</i>
+	 * <p>Get the maximum size of the whole set of all tables uploaded in one request.
+	 * This size is expressed in bytes.</p>
+	 * 
+	 * <p><b>IMPORTANT 1:
+	 * 	This value is always used when the upload capability is enabled.
+	 * </b></p>
+	 * 
+	 * <p><b>IMPORTANT 2:
+	 * 	The value returned by this function MUST always be positive.
+	 * 	A zero or negative value will throw an exception later while
+	 * 	reading parameters in a request with some uploaded tables.
+	 * </b></p>
+	 * 
+	 * @return	A positive (&gt;0) value corresponding to the maximum number of bytes of all uploaded tables sent in one request.
+	 */
 	public int getMaxUploadSize();
 
+	/**
+	 * <i><b>[MANDATORY]</b></i>
+	 * <p>Get the list of all available tables and columns.</p>
+	 * 
+	 * <p>
+	 * 	This object is really important since it lets the library check ADQL queries properly and set the good type
+	 * 	and formatting in the query results.
+	 * </p>
+	 *  
+	 * @return	A TAPMetadata object. <b>NULL is not allowed and will throw a grave error at the service initialization.</b>
+	 */
 	public TAPMetadata getTAPMetadata();
 
+	/**
+	 * <i>[OPTIONAL]</i>
+	 * <p>Get the list of all allowed coordinate systems.</p>
+	 * 
+	 * <u><b>Special values</b></u>
+	 * 
+	 * <p>Two special values can be returned by this function:</p>
+	 * <ul>
+	 * 	<li><b>NULL</b> which means that all coordinate systems are allowed,</li>
+	 * 	<li><b>the empty list</b> which means that no coordinate system - except
+	 * 	    the default one (which can be reduced to an empty string) - is allowed.</li>
+	 * </ul>
+	 * 
+	 * <u><b>List item syntax</b></u>
+	 * 
+	 * <p>
+	 * 	Each item of this list is a <b>pattern</b> and not a simple coordinate system.
+	 * 	Thus each item MUST respect the following syntax:
+	 * </p>
+	 * <pre>{framePattern} {refposPattern} {flavorPattern}</pre>
+	 * <p>
+	 * 	Contrary to a coordinate system expression, all these 3 information are required.
+	 * 	Each may take 3 kinds of value:
+	 * </p>
+	 * <ul>
+	 * 	<li>a single value (i.e. "ICRS"),</li>
+	 * 	<li>a list of values with the syntax <code>({value1}|{value2}|...)</code> (i.e. "(ICRS|FK4)"),</li>
+	 * 	<li>a "*" which means that all values are possible.
+	 * </ul>
+	 * <p>
+	 * 	For instance: <code>(ICRS|FK4) HELIOCENTER *</code> is a good syntax,
+	 * 	but not <code>ICRS</code> or <code>ICRS HELIOCENTER</code>.
+	 * </p>
+	 * 
+	 * <p><i>Note:
+	 * 	Even if not explicitly part of the possible values, the default value of each part (i.e. UNKNOWNFRAME for frame) is always taken into account by the library.
+	 * 	Particularly, the empty string will always be allowed even if not explicitly listed in the list returned by this function.
+	 * </i></p>
+	 * 
+	 * @return	NULL to allow ALL coordinate systems, an empty list to allow NO coordinate system,
+	 *        	or a list of coordinate system patterns otherwise.
+	 * 
+	 * @since 2.0
+	 */
 	public Collection<String> getCoordinateSystems();
 
 	/**
+	 * <i>[OPTIONAL]</i>
+	 * <p>Get the list of all allowed geometrical functions.</p>
+	 * 
+	 * <u><b>Special values</b></u>
+	 * 
+	 * <p>Two special values can be returned by this function:</p>
+	 * <ul>
+	 * 	<li><b>NULL</b> which means that all geometrical functions are allowed,</li>
+	 * 	<li><b>the empty list</b> which means that no geometrical functions is allowed.</li>
+	 * </ul>
+	 * 
+	 * <u><b>List item syntax</b></u>
+	 * 
+	 * <p>
+	 * 	Each item of the returned list MUST be a function name (i.e. "CONTAINS", "POINT").
+	 * 	It can also be a type of STC region to forbid (i.e. "POSITION", "UNION").
+	 * </p>
+	 * 
+	 * <p>The given names are not case sensitive.</p>
+	 * 
+	 * @return	NULL to allow ALL geometrical functions, an empty list to allow NO geometrical function,
+	 *        	or a list of geometrical function names otherwise.
+	 * 
+	 * @since 2.0
+	 */
+	public Collection<String> getGeometries();
+
+	/**
+	 * <i>[OPTIONAL]</i>
+	 * <p>Get the list of all allowed User Defined Functions (UDFs).</p>
+	 * 
+	 * <u><b>Special values</b></u>
+	 * 
+	 * <p>Two special values can be returned by this function:</p>
+	 * <ul>
+	 * 	<li><b>NULL</b> which means that all unknown functions (which should be UDFs) are allowed,</li>
+	 * 	<li><b>the empty list</b> which means that no unknown functions (which should be UDFs) is allowed.</li>
+	 * </ul>
+	 * 
+	 * <u><b>List item syntax</b></u>
+	 * 
+	 * <p>
+	 * 	Each item of the returned list MUST be an instance of {@link FunctionDef}.
+	 * </p>
+	 * 
+	 * @return	NULL to allow ALL unknown functions, an empty list to allow NO unknown function,
+	 *        	or a list of user defined functions otherwise.
+	 * 
+	 * @since 2.0
+	 */
+	public Collection<FunctionDef> getUDFs();
+
+	/**
+	 * <i>[OPTIONAL]</i>
+	 * 
 	 * <p>Get the maximum number of asynchronous jobs that can run in the same time.</p>
 	 * 
 	 * <p>A null or negative value means <b>no limit</b> on the number of running asynchronous jobs.</p> 
@@ -91,14 +447,88 @@ public interface ServiceConnection {
 	 */
 	public int getNbMaxAsyncJobs();
 
+	/**
+	 * <i><b>[MANDATORY]</b></i>
+	 * <p>Get the logger to use in the whole service when any error, warning or info happens.</p>
+	 * 
+	 * <p><b>IMPORTANT:
+	 * 	If NULL is returned by this function, grave errors will occur while executing a query or managing an error.
+	 * 	It is strongly recommended to provide a logger, even a basic implementation.
+	 * </b></p>
+	 * 
+	 * <p><i>Piece of advice:
+	 * 	A default implementation like {@link DefaultTAPLog} would be most of time largely enough.
+	 * </i></p>
+	 * 
+	 * @return	An instance of {@link TAPLog}.
+	 */
 	public TAPLog getLogger();
 
+	/**
+	 * <i><b>[MANDATORY]</b></i>
+	 * <p>Get the object able to build other objects essentials to configure the TAP service or to run every queries.</p>
+	 * 
+	 * <p><b>IMPORTANT:
+	 * 	If NULL is returned by this function, grave errors will occur while initializing the service.
+	 * </b></p>
+	 * 
+	 * <p><i>Piece of advice:
+	 * 	The {@link TAPFactory} is an interface which contains a lot of functions to implement.
+	 * 	It is rather recommended to extend {@link AbstractTAPFactory}: just 3 functions ({@link AbstractTAPFactory#countFreeConnections()},
+	 * 	{@link AbstractTAPFactory#freeConnection()}, {@link AbstractTAPFactory#getConnection(String)}) will have to be implemented. 
+	 * </i></p>
+	 * 
+	 * @return	An instance of {@link TAPFactory}.
+	 * 
+	 * @see AbstractTAPFactory
+	 */
 	public TAPFactory getFactory();
 
+	/**
+	 * <i><b>[MANDATORY]</b></i>
+	 * <p>Get the object in charge of the files management.
+	 * This object manages log, error, result and backup files of the whole service.</p>
+	 * 
+	 * <p><b>IMPORTANT:
+	 * 	If NULL is returned by this function, grave errors will occur while initializing the service.
+	 * </b></p>
+	 * 
+	 * <p><i>Piece of advice:
+	 * 	The library provides a default implementation of the interface {@link TAPFileManager}:
+	 * 	{@link LocalTAPFileManager}, which stores all files on the local file-system.
+	 * </i></p>
+	 * 
+	 * @return	An instance of {@link TAPFileManager}.
+	 */
 	public TAPFileManager getFileManager();
 
+	/**
+	 * <i><b>[MANDATORY]</b></i>
+	 * <p>Get the list of all available output formats.</p>
+	 * 
+	 * <p><b>IMPORTANT:</b></p>
+	 * <ul>
+	 * 	<li>All formats of this list MUST have a different MIME type.</li>
+	 * 	<li>At least one item must correspond to the MIME type "votable".</li>
+	 * 	<li>If NULL is returned by this function, grave errors will occur while writing the capabilities of this service.</li>
+	 * </li>
+	 * 
+	 * @return	An iterator on the list of all available output formats.
+	 */
 	public Iterator<OutputFormat> getOutputFormats();
 
+	/**
+	 * <i><b>[MANDATORY]</b></i>
+	 * <p>Get the output format having the given MIME type (or short MIME type ~ alias).</p>
+	 * 
+	 * <p><b>IMPORTANT:
+	 * 	This function MUST always return an {@link OutputFormat} instance when the MIME type "votable" is given in parameter.
+	 * </b></p>
+	 * 
+	 * @param mimeOrAlias	MIME type or short MIME type of the format to get.
+	 * 
+	 * @return	The corresponding {@link OutputFormat} or NULL if not found.
+	 */
 	public OutputFormat getOutputFormat(final String mimeOrAlias);
 
 }
diff --git a/src/tap/data/LimitedTableIterator.java b/src/tap/data/LimitedTableIterator.java
index 5703511..97f82e6 100644
--- a/src/tap/data/LimitedTableIterator.java
+++ b/src/tap/data/LimitedTableIterator.java
@@ -27,8 +27,8 @@ import java.util.NoSuchElementException;
 
 import tap.ServiceConnection.LimitUnit;
 import tap.metadata.TAPColumn;
-import tap.metadata.TAPType;
 import tap.upload.LimitedSizeInputStream;
+import adql.db.DBType;
 
 import com.oreilly.servlet.multipart.ExceededSizeException;
 
@@ -219,7 +219,7 @@ public class LimitedTableIterator implements TableIterator {
 	}
 
 	@Override
-	public TAPType getColType() throws IllegalStateException, DataReadException{
+	public DBType getColType() throws IllegalStateException, DataReadException{
 		testOverflow();
 		return innerIt.getColType();
 	}
diff --git a/src/tap/data/ResultSetTableIterator.java b/src/tap/data/ResultSetTableIterator.java
index 6f9f6d7..a580c87 100644
--- a/src/tap/data/ResultSetTableIterator.java
+++ b/src/tap/data/ResultSetTableIterator.java
@@ -26,10 +26,10 @@ import java.sql.Timestamp;
 import java.util.NoSuchElementException;
 
 import tap.metadata.TAPColumn;
-import tap.metadata.TAPType;
-import tap.metadata.TAPType.TAPDatatype;
 import uws.ISO8601Format;
 import adql.db.DBColumn;
+import adql.db.DBType;
+import adql.db.DBType.DBDatatype;
 
 /**
  * <p>{@link TableIterator} which lets iterate over a SQL {@link ResultSet}.</p>
@@ -198,11 +198,11 @@ public class ResultSetTableIterator implements TableIterator {
 					try{
 						colMeta[i - 1] = (TAPColumn)resultMeta[i - 1];
 					}catch(ClassCastException cce){
-						TAPType datatype = convertType(metadata.getColumnTypeName(i), dbms);
+						DBType datatype = convertType(metadata.getColumnTypeName(i), dbms);
 						colMeta[i - 1] = new TAPColumn(resultMeta[i - 1].getADQLName(), datatype);
 					}
 				}else{
-					TAPType datatype = convertType(metadata.getColumnTypeName(i), dbms);
+					DBType datatype = convertType(metadata.getColumnTypeName(i), dbms);
 					colMeta[i - 1] = new TAPColumn(metadata.getColumnLabel(i), datatype);
 				}
 			}
@@ -284,7 +284,7 @@ public class ResultSetTableIterator implements TableIterator {
 	}
 
 	@Override
-	public TAPType getColType() throws IllegalStateException, DataReadException{
+	public DBType getColType() throws IllegalStateException, DataReadException{
 		// Basically check the read state (for rows iteration):
 		checkReadState();
 
@@ -299,7 +299,7 @@ public class ResultSetTableIterator implements TableIterator {
 	}
 
 	/**
-	 * <p>Convert the given DBMS type into the better matching {@link TAPType} instance.
+	 * <p>Convert the given DBMS type into the better matching {@link DBType} instance.
 	 * This function is used to guess the TAP type of a column when it is not provided in the constructor.
 	 * It aims not to be exhaustive, but just to provide a type when the given TAP metadata are incomplete.</p>
 	 * 
@@ -328,12 +328,12 @@ public class ResultSetTableIterator implements TableIterator {
 	 * @param dbmsType	DBMS column datatype name.
 	 * @param dbms		Lower-case string which indicates which DBMS the ResultSet is coming from. <i>note: MAY be NULL.</i>
 	 * 
-	 * @return	The best suited {@link TAPType} object.
+	 * @return	The best suited {@link DBType} object.
 	 */
-	protected TAPType convertType(String dbmsType, final String dbms){
+	protected DBType convertType(String dbmsType, final String dbms){
 		// If no type is provided return VARCHAR:
 		if (dbmsType == null || dbmsType.trim().length() == 0)
-			return new TAPType(TAPDatatype.VARCHAR, TAPType.NO_LENGTH);
+			return new DBType(DBDatatype.VARCHAR, DBType.NO_LENGTH);
 
 		// Extract the type prefix and lower-case it:
 		dbmsType = dbmsType.toLowerCase();
@@ -345,61 +345,61 @@ public class ResultSetTableIterator implements TableIterator {
 		if (dbms != null && dbms.equals("sqlite")){
 			// INTEGER -> SMALLINT, INTEGER, BIGINT
 			if (dbmsTypePrefix.equals("integer"))
-				return new TAPType(TAPDatatype.BIGINT);
+				return new DBType(DBDatatype.BIGINT);
 			// REAL -> REAL, DOUBLE
 			else if (dbmsTypePrefix.equals("real"))
-				return new TAPType(TAPDatatype.DOUBLE);
+				return new DBType(DBDatatype.DOUBLE);
 			// TEXT -> CHAR, VARCHAR, CLOB, TIMESTAMP
 			else if (dbmsTypePrefix.equals("text"))
-				return new TAPType(TAPDatatype.VARCHAR);
+				return new DBType(DBDatatype.VARCHAR);
 			// BLOB -> BINARY, VARBINARY, BLOB
 			else if (dbmsTypePrefix.equals("blob"))
-				return new TAPType(TAPDatatype.BLOB);
+				return new DBType(DBDatatype.BLOB);
 			// Default:
 			else
-				return new TAPType(TAPDatatype.VARCHAR, TAPType.NO_LENGTH);
+				return new DBType(DBDatatype.VARCHAR, DBType.NO_LENGTH);
 		}
 		// CASE: OTHER DBMS
 		else{
 			// SMALLINT
 			if (dbmsTypePrefix.equals("smallint") || dbmsTypePrefix.equals("int2"))
-				return new TAPType(TAPDatatype.SMALLINT);
+				return new DBType(DBDatatype.SMALLINT);
 			// INTEGER
 			else if (dbmsTypePrefix.equals("integer") || dbmsTypePrefix.equals("int") || dbmsTypePrefix.equals("int4"))
-				return new TAPType(TAPDatatype.INTEGER);
+				return new DBType(DBDatatype.INTEGER);
 			// BIGINT
-			else if (dbmsTypePrefix.equals("bigint") || dbmsTypePrefix.equals("int8") || dbmsTypePrefix.equals("int4") || dbmsTypePrefix.equals("number"))
-				return new TAPType(TAPDatatype.BIGINT);
+			else if (dbmsTypePrefix.equals("bigint") || dbmsTypePrefix.equals("int8") || dbmsTypePrefix.equals("number"))
+				return new DBType(DBDatatype.BIGINT);
 			// REAL
 			else if (dbmsTypePrefix.equals("float4") || (dbmsTypePrefix.equals("float") && firstParam <= 63))
-				return new TAPType(TAPDatatype.REAL);
+				return new DBType(DBDatatype.REAL);
 			// DOUBLE
 			else if (dbmsTypePrefix.equals("double") || dbmsTypePrefix.equals("double precision") || dbmsTypePrefix.equals("float8") || (dbmsTypePrefix.equals("float") && firstParam > 63))
-				return new TAPType(TAPDatatype.DOUBLE);
+				return new DBType(DBDatatype.DOUBLE);
 			// BINARY
 			else if (dbmsTypePrefix.equals("binary") || dbmsTypePrefix.equals("raw") || ((dbmsTypePrefix.equals("char") || dbmsTypePrefix.equals("character")) && dbmsType.endsWith(" for bit data")))
-				return new TAPType(TAPDatatype.BINARY, firstParam);
+				return new DBType(DBDatatype.BINARY, firstParam);
 			// VARBINARY
 			else if (dbmsTypePrefix.equals("varbinary") || dbmsTypePrefix.equals("long raw") || ((dbmsTypePrefix.equals("varchar") || dbmsTypePrefix.equals("character varying")) && dbmsType.endsWith(" for bit data")))
-				return new TAPType(TAPDatatype.VARBINARY, firstParam);
+				return new DBType(DBDatatype.VARBINARY, firstParam);
 			// CHAR
 			else if (dbmsTypePrefix.equals("char") || dbmsTypePrefix.equals("character"))
-				return new TAPType(TAPDatatype.CHAR, firstParam);
+				return new DBType(DBDatatype.CHAR, firstParam);
 			// VARCHAR
 			else if (dbmsTypePrefix.equals("varchar") || dbmsTypePrefix.equals("varchar2") || dbmsTypePrefix.equals("character varying"))
-				return new TAPType(TAPDatatype.VARBINARY, firstParam);
+				return new DBType(DBDatatype.VARBINARY, firstParam);
 			// BLOB
 			else if (dbmsTypePrefix.equals("bytea") || dbmsTypePrefix.equals("blob") || dbmsTypePrefix.equals("binary large object"))
-				return new TAPType(TAPDatatype.BLOB);
+				return new DBType(DBDatatype.BLOB);
 			// CLOB
 			else if (dbmsTypePrefix.equals("text") || dbmsTypePrefix.equals("clob") || dbmsTypePrefix.equals("character large object"))
-				return new TAPType(TAPDatatype.CLOB);
+				return new DBType(DBDatatype.CLOB);
 			// TIMESTAMP
 			else if (dbmsTypePrefix.equals("timestamp"))
-				return new TAPType(TAPDatatype.TIMESTAMP);
+				return new DBType(DBDatatype.TIMESTAMP);
 			// Default:
 			else
-				return new TAPType(TAPDatatype.VARCHAR, TAPType.NO_LENGTH);
+				return new DBType(DBDatatype.VARCHAR, DBType.NO_LENGTH);
 		}
 	}
 
@@ -409,22 +409,22 @@ public class ResultSetTableIterator implements TableIterator {
 	 * <p>
 	 * 	If the given type string does not contain any parameter
 	 * 	OR if the first parameter can not be casted into an integer,
-	 * 	{@link TAPType#NO_LENGTH} will be returned.
+	 * 	{@link DBType#NO_LENGTH} will be returned.
 	 * </p>
 	 * 
 	 * @param dbmsType		DBMS type string (containing the datatype and the 'length' parameter).
 	 * @param paramIndex	Index of the open bracket.
 	 * 
-	 * @return	The 'length' parameter value if found, {@link TAPType#NO_LENGTH} otherwise.
+	 * @return	The 'length' parameter value if found, {@link DBType#NO_LENGTH} otherwise.
 	 */
 	protected final int getLengthParam(final String dbmsType, final int paramIndex){
 		// If no parameter has been previously detected, no length parameter:
 		if (paramIndex <= 0)
-			return TAPType.NO_LENGTH;
+			return DBType.NO_LENGTH;
 
 		// If there is one and that at least ONE parameter is provided....
 		else{
-			int lengthParam = TAPType.NO_LENGTH;
+			int lengthParam = DBType.NO_LENGTH;
 			String paramsStr = dbmsType.substring(paramIndex + 1);
 
 			// ...extract the 'length' parameter:
diff --git a/src/tap/data/TableIterator.java b/src/tap/data/TableIterator.java
index 0aed73c..17d8445 100644
--- a/src/tap/data/TableIterator.java
+++ b/src/tap/data/TableIterator.java
@@ -21,8 +21,8 @@ package tap.data;
 
 import java.util.NoSuchElementException;
 
+import adql.db.DBType;
 import tap.metadata.TAPColumn;
-import tap.metadata.TAPType;
 
 /**
  * <p>Let's iterate on each row and then on each column over a table dataset.</p>
@@ -126,7 +126,7 @@ public interface TableIterator {
 	 * @throws IllegalStateException	If {@link #nextCol()} has not yet been called.
 	 * @throws DataReadException		If an error occurs while reading the table dataset.
 	 */
-	public TAPType getColType() throws IllegalStateException, DataReadException;
+	public DBType getColType() throws IllegalStateException, DataReadException;
 
 	/**
 	 * Close the stream or input over which this class iterates.
diff --git a/src/tap/data/VOTableIterator.java b/src/tap/data/VOTableIterator.java
index 861ac52..c79dd05 100644
--- a/src/tap/data/VOTableIterator.java
+++ b/src/tap/data/VOTableIterator.java
@@ -23,9 +23,9 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.util.NoSuchElementException;
 
+import adql.db.DBType;
 import tap.TAPException;
 import tap.metadata.TAPColumn;
-import tap.metadata.TAPType;
 import tap.metadata.VotType;
 import tap.metadata.VotType.VotDatatype;
 import uk.ac.starlink.table.ColumnInfo;
@@ -131,7 +131,7 @@ public class VOTableIterator implements TableIterator {
 			String xtype = getAuxDatumValue(colInfo, "xtype");
 
 			// Resolve the field type:
-			TAPType type = resolveVotType(datatype, arraysize, xtype).toTAPType();
+			DBType type = resolveVotType(datatype, arraysize, xtype).toTAPType();
 
 			// build the TAPColumn object:
 			TAPColumn col = new TAPColumn(colInfo.getName(), type, colInfo.getDescription(), colInfo.getUnitString(), colInfo.getUCD(), colInfo.getUtype());
@@ -255,7 +255,7 @@ public class VOTableIterator implements TableIterator {
 	}
 
 	@Override
-	public TAPType getColType() throws IllegalStateException, DataReadException{
+	public DBType getColType() throws IllegalStateException, DataReadException{
 		// Basically check the read state (for rows iteration):
 		checkReadState();
 
diff --git a/src/tap/db/JDBCConnection.java b/src/tap/db/JDBCConnection.java
index fc1331f..1e1a836 100644
--- a/src/tap/db/JDBCConnection.java
+++ b/src/tap/db/JDBCConnection.java
@@ -17,7 +17,7 @@ package tap.db;
  * along with TAPLibrary.  If not, see <http://www.gnu.org/licenses/>.
  * 
  * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
- *                       Astronomishes Rechen Institut (ARI)
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import java.sql.Connection;
@@ -47,10 +47,10 @@ import tap.metadata.TAPMetadata.STDTable;
 import tap.metadata.TAPSchema;
 import tap.metadata.TAPTable;
 import tap.metadata.TAPTable.TableType;
-import tap.metadata.TAPType;
-import tap.metadata.TAPType.TAPDatatype;
 import uws.ISO8601Format;
 import uws.service.log.UWSLog.LogLevel;
+import adql.db.DBType;
+import adql.db.DBType.DBDatatype;
 import adql.query.ADQLQuery;
 import adql.query.IdentifierField;
 import adql.translator.ADQLTranslator;
@@ -97,7 +97,7 @@ import adql.translator.TranslationException;
  * 
  * <h3>Datatypes</h3>
  * 
- * <p>Column types are converted from DBMS to TAP types with {@link #getTAPType(String)} and from TAP to DBMS types with {@link #getDBMSDatatype(TAPType)}.</p>
+ * <p>Column types are converted from DBMS to TAP types with {@link #getTAPType(String)} and from TAP to DBMS types with {@link #getDBMSDatatype(DBType)}.</p>
  * 
  * <p>
  * 	All typical DBMS datatypes are taken into account, <b>EXCEPT the geometrical types</b> (POINT and REGION). For these types, the only object having this
@@ -635,19 +635,19 @@ public class JDBCConnection implements DBConnection {
 				}
 
 				// resolve the column type (if any) ; by default, it will be "VARCHAR" if unknown or missing:
-				TAPDatatype tapDatatype = null;
+				DBDatatype tapDatatype = null;
 				// ...try to resolve the datatype in function of all datatypes declared by the TAP standard.
 				if (datatype != null){
 					try{
-						tapDatatype = TAPDatatype.valueOf(datatype.toUpperCase());
+						tapDatatype = DBDatatype.valueOf(datatype.toUpperCase());
 					}catch(IllegalArgumentException iae){}
 				}
 				// ...build the column type:
-				TAPType type;
+				DBType type;
 				if (tapDatatype == null)
-					type = new TAPType(TAPDatatype.VARCHAR);
+					type = new DBType(DBDatatype.VARCHAR);
 				else
-					type = new TAPType(tapDatatype, size);
+					type = new DBType(tapDatatype, size);
 
 				// create the new column:
 				TAPColumn newColumn = new TAPColumn(columnName, type, nullifyIfNeeded(description), nullifyIfNeeded(unit), nullifyIfNeeded(ucd), nullifyIfNeeded(utype));
@@ -1591,7 +1591,7 @@ public class JDBCConnection implements DBConnection {
 					Object val = data.nextCol();
 					/* If the value is supposed to be a Timestamp, parse it
 					 * and build an appropriate SQL object: */
-					if (val != null && cols[c - 1].getDatatype().type == TAPDatatype.TIMESTAMP){
+					if (val != null && cols[c - 1].getDatatype().type == DBDatatype.TIMESTAMP){
 						try{
 							val = new Timestamp(ISO8601Format.parse(val.toString()));
 						}catch(ParseException pe){
@@ -1728,9 +1728,9 @@ public class JDBCConnection implements DBConnection {
 	 * 
 	 * @return	The corresponding DB type, or NULL if the given type is not managed or is NULL.
 	 */
-	protected String getDBMSDatatype(TAPType datatype){
+	protected String getDBMSDatatype(DBType datatype){
 		if (datatype == null)
-			datatype = new TAPType(TAPDatatype.VARCHAR);
+			datatype = new DBType(DBDatatype.VARCHAR);
 
 		switch(datatype.type){
 
diff --git a/src/tap/error/DefaultTAPErrorWriter.java b/src/tap/error/DefaultTAPErrorWriter.java
index d347c67..eb194e3 100644
--- a/src/tap/error/DefaultTAPErrorWriter.java
+++ b/src/tap/error/DefaultTAPErrorWriter.java
@@ -114,7 +114,7 @@ public class DefaultTAPErrorWriter implements ServiceErrorWriter {
 		// Otherwise, log it and write a message to the user:
 		else
 			// write a message to the user:
-			writeError("INTERNAL SERVER ERROR! Sorry, this error is grave and unexpected. No explaination can be provided for the moment. Details about this error have been reported in the service log files ; you should try again your request later or notify the administrator(s) by yourself (with the following REQ_ID).", ErrorType.FATAL, UWSException.INTERNAL_SERVER_ERROR, response, request, reqID, user, action);
+			writeError("INTERNAL SERVER ERROR! Sorry, this error is grave and unexpected. No explanation can be provided for the moment. Details about this error have been reported in the service log files ; you should try again your request later or notify the administrator(s) by yourself (with the following REQ_ID).", ErrorType.FATAL, UWSException.INTERNAL_SERVER_ERROR, response, request, reqID, user, action);
 	}
 
 	@Override
diff --git a/src/tap/formatter/JSONFormat.java b/src/tap/formatter/JSONFormat.java
index 06f3e23..1596827 100644
--- a/src/tap/formatter/JSONFormat.java
+++ b/src/tap/formatter/JSONFormat.java
@@ -32,11 +32,11 @@ import tap.TAPException;
 import tap.TAPExecutionReport;
 import tap.data.TableIterator;
 import tap.metadata.TAPColumn;
-import tap.metadata.TAPType;
-import tap.metadata.TAPType.TAPDatatype;
 import tap.metadata.VotType;
 import uws.service.log.UWSLog.LogLevel;
 import adql.db.DBColumn;
+import adql.db.DBType;
+import adql.db.DBType.DBDatatype;
 
 /**
  * Format any given query (table) result into JSON.
@@ -212,7 +212,7 @@ public class JSONFormat implements OutputFormat {
 			else
 				return (TAPColumn)typeFromResult.copy();
 		}else
-			return new TAPColumn((typeFromQuery != null) ? typeFromQuery.getADQLName() : "?", new TAPType(TAPDatatype.VARCHAR), "?");
+			return new TAPColumn((typeFromQuery != null) ? typeFromQuery.getADQLName() : "?", new DBType(DBDatatype.VARCHAR), "?");
 	}
 
 	/**
@@ -237,7 +237,7 @@ public class JSONFormat implements OutputFormat {
 			out.key("description").value(tapCol.getDescription());
 
 		// "datatype": "..."
-		VotType votType = tapCol.getDatatype().toVotType();
+		VotType votType = new VotType(tapCol.getDatatype());
 		out.key("datatype").value(votType.datatype);
 
 		// "arraysize": "..." (if any)
diff --git a/src/tap/formatter/VOTableFormat.java b/src/tap/formatter/VOTableFormat.java
index a1beb23..7ce1f63 100644
--- a/src/tap/formatter/VOTableFormat.java
+++ b/src/tap/formatter/VOTableFormat.java
@@ -35,8 +35,6 @@ import tap.data.DataReadException;
 import tap.data.TableIterator;
 import tap.error.DefaultTAPErrorWriter;
 import tap.metadata.TAPColumn;
-import tap.metadata.TAPType;
-import tap.metadata.TAPType.TAPDatatype;
 import tap.metadata.VotType;
 import tap.metadata.VotType.VotDatatype;
 import uk.ac.starlink.table.AbstractStarTable;
@@ -50,6 +48,8 @@ import uk.ac.starlink.votable.VOSerializer;
 import uk.ac.starlink.votable.VOTableVersion;
 import uws.service.log.UWSLog.LogLevel;
 import adql.db.DBColumn;
+import adql.db.DBType;
+import adql.db.DBType.DBDatatype;
 
 /**
  * <p>Format any given query (table) result into VOTable.</p>
@@ -426,6 +426,12 @@ public class VOTableFormat implements OutputFormat {
 			out.newLine();
 		}
 
+		/* TODO Add somewhere in the table header the different Coordinate Systems used in this result!
+		 * 2 ways to do so:
+		 * 	1/ COOSYS (deprecated from VOTable 1.2, but soon un-deprecated)
+		 * 	2/ a GROUP item with the STC expression of the coordinate system. 
+		 */
+
 		out.flush();
 	}
 
@@ -495,7 +501,7 @@ public class VOTableFormat implements OutputFormat {
 			else
 				return (TAPColumn)typeFromResult.copy();
 		}else
-			return new TAPColumn((typeFromQuery != null) ? typeFromQuery.getADQLName() : "?", new TAPType(TAPDatatype.VARCHAR), "?");
+			return new TAPColumn((typeFromQuery != null) ? typeFromQuery.getADQLName() : "?", new DBType(DBDatatype.VARCHAR), "?");
 	}
 
 	/**
@@ -507,7 +513,7 @@ public class VOTableFormat implements OutputFormat {
 	 */
 	protected static final ColumnInfo getColumnInfo(final TAPColumn tapCol){
 		// Get the VOTable type:
-		VotType votType = tapCol.getDatatype().toVotType();
+		VotType votType = new VotType(tapCol.getDatatype());
 
 		// Build a ColumnInfo with the name, type and description:
 		ColumnInfo colInfo = new ColumnInfo(tapCol.getADQLName(), getDatatypeClass(votType.datatype, votType.arraysize), tapCol.getDescription());
diff --git a/src/tap/metadata/TAPColumn.java b/src/tap/metadata/TAPColumn.java
index 3e56db1..13cb81a 100644
--- a/src/tap/metadata/TAPColumn.java
+++ b/src/tap/metadata/TAPColumn.java
@@ -25,9 +25,10 @@ import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.Map;
 
-import tap.metadata.TAPType.TAPDatatype;
 import adql.db.DBColumn;
 import adql.db.DBTable;
+import adql.db.DBType;
+import adql.db.DBType.DBDatatype;
 
 /**
  * <p>Represent a column as described by the IVOA standard in the TAP protocol definition.</p>
@@ -101,7 +102,7 @@ public class TAPColumn implements DBColumn {
 
 	/** Type of this column.
 	 * <i>Note: Standard TAP column field ; CAN'T be NULL.</i> */
-	private TAPType datatype = new TAPType(TAPDatatype.VARCHAR);
+	private DBType datatype = new DBType(DBDatatype.VARCHAR);
 
 	/** Flag indicating whether this column is one of those that should be returned by default.
 	 * <i>Note: Standard TAP column field ; FALSE by default.</i> */
@@ -170,16 +171,16 @@ public class TAPColumn implements DBColumn {
 	 * </i></p>
 	 * 
 	 * <p><i>Note:
-	 *	The datatype is set by calling the function {@link #setDatatype(TAPType)} which does not do
+	 *	The datatype is set by calling the function {@link #setDatatype(DBType)} which does not do
 	 *	anything if the given datatype is NULL.
 	 * </i></p>
 	 * 
 	 * @param columnName	Name that this column MUST have in ADQL queries. <i>CAN'T be NULL ; this name can never be changed after.</i>
 	 * @param type			Datatype of this column. <i>If NULL, VARCHAR will be the datatype of this column</i>
 	 * 
-	 * @see #setDatatype(TAPType)
+	 * @see #setDatatype(DBType)
 	 */
-	public TAPColumn(String columnName, TAPType type){
+	public TAPColumn(String columnName, DBType type){
 		this(columnName);
 		setDatatype(type);
 	}
@@ -201,7 +202,7 @@ public class TAPColumn implements DBColumn {
 	 * @param description	Description of the column's content. <i>May be NULL</i>
 	 */
 	public TAPColumn(String columnName, String description){
-		this(columnName, (TAPType)null, description);
+		this(columnName, (DBType)null, description);
 	}
 
 	/**
@@ -218,7 +219,7 @@ public class TAPColumn implements DBColumn {
 	 * </i></p>
 	 * 
 	 * <p><i>Note:
-	 *	The datatype is set by calling the function {@link #setDatatype(TAPType)} which does do
+	 *	The datatype is set by calling the function {@link #setDatatype(DBType)} which does do
 	 *	anything if the given datatype is NULL.
 	 * </i></p>
 	 * 
@@ -226,7 +227,7 @@ public class TAPColumn implements DBColumn {
 	 * @param type			Datatype of this column. <i>If NULL, VARCHAR will be the datatype of this column</i>
 	 * @param description	Description of the column's content. <i>May be NULL</i>
 	 */
-	public TAPColumn(String columnName, TAPType type, String description){
+	public TAPColumn(String columnName, DBType type, String description){
 		this(columnName, type);
 		this.description = description;
 	}
@@ -266,7 +267,7 @@ public class TAPColumn implements DBColumn {
 	 * </i></p>
 	 * 
 	 * <p><i>Note:
-	 *	The datatype is set by calling the function {@link #setDatatype(TAPType)} which does do
+	 *	The datatype is set by calling the function {@link #setDatatype(DBType)} which does do
 	 *	anything if the given datatype is NULL.
 	 * </i></p>
 	 * 
@@ -275,7 +276,7 @@ public class TAPColumn implements DBColumn {
 	 * @param description	Description of the column's content. <i>May be NULL</i>
 	 * @param unit			Unit of the column's values. <i>May be NULL</i>
 	 */
-	public TAPColumn(String columnName, TAPType type, String description, String unit){
+	public TAPColumn(String columnName, DBType type, String description, String unit){
 		this(columnName, type, description);
 		this.unit = unit;
 	}
@@ -294,7 +295,7 @@ public class TAPColumn implements DBColumn {
 	 * </i></p>
 	 * 
 	 * <p><i>Note:
-	 *	The datatype is set by calling the function {@link #setDatatype(TAPType)} which does do
+	 *	The datatype is set by calling the function {@link #setDatatype(DBType)} which does do
 	 *	anything if the given datatype is NULL.
 	 * </i></p>
 	 * 
@@ -322,7 +323,7 @@ public class TAPColumn implements DBColumn {
 	 * </i></p>
 	 * 
 	 * <p><i>Note:
-	 *	The datatype is set by calling the function {@link #setDatatype(TAPType)} which does do
+	 *	The datatype is set by calling the function {@link #setDatatype(DBType)} which does do
 	 *	anything if the given datatype is NULL.
 	 * </i></p>
 	 * 
@@ -333,7 +334,7 @@ public class TAPColumn implements DBColumn {
 	 * @param ucd			UCD describing the scientific content of this column.
 	 * @param utype			UType associating this column with a data-model.
 	 */
-	public TAPColumn(String columnName, TAPType type, String description, String unit, String ucd, String utype){
+	public TAPColumn(String columnName, DBType type, String description, String unit, String ucd, String utype){
 		this(columnName, type, description, unit);
 		this.ucd = ucd;
 		this.utype = utype;
@@ -479,7 +480,7 @@ public class TAPColumn implements DBColumn {
 	 * 
 	 * @return	Its datatype. <i>CAN'T be NULL</i>
 	 */
-	public final TAPType getDatatype(){
+	public final DBType getDatatype(){
 		return datatype;
 	}
 
@@ -492,7 +493,7 @@ public class TAPColumn implements DBColumn {
 	 * 
 	 * @param type	Its new datatype.
 	 */
-	public final void setDatatype(final TAPType type){
+	public final void setDatatype(final DBType type){
 		if (type != null)
 			datatype = type;
 	}
diff --git a/src/tap/metadata/TAPMetadata.java b/src/tap/metadata/TAPMetadata.java
index 15a57b0..68e0b1c 100644
--- a/src/tap/metadata/TAPMetadata.java
+++ b/src/tap/metadata/TAPMetadata.java
@@ -34,11 +34,12 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 import tap.metadata.TAPTable.TableType;
-import tap.metadata.TAPType.TAPDatatype;
 import tap.resource.Capabilities;
 import tap.resource.TAPResource;
 import tap.resource.VOSIResource;
 import adql.db.DBTable;
+import adql.db.DBType;
+import adql.db.DBType.DBDatatype;
 
 /**
  * <p>Let listing all schemas, tables and columns available in a TAP service.
@@ -730,49 +731,49 @@ public class TAPMetadata implements Iterable<TAPSchema>, VOSIResource, TAPResour
 
 			case SCHEMAS:
 				TAPTable schemas = new TAPTable(STDTable.SCHEMAS.toString(), TableType.table, "List of schemas published in this TAP service.", null);
-				schemas.addColumn("schema_name", new TAPType(TAPDatatype.VARCHAR), "schema name, possibly qualified", null, null, null, true, true, true);
-				schemas.addColumn("description", new TAPType(TAPDatatype.VARCHAR), "brief description of schema", null, null, null, false, false, true);
-				schemas.addColumn("utype", new TAPType(TAPDatatype.VARCHAR), "UTYPE if schema corresponds to a data model", null, null, null, false, false, true);
+				schemas.addColumn("schema_name", new DBType(DBDatatype.VARCHAR), "schema name, possibly qualified", null, null, null, true, true, true);
+				schemas.addColumn("description", new DBType(DBDatatype.VARCHAR), "brief description of schema", null, null, null, false, false, true);
+				schemas.addColumn("utype", new DBType(DBDatatype.VARCHAR), "UTYPE if schema corresponds to a data model", null, null, null, false, false, true);
 				return schemas;
 
 			case TABLES:
 				TAPTable tables = new TAPTable(STDTable.TABLES.toString(), TableType.table, "List of tables published in this TAP service.", null);
-				tables.addColumn("schema_name", new TAPType(TAPDatatype.VARCHAR), "the schema name from TAP_SCHEMA.schemas", null, null, null, true, true, true);
-				tables.addColumn("table_name", new TAPType(TAPDatatype.VARCHAR), "table name as it should be used in queries", null, null, null, true, true, true);
-				tables.addColumn("table_type", new TAPType(TAPDatatype.VARCHAR), "one of: table, view", null, null, null, false, false, true);
-				tables.addColumn("description", new TAPType(TAPDatatype.VARCHAR), "brief description of table", null, null, null, false, false, true);
-				tables.addColumn("utype", new TAPType(TAPDatatype.VARCHAR), "UTYPE if table corresponds to a data model", null, null, null, false, false, true);
+				tables.addColumn("schema_name", new DBType(DBDatatype.VARCHAR), "the schema name from TAP_SCHEMA.schemas", null, null, null, true, true, true);
+				tables.addColumn("table_name", new DBType(DBDatatype.VARCHAR), "table name as it should be used in queries", null, null, null, true, true, true);
+				tables.addColumn("table_type", new DBType(DBDatatype.VARCHAR), "one of: table, view", null, null, null, false, false, true);
+				tables.addColumn("description", new DBType(DBDatatype.VARCHAR), "brief description of table", null, null, null, false, false, true);
+				tables.addColumn("utype", new DBType(DBDatatype.VARCHAR), "UTYPE if table corresponds to a data model", null, null, null, false, false, true);
 				return tables;
 
 			case COLUMNS:
 				TAPTable columns = new TAPTable(STDTable.COLUMNS.toString(), TableType.table, "List of columns of all tables listed in TAP_SCHEMA.TABLES and published in this TAP service.", null);
-				columns.addColumn("table_name", new TAPType(TAPDatatype.VARCHAR), "table name from TAP_SCHEMA.tables", null, null, null, true, true, true);
-				columns.addColumn("column_name", new TAPType(TAPDatatype.VARCHAR), "column name", null, null, null, true, true, true);
-				columns.addColumn("description", new TAPType(TAPDatatype.VARCHAR), "brief description of column", null, null, null, false, false, true);
-				columns.addColumn("unit", new TAPType(TAPDatatype.VARCHAR), "unit in VO standard format", null, null, null, false, false, true);
-				columns.addColumn("ucd", new TAPType(TAPDatatype.VARCHAR), "UCD of column if any", null, null, null, false, false, true);
-				columns.addColumn("utype", new TAPType(TAPDatatype.VARCHAR), "UTYPE of column if any", null, null, null, false, false, true);
-				columns.addColumn("datatype", new TAPType(TAPDatatype.VARCHAR), "ADQL datatype as in section 2.5", null, null, null, false, false, true);
-				columns.addColumn("size", new TAPType(TAPDatatype.INTEGER), "length of variable length datatypes", null, null, null, false, false, true);
-				columns.addColumn("principal", new TAPType(TAPDatatype.INTEGER), "a principal column; 1 means true, 0 means false", null, null, null, false, false, true);
-				columns.addColumn("indexed", new TAPType(TAPDatatype.INTEGER), "an indexed column; 1 means true, 0 means false", null, null, null, false, false, true);
-				columns.addColumn("std", new TAPType(TAPDatatype.INTEGER), "a standard column; 1 means true, 0 means false", null, null, null, false, false, true);
+				columns.addColumn("table_name", new DBType(DBDatatype.VARCHAR), "table name from TAP_SCHEMA.tables", null, null, null, true, true, true);
+				columns.addColumn("column_name", new DBType(DBDatatype.VARCHAR), "column name", null, null, null, true, true, true);
+				columns.addColumn("description", new DBType(DBDatatype.VARCHAR), "brief description of column", null, null, null, false, false, true);
+				columns.addColumn("unit", new DBType(DBDatatype.VARCHAR), "unit in VO standard format", null, null, null, false, false, true);
+				columns.addColumn("ucd", new DBType(DBDatatype.VARCHAR), "UCD of column if any", null, null, null, false, false, true);
+				columns.addColumn("utype", new DBType(DBDatatype.VARCHAR), "UTYPE of column if any", null, null, null, false, false, true);
+				columns.addColumn("datatype", new DBType(DBDatatype.VARCHAR), "ADQL datatype as in section 2.5", null, null, null, false, false, true);
+				columns.addColumn("size", new DBType(DBDatatype.INTEGER), "length of variable length datatypes", null, null, null, false, false, true);
+				columns.addColumn("principal", new DBType(DBDatatype.INTEGER), "a principal column; 1 means true, 0 means false", null, null, null, false, false, true);
+				columns.addColumn("indexed", new DBType(DBDatatype.INTEGER), "an indexed column; 1 means true, 0 means false", null, null, null, false, false, true);
+				columns.addColumn("std", new DBType(DBDatatype.INTEGER), "a standard column; 1 means true, 0 means false", null, null, null, false, false, true);
 				return columns;
 
 			case KEYS:
 				TAPTable keys = new TAPTable(STDTable.KEYS.toString(), TableType.table, "List all foreign keys but provides just the tables linked by the foreign key. To know which columns of these tables are linked, see in TAP_SCHEMA.key_columns using the key_id.", null);
-				keys.addColumn("key_id", new TAPType(TAPDatatype.VARCHAR), "unique key identifier", null, null, null, true, true, true);
-				keys.addColumn("from_table", new TAPType(TAPDatatype.VARCHAR), "fully qualified table name", null, null, null, false, false, true);
-				keys.addColumn("target_table", new TAPType(TAPDatatype.VARCHAR), "fully qualified table name", null, null, null, false, false, true);
-				keys.addColumn("description", new TAPType(TAPDatatype.VARCHAR), "description of this key", null, null, null, false, false, true);
-				keys.addColumn("utype", new TAPType(TAPDatatype.VARCHAR), "utype of this key", null, null, null, false, false, true);
+				keys.addColumn("key_id", new DBType(DBDatatype.VARCHAR), "unique key identifier", null, null, null, true, true, true);
+				keys.addColumn("from_table", new DBType(DBDatatype.VARCHAR), "fully qualified table name", null, null, null, false, false, true);
+				keys.addColumn("target_table", new DBType(DBDatatype.VARCHAR), "fully qualified table name", null, null, null, false, false, true);
+				keys.addColumn("description", new DBType(DBDatatype.VARCHAR), "description of this key", null, null, null, false, false, true);
+				keys.addColumn("utype", new DBType(DBDatatype.VARCHAR), "utype of this key", null, null, null, false, false, true);
 				return keys;
 
 			case KEY_COLUMNS:
 				TAPTable key_columns = new TAPTable(STDTable.KEY_COLUMNS.toString(), TableType.table, "List all foreign keys but provides just the columns linked by the foreign key. To know the table of these columns, see in TAP_SCHEMA.keys using the key_id.", null);
-				key_columns.addColumn("key_id", new TAPType(TAPDatatype.VARCHAR), "unique key identifier", null, null, null, true, true, true);
-				key_columns.addColumn("from_column", new TAPType(TAPDatatype.VARCHAR), "key column name in the from_table", null, null, null, false, false, true);
-				key_columns.addColumn("target_column", new TAPType(TAPDatatype.VARCHAR), "key column name in the target_table", null, null, null, false, false, true);
+				key_columns.addColumn("key_id", new DBType(DBDatatype.VARCHAR), "unique key identifier", null, null, null, true, true, true);
+				key_columns.addColumn("from_column", new DBType(DBDatatype.VARCHAR), "key column name in the from_table", null, null, null, false, false, true);
+				key_columns.addColumn("target_column", new DBType(DBDatatype.VARCHAR), "key column name in the target_table", null, null, null, false, false, true);
 				return key_columns;
 
 			default:
diff --git a/src/tap/metadata/TAPTable.java b/src/tap/metadata/TAPTable.java
index 4685a41..9f001f3 100644
--- a/src/tap/metadata/TAPTable.java
+++ b/src/tap/metadata/TAPTable.java
@@ -30,6 +30,7 @@ import java.util.Map;
 import tap.TAPException;
 import adql.db.DBColumn;
 import adql.db.DBTable;
+import adql.db.DBType;
 
 /**
  * <p>Represent a table as described by the IVOA standard in the TAP protocol definition.</p>
@@ -433,10 +434,10 @@ public class TAPTable implements DBTable {
 	 * @return	The created and added {@link TAPColumn} object,
 	 *        	or NULL if the given name is NULL or an empty string.
 	 * 
-	 * @see TAPColumn#TAPColumn(String, TAPType, String, String, String, String)
+	 * @see TAPColumn#TAPColumn(String, DBType, String, String, String, String)
 	 * @see #addColumn(TAPColumn)
 	 */
-	public TAPColumn addColumn(String columnName, TAPType datatype, String description, String unit, String ucd, String utype){
+	public TAPColumn addColumn(String columnName, DBType datatype, String description, String unit, String ucd, String utype){
 		if (columnName == null || columnName.trim().length() <= 0)
 			return null;
 
@@ -466,13 +467,13 @@ public class TAPTable implements DBTable {
 	 * @return	The created and added {@link TAPColumn} object,
 	 *        	or NULL if the given name is NULL or an empty string.
 	 * 
-	 * @see TAPColumn#TAPColumn(String, TAPType, String, String, String, String)
+	 * @see TAPColumn#TAPColumn(String, DBType, String, String, String, String)
 	 * @see TAPColumn#setPrincipal(boolean)
 	 * @see TAPColumn#setIndexed(boolean)
 	 * @see TAPColumn#setStd(boolean)
 	 * @see #addColumn(TAPColumn)
 	 */
-	public TAPColumn addColumn(String columnName, TAPType datatype, String description, String unit, String ucd, String utype, boolean principal, boolean indexed, boolean std){
+	public TAPColumn addColumn(String columnName, DBType datatype, String description, String unit, String ucd, String utype, boolean principal, boolean indexed, boolean std){
 		if (columnName == null || columnName.trim().length() <= 0)
 			return null;
 
diff --git a/src/tap/metadata/VotType.java b/src/tap/metadata/VotType.java
index 203c01d..35f8bab 100644
--- a/src/tap/metadata/VotType.java
+++ b/src/tap/metadata/VotType.java
@@ -17,11 +17,12 @@ package tap.metadata;
  * along with TAPLibrary.  If not, see <http://www.gnu.org/licenses/>.
  * 
  * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
- *                       Astronomishes Rechen Institut (ARI)
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
+import adql.db.DBType;
+import adql.db.DBType.DBDatatype;
 import tap.TAPException;
-import tap.metadata.TAPType.TAPDatatype;
 import uk.ac.starlink.votable.VOSerializer;
 
 /**
@@ -120,11 +121,11 @@ public final class VotType {
 	}
 
 	/**
-	 * Build a {@link VotType} object by converting the given {@link TAPType}.
+	 * Build a {@link VotType} object by converting the given {@link DBType}.
 	 * 
-	 * @param tapType	{@link TAPType} to convert.
+	 * @param tapType	{@link DBType} to convert.
 	 */
-	public VotType(final TAPType tapType){
+	public VotType(final DBType tapType){
 		switch(tapType.type){
 			case SMALLINT:
 				this.datatype = VotDatatype.SHORT;
@@ -250,11 +251,11 @@ public final class VotType {
 	/**
 	 * Convert this VOTable type definition into a TAPColumn type.
 	 * 
-	 * @return	The corresponding {@link TAPType}.
+	 * @return	The corresponding {@link DBType}.
 	 * 
 	 * @throws TAPException	If the conversion is impossible (particularly if the array-size refers to a multi-dimensional array ; only 1D arrays are allowed). 
 	 */
-	public TAPType toTAPType() throws TAPException{
+	public DBType toTAPType() throws TAPException{
 
 		/* Stop immediately if the arraysize refers to a multi-dimensional array:
 		 * (Note: 'x' is the dimension separator of the VOTable attribute 'arraysize') */
@@ -266,29 +267,29 @@ public final class VotType {
 		/* NUMERIC TYPES */
 			case SHORT:
 			case BOOLEAN:
-				return convertNumericType(TAPDatatype.SMALLINT);
+				return convertNumericType(DBDatatype.SMALLINT);
 
 			case INT:
-				return convertNumericType(TAPDatatype.INTEGER);
+				return convertNumericType(DBDatatype.INTEGER);
 
 			case LONG:
-				return convertNumericType(TAPDatatype.BIGINT);
+				return convertNumericType(DBDatatype.BIGINT);
 
 			case FLOAT:
-				return convertNumericType(TAPDatatype.REAL);
+				return convertNumericType(DBDatatype.REAL);
 
 			case DOUBLE:
-				return convertNumericType(TAPDatatype.DOUBLE);
+				return convertNumericType(DBDatatype.DOUBLE);
 
 				/* BINARY TYPES */
 			case UNSIGNED_BYTE:
 				// BLOB exception:
 				if (xtype != null && xtype.equalsIgnoreCase(XTYPE_BLOB))
-					return new TAPType(TAPDatatype.BLOB);
+					return new DBType(DBDatatype.BLOB);
 
 				// Or else, just (var)binary:
 				else
-					return convertVariableLengthType(TAPDatatype.VARBINARY, TAPDatatype.BINARY);
+					return convertVariableLengthType(DBDatatype.VARBINARY, DBDatatype.BINARY);
 
 				/* CHARACTER TYPES */
 			case CHAR:
@@ -296,47 +297,47 @@ public final class VotType {
 				/* Special type cases: */
 				if (xtype != null){
 					if (xtype.equalsIgnoreCase(VotType.XTYPE_CLOB))
-						return new TAPType(TAPDatatype.CLOB);
+						return new DBType(DBDatatype.CLOB);
 					else if (xtype.equalsIgnoreCase(VotType.XTYPE_TIMESTAMP))
-						return new TAPType(TAPDatatype.TIMESTAMP);
+						return new DBType(DBDatatype.TIMESTAMP);
 					else if (xtype.equalsIgnoreCase(VotType.XTYPE_POINT))
-						return new TAPType(TAPDatatype.POINT);
+						return new DBType(DBDatatype.POINT);
 					else if (xtype.equalsIgnoreCase(VotType.XTYPE_REGION))
-						return new TAPType(TAPDatatype.REGION);
+						return new DBType(DBDatatype.REGION);
 				}
 
 				// Or if not known or missing, just a (var)char:
-				return convertVariableLengthType(TAPDatatype.VARCHAR, TAPDatatype.CHAR);
+				return convertVariableLengthType(DBDatatype.VARCHAR, DBDatatype.CHAR);
 		}
 	}
 
 	/**
-	 * <p>Convert this numeric {@link VotType} object into a corresponding {@link TAPType} whose the datatype is provided in parameter.</p>
+	 * <p>Convert this numeric {@link VotType} object into a corresponding {@link DBType} whose the datatype is provided in parameter.</p>
 	 * 
 	 * <p>
 	 * 	Thus, just the arraysize must be managed here. If there is no arraysize or if equals to '1', the given datatype will be used.
-	 * 	Otherwise, it is ignored and a {@link TAPType} with VARBINARY is returned.
+	 * 	Otherwise, it is ignored and a {@link DBType} with VARBINARY is returned.
 	 * </p>
 	 * 
 	 * @param tapDatatype	TAP datatype corresponding to this {@link VotType} (only when arraysize != '*' and 'n').
 	 * 
-	 * @return	The corresponding {@link TAPType}.
+	 * @return	The corresponding {@link DBType}.
 	 */
-	protected TAPType convertNumericType(final TAPDatatype tapDatatype){
+	protected DBType convertNumericType(final DBDatatype tapDatatype){
 		// If no arraysize:
 		if (arraysize == null || arraysize.equals("1"))
-			return new TAPType(tapDatatype);
+			return new DBType(tapDatatype);
 
 		// If only one dimension:
 		else
-			return new TAPType(TAPDatatype.VARBINARY);
+			return new DBType(DBDatatype.VARBINARY);
 
 		/* Note: The test of multi-dimensional array should have been already done at the beginning of #toTAPType(). */
 	}
 
 	/**
 	 * <p>
-	 * 	Convert this variable length {@link VotType} (unsignedByte and char) object into a corresponding {@link TAPType}
+	 * 	Convert this variable length {@link VotType} (unsignedByte and char) object into a corresponding {@link DBType}
 	 * 	whose the variable length and fixed length versions are given in parameters.
 	 * </p>
 	 * 
@@ -350,23 +351,23 @@ public final class VotType {
 	 * @param varType		Variable length type (i.e. VARCHAR, VARBINARY).
 	 * @param fixedType		Fixed length type (i.e. CHAR, BINARY).
 	 * 
-	 * @return	The corresponding {@link TAPType}.
+	 * @return	The corresponding {@link DBType}.
 	 * 
 	 * @throws TAPException	If the arraysize is not valid (that's to say, different from the following syntaxes: NULL, '*', 'n' or 'n*' (where n is a positive and not-null integer)).
 	 */
-	protected TAPType convertVariableLengthType(final TAPDatatype varType, final TAPDatatype fixedType) throws TAPException{
+	protected DBType convertVariableLengthType(final DBDatatype varType, final DBDatatype fixedType) throws TAPException{
 		try{
 			// no arraysize or '*' => VARCHAR or VARBINARY
 			if (arraysize == null || arraysize.equals("*"))
-				return new TAPType(varType);
+				return new DBType(varType);
 
 			// 'n*' => VARCHAR(n) or VARBINARY(n)
 			else if (arraysize.charAt(arraysize.length() - 1) == '*')
-				return new TAPType(varType, Integer.parseInt(arraysize.substring(0, arraysize.length() - 1)));
+				return new DBType(varType, Integer.parseInt(arraysize.substring(0, arraysize.length() - 1)));
 
 			// 'n' => CHAR(n) or BINARY(n)
 			else
-				return new TAPType(fixedType, Integer.parseInt(arraysize));
+				return new DBType(fixedType, Integer.parseInt(arraysize));
 
 		}catch(NumberFormatException nfe){
 			throw new TAPException("failed conversion of a VOTable datatype: non-numeric arraysize (" + arraysize + ")!");
diff --git a/src/tap/parameters/TAPParameters.java b/src/tap/parameters/TAPParameters.java
index 461c7ee..6eb4f09 100644
--- a/src/tap/parameters/TAPParameters.java
+++ b/src/tap/parameters/TAPParameters.java
@@ -81,7 +81,7 @@ public class TAPParameters extends UWSParameters {
 		// Multipart HTTP parameters:
 		if (isMultipartContent(request)){
 			if (!service.uploadEnabled())
-				throw new TAPException("Request error ! This TAP service has no Upload capability !", UWSException.BAD_REQUEST);
+				throw new TAPException("Request error! This TAP service has no Upload capability!", UWSException.BAD_REQUEST);
 
 			File uploadDir = service.getFileManager().getUploadDirectory();
 			try{
@@ -97,14 +97,14 @@ public class TAPParameters extends UWSParameters {
 					set(param, multipart.getParameter(param));
 				}
 			}catch(IOException ioe){
-				throw new TAPException("Error while reading the Multipart content !", ioe);
+				throw new TAPException("Error while reading the Multipart content!", ioe);
 			}catch(IllegalArgumentException iae){
 				String confError = iae.getMessage();
 				if (service.getMaxUploadSize() <= 0)
-					confError = "The maximum upload size (see ServiceConnection.getMaxUploadSize() must be positive !";
+					confError = "The maximum upload size (see ServiceConnection.getMaxUploadSize() must be positive!";
 				else if (uploadDir == null)
-					confError = "Missing upload directory (see TAPFileManager.getUploadDirectory()) !";
-				throw new TAPException("Incorrect Upload capability configuration ! " + confError, iae);
+					confError = "Missing upload directory (see TAPFileManager.getUploadDirectory())!";
+				throw new TAPException("Incorrect Upload capability configuration! " + confError, iae);
 			}
 
 		}// Classic HTTP parameters (GET or POST):
diff --git a/src/tap/resource/TAP.java b/src/tap/resource/TAP.java
index 00e8496..54724c8 100644
--- a/src/tap/resource/TAP.java
+++ b/src/tap/resource/TAP.java
@@ -50,6 +50,7 @@ import uws.service.UWSService;
 import uws.service.UWSUrl;
 import uws.service.error.ServiceErrorWriter;
 import uws.service.log.UWSLog.LogLevel;
+import adql.db.FunctionDef;
 
 /**
  * <p>Root/Home of the TAP service. It is also the resource (HOME) which gathers all the others of the same TAP service.</p>
@@ -57,7 +58,7 @@ import uws.service.log.UWSLog.LogLevel;
  * <p>At its creation it is creating and configuring the other resources in function of the given description of the TAP service.</p>
  * 
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 2.0 (09/2014)
+ * @version 2.0 (10/2014)
  */
 public class TAP implements VOSIResource {
 
@@ -113,8 +114,7 @@ public class TAP implements VOSIResource {
 		resources.put(res.getName(), res);
 
 		TAPMetadata metadata = service.getTAPMetadata();
-		if (metadata != null)
-			resources.put(metadata.getName(), metadata);
+		resources.put(metadata.getName(), metadata);
 	}
 
 	/**
@@ -422,6 +422,35 @@ public class TAP implements VOSIResource {
 		xml.append("\t\t<name>ADQL</name>\n");
 		xml.append("\t\t<version ivo-id=\"ivo://ivoa.net/std/ADQL#v2.0\">2.0</version>\n");
 		xml.append("\t\t<description>ADQL 2.0</description>\n");
+
+		// Geometrical functions:
+		if (service.getGeometries() != null && service.getGeometries().size() > 0){
+			xml.append("\t\t<languageFeatures type=\"ivo://ivoa.net/std/TAPRegExt#features-adqlgeo\">");
+			for(String geom : service.getGeometries()){
+				if (geom != null){
+					xml.append("\t\t\t<feature>");
+					xml.append("\t\t\t\t<form>").append(VOSerializer.formatText(geom.toUpperCase())).append("</form>");
+					xml.append("\t\t\t</feature>");
+				}
+			}
+			xml.append("\t\t</languageFeatures>");
+		}
+
+		// User Defined Functions (UDFs):
+		if (service.getUDFs() != null && service.getUDFs().size() > 0){
+			xml.append("\t\t<languageFeatures type=\"ivo://ivoa.net/std/TAPRegExt#features-udf\">");
+			for(FunctionDef udf : service.getUDFs()){
+				if (udf != null){
+					xml.append("\t\t\t<feature>");
+					xml.append("\t\t\t\t<form>").append(VOSerializer.formatText(udf.toString())).append("</form>");
+					if (udf.description != null && udf.description.length() > 0)
+						xml.append("\t\t\t\t<description>").append(VOSerializer.formatText(udf.description)).append("</description>");
+					xml.append("\t\t\t</feature>");
+				}
+			}
+			xml.append("\t\t</languageFeatures>");
+		}
+
 		xml.append("\t</language>\n");
 
 		// Available output formats:
diff --git a/src/uws/service/error/DefaultUWSErrorWriter.java b/src/uws/service/error/DefaultUWSErrorWriter.java
index f9854a4..74366d3 100644
--- a/src/uws/service/error/DefaultUWSErrorWriter.java
+++ b/src/uws/service/error/DefaultUWSErrorWriter.java
@@ -104,7 +104,7 @@ public class DefaultUWSErrorWriter implements ServiceErrorWriter {
 			// log the error as GRAVE/FATAL (because unexpected/unmanaged):
 			logger.logUWS(LogLevel.FATAL, null, null, "[REQUEST N°" + reqID + "] " + t.getMessage(), t);
 			// write a message to the user:
-			writeError("INTERNAL SERVER ERROR! Sorry, this error is unexpected and no explaination can be provided for the moment. Details about this error have been reported in the service log files ; you should try again your request later or notify the administrator(s) by yourself (with the following 'Request ID').", ErrorType.FATAL, UWSException.INTERNAL_SERVER_ERROR, response, request, reqID, user, action);
+			writeError("INTERNAL SERVER ERROR! Sorry, this error is unexpected and no explanation can be provided for the moment. Details about this error have been reported in the service log files ; you should try again your request later or notify the administrator(s) by yourself (with the following 'Request ID').", ErrorType.FATAL, UWSException.INTERNAL_SERVER_ERROR, response, request, reqID, user, action);
 		}
 	}
 
diff --git a/test/adql/SearchColumnListTest.java b/test/adql/SearchColumnListTest.java
index b2e5988..d3f614f 100644
--- a/test/adql/SearchColumnListTest.java
+++ b/test/adql/SearchColumnListTest.java
@@ -9,12 +9,12 @@ import tap.metadata.TAPColumn;
 import tap.metadata.TAPSchema;
 import tap.metadata.TAPTable;
 import tap.metadata.TAPTable.TableType;
-import tap.metadata.TAPType;
-import tap.metadata.TAPType.TAPDatatype;
 import adql.db.DBColumn;
 import adql.db.DBCommonColumn;
 import adql.db.DBTable;
+import adql.db.DBType;
 import adql.db.SearchColumnList;
+import adql.db.DBType.DBDatatype;
 import adql.db.exception.UnresolvedJoin;
 import adql.parser.ParseException;
 import adql.query.IdentifierField;
@@ -32,16 +32,16 @@ public class SearchColumnListTest {
 		TAPTable tableD = new TAPTable("D", TableType.table, "NATURAL JOIN Test table", null);
 
 		// Describe its columns:
-		tableA.addColumn(new TAPColumn("id", new TAPType(TAPDatatype.VARCHAR), "Object ID"));
-		tableA.addColumn(new TAPColumn("txta", new TAPType(TAPDatatype.VARCHAR), "Text of table A"));
-		tableB.addColumn(new TAPColumn("id", new TAPType(TAPDatatype.VARCHAR), "Object ID"));
-		tableB.addColumn(new TAPColumn("txtb", new TAPType(TAPDatatype.VARCHAR), "Text of table B"));
-		tableC.addColumn(new TAPColumn("Id", new TAPType(TAPDatatype.VARCHAR), "Object ID"));
-		tableC.addColumn(new TAPColumn("txta", new TAPType(TAPDatatype.VARCHAR), "Text of table A"));
-		tableC.addColumn(new TAPColumn("txtc", new TAPType(TAPDatatype.VARCHAR), "Text of table C"));
-		tableD.addColumn(new TAPColumn("id", new TAPType(TAPDatatype.VARCHAR), "Object ID"));
-		tableD.addColumn(new TAPColumn("txta", new TAPType(TAPDatatype.VARCHAR), "Text of table A"));
-		tableD.addColumn(new TAPColumn("txtd", new TAPType(TAPDatatype.VARCHAR), "Text of table D"));
+		tableA.addColumn(new TAPColumn("id", new DBType(DBDatatype.VARCHAR), "Object ID"));
+		tableA.addColumn(new TAPColumn("txta", new DBType(DBDatatype.VARCHAR), "Text of table A"));
+		tableB.addColumn(new TAPColumn("id", new DBType(DBDatatype.VARCHAR), "Object ID"));
+		tableB.addColumn(new TAPColumn("txtb", new DBType(DBDatatype.VARCHAR), "Text of table B"));
+		tableC.addColumn(new TAPColumn("Id", new DBType(DBDatatype.VARCHAR), "Object ID"));
+		tableC.addColumn(new TAPColumn("txta", new DBType(DBDatatype.VARCHAR), "Text of table A"));
+		tableC.addColumn(new TAPColumn("txtc", new DBType(DBDatatype.VARCHAR), "Text of table C"));
+		tableD.addColumn(new TAPColumn("id", new DBType(DBDatatype.VARCHAR), "Object ID"));
+		tableD.addColumn(new TAPColumn("txta", new DBType(DBDatatype.VARCHAR), "Text of table A"));
+		tableD.addColumn(new TAPColumn("txtd", new DBType(DBDatatype.VARCHAR), "Text of table D"));
 
 		// List all available tables:
 		TAPSchema schema = new TAPSchema("public");
diff --git a/test/adql/db/TestDBChecker.java b/test/adql/db/TestDBChecker.java
new file mode 100644
index 0000000..c8e8b4b
--- /dev/null
+++ b/test/adql/db/TestDBChecker.java
@@ -0,0 +1,609 @@
+package adql.db;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import adql.db.DBType.DBDatatype;
+import adql.db.FunctionDef.FunctionParam;
+import adql.db.exception.UnresolvedIdentifiersException;
+import adql.parser.ADQLParser;
+import adql.parser.ParseException;
+import adql.query.ADQLQuery;
+import adql.query.operand.ADQLColumn;
+import adql.query.operand.ADQLOperand;
+import adql.query.operand.function.DefaultUDF;
+
+public class TestDBChecker {
+
+	private static List<DBTable> tables;
+
+	@BeforeClass
+	public static void setUpBeforeClass() throws Exception{
+		tables = new ArrayList<DBTable>();
+
+		DefaultDBTable fooTable = new DefaultDBTable("foo");
+		DBColumn col = new DefaultDBColumn("colS", new DBType(DBDatatype.VARCHAR), fooTable);
+		fooTable.addColumn(col);
+		col = new DefaultDBColumn("colI", new DBType(DBDatatype.INTEGER), fooTable);
+		fooTable.addColumn(col);
+		col = new DefaultDBColumn("colG", new DBType(DBDatatype.POINT), fooTable);
+		fooTable.addColumn(col);
+
+		tables.add(fooTable);
+	}
+
+	@AfterClass
+	public static void tearDownAfterClass() throws Exception{}
+
+	@Before
+	public void setUp() throws Exception{}
+
+	@After
+	public void tearDown() throws Exception{}
+
+	@Test
+	public void testNumericOrStringValueExpressionPrimary(){
+		ADQLParser parser = new ADQLParser();
+		try{
+			assertNotNull(parser.parseQuery("SELECT 'toto' FROM foo;"));
+			assertNotNull(parser.parseQuery("SELECT ('toto') FROM foo;"));
+			assertNotNull(parser.parseQuery("SELECT (('toto')) FROM foo;"));
+			assertNotNull(parser.parseQuery("SELECT 'toto' || 'blabla' FROM foo;"));
+			assertNotNull(parser.parseQuery("SELECT ('toto' || 'blabla') FROM foo;"));
+			assertNotNull(parser.parseQuery("SELECT (('toto' || 'blabla')) FROM foo;"));
+			assertNotNull(parser.parseQuery("SELECT (('toto') || (('blabla'))) FROM foo;"));
+			assertNotNull(parser.parseQuery("SELECT 3 FROM foo;"));
+			assertNotNull(parser.parseQuery("SELECT ((2+3)*5) FROM foo;"));
+			assertNotNull(parser.parseQuery("SELECT ABS(-123) FROM foo;"));
+			assertNotNull(parser.parseQuery("SELECT ABS(2*-1+5) FROM foo;"));
+			assertNotNull(parser.parseQuery("SELECT ABS(COUNT(*)) FROM foo;"));
+			assertNotNull(parser.parseQuery("SELECT toto FROM foo;"));
+			assertNotNull(parser.parseQuery("SELECT toto * 3 FROM foo;"));
+			assertNotNull(parser.parseQuery("SELECT toto || 'blabla' FROM foo;"));
+		}catch(ParseException pe){
+			pe.printStackTrace();
+			fail();
+		}
+		try{
+			parser.parseQuery("SELECT ABS('toto') FROM foo;");
+			fail();
+		}catch(ParseException pe){}
+		try{
+			parser.parseQuery("SELECT ABS(('toto' || 'blabla')) FROM foo;");
+			fail();
+		}catch(ParseException pe){}
+		try{
+			parser.parseQuery("SELECT 'toto' || 1 FROM foo;");
+			fail();
+		}catch(ParseException pe){}
+		try{
+			parser.parseQuery("SELECT 1 || 'toto' FROM foo;");
+			fail();
+		}catch(ParseException pe){}
+		try{
+			parser.parseQuery("SELECT 'toto' * 3 FROM foo;");
+			fail();
+		}catch(ParseException pe){}
+	}
+
+	@Test
+	public void testUDFManagement(){
+		// UNKNOWN FUNCTIONS ARE NOT ALLOWED:
+		ADQLParser parser = new ADQLParser(new DBChecker(tables, new ArrayList<FunctionDef>(0)));
+
+		// Test with a simple ADQL query without unknown or user defined function:
+		try{
+			assertNotNull(parser.parseQuery("SELECT * FROM foo;"));
+		}catch(ParseException e){
+			e.printStackTrace();
+			fail("A simple and basic query should not be a problem for the parser!");
+		}
+
+		// Test with an ADQL query containing one not declared UDF:
+		try{
+			parser.parseQuery("SELECT toto() FROM foo;");
+			fail("This query contains a UDF while it's not allowed: this test should have failed!");
+		}catch(ParseException e){
+			assertTrue(e instanceof UnresolvedIdentifiersException);
+			UnresolvedIdentifiersException ex = (UnresolvedIdentifiersException)e;
+			assertEquals(1, ex.getNbErrors());
+			assertEquals("Unresolved function: \"toto()\"! No UDF has been defined or found with the signature: toto().", ex.getErrors().next().getMessage());
+		}
+
+		// DECLARE THE UDFs:
+		FunctionDef[] udfs = new FunctionDef[]{new FunctionDef("toto", new DBType(DBDatatype.VARCHAR)),new FunctionDef("tata", new DBType(DBDatatype.INTEGER))};
+		parser = new ADQLParser(new DBChecker(tables, Arrays.asList(udfs)));
+
+		// Test again:
+		try{
+			assertNotNull(parser.parseQuery("SELECT toto() FROM foo;"));
+			assertNotNull(parser.parseQuery("SELECT tata() FROM foo;"));
+		}catch(ParseException e){
+			e.printStackTrace();
+			fail("This query contains a DECLARED UDF: this test should have succeeded!");
+		}
+
+		// Test but with at least one parameter:
+		try{
+			parser.parseQuery("SELECT toto('blabla') FROM foo;");
+			fail("This query contains an unknown UDF signature (the fct toto is declared with no parameter): this test should have failed!");
+		}catch(ParseException e){
+			assertTrue(e instanceof UnresolvedIdentifiersException);
+			UnresolvedIdentifiersException ex = (UnresolvedIdentifiersException)e;
+			assertEquals(1, ex.getNbErrors());
+			assertEquals("Unresolved function: \"toto('blabla')\"! No UDF has been defined or found with the signature: toto(STRING).", ex.getErrors().next().getMessage());
+		}
+	}
+
+	@Test
+	public void testGeometry(){
+		// DECLARE A SIMPLE PARSER where all geometries are allowed by default:
+		ADQLParser parser = new ADQLParser(new DBChecker(tables));
+
+		// Test with several geometries while all are allowed:
+		try{
+			assertNotNull(parser.parseQuery("SELECT * FROM foo WHERE CONTAINS(POINT('', 12.3, 45.6), CIRCLE('', 1.2, 2.3, 5)) = 1;"));
+		}catch(ParseException pe){
+			pe.printStackTrace();
+			fail("This query contains several geometries, and all are theoretically allowed: this test should have succeeded!");
+		}
+
+		// Test with several geometries while only the allowed ones:
+		try{
+			parser = new ADQLParser(new DBChecker(tables, new ArrayList<FunctionDef>(0), Arrays.asList(new String[]{"CONTAINS","POINT","CIRCLE"}), null));
+			assertNotNull(parser.parseQuery("SELECT * FROM foo WHERE CONTAINS(POINT('', 12.3, 45.6), CIRCLE('', 1.2, 2.3, 5)) = 1;"));
+		}catch(ParseException pe){
+			pe.printStackTrace();
+			fail("This query contains several geometries, and all are theoretically allowed: this test should have succeeded!");
+		}
+		try{
+			parser.parseQuery("SELECT * FROM foo WHERE INTERSECTS(POINT('', 12.3, 45.6), CIRCLE('', 1.2, 2.3, 5)) = 1;");
+			fail("This query contains a not-allowed geometry function (INTERSECTS): this test should have failed!");
+		}catch(ParseException pe){
+			assertTrue(pe instanceof UnresolvedIdentifiersException);
+			UnresolvedIdentifiersException ex = (UnresolvedIdentifiersException)pe;
+			assertEquals(1, ex.getNbErrors());
+			assertEquals("The geometrical function \"INTERSECTS\" is not available in this implementation!", ex.getErrors().next().getMessage());
+		}
+
+		// Test by adding REGION:
+		try{
+			parser = new ADQLParser(new DBChecker(tables, new ArrayList<FunctionDef>(0), Arrays.asList(new String[]{"CONTAINS","POINT","CIRCLE","REGION"}), null));
+			assertNotNull(parser.parseQuery("SELECT * FROM foo WHERE CONTAINS(REGION('Position 12.3 45.6'), REGION('circle 1.2 2.3 5')) = 1;"));
+		}catch(ParseException pe){
+			pe.printStackTrace();
+			fail("This query contains several geometries, and all are theoretically allowed: this test should have succeeded!");
+		}
+		try{
+			parser.parseQuery("SELECT * FROM foo WHERE CONTAINS(REGION('Position 12.3 45.6'), REGION('BOX 1.2 2.3 5 9')) = 1;");
+			fail("This query contains a not-allowed geometry function (BOX): this test should have failed!");
+		}catch(ParseException pe){
+			assertTrue(pe instanceof UnresolvedIdentifiersException);
+			UnresolvedIdentifiersException ex = (UnresolvedIdentifiersException)pe;
+			assertEquals(1, ex.getNbErrors());
+			assertEquals("The geometrical function \"BOX\" is not available in this implementation!", ex.getErrors().next().getMessage());
+		}
+
+		// Test with several geometries while none geometry is allowed:
+		try{
+			parser = new ADQLParser(new DBChecker(tables, new ArrayList<FunctionDef>(0), new ArrayList<String>(0), null));
+			parser.parseQuery("SELECT * FROM foo WHERE CONTAINS(POINT('', 12.3, 45.6), CIRCLE('', 1.2, 2.3, 5)) = 1;");
+			fail("This query contains geometries while they are all forbidden: this test should have failed!");
+		}catch(ParseException pe){
+			assertTrue(pe instanceof UnresolvedIdentifiersException);
+			UnresolvedIdentifiersException ex = (UnresolvedIdentifiersException)pe;
+			assertEquals(3, ex.getNbErrors());
+			Iterator<ParseException> itErrors = ex.getErrors();
+			assertEquals("The geometrical function \"CONTAINS\" is not available in this implementation!", itErrors.next().getMessage());
+			assertEquals("The geometrical function \"POINT\" is not available in this implementation!", itErrors.next().getMessage());
+			assertEquals("The geometrical function \"CIRCLE\" is not available in this implementation!", itErrors.next().getMessage());
+		}
+	}
+
+	@Test
+	public void testCoordSys(){
+		// DECLARE A SIMPLE PARSER where all coordinate systems are allowed by default:
+		ADQLParser parser = new ADQLParser(new DBChecker(tables));
+
+		// Test with several coordinate systems while all are allowed:
+		try{
+			assertNotNull(parser.parseQuery("SELECT * FROM foo WHERE CONTAINS(POINT('', 12.3, 45.6), CIRCLE('', 1.2, 2.3, 5)) = 1;"));
+			assertNotNull(parser.parseQuery("SELECT * FROM foo WHERE CONTAINS(POINT('icrs', 12.3, 45.6), CIRCLE('cartesian2', 1.2, 2.3, 5)) = 1;"));
+			assertNotNull(parser.parseQuery("SELECT * FROM foo WHERE CONTAINS(POINT('lsr', 12.3, 45.6), CIRCLE('galactic heliocenter', 1.2, 2.3, 5)) = 1;"));
+			assertNotNull(parser.parseQuery("SELECT * FROM foo WHERE CONTAINS(POINT('unknownframe', 12.3, 45.6), CIRCLE('galactic unknownrefpos spherical2', 1.2, 2.3, 5)) = 1;"));
+			assertNotNull(parser.parseQuery("SELECT * FROM foo WHERE CONTAINS(REGION('position icrs lsr 12.3 45.6'), REGION('circle fk5 1.2 2.3 5')) = 1;"));
+			assertNotNull(parser.parseQuery("SELECT Region('not(position 1 2)') FROM foo;"));
+		}catch(ParseException pe){
+			pe.printStackTrace();
+			fail("This query contains several valid coordinate systems, and all are theoretically allowed: this test should have succeeded!");
+		}
+
+		// Concatenation as coordinate systems not checked:
+		try{
+			assertNotNull(parser.parseQuery("SELECT * FROM foo WHERE CONTAINS(POINT('From ' || 'here', 12.3, 45.6), CIRCLE('', 1.2, 2.3, 5)) = 1;"));
+		}catch(ParseException pe){
+			pe.printStackTrace();
+			fail("This query contains a concatenation as coordinate systems (but only string constants are checked): this test should have succeeded!");
+		}
+
+		// Test with several coordinate systems while only some allowed:
+		try{
+			parser = new ADQLParser(new DBChecker(tables, new ArrayList<FunctionDef>(0), null, Arrays.asList(new String[]{"icrs * *","fk4 geocenter *","galactic * spherical2"})));
+			assertNotNull(parser.parseQuery("SELECT * FROM foo WHERE CONTAINS(POINT('', 12.3, 45.6), CIRCLE('', 1.2, 2.3, 5)) = 1;"));
+			assertNotNull(parser.parseQuery("SELECT * FROM foo WHERE CONTAINS(POINT('icrs', 12.3, 45.6), CIRCLE('cartesian3', 1.2, 2.3, 5)) = 1;"));
+			assertNotNull(parser.parseQuery("SELECT POINT('fk4', 12.3, 45.6) FROM foo;"));
+			assertNotNull(parser.parseQuery("SELECT * FROM foo WHERE CONTAINS(POINT('fk4 geocenter', 12.3, 45.6), CIRCLE('cartesian2', 1.2, 2.3, 5)) = 1;"));
+			assertNotNull(parser.parseQuery("SELECT * FROM foo WHERE CONTAINS(POINT('galactic', 12.3, 45.6), CIRCLE('galactic spherical2', 1.2, 2.3, 5)) = 1;"));
+			assertNotNull(parser.parseQuery("SELECT * FROM foo WHERE CONTAINS(POINT('galactic geocenter', 12.3, 45.6), CIRCLE('galactic lsr spherical2', 1.2, 2.3, 5)) = 1;"));
+			assertNotNull(parser.parseQuery("SELECT * FROM foo WHERE CONTAINS(REGION('position galactic lsr 12.3 45.6'), REGION('circle icrs 1.2 2.3 5')) = 1;"));
+			assertNotNull(parser.parseQuery("SELECT Region('not(position 1 2)') FROM foo;"));
+		}catch(ParseException pe){
+			pe.printStackTrace();
+			fail("This query contains several valid coordinate systems, and all are theoretically allowed: this test should have succeeded!");
+		}
+		try{
+			parser.parseQuery("SELECT POINT('fk5 geocenter', 12.3, 45.6) FROM foo;");
+			fail("This query contains a not-allowed coordinate system ('fk5' is not allowed): this test should have failed!");
+		}catch(ParseException pe){
+			assertTrue(pe instanceof UnresolvedIdentifiersException);
+			UnresolvedIdentifiersException ex = (UnresolvedIdentifiersException)pe;
+			assertEquals(1, ex.getNbErrors());
+			assertEquals("Coordinate system \"fk5 geocenter\" (= \"FK5 GEOCENTER SPHERICAL2\") not allowed in this implementation.", ex.getErrors().next().getMessage());
+		}
+		try{
+			parser.parseQuery("SELECT Region('not(position fk5 heliocenter 1 2)') FROM foo;");
+			fail("This query contains a not-allowed coordinate system ('fk5' is not allowed): this test should have failed!");
+		}catch(ParseException pe){
+			assertTrue(pe instanceof UnresolvedIdentifiersException);
+			UnresolvedIdentifiersException ex = (UnresolvedIdentifiersException)pe;
+			assertEquals(1, ex.getNbErrors());
+			assertEquals("Coordinate system \"FK5 HELIOCENTER\" (= \"FK5 HELIOCENTER SPHERICAL2\") not allowed in this implementation.", ex.getErrors().next().getMessage());
+		}
+
+		// Test with a coordinate system while none is allowed:
+		try{
+			parser = new ADQLParser(new DBChecker(tables, new ArrayList<FunctionDef>(0), null, new ArrayList<String>(0)));
+			assertNotNull(parser.parseQuery("SELECT * FROM foo WHERE CONTAINS(POINT('', 12.3, 45.6), CIRCLE('', 1.2, 2.3, 5)) = 1;"));
+			assertNotNull(parser.parseQuery("SELECT * FROM foo WHERE CONTAINS(REGION('position 12.3 45.6'), REGION('circle 1.2 2.3 5')) = 1;"));
+			assertNotNull(parser.parseQuery("SELECT Region('not(position 1 2)') FROM foo;"));
+		}catch(ParseException pe){
+			pe.printStackTrace();
+			fail("This query specifies none coordinate system: this test should have succeeded!");
+		}
+		try{
+			parser.parseQuery("SELECT * FROM foo WHERE CONTAINS(POINT('ICRS SPHERICAL2', 12.3, 45.6), CIRCLE('icrs', 1.2, 2.3, 5)) = 1;");
+			fail("This query specifies coordinate systems while they are all forbidden: this test should have failed!");
+		}catch(ParseException pe){
+			assertTrue(pe instanceof UnresolvedIdentifiersException);
+			UnresolvedIdentifiersException ex = (UnresolvedIdentifiersException)pe;
+			assertEquals(2, ex.getNbErrors());
+			Iterator<ParseException> itErrors = ex.getErrors();
+			assertEquals("Coordinate system \"ICRS SPHERICAL2\" (= \"ICRS UNKNOWNREFPOS SPHERICAL2\") not allowed in this implementation.", itErrors.next().getMessage());
+			assertEquals("Coordinate system \"icrs\" (= \"ICRS UNKNOWNREFPOS SPHERICAL2\") not allowed in this implementation.", itErrors.next().getMessage());
+		}
+		try{
+			parser.parseQuery("SELECT Region('not(position fk4 1 2)') FROM foo;");
+			fail("This query specifies coordinate systems while they are all forbidden: this test should have failed!");
+		}catch(ParseException pe){
+			assertTrue(pe instanceof UnresolvedIdentifiersException);
+			UnresolvedIdentifiersException ex = (UnresolvedIdentifiersException)pe;
+			assertEquals(1, ex.getNbErrors());
+			assertEquals("Coordinate system \"FK4\" (= \"FK4 UNKNOWNREFPOS SPHERICAL2\") not allowed in this implementation.", ex.getErrors().next().getMessage());
+		}
+	}
+
+	@Test
+	public void testTypesChecking(){
+		// DECLARE A SIMPLE PARSER:
+		ADQLParser parser = new ADQLParser(new DBChecker(tables));
+
+		// Test the type of columns generated by the parser:
+		try{
+			ADQLQuery query = parser.parseQuery("SELECT colS, colI, colG FROM foo;");
+			ADQLOperand colS = query.getSelect().get(0).getOperand();
+			ADQLOperand colI = query.getSelect().get(1).getOperand();
+			ADQLOperand colG = query.getSelect().get(2).getOperand();
+			// test string column:
+			assertTrue(colS instanceof ADQLColumn);
+			assertTrue(colS.isString());
+			assertFalse(colS.isNumeric());
+			assertFalse(colS.isGeometry());
+			// test integer column:
+			assertTrue(colI instanceof ADQLColumn);
+			assertFalse(colI.isString());
+			assertTrue(colI.isNumeric());
+			assertFalse(colI.isGeometry());
+			// test geometry column:
+			assertTrue(colG instanceof ADQLColumn);
+			assertFalse(colG.isString());
+			assertFalse(colG.isNumeric());
+			assertTrue(colG.isGeometry());
+		}catch(ParseException e1){
+			if (e1 instanceof UnresolvedIdentifiersException)
+				((UnresolvedIdentifiersException)e1).getErrors().next().printStackTrace();
+			else
+				e1.printStackTrace();
+			fail("This query contains known columns: this test should have succeeded!");
+		}
+
+		// Test the expected type - NUMERIC - generated by the parser:
+		try{
+			assertNotNull(parser.parseQuery("SELECT colI * 3 FROM foo;"));
+		}catch(ParseException e){
+			e.printStackTrace();
+			fail("This query contains a product between 2 numerics: this test should have succeeded!");
+		}
+		try{
+			parser.parseQuery("SELECT colS * 3 FROM foo;");
+			fail("This query contains a product between a string and an integer: this test should have failed!");
+		}catch(ParseException e){
+			assertTrue(e instanceof UnresolvedIdentifiersException);
+			UnresolvedIdentifiersException ex = (UnresolvedIdentifiersException)e;
+			assertEquals(1, ex.getNbErrors());
+			assertEquals("Type mismatch! A numeric value was expected instead of \"colS\".", ex.getErrors().next().getMessage());
+		}
+		try{
+			parser.parseQuery("SELECT colG * 3 FROM foo;");
+			fail("This query contains a product between a geometry and an integer: this test should have failed!");
+		}catch(ParseException e){
+			assertTrue(e instanceof UnresolvedIdentifiersException);
+			UnresolvedIdentifiersException ex = (UnresolvedIdentifiersException)e;
+			assertEquals(1, ex.getNbErrors());
+			assertEquals("Type mismatch! A numeric value was expected instead of \"colG\".", ex.getErrors().next().getMessage());
+		}
+
+		// Test the expected type - STRING - generated by the parser:
+		try{
+			assertNotNull(parser.parseQuery("SELECT colS || 'blabla' FROM foo;"));
+		}catch(ParseException e){
+			e.printStackTrace();
+			fail("This query contains a concatenation between 2 strings: this test should have succeeded!");
+		}
+		try{
+			parser.parseQuery("SELECT colI || 'blabla' FROM foo;");
+			fail("This query contains a concatenation between an integer and a string: this test should have failed!");
+		}catch(ParseException e){
+			assertTrue(e instanceof UnresolvedIdentifiersException);
+			UnresolvedIdentifiersException ex = (UnresolvedIdentifiersException)e;
+			assertEquals(1, ex.getNbErrors());
+			assertEquals("Type mismatch! A string value was expected instead of \"colI\".", ex.getErrors().next().getMessage());
+		}
+		try{
+			parser.parseQuery("SELECT colG || 'blabla' FROM foo;");
+			fail("This query contains a concatenation between a geometry and a string: this test should have failed!");
+		}catch(ParseException e){
+			assertTrue(e instanceof UnresolvedIdentifiersException);
+			UnresolvedIdentifiersException ex = (UnresolvedIdentifiersException)e;
+			assertEquals(1, ex.getNbErrors());
+			assertEquals("Type mismatch! A string value was expected instead of \"colG\".", ex.getErrors().next().getMessage());
+		}
+
+		// Test the expected type - GEOMETRY - generated by the parser:
+		try{
+			assertNotNull(parser.parseQuery("SELECT CONTAINS(colG, CIRCLE('', 1, 2, 5)) FROM foo;"));
+		}catch(ParseException e){
+			e.printStackTrace();
+			fail("This query contains a geometrical predicate between 2 geometries: this test should have succeeded!");
+		}
+		try{
+			parser.parseQuery("SELECT CONTAINS(colI, CIRCLE('', 1, 2, 5)) FROM foo;");
+			fail("This query contains a geometrical predicate between an integer and a geometry: this test should have failed!");
+		}catch(ParseException e){
+			assertTrue(e instanceof UnresolvedIdentifiersException);
+			UnresolvedIdentifiersException ex = (UnresolvedIdentifiersException)e;
+			assertEquals(1, ex.getNbErrors());
+			assertEquals("Type mismatch! A geometry was expected instead of \"colI\".", ex.getErrors().next().getMessage());
+		}
+		try{
+			parser.parseQuery("SELECT CONTAINS(colS, CIRCLE('', 1, 2, 5)) FROM foo;");
+			fail("This query contains a geometrical predicate between a string and a geometry: this test should have failed!");
+		}catch(ParseException e){
+			assertTrue(e instanceof UnresolvedIdentifiersException);
+			UnresolvedIdentifiersException ex = (UnresolvedIdentifiersException)e;
+			assertEquals(1, ex.getNbErrors());
+			assertEquals("Type mismatch! A geometry was expected instead of \"colS\".", ex.getErrors().next().getMessage());
+		}
+
+		// DECLARE SOME UDFs:
+		FunctionDef[] udfs = new FunctionDef[]{new FunctionDef("toto", new DBType(DBDatatype.VARCHAR)),new FunctionDef("tata", new DBType(DBDatatype.INTEGER)),new FunctionDef("titi", new DBType(DBDatatype.REGION))};
+		parser = new ADQLParser(new DBChecker(tables, Arrays.asList(udfs)));
+
+		// Test the return type of the function TOTO generated by the parser:
+		try{
+			ADQLQuery query = parser.parseQuery("SELECT toto() FROM foo;");
+			ADQLOperand fct = query.getSelect().get(0).getOperand();
+			assertTrue(fct instanceof DefaultUDF);
+			assertNotNull(((DefaultUDF)fct).getDefinition());
+			assertTrue(fct.isString());
+			assertFalse(fct.isNumeric());
+			assertFalse(fct.isGeometry());
+		}catch(ParseException e1){
+			e1.printStackTrace();
+			fail("This query contains a DECLARED UDF: this test should have succeeded!");
+		}
+
+		// Test the return type checking inside a whole query:
+		try{
+			assertNotNull(parser.parseQuery("SELECT toto() || 'Blabla ' AS \"SuperText\" FROM foo;"));
+		}catch(ParseException e1){
+			e1.printStackTrace();
+			fail("This query contains a DECLARED UDF concatenated to a String: this test should have succeeded!");
+		}
+		try{
+			parser.parseQuery("SELECT toto()*3 AS \"SuperError\" FROM foo;");
+			fail("This query contains a DECLARED UDF BUT used as numeric...which is here not possible: this test should have failed!");
+		}catch(ParseException e1){
+			assertTrue(e1 instanceof UnresolvedIdentifiersException);
+			UnresolvedIdentifiersException ex = (UnresolvedIdentifiersException)e1;
+			assertEquals(1, ex.getNbErrors());
+			assertEquals("Type mismatch! A numeric value was expected instead of \"toto()\".", ex.getErrors().next().getMessage());
+		}
+
+		// Test the return type of the function TATA generated by the parser:
+		try{
+			ADQLQuery query = parser.parseQuery("SELECT tata() FROM foo;");
+			ADQLOperand fct = query.getSelect().get(0).getOperand();
+			assertTrue(fct instanceof DefaultUDF);
+			assertNotNull(((DefaultUDF)fct).getDefinition());
+			assertFalse(fct.isString());
+			assertTrue(fct.isNumeric());
+			assertFalse(fct.isGeometry());
+		}catch(ParseException e1){
+			e1.printStackTrace();
+			fail("This query contains a DECLARED UDF: this test should have succeeded!");
+		}
+
+		// Test the return type checking inside a whole query:
+		try{
+			assertNotNull(parser.parseQuery("SELECT tata()*3 AS \"aNumeric\" FROM foo;"));
+		}catch(ParseException e1){
+			e1.printStackTrace();
+			fail("This query contains a DECLARED UDF multiplicated by 3: this test should have succeeded!");
+		}
+		try{
+			parser.parseQuery("SELECT 'Blabla ' || tata() AS \"SuperError\" FROM foo;");
+			fail("This query contains a DECLARED UDF BUT used as string...which is here not possible: this test should have failed!");
+		}catch(ParseException e1){
+			assertTrue(e1 instanceof UnresolvedIdentifiersException);
+			UnresolvedIdentifiersException ex = (UnresolvedIdentifiersException)e1;
+			assertEquals(1, ex.getNbErrors());
+			assertEquals("Type mismatch! A string value was expected instead of \"tata()\".", ex.getErrors().next().getMessage());
+		}
+		try{
+			parser.parseQuery("SELECT tata() || 'Blabla ' AS \"SuperError\" FROM foo;");
+			fail("This query contains a DECLARED UDF BUT used as string...which is here not possible: this test should have failed!");
+		}catch(ParseException e1){
+			assertTrue(e1 instanceof UnresolvedIdentifiersException);
+			UnresolvedIdentifiersException ex = (UnresolvedIdentifiersException)e1;
+			assertEquals(1, ex.getNbErrors());
+			assertEquals("Type mismatch! A string value was expected instead of \"tata()\".", ex.getErrors().next().getMessage());
+		}
+
+		// Test the return type of the function TITI generated by the parser:
+		try{
+			ADQLQuery query = parser.parseQuery("SELECT titi() FROM foo;");
+			ADQLOperand fct = query.getSelect().get(0).getOperand();
+			assertTrue(fct instanceof DefaultUDF);
+			assertNotNull(((DefaultUDF)fct).getDefinition());
+			assertFalse(fct.isString());
+			assertFalse(fct.isNumeric());
+			assertTrue(fct.isGeometry());
+		}catch(ParseException e1){
+			e1.printStackTrace();
+			fail("This query contains a DECLARED UDF: this test should have succeeded!");
+		}
+
+		// Test the return type checking inside a whole query:
+		try{
+			parser.parseQuery("SELECT CONTAINS(colG, titi()) ' AS \"Super\" FROM foo;");
+			fail("Geometrical UDFs are not allowed for the moment in the ADQL language: this test should have failed!");
+		}catch(ParseException e1){
+			assertTrue(e1 instanceof ParseException);
+			assertEquals(" Encountered \"(\". Was expecting one of: \")\" \".\" \".\" \")\" ", e1.getMessage());
+		}
+		try{
+			parser.parseQuery("SELECT titi()*3 AS \"SuperError\" FROM foo;");
+			fail("This query contains a DECLARED UDF BUT used as numeric...which is here not possible: this test should have failed!");
+		}catch(ParseException e1){
+			assertTrue(e1 instanceof UnresolvedIdentifiersException);
+			UnresolvedIdentifiersException ex = (UnresolvedIdentifiersException)e1;
+			assertEquals(1, ex.getNbErrors());
+			assertEquals("Type mismatch! A numeric value was expected instead of \"titi()\".", ex.getErrors().next().getMessage());
+		}
+
+		// CLEAR ALL UDFs AND ALLOW UNKNOWN FUNCTION:
+		parser = new ADQLParser(new DBChecker(tables, null));
+
+		// Test again:
+		try{
+			assertNotNull(parser.parseQuery("SELECT toto() FROM foo;"));
+		}catch(ParseException e){
+			e.printStackTrace();
+			fail("The parser allow ANY unknown function: this test should have succeeded!");
+		}
+
+		// Test the return type of the function generated by the parser:
+		try{
+			ADQLQuery query = parser.parseQuery("SELECT toto() FROM foo;");
+			ADQLOperand fct = query.getSelect().get(0).getOperand();
+			assertTrue(fct instanceof DefaultUDF);
+			assertNull(((DefaultUDF)fct).getDefinition());
+			assertTrue(fct.isString());
+			assertTrue(fct.isNumeric());
+		}catch(ParseException e1){
+			e1.printStackTrace();
+			fail("The parser allow ANY unknown function: this test should have succeeded!");
+		}
+
+		// DECLARE THE UDF (while unknown functions are allowed):
+		parser = new ADQLParser(new DBChecker(tables, Arrays.asList(new FunctionDef[]{new FunctionDef("toto", new DBType(DBDatatype.VARCHAR))})));
+
+		// Test the return type of the function generated by the parser:
+		try{
+			ADQLQuery query = parser.parseQuery("SELECT toto() FROM foo;");
+			ADQLOperand fct = query.getSelect().get(0).getOperand();
+			assertTrue(fct instanceof DefaultUDF);
+			assertNotNull(((DefaultUDF)fct).getDefinition());
+			assertTrue(fct.isString());
+			assertFalse(fct.isNumeric());
+		}catch(ParseException e1){
+			e1.printStackTrace();
+			fail("The parser allow ANY unknown function: this test should have succeeded!");
+		}
+
+		// DECLARE UDFs WITH SAME NAMES BUT DIFFERENT TYPE OF ARGUMENT:
+		udfs = new FunctionDef[]{new FunctionDef("toto", new DBType(DBDatatype.VARCHAR), new FunctionParam[]{new FunctionParam("attr", new DBType(DBDatatype.VARCHAR))}),new FunctionDef("toto", new DBType(DBDatatype.INTEGER), new FunctionParam[]{new FunctionParam("attr", new DBType(DBDatatype.INTEGER))}),new FunctionDef("toto", new DBType(DBDatatype.INTEGER), new FunctionParam[]{new FunctionParam("attr", new DBType(DBDatatype.POINT))})};
+		parser = new ADQLParser(new DBChecker(tables, Arrays.asList(udfs)));
+
+		// Test the return type in function of the parameter:
+		try{
+			assertNotNull(parser.parseQuery("SELECT toto('blabla') AS toto1, toto(123) AS toto2, toto(POINT('', 1, 2)) AS toto3  FROM foo;"));
+		}catch(ParseException e1){
+			e1.printStackTrace();
+			fail("This query contains two DECLARED UDFs used here: this test should have succeeded!");
+		}
+		try{
+			parser.parseQuery("SELECT toto('blabla') * 123 AS \"SuperError\" FROM foo;");
+			fail("This query contains a DECLARED UDF BUT used as numeric...which is here not possible: this test should have failed!");
+		}catch(ParseException e){
+			assertTrue(e instanceof UnresolvedIdentifiersException);
+			UnresolvedIdentifiersException ex = (UnresolvedIdentifiersException)e;
+			assertEquals(1, ex.getNbErrors());
+			assertEquals("Type mismatch! A numeric value was expected instead of \"toto('blabla')\".", ex.getErrors().next().getMessage());
+		}
+		try{
+			parser.parseQuery("SELECT toto(123) || 'blabla' AS \"SuperError\" FROM foo;");
+			fail("This query contains a DECLARED UDF BUT used as string...which is here not possible: this test should have failed!");
+		}catch(ParseException e){
+			assertTrue(e instanceof UnresolvedIdentifiersException);
+			UnresolvedIdentifiersException ex = (UnresolvedIdentifiersException)e;
+			assertEquals(1, ex.getNbErrors());
+			assertEquals("Type mismatch! A string value was expected instead of \"toto(123)\".", ex.getErrors().next().getMessage());
+		}
+		try{
+			parser.parseQuery("SELECT toto(POINT('', 1, 2)) || 'blabla' AS \"SuperError\" FROM foo;");
+			fail("This query contains a DECLARED UDF BUT used as string...which is here not possible: this test should have failed!");
+		}catch(ParseException e){
+			assertTrue(e instanceof UnresolvedIdentifiersException);
+			UnresolvedIdentifiersException ex = (UnresolvedIdentifiersException)e;
+			assertEquals(1, ex.getNbErrors());
+			assertEquals("Type mismatch! A string value was expected instead of \"toto(POINT('', 1, 2))\".", ex.getErrors().next().getMessage());
+		}
+	}
+
+}
diff --git a/test/adql/db/TestFunctionDef.java b/test/adql/db/TestFunctionDef.java
new file mode 100644
index 0000000..c3d738a
--- /dev/null
+++ b/test/adql/db/TestFunctionDef.java
@@ -0,0 +1,312 @@
+package adql.db;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+
+import adql.db.DBType.DBDatatype;
+import adql.db.FunctionDef.FunctionParam;
+import adql.parser.ParseException;
+import adql.query.operand.ADQLOperand;
+import adql.query.operand.NumericConstant;
+import adql.query.operand.StringConstant;
+import adql.query.operand.function.ADQLFunction;
+import adql.query.operand.function.DefaultUDF;
+import adql.query.operand.function.geometry.PointFunction;
+
+public class TestFunctionDef {
+
+	@Test
+	public void testIsString(){
+		for(DBDatatype type : DBDatatype.values()){
+			switch(type){
+				case CHAR:
+				case VARCHAR:
+				case TIMESTAMP:
+				case CLOB:
+					assertTrue(new FunctionDef("foo", new DBType(type)).isString);
+					break;
+				default:
+					assertFalse(new FunctionDef("foo", new DBType(type)).isString);
+			}
+		}
+	}
+
+	@Test
+	public void testIsGeometry(){
+		for(DBDatatype type : DBDatatype.values()){
+			switch(type){
+				case POINT:
+				case REGION:
+					assertTrue(new FunctionDef("foo", new DBType(type)).isGeometry);
+					break;
+				default:
+					assertFalse(new FunctionDef("foo", new DBType(type)).isGeometry);
+			}
+		}
+	}
+
+	@Test
+	public void testIsNumeric(){
+		for(DBDatatype type : DBDatatype.values()){
+			switch(type){
+				case CHAR:
+				case VARCHAR:
+				case TIMESTAMP:
+				case POINT:
+				case REGION:
+				case CLOB:
+					assertFalse(new FunctionDef("foo", new DBType(type)).isNumeric);
+					break;
+				default:
+					assertTrue(new FunctionDef("foo", new DBType(type)).isNumeric);
+			}
+		}
+	}
+
+	@Test
+	public void testToString(){
+		assertEquals("fct1()", new FunctionDef("fct1").toString());
+		assertEquals("fct1() -> VARCHAR", new FunctionDef("fct1", new DBType(DBDatatype.VARCHAR)).toString());
+		assertEquals("fct1(foo DOUBLE) -> VARCHAR", new FunctionDef("fct1", new DBType(DBDatatype.VARCHAR), new FunctionParam[]{new FunctionParam("foo", new DBType(DBDatatype.DOUBLE))}).toString());
+		assertEquals("fct1(foo DOUBLE)", new FunctionDef("fct1", new FunctionParam[]{new FunctionParam("foo", new DBType(DBDatatype.DOUBLE))}).toString());
+		assertEquals("fct1(foo DOUBLE, pt POINT) -> VARCHAR", new FunctionDef("fct1", new DBType(DBDatatype.VARCHAR), new FunctionParam[]{new FunctionParam("foo", new DBType(DBDatatype.DOUBLE)),new FunctionParam("pt", new DBType(DBDatatype.POINT))}).toString());
+		assertEquals("fct1(foo DOUBLE, pt POINT)", new FunctionDef("fct1", null, new FunctionParam[]{new FunctionParam("foo", new DBType(DBDatatype.DOUBLE)),new FunctionParam("pt", new DBType(DBDatatype.POINT))}).toString());
+	}
+
+	@Test
+	public void testParse(){
+		final String WRONG_FULL_SYNTAX = "Wrong function definition syntax! Expected syntax: \"<regular_identifier>(<parameters>?) <return_type>?\", where <regular_identifier>=\"[a-zA-Z]+[a-zA-Z0-9_]*\", <return_type>=\" -> <type_name>\", <parameters>=\"(<regular_identifier> <type_name> (, <regular_identifier> <type_name>)*)\", <type_name> should be one of the types described in the UPLOAD section of the TAP documentation. Examples of good syntax: \"foo()\", \"foo() -> VARCHAR\", \"foo(param INTEGER)\", \"foo(param1 INTEGER, param2 DOUBLE) -> DOUBLE\"";
+		final String WRONG_PARAM_SYNTAX = "Wrong parameters syntax! Expected syntax: \"(<regular_identifier> <type_name> (, <regular_identifier> <type_name>)*)\", where <regular_identifier>=\"[a-zA-Z]+[a-zA-Z0-9_]*\", <type_name> should be one of the types described in the UPLOAD section of the TAP documentation. Examples of good syntax: \"()\", \"(param INTEGER)\", \"(param1 INTEGER, param2 DOUBLE)\"";
+
+		// NULL test:
+		try{
+			FunctionDef.parse(null);
+			fail("A NULL string is not valide!");
+		}catch(Exception ex){
+			assertTrue(ex instanceof NullPointerException);
+			assertEquals("Missing string definition to build a FunctionDef!", ex.getMessage());
+		}
+
+		// EMPTY STRING test:
+		try{
+			FunctionDef.parse("");
+			fail("An empty string is not valide!");
+		}catch(Exception ex){
+			assertTrue(ex instanceof ParseException);
+			assertEquals(WRONG_FULL_SYNTAX, ex.getMessage());
+		}
+
+		// CORRECT string definitions:
+		try{
+			assertEquals("foo()", FunctionDef.parse("foo()").toString());
+			assertEquals("foo() -> VARCHAR", FunctionDef.parse("foo() -> string").toString());
+			assertEquals("foo() -> VARCHAR", FunctionDef.parse("foo()->string").toString());
+			assertEquals("foo(toto VARCHAR) -> SMALLINT", FunctionDef.parse("foo(toto varchar) -> boolean").toString());
+			assertEquals("foo(param1 DOUBLE, param2 INTEGER) -> DOUBLE", FunctionDef.parse(" foo ( param1	numeric,	param2    int )	->	DOUBLE ").toString());
+			assertEquals("foo_ALTernative2first(p POINT, d TIMESTAMP) -> TIMESTAMP", FunctionDef.parse("foo_ALTernative2first	(p POINT,d date) -> time").toString());
+			assertEquals("blabla_123(toto INTEGER, bla SMALLINT, truc CLOB, bidule CHAR, smurph POINT, date TIMESTAMP) -> SMALLINT", FunctionDef.parse("blabla_123(toto int4, bla bool, truc text, bidule character, smurph point, date timestamp) -> BOOLEAN").toString());
+		}catch(Exception ex){
+			ex.printStackTrace(System.err);
+			fail("All this string definitions are correct.");
+		}
+
+		// TYPE PARAMETER test:
+		try{
+			for(DBDatatype t : DBDatatype.values()){
+				switch(t){
+					case CHAR:
+					case VARCHAR:
+					case BINARY:
+					case VARBINARY:
+						assertEquals("foo() -> " + t.toString() + "(10)", FunctionDef.parse("foo() -> " + t.toString() + "(10)").toString());
+						break;
+					default:
+						assertEquals("foo() -> " + t.toString(), FunctionDef.parse("foo() -> " + t.toString() + "(10)").toString());
+				}
+			}
+		}catch(Exception ex){
+			ex.printStackTrace(System.err);
+			fail("Wrong type parsing!");
+		}
+
+		// WRONG string definitions:
+		try{
+			FunctionDef.parse("123()");
+			fail("No number is allowed as first character of a function name!");
+		}catch(Exception ex){
+			assertTrue(ex instanceof ParseException);
+			assertEquals(WRONG_FULL_SYNTAX, ex.getMessage());
+		}
+		try{
+			FunctionDef.parse("1foo()");
+			fail("No number is allowed as first character of a function name!");
+		}catch(Exception ex){
+			assertTrue(ex instanceof ParseException);
+			assertEquals(WRONG_FULL_SYNTAX, ex.getMessage());
+		}
+		try{
+			FunctionDef.parse("foo,truc()");
+			fail("No other character than [a-zA-Z0-9_] is allowed after a first character [a-zA-Z] in a function name!");
+		}catch(Exception ex){
+			assertTrue(ex instanceof ParseException);
+			assertEquals(WRONG_FULL_SYNTAX, ex.getMessage());
+		}
+		try{
+			FunctionDef.parse("foo");
+			fail("A function definition must contain at list parenthesis even if there is no parameter.");
+		}catch(Exception ex){
+			assertTrue(ex instanceof ParseException);
+			assertEquals(WRONG_FULL_SYNTAX, ex.getMessage());
+		}
+		try{
+			FunctionDef.parse("foo(param)");
+			fail("A parameter must always have a type!");
+		}catch(Exception ex){
+			assertTrue(ex instanceof ParseException);
+			assertEquals("Wrong syntax for the 1-th parameter: \"param\"! Expected syntax: \"(<regular_identifier> <type_name> (, <regular_identifier> <type_name>)*)\", where <regular_identifier>=\"[a-zA-Z]+[a-zA-Z0-9_]*\", <type_name> should be one of the types described in the UPLOAD section of the TAP documentation. Examples of good syntax: \"()\", \"(param INTEGER)\", \"(param1 INTEGER, param2 DOUBLE)\"", ex.getMessage());
+		}
+		try{
+			FunctionDef.parse("foo()->aType");
+			fail("Wrong (return) type!");
+		}catch(Exception ex){
+			assertTrue(ex instanceof ParseException);
+			assertEquals("Unknown return type: \"aType\"!", ex.getMessage());
+		}
+		try{
+			FunctionDef.parse("foo()->aType(10)");
+			fail("Wrong (return) type!");
+		}catch(Exception ex){
+			assertTrue(ex instanceof ParseException);
+			assertEquals("Unknown return type: \"aType(10)\"!", ex.getMessage());
+		}
+		try{
+			FunctionDef.parse("foo() -> ");
+			fail("The return type is missing!");
+		}catch(Exception ex){
+			assertTrue(ex instanceof ParseException);
+			assertEquals(WRONG_FULL_SYNTAX, ex.getMessage());
+		}
+		try{
+			FunctionDef.parse("foo(,)");
+			fail("Missing parameter definition!");
+		}catch(Exception ex){
+			assertTrue(ex instanceof ParseException);
+			assertEquals(WRONG_PARAM_SYNTAX, ex.getMessage());
+		}
+		try{
+			FunctionDef.parse("foo(param1 int,)");
+			fail("Missing parameter definition!");
+		}catch(Exception ex){
+			assertTrue(ex instanceof ParseException);
+			assertEquals(WRONG_PARAM_SYNTAX, ex.getMessage());
+		}
+		try{
+			FunctionDef.parse("foo(param1 aType)");
+			fail("Wrong parameter type!");
+		}catch(Exception ex){
+			assertTrue(ex instanceof ParseException);
+			assertEquals("Unknown type for the parameter \"param1\": \"aType\"!", ex.getMessage());
+		}
+		try{
+			FunctionDef.parse("foo(param1 aType(10))");
+			fail("Wrong parameter type!");
+		}catch(Exception ex){
+			assertTrue(ex instanceof ParseException);
+			assertEquals("Unknown type for the parameter \"param1\": \"aType(10)\"!", ex.getMessage());
+		}
+	}
+
+	@Test
+	public void testCompareToFunctionDef(){
+		// DEFINITION 1 :: fct1() -> VARCHAR
+		FunctionDef def1 = new FunctionDef("fct1", new DBType(DBDatatype.VARCHAR));
+
+		// TEST :: Identity test (def1 with def1): [EQUAL]
+		assertEquals(0, def1.compareTo(def1));
+
+		// TEST :: With a function having a different name and also no parameter: [GREATER]
+		assertEquals(1, def1.compareTo(new FunctionDef("fct0", new DBType(DBDatatype.VARCHAR))));
+
+		// TEST :: With a function having the same name, but a different return type: [EQUAL}
+		assertEquals(0, def1.compareTo(new FunctionDef("fct1", new DBType(DBDatatype.INTEGER))));
+
+		// TEST :: With a function having the same name, but 2 parameters: [LESS (4 characters: ø against 1010)]
+		assertEquals(-6, def1.compareTo(new FunctionDef("fct1", new DBType(DBDatatype.INTEGER), new FunctionParam[]{new FunctionParam("foo", new DBType(DBDatatype.INTEGER)),new FunctionParam("foo", new DBType(DBDatatype.INTEGER))})));
+
+		// DEFINITION 1 :: fct1(foo1 CHAR(12), foo2 DOUBLE) -> VARCHAR
+		def1 = new FunctionDef("fct1", new DBType(DBDatatype.VARCHAR), new FunctionParam[]{new FunctionParam("foo1", new DBType(DBDatatype.CHAR, 12)),new FunctionParam("foo2", new DBType(DBDatatype.DOUBLE))});
+
+		// TEST :: Identity test (def1 with def1): [EQUAL]
+		assertEquals(0, def1.compareTo(def1));
+
+		// DEFINITION 2 :: fct1(foo1 CHAR(12), foo2 VARCHAR) -> VARCHAR
+		FunctionDef def2 = new FunctionDef("fct1", new DBType(DBDatatype.VARCHAR), new FunctionParam[]{new FunctionParam("foo1", new DBType(DBDatatype.CHAR, 12)),new FunctionParam("foo2", new DBType(DBDatatype.VARCHAR))});
+
+		// TEST :: Identity test (def2 with def2): [EQUAL]
+		assertEquals(0, def2.compareTo(def2));
+
+		// TEST :: Same name, but different type for the last parameter only: [GREATER (because Numeric = 10 > String = 01)]
+		assertEquals(1, def1.compareTo(def2));
+
+		// DEFINITION 2 :: fct2(foo1 CHAR(12), foo2 DOUBLE) -> VARCHAR
+		def2 = new FunctionDef("fct2", new DBType(DBDatatype.VARCHAR), new FunctionParam[]{new FunctionParam("foo1", new DBType(DBDatatype.CHAR, 12)),new FunctionParam("foo2", new DBType(DBDatatype.DOUBLE))});
+
+		// TEST :: Identity test (def2 with def2): [EQUAL]
+		assertEquals(0, def2.compareTo(def2));
+
+		// TEST :: Different name but same parameters: [LESS]
+		assertEquals(-1, def1.compareTo(def2));
+
+		// DEFINITION 2 :: fct1(foo1 CHAR(12), foo2 POINT) -> VARCHAR
+		def2 = new FunctionDef("fct1", new DBType(DBDatatype.VARCHAR), new FunctionParam[]{new FunctionParam("foo1", new DBType(DBDatatype.CHAR, 12)),new FunctionParam("foo2", new DBType(DBDatatype.POINT))});
+
+		// TEST :: Identity test (def2 with def2): [EQUAL]
+		assertEquals(0, def2.compareTo(def2));
+
+		// TEST :: Same name, but different type for the last parameter only: [GREATER]
+		assertEquals(1, def1.compareTo(def2));
+	}
+
+	@Test
+	public void testCompareToADQLFunction(){
+		// DEFINITION :: fct1() -> VARCHAR
+		FunctionDef def = new FunctionDef("fct1", new DBType(DBDatatype.VARCHAR));
+
+		// TEST :: NULL:
+		try{
+			def.compareTo((ADQLFunction)null);
+			fail("Missing ADQL function for comparison with FunctionDef!");
+		}catch(Exception e){
+			assertTrue(e instanceof NullPointerException);
+			assertEquals("Missing ADQL function with which comparing this function definition!", e.getMessage());
+		}
+
+		// TEST :: "fct1()": [EQUAL]
+		assertEquals(0, def.compareTo(new DefaultUDF("fct1", null)));
+
+		// TEST :: "fct0()": [GREATER]
+		assertEquals(1, def.compareTo(new DefaultUDF("fct0", null)));
+
+		// TEST :: "fct1(12.3, 3.14)": [LESS (of 2 params)]
+		assertEquals(-2, def.compareTo(new DefaultUDF("fct1", new ADQLOperand[]{new NumericConstant(12.3),new NumericConstant(3.14)})));
+
+		// DEFINITION :: fct1(foo1 CHAR(12), foo2 DOUBLE) -> VARCHAR
+		def = new FunctionDef("fct1", new DBType(DBDatatype.VARCHAR), new FunctionParam[]{new FunctionParam("foo1", new DBType(DBDatatype.CHAR, 12)),new FunctionParam("foo2", new DBType(DBDatatype.DOUBLE))});
+
+		// TEST :: "fct1('blabla', 'blabla2')": [GREATER (because the second param is numeric and Numeric = 10 > String = 01)]
+		assertEquals(1, def.compareTo(new DefaultUDF("fct1", new ADQLOperand[]{new StringConstant("blabla"),new StringConstant("blabla2")})));
+
+		// TEST :: "fct1('blabla', POINT('COORDSYS', 1.2, 3.4))": [GREATER (same reason ; POINT is considered as a String)]
+		try{
+			assertEquals(1, def.compareTo(new DefaultUDF("fct1", new ADQLOperand[]{new StringConstant("blabla"),new PointFunction(new StringConstant("COORDSYS"), new NumericConstant(1.2), new NumericConstant(3.4))})));
+		}catch(Exception e){
+			e.printStackTrace();
+			fail();
+		}
+	}
+
+}
diff --git a/test/adql/db/TestSTCS.java b/test/adql/db/TestSTCS.java
new file mode 100644
index 0000000..8ff7646
--- /dev/null
+++ b/test/adql/db/TestSTCS.java
@@ -0,0 +1,536 @@
+package adql.db;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.StringBufferInputStream;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import adql.db.STCS.CoordSys;
+import adql.db.STCS.Flavor;
+import adql.db.STCS.Frame;
+import adql.db.STCS.RefPos;
+import adql.db.STCS.Region;
+import adql.db.STCS.RegionType;
+import adql.parser.ADQLParser;
+import adql.parser.ParseException;
+import adql.query.operand.ADQLColumn;
+import adql.query.operand.ADQLOperand;
+import adql.query.operand.NegativeOperand;
+import adql.query.operand.NumericConstant;
+import adql.query.operand.Operation;
+import adql.query.operand.OperationType;
+import adql.query.operand.StringConstant;
+import adql.query.operand.function.geometry.BoxFunction;
+import adql.query.operand.function.geometry.CircleFunction;
+import adql.query.operand.function.geometry.ContainsFunction;
+import adql.query.operand.function.geometry.GeometryFunction;
+import adql.query.operand.function.geometry.GeometryFunction.GeometryValue;
+import adql.query.operand.function.geometry.PointFunction;
+import adql.query.operand.function.geometry.PolygonFunction;
+import adql.query.operand.function.geometry.RegionFunction;
+
+@SuppressWarnings("deprecation")
+public class TestSTCS {
+
+	@BeforeClass
+	public static void setUpBeforeClass() throws Exception{}
+
+	@AfterClass
+	public static void tearDownAfterClass() throws Exception{}
+
+	@Before
+	public void setUp() throws Exception{}
+
+	@After
+	public void tearDown() throws Exception{}
+
+	@Test
+	public void buildRegion(){
+		// Special values:
+		try{
+			new Region((GeometryFunction)null);
+			fail();
+		}catch(Exception e){
+			assertTrue(e instanceof NullPointerException);
+			assertEquals("Missing geometry to convert into STCS.Region!", e.getMessage());
+		}
+
+		try{
+			new Region((Region)null);
+			fail();
+		}catch(Exception e){
+			assertTrue(e instanceof NullPointerException);
+			assertEquals("Missing region to NOT select!", e.getMessage());
+		}
+
+		try{
+			new Region(new ContainsFunction(new GeometryValue<GeometryFunction>(new RegionFunction(new StringConstant("position 1 2"))), new GeometryValue<GeometryFunction>(new RegionFunction(new StringConstant("circle 0 1 4")))));
+			fail();
+		}catch(Exception e){
+			assertTrue(e instanceof IllegalArgumentException);
+			assertEquals("Unknown region type! Only geometrical function PointFunction, CircleFunction, BoxFunction, PolygonFunction and RegionFunction are allowed.", e.getMessage());
+		}
+
+		// Allowed values (1 test for each type of region):
+		try{
+			Region r = new Region(new PointFunction(new StringConstant(""), new NumericConstant(1.2), new NegativeOperand(new NumericConstant(2.3))));
+			assertEquals(RegionType.POSITION, r.type);
+			assertEquals("", r.coordSys.toSTCS());
+			assertEquals(1, r.coordinates.length);
+			assertEquals(2, r.coordinates[0].length);
+			assertEquals(1.2, r.coordinates[0][0], 0);
+			assertEquals(-2.3, r.coordinates[0][1], 0);
+			assertEquals(Double.NaN, r.radius, 0);
+			assertEquals(Double.NaN, r.width, 0);
+			assertEquals(Double.NaN, r.height, 0);
+			assertNull(r.regions);
+			assertEquals("POSITION 1.2 -2.3", r.toSTCS());
+
+			r = new Region(new CircleFunction(new StringConstant("ICRS"), new NumericConstant(1.2), new NegativeOperand(new NumericConstant(2.3)), new NumericConstant(5)));
+			assertEquals(RegionType.CIRCLE, r.type);
+			assertEquals("ICRS", r.coordSys.toSTCS());
+			assertEquals(1, r.coordinates.length);
+			assertEquals(2, r.coordinates[0].length);
+			assertEquals(1.2, r.coordinates[0][0], 0);
+			assertEquals(-2.3, r.coordinates[0][1], 0);
+			assertEquals(5, r.radius, 0);
+			assertEquals(Double.NaN, r.width, 0);
+			assertEquals(Double.NaN, r.height, 0);
+			assertNull(r.regions);
+			assertEquals("CIRCLE ICRS 1.2 -2.3 5.0", r.toSTCS());
+
+			r = new Region(new BoxFunction(new StringConstant("ICRS heliocenter"), new NumericConstant(1.2), new NegativeOperand(new NumericConstant(2.3)), new NumericConstant(5), new NumericConstant(4.6)));
+			assertEquals(RegionType.BOX, r.type);
+			assertEquals("ICRS HELIOCENTER", r.coordSys.toSTCS());
+			assertEquals(1, r.coordinates.length);
+			assertEquals(2, r.coordinates[0].length);
+			assertEquals(1.2, r.coordinates[0][0], 0);
+			assertEquals(-2.3, r.coordinates[0][1], 0);
+			assertEquals(Double.NaN, r.radius, 0);
+			assertEquals(5, r.width, 0);
+			assertEquals(4.6, r.height, 0);
+			assertNull(r.regions);
+			assertEquals("BOX ICRS HELIOCENTER 1.2 -2.3 5.0 4.6", r.toSTCS());
+
+			r = new Region(new PolygonFunction(new StringConstant("cartesian2"), new ADQLOperand[]{new NumericConstant(1.2),new NegativeOperand(new NumericConstant(2.3)),new NumericConstant(5),new NumericConstant(4.6),new NegativeOperand(new NumericConstant(.89)),new NumericConstant(1)}));
+			assertEquals(RegionType.POLYGON, r.type);
+			assertEquals("CARTESIAN2", r.coordSys.toSTCS());
+			assertEquals(3, r.coordinates.length);
+			assertEquals(2, r.coordinates[0].length);
+			assertEquals(1.2, r.coordinates[0][0], 0);
+			assertEquals(-2.3, r.coordinates[0][1], 0);
+			assertEquals(5, r.coordinates[1][0], 0);
+			assertEquals(4.6, r.coordinates[1][1], 0);
+			assertEquals(-0.89, r.coordinates[2][0], 0);
+			assertEquals(1, r.coordinates[2][1], 0);
+			assertEquals(Double.NaN, r.radius, 0);
+			assertEquals(Double.NaN, r.width, 0);
+			assertEquals(Double.NaN, r.height, 0);
+			assertNull(r.regions);
+			assertEquals("POLYGON CARTESIAN2 1.2 -2.3 5.0 4.6 -0.89 1.0", r.toSTCS());
+
+			r = new Region(new RegionFunction(new StringConstant("position ICrs 1.2 -2.3")));
+			assertEquals(RegionType.POSITION, r.type);
+			assertEquals("ICRS", r.coordSys.toSTCS());
+			assertEquals(1, r.coordinates.length);
+			assertEquals(2, r.coordinates[0].length);
+			assertEquals(1.2, r.coordinates[0][0], 0);
+			assertEquals(-2.3, r.coordinates[0][1], 0);
+			assertEquals(Double.NaN, r.radius, 0);
+			assertEquals(Double.NaN, r.width, 0);
+			assertEquals(Double.NaN, r.height, 0);
+			assertNull(r.regions);
+			assertEquals("POSITION ICRS 1.2 -2.3", r.toSTCS());
+
+			r = new Region(new RegionFunction(new StringConstant("Union ICRS (Polygon 1 4 2 4 2 5 1 5 Polygon 3 4 4 4 4 5 3 5)")));
+			assertEquals(RegionType.UNION, r.type);
+			assertEquals("ICRS", r.coordSys.toSTCS());
+			assertNull(r.coordinates);
+			assertEquals(Double.NaN, r.radius, 0);
+			assertEquals(Double.NaN, r.width, 0);
+			assertEquals(Double.NaN, r.height, 0);
+			assertEquals(2, r.regions.length);
+			assertEquals("UNION ICRS (POLYGON 1.0 4.0 2.0 4.0 2.0 5.0 1.0 5.0 POLYGON 3.0 4.0 4.0 4.0 4.0 5.0 3.0 5.0)", r.toString());
+			// inner region 1
+			Region innerR = r.regions[0];
+			assertEquals(RegionType.POLYGON, innerR.type);
+			assertEquals("", innerR.coordSys.toSTCS());
+			assertEquals(4, innerR.coordinates.length);
+			assertEquals(2, innerR.coordinates[0].length);
+			assertEquals(1, innerR.coordinates[0][0], 0);
+			assertEquals(4, innerR.coordinates[0][1], 0);
+			assertEquals(2, innerR.coordinates[1][0], 0);
+			assertEquals(4, innerR.coordinates[1][1], 0);
+			assertEquals(2, innerR.coordinates[2][0], 0);
+			assertEquals(5, innerR.coordinates[2][1], 0);
+			assertEquals(1, innerR.coordinates[3][0], 0);
+			assertEquals(5, innerR.coordinates[3][1], 0);
+			assertEquals(Double.NaN, innerR.radius, 0);
+			assertEquals(Double.NaN, innerR.width, 0);
+			assertEquals(Double.NaN, innerR.height, 0);
+			assertNull(innerR.regions);
+			assertEquals("POLYGON 1.0 4.0 2.0 4.0 2.0 5.0 1.0 5.0", innerR.toSTCS());
+			// inner region 2
+			innerR = r.regions[1];
+			assertEquals(RegionType.POLYGON, innerR.type);
+			assertEquals("", innerR.coordSys.toSTCS());
+			assertEquals(4, innerR.coordinates.length);
+			assertEquals(2, innerR.coordinates[0].length);
+			assertEquals(3, innerR.coordinates[0][0], 0);
+			assertEquals(4, innerR.coordinates[0][1], 0);
+			assertEquals(4, innerR.coordinates[1][0], 0);
+			assertEquals(4, innerR.coordinates[1][1], 0);
+			assertEquals(4, innerR.coordinates[2][0], 0);
+			assertEquals(5, innerR.coordinates[2][1], 0);
+			assertEquals(3, innerR.coordinates[3][0], 0);
+			assertEquals(5, innerR.coordinates[3][1], 0);
+			assertEquals(Double.NaN, innerR.radius, 0);
+			assertEquals(Double.NaN, innerR.width, 0);
+			assertEquals(Double.NaN, innerR.height, 0);
+			assertNull(innerR.regions);
+			assertEquals("POLYGON 3.0 4.0 4.0 4.0 4.0 5.0 3.0 5.0", innerR.toSTCS());
+
+			r = new Region(new RegionFunction(new StringConstant("NOT(CIRCLE ICRS 1.2 -2.3 5)")));
+			assertEquals(RegionType.NOT, r.type);
+			assertNull(r.coordSys);
+			assertNull(r.coordinates);
+			assertEquals(Double.NaN, r.radius, 0);
+			assertEquals(Double.NaN, r.width, 0);
+			assertEquals(Double.NaN, r.height, 0);
+			assertEquals(1, r.regions.length);
+			assertEquals("NOT(CIRCLE ICRS 1.2 -2.3 5.0)", r.toSTCS());
+			// inner region
+			innerR = r.regions[0];
+			assertEquals(RegionType.CIRCLE, innerR.type);
+			assertEquals("ICRS", innerR.coordSys.toSTCS());
+			assertEquals(1, innerR.coordinates.length);
+			assertEquals(2, innerR.coordinates[0].length);
+			assertEquals(1.2, innerR.coordinates[0][0], 0);
+			assertEquals(-2.3, innerR.coordinates[0][1], 0);
+			assertEquals(5, innerR.radius, 0);
+			assertEquals(Double.NaN, innerR.width, 0);
+			assertEquals(Double.NaN, innerR.height, 0);
+			assertNull(innerR.regions);
+			assertEquals("CIRCLE ICRS 1.2 -2.3 5.0", innerR.toSTCS());
+		}catch(Exception e){
+			e.printStackTrace(System.err);
+			fail();
+		}
+
+		// Test with incorrect syntaxes:
+		try{
+			new Region(new PointFunction(new StringConstant(""), new StringConstant("1.2"), new NegativeOperand(new NumericConstant(2.3))));
+			fail("The first coordinate is a StringConstant rather than a NumericConstant!");
+		}catch(Exception e){
+			assertTrue(e instanceof ParseException);
+			assertEquals("Can not convert into STC-S a non numeric argument (including ADQLColumn and Operation)!", e.getMessage());
+		}
+		try{
+			new Region(new PointFunction(new NumericConstant(.65), new NumericConstant(1.2), new NegativeOperand(new NumericConstant(2.3))));
+			fail("The coordinate system is a NumericConstant rather than a StringConstant!");
+		}catch(Exception e){
+			assertTrue(e instanceof ParseException);
+			assertEquals("A coordinate system must be a string literal: \"0.65\" is not a string operand!", e.getMessage());
+		}
+		try{
+			new Region(new PointFunction(new StringConstant(""), null, new NegativeOperand(new NumericConstant(2.3))));
+			fail("The first coordinate is missing!");
+		}catch(Exception e){
+			assertTrue(e instanceof NullPointerException);
+			assertEquals("The POINT function must have non-null coordinates!", e.getMessage());
+		}
+		try{
+			new Region(new RegionFunction(new StringConstant("")));
+			fail("Missing STC-S expression!");
+		}catch(Exception e){
+			assertTrue(e instanceof ParseException);
+			assertEquals("Missing STC-S expression to parse!", e.getMessage());
+		}
+		try{
+			new Region(new RegionFunction(new StringConstant("MyRegion HERE 1.2")));
+			fail("Totally incorrect region type!");
+		}catch(Exception e){
+			assertTrue(e instanceof ParseException);
+			assertEquals("Unknown STC region type: \"MYREGION\"!", e.getMessage());
+		}
+		try{
+			new Region(new RegionFunction((new ADQLParser(new StringBufferInputStream("'POSITION ' || coordinateSys || ' ' || ra || ' ' || dec"))).StringExpression()));
+			fail("String concatenation can not be managed!");
+		}catch(Exception e){
+			assertTrue(e instanceof ParseException);
+			assertEquals("Can not convert into STC-S a non string argument (including ADQLColumn and Concatenation)!", e.getMessage());
+		}
+		try{
+			new Region(new PointFunction(new ADQLColumn("coordSys"), new NumericConstant(1), new NumericConstant(2)));
+			fail("Columns can not be managed!");
+		}catch(Exception e){
+			assertTrue(e instanceof ParseException);
+			assertEquals("Can not convert into STC-S a non string argument (including ADQLColumn and Concatenation)!", e.getMessage());
+		}
+		try{
+			new Region(new PointFunction(new StringConstant("ICRS"), new Operation(new NumericConstant(2), OperationType.MULT, new NumericConstant(5)), new NumericConstant(2)));
+			fail("Operations can not be managed!");
+		}catch(Exception e){
+			assertTrue(e instanceof ParseException);
+			assertEquals("Can not convert into STC-S a non numeric argument (including ADQLColumn and Operation)!", e.getMessage());
+		}
+	}
+
+	@Test
+	public void parseCoordSys(){
+		// GOOD SYNTAXES:
+		try{
+			CoordSys p;
+
+			// Default coordinate system (should be then interpreted as local coordinate system):
+			for(String s : new String[]{null,"","  	"}){
+				p = STCS.parseCoordSys(s);
+				assertEquals(Frame.UNKNOWNFRAME, p.frame);
+				assertEquals(RefPos.UNKNOWNREFPOS, p.refpos);
+				assertEquals(Flavor.SPHERICAL2, p.flavor);
+				assertTrue(p.isDefault());
+			}
+
+			// Just a frame:
+			p = STCS.parseCoordSys("ICRS");
+			assertEquals(Frame.ICRS, p.frame);
+			assertEquals(RefPos.UNKNOWNREFPOS, p.refpos);
+			assertEquals(Flavor.SPHERICAL2, p.flavor);
+			assertFalse(p.isDefault());
+
+			// Just a reference position:
+			p = STCS.parseCoordSys("LSR");
+			assertEquals(Frame.UNKNOWNFRAME, p.frame);
+			assertEquals(RefPos.LSR, p.refpos);
+			assertEquals(Flavor.SPHERICAL2, p.flavor);
+			assertFalse(p.isDefault());
+
+			// Just a flavor:
+			p = STCS.parseCoordSys("CARTESIAN2");
+			assertEquals(Frame.UNKNOWNFRAME, p.frame);
+			assertEquals(RefPos.UNKNOWNREFPOS, p.refpos);
+			assertEquals(Flavor.CARTESIAN2, p.flavor);
+			assertFalse(p.isDefault());
+
+			// Frame + RefPos:
+			p = STCS.parseCoordSys("ICRS LSR");
+			assertEquals(Frame.ICRS, p.frame);
+			assertEquals(RefPos.LSR, p.refpos);
+			assertEquals(Flavor.SPHERICAL2, p.flavor);
+			assertFalse(p.isDefault());
+
+			// Frame + Flavor:
+			p = STCS.parseCoordSys("ICRS SPHERICAL2");
+			assertEquals(Frame.ICRS, p.frame);
+			assertEquals(RefPos.UNKNOWNREFPOS, p.refpos);
+			assertEquals(Flavor.SPHERICAL2, p.flavor);
+			assertFalse(p.isDefault());
+
+			// RefPos + Flavor:
+			p = STCS.parseCoordSys("HELIOCENTER SPHERICAL2");
+			assertEquals(Frame.UNKNOWNFRAME, p.frame);
+			assertEquals(RefPos.HELIOCENTER, p.refpos);
+			assertEquals(Flavor.SPHERICAL2, p.flavor);
+			assertFalse(p.isDefault());
+
+			// Frame + RefPos + Flavor
+			p = STCS.parseCoordSys("ICRS GEOCENTER SPHERICAL2");
+			assertEquals(Frame.ICRS, p.frame);
+			assertEquals(RefPos.GEOCENTER, p.refpos);
+			assertEquals(Flavor.SPHERICAL2, p.flavor);
+			assertFalse(p.isDefault());
+
+			// Lets try in a different case:
+			p = STCS.parseCoordSys("icrs Geocenter SpheriCAL2");
+			assertEquals(Frame.ICRS, p.frame);
+			assertEquals(RefPos.GEOCENTER, p.refpos);
+			assertEquals(Flavor.SPHERICAL2, p.flavor);
+			assertFalse(p.isDefault());
+		}catch(Exception e){
+			e.printStackTrace(System.err);
+			fail();
+		}
+
+		// WRONG SYNTAXES:
+		try{
+			STCS.parseCoordSys("HOME");
+			fail();
+		}catch(Exception e){
+			assertTrue(e instanceof ParseException);
+			assertEquals("Incorrect syntax: \"HOME\" was unexpected! Expected syntax: \"[(ECLIPTIC|FK4|FK5|GALACTIC|ICRS|UNKNOWNFRAME)] [(BARYCENTER|GEOCENTER|HELIOCENTER|LSR|TOPOCENTER|RELOCATABLE|UNKNOWNREFPOS)] [(CARTESIAN2|CARTESIAN3|SPHERICAL2)]\" ; an empty string is also allowed and will be interpreted as the coordinate system locally used.", e.getMessage());
+		}
+
+		// With wrong reference position:
+		try{
+			STCS.parseCoordSys("ICRS HOME SPHERICAL2");
+			fail();
+		}catch(Exception e){
+			assertTrue(e instanceof ParseException);
+			assertEquals("Incorrect syntax: \"HOME SPHERICAL2\" was unexpected! Expected syntax: \"[(ECLIPTIC|FK4|FK5|GALACTIC|ICRS|UNKNOWNFRAME)] [(BARYCENTER|GEOCENTER|HELIOCENTER|LSR|TOPOCENTER|RELOCATABLE|UNKNOWNREFPOS)] [(CARTESIAN2|CARTESIAN3|SPHERICAL2)]\" ; an empty string is also allowed and will be interpreted as the coordinate system locally used.", e.getMessage());
+		}
+
+		// With a cartesian flavor:
+		try{
+			STCS.parseCoordSys("ICRS CARTESIAN2");
+			fail();
+		}catch(Exception e){
+			assertTrue(e instanceof ParseException);
+			assertEquals("a coordinate system expressed with a cartesian flavor MUST have an UNKNOWNFRAME and UNKNOWNREFPOS!", e.getMessage());
+		}
+		try{
+			STCS.parseCoordSys("LSR CARTESIAN3");
+			fail();
+		}catch(Exception e){
+			assertTrue(e instanceof ParseException);
+			assertEquals("a coordinate system expressed with a cartesian flavor MUST have an UNKNOWNFRAME and UNKNOWNREFPOS!", e.getMessage());
+		}
+		try{
+			CoordSys p = STCS.parseCoordSys("CARTESIAN2");
+			assertEquals(Frame.UNKNOWNFRAME, p.frame);
+			assertEquals(RefPos.UNKNOWNREFPOS, p.refpos);
+			assertEquals(Flavor.CARTESIAN2, p.flavor);
+
+			p = STCS.parseCoordSys("CARTESIAN3");
+			assertEquals(Frame.UNKNOWNFRAME, p.frame);
+			assertEquals(RefPos.UNKNOWNREFPOS, p.refpos);
+			assertEquals(Flavor.CARTESIAN3, p.flavor);
+		}catch(Exception e){
+			e.printStackTrace(System.err);
+			fail();
+		}
+
+		// Without spaces:
+		try{
+			STCS.parseCoordSys("icrsGeocentercarteSIAN2");
+			fail();
+		}catch(Exception e){
+			assertTrue(e instanceof ParseException);
+			assertEquals("Incorrect syntax: \"icrsGeocentercarteSIAN2\" was unexpected! Expected syntax: \"[(ECLIPTIC|FK4|FK5|GALACTIC|ICRS|UNKNOWNFRAME)] [(BARYCENTER|GEOCENTER|HELIOCENTER|LSR|TOPOCENTER|RELOCATABLE|UNKNOWNREFPOS)] [(CARTESIAN2|CARTESIAN3|SPHERICAL2)]\" ; an empty string is also allowed and will be interpreted as the coordinate system locally used.", e.getMessage());
+		}
+	}
+
+	@Test
+	public void serializeCoordSys(){
+		try{
+			assertEquals("", STCS.toSTCS((CoordSys)null));
+
+			assertEquals("", STCS.toSTCS(new CoordSys()));
+
+			assertEquals("", STCS.toSTCS(new CoordSys(null, null, null)));
+			assertEquals("", STCS.toSTCS(new CoordSys(Frame.DEFAULT, RefPos.DEFAULT, Flavor.DEFAULT)));
+			assertEquals("", STCS.toSTCS(new CoordSys(Frame.UNKNOWNFRAME, RefPos.UNKNOWNREFPOS, Flavor.SPHERICAL2)));
+
+			assertEquals("", STCS.toSTCS(new CoordSys(null)));
+			assertEquals("", STCS.toSTCS(new CoordSys("")));
+			assertEquals("", STCS.toSTCS(new CoordSys("   	\n\r")));
+
+			assertEquals("ICRS", STCS.toSTCS(new CoordSys(Frame.ICRS, null, null)));
+			assertEquals("ICRS", STCS.toSTCS(new CoordSys(Frame.ICRS, RefPos.DEFAULT, Flavor.DEFAULT)));
+			assertEquals("ICRS", STCS.toSTCS(new CoordSys(Frame.ICRS, RefPos.UNKNOWNREFPOS, Flavor.SPHERICAL2)));
+
+			assertEquals("GEOCENTER", STCS.toSTCS(new CoordSys(null, RefPos.GEOCENTER, null)));
+			assertEquals("GEOCENTER", STCS.toSTCS(new CoordSys(Frame.DEFAULT, RefPos.GEOCENTER, Flavor.DEFAULT)));
+			assertEquals("GEOCENTER", STCS.toSTCS(new CoordSys(Frame.UNKNOWNFRAME, RefPos.GEOCENTER, Flavor.SPHERICAL2)));
+
+			assertEquals("CARTESIAN3", STCS.toSTCS(new CoordSys(null, null, Flavor.CARTESIAN3)));
+			assertEquals("CARTESIAN3", STCS.toSTCS(new CoordSys(Frame.DEFAULT, RefPos.UNKNOWNREFPOS, Flavor.CARTESIAN3)));
+			assertEquals("CARTESIAN3", STCS.toSTCS(new CoordSys(Frame.UNKNOWNFRAME, RefPos.UNKNOWNREFPOS, Flavor.CARTESIAN3)));
+
+			assertEquals("ICRS GEOCENTER", STCS.toSTCS(new CoordSys(Frame.ICRS, RefPos.GEOCENTER, null)));
+			assertEquals("ICRS GEOCENTER", STCS.toSTCS(new CoordSys(Frame.ICRS, RefPos.GEOCENTER, Flavor.DEFAULT)));
+
+			assertEquals("UNKNOWNFRAME UNKNOWNREFPOS SPHERICAL2", new CoordSys().toFullSTCS());
+			assertEquals("UNKNOWNFRAME UNKNOWNREFPOS SPHERICAL2", new CoordSys("").toFullSTCS());
+			assertEquals("UNKNOWNFRAME UNKNOWNREFPOS SPHERICAL2", new CoordSys(null).toFullSTCS());
+			assertEquals("UNKNOWNFRAME UNKNOWNREFPOS SPHERICAL2", new CoordSys("   	\n\t").toFullSTCS());
+			assertEquals("UNKNOWNFRAME UNKNOWNREFPOS SPHERICAL2", new CoordSys(null, null, null).toFullSTCS());
+			assertEquals("UNKNOWNFRAME UNKNOWNREFPOS SPHERICAL2", new CoordSys(Frame.DEFAULT, RefPos.DEFAULT, Flavor.DEFAULT).toFullSTCS());
+			assertEquals("ICRS UNKNOWNREFPOS SPHERICAL2", new CoordSys(Frame.ICRS, null, null).toFullSTCS());
+			assertEquals("ICRS UNKNOWNREFPOS SPHERICAL2", new CoordSys(Frame.ICRS, RefPos.DEFAULT, Flavor.DEFAULT).toFullSTCS());
+			assertEquals("UNKNOWNFRAME GEOCENTER SPHERICAL2", new CoordSys(Frame.UNKNOWNFRAME, RefPos.GEOCENTER, Flavor.DEFAULT).toFullSTCS());
+			assertEquals("UNKNOWNFRAME UNKNOWNREFPOS CARTESIAN3", new CoordSys(Frame.DEFAULT, RefPos.DEFAULT, Flavor.CARTESIAN3).toFullSTCS());
+			assertEquals("ICRS GEOCENTER SPHERICAL2", new CoordSys(Frame.ICRS, RefPos.GEOCENTER, Flavor.DEFAULT).toFullSTCS());
+		}catch(ParseException pe){
+			pe.printStackTrace(System.err);
+			fail();
+		}
+	}
+
+	@Test
+	public void parseRegion(){
+		// TESTS WITH NO STC-S:
+		try{
+			STCS.parseRegion(null);
+			fail();
+		}catch(Exception e){
+			assertTrue(e instanceof ParseException);
+			assertEquals("Missing STC-S expression to parse!", e.getMessage());
+		}
+		try{
+			STCS.parseRegion("");
+			fail();
+		}catch(Exception e){
+			assertTrue(e instanceof ParseException);
+			assertEquals("Missing STC-S expression to parse!", e.getMessage());
+		}
+		try{
+			STCS.parseRegion("   	\n\r");
+			fail();
+		}catch(Exception e){
+			assertTrue(e instanceof ParseException);
+			assertEquals("Missing STC-S expression to parse!", e.getMessage());
+		}
+
+		// TESTS WITH A VALID EXPRESSION, EACH OF A DIFFERENT REGION TYPE:
+		String[] expressions = new String[]{" Position GALACTIC 10 20","Circle  	 ICRS    GEOCENTER	10	20	0.5 ","BOX cartesian2 3 3 2 2","Polygon 1 4 2 4 2 5 1 5","Union ICRS (Polygon 1 4 2 4 2 5 1 5 Polygon 3 4 4 4 4 5 3 5)","INTERSECTION ICRS (Polygon 1 4 2 4 2 5 1 5 Polygon 3 4 4 4 4 5 3 5)","NOT(Circle ICRS GEOCENTER 10 20 0.5)"};
+		try{
+			for(String e : expressions)
+				STCS.parseRegion(e);
+		}catch(Exception ex){
+			ex.printStackTrace(System.err);
+			fail();
+		}
+
+		// TEST WITH A MISSING PARAMETER:
+		expressions = new String[]{" Position GALACTIC 10 ","BOX cartesian2 3 3 2","NOT()"};
+		for(String e : expressions){
+			try{
+				STCS.parseRegion(e);
+				fail();
+			}catch(Exception ex){
+				assertTrue(ex instanceof ParseException);
+				assertTrue(ex.getMessage().startsWith("Unexpected End Of Expression! Expected syntax: \""));
+			}
+		}
+
+		// TEST WITH A WRONG COORDINATE SYSTEM (since it is optional in all these expressions, it will be considered as a coordinate...which is of course, not the case):
+		try{
+			STCS.parseRegion("Circle  	 HERE	10	20	0.5 ");
+			fail();
+		}catch(Exception ex){
+			assertTrue(ex instanceof ParseException);
+			assertTrue(ex.getMessage().startsWith("Incorrect syntax: a coordinates pair (2 numerics separated by one or more spaces) was expected! Expected syntax: \"CIRCLE <coordSys> <coordPair> <radius>\", where coordPair=\"<numeric> <numeric>\", radius=\"<numeric>\" and coordSys=\"[(ECLIPTIC|FK4|FK5|GALACTIC|ICRS|UNKNOWNFRAME)] [(BARYCENTER|GEOCENTER|HELIOCENTER|LSR|TOPOCENTER|RELOCATABLE|UNKNOWNREFPOS)] [(CARTESIAN2|CARTESIAN3|SPHERICAL2)]\" ; an empty string is also allowed and will be interpreted as the coordinate system locally used."));
+		}
+
+		// TEST WITH EITHER A WRONG NUMERIC (L in lower case instead of 1) OR A MISSING OPENING PARENTHESIS:
+		expressions = new String[]{"Polygon 1 4 2 4 2 5 l 5","Union ICRS Polygon 1 4 2 4 2 5 1 5 Polygon 3 4 4 4 4 5 3 5)"};
+		for(String e : expressions){
+			try{
+				STCS.parseRegion(e);
+				fail();
+			}catch(Exception ex){
+				assertTrue(ex instanceof ParseException);
+				assertTrue(ex.getMessage().startsWith("Incorrect syntax: "));
+			}
+		}
+	}
+}
diff --git a/test/adql/parser/TestADQLParser.java b/test/adql/parser/TestADQLParser.java
new file mode 100644
index 0000000..10837d5
--- /dev/null
+++ b/test/adql/parser/TestADQLParser.java
@@ -0,0 +1,43 @@
+package adql.parser;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import adql.query.ADQLQuery;
+import adql.query.operand.StringConstant;
+
+public class TestADQLParser {
+
+	@BeforeClass
+	public static void setUpBeforeClass() throws Exception{}
+
+	@AfterClass
+	public static void tearDownAfterClass() throws Exception{}
+
+	@Before
+	public void setUp() throws Exception{}
+
+	@After
+	public void tearDown() throws Exception{}
+
+	@Test
+	public void test(){
+		ADQLParser parser = new ADQLParser();
+		try{
+			ADQLQuery query = parser.parseQuery("SELECT 'truc''machin'  	'bidule' -- why not a comment now ^^\n'FIN' FROM foo;");
+			assertNotNull(query);
+			assertEquals("truc'machinbiduleFIN", ((StringConstant)(query.getSelect().get(0).getOperand())).getValue());
+			assertEquals("'truc''machinbiduleFIN'", query.getSelect().get(0).getOperand().toADQL());
+		}catch(Exception ex){
+			fail("String litteral concatenation is perfectly legal according to the ADQL standard.");
+		}
+	}
+
+}
diff --git a/test/tap/db/JDBCConnectionTest.java b/test/tap/db/JDBCConnectionTest.java
index 2cd7457..b645e85 100644
--- a/test/tap/db/JDBCConnectionTest.java
+++ b/test/tap/db/JDBCConnectionTest.java
@@ -32,12 +32,12 @@ import tap.metadata.TAPMetadata.STDSchema;
 import tap.metadata.TAPMetadata.STDTable;
 import tap.metadata.TAPSchema;
 import tap.metadata.TAPTable;
-import tap.metadata.TAPType;
-import tap.metadata.TAPType.TAPDatatype;
 import testtools.DBTools;
 import adql.db.DBChecker;
 import adql.db.DBColumn;
 import adql.db.DBTable;
+import adql.db.DBType;
+import adql.db.DBType.DBDatatype;
 import adql.parser.ADQLParser;
 import adql.parser.ParseException;
 import adql.query.ADQLQuery;
@@ -179,8 +179,8 @@ public class JDBCConnectionTest {
 		assertEquals("VARCHAR", pgJDBCConnection.getDBMSDatatype(null));
 		assertEquals("TEXT", sqliteJDBCConnection.getDBMSDatatype(null));
 
-		assertEquals("bytea", pgJDBCConnection.getDBMSDatatype(new TAPType(TAPDatatype.VARBINARY)));
-		assertEquals("BLOB", sqliteJDBCConnection.getDBMSDatatype(new TAPType(TAPDatatype.VARBINARY)));
+		assertEquals("bytea", pgJDBCConnection.getDBMSDatatype(new DBType(DBDatatype.VARBINARY)));
+		assertEquals("BLOB", sqliteJDBCConnection.getDBMSDatatype(new DBType(DBDatatype.VARBINARY)));
 	}
 
 	@Test
@@ -854,7 +854,7 @@ public class JDBCConnectionTest {
 		return true;
 	}
 
-	private static boolean equals(final TAPType type1, final TAPType type2){
+	private static boolean equals(final DBType type1, final DBType type2){
 		return type1 != null && type2 != null && type1.type == type2.type && type1.length == type2.length;
 	}
 
@@ -862,7 +862,7 @@ public class JDBCConnectionTest {
 		TAPMetadata tapMeta = new TAPMetadata();
 		TAPSchema tapSchema = new TAPSchema(STDSchema.TAPSCHEMA.toString());
 		TAPTable customColumns = (TAPTable)TAPMetadata.getStdTable(STDTable.COLUMNS).copy("Columns", STDTable.COLUMNS.label);
-		customColumns.addColumn("TestNewColumn", new TAPType(TAPDatatype.VARCHAR), "This is a fake column, just for test purpose.", null, null, null);
+		customColumns.addColumn("TestNewColumn", new DBType(DBDatatype.VARCHAR), "This is a fake column, just for test purpose.", null, null, null);
 		tapSchema.addTable(customColumns);
 		TAPTable addTable = new TAPTable("AdditionalTable");
 		addTable.addColumn("Blabla");
diff --git a/test/tap/formatter/JSONFormatTest.java b/test/tap/formatter/JSONFormatTest.java
index 527d7e2..3e6a845 100644
--- a/test/tap/formatter/JSONFormatTest.java
+++ b/test/tap/formatter/JSONFormatTest.java
@@ -31,11 +31,12 @@ import tap.file.TAPFileManager;
 import tap.log.TAPLog;
 import tap.metadata.TAPColumn;
 import tap.metadata.TAPMetadata;
-import tap.metadata.TAPType;
-import tap.metadata.TAPType.TAPDatatype;
 import tap.parameters.TAPParameters;
 import testtools.DBTools;
 import uws.service.UserIdentifier;
+import adql.db.DBType;
+import adql.db.DBType.DBDatatype;
+import adql.db.FunctionDef;
 
 /**
  * <p>Test the JSONFormat function {@link JSONFormat#writeResult(TableIterator, OutputStream, TAPExecutionReport, Thread)}.</p>
@@ -58,10 +59,10 @@ public class JSONFormatTest {
 		serviceConn = new ServiceConnectionTest();
 
 		resultingColumns = new TAPColumn[4];
-		resultingColumns[0] = new TAPColumn("ID", new TAPType(TAPDatatype.VARCHAR));
-		resultingColumns[1] = new TAPColumn("ra", new TAPType(TAPDatatype.DOUBLE), "Right ascension", "deg", "pos.eq.ra", null);
-		resultingColumns[2] = new TAPColumn("deg", new TAPType(TAPDatatype.DOUBLE), "Declination", "deg", "pos.eq.dec", null);
-		resultingColumns[3] = new TAPColumn("gmag", new TAPType(TAPDatatype.DOUBLE), "G magnitude", "mag", "phot.mag;em.opt.B", null);
+		resultingColumns[0] = new TAPColumn("ID", new DBType(DBDatatype.VARCHAR));
+		resultingColumns[1] = new TAPColumn("ra", new DBType(DBDatatype.DOUBLE), "Right ascension", "deg", "pos.eq.ra", null);
+		resultingColumns[2] = new TAPColumn("deg", new DBType(DBDatatype.DOUBLE), "Declination", "deg", "pos.eq.dec", null);
+		resultingColumns[3] = new TAPColumn("gmag", new DBType(DBDatatype.DOUBLE), "G magnitude", "mag", "phot.mag;em.opt.B", null);
 
 		if (!jsonFile.exists())
 			jsonFile.createNewFile();
@@ -223,6 +224,16 @@ public class JSONFormatTest {
 			return null;
 		}
 
+		@Override
+		public Collection<String> getGeometries(){
+			return null;
+		}
+
+		@Override
+		public Collection<FunctionDef> getUDFs(){
+			return null;
+		}
+
 		@Override
 		public TAPLog getLogger(){
 			return null;
diff --git a/test/tap/formatter/SVFormatTest.java b/test/tap/formatter/SVFormatTest.java
index d71c6ac..d50f0fe 100644
--- a/test/tap/formatter/SVFormatTest.java
+++ b/test/tap/formatter/SVFormatTest.java
@@ -28,12 +28,13 @@ import tap.file.TAPFileManager;
 import tap.log.TAPLog;
 import tap.metadata.TAPColumn;
 import tap.metadata.TAPMetadata;
-import tap.metadata.TAPType;
-import tap.metadata.TAPType.TAPDatatype;
 import tap.parameters.TAPParameters;
 import testtools.CommandExecute;
 import testtools.DBTools;
 import uws.service.UserIdentifier;
+import adql.db.DBType;
+import adql.db.DBType.DBDatatype;
+import adql.db.FunctionDef;
 
 /**
  * <p>Test the SVFormat function {@link SVFormat#writeResult(TableIterator, OutputStream, TAPExecutionReport, Thread)}.</p>
@@ -56,10 +57,10 @@ public class SVFormatTest {
 		serviceConn = new ServiceConnectionTest();
 
 		resultingColumns = new TAPColumn[4];
-		resultingColumns[0] = new TAPColumn("ID", new TAPType(TAPDatatype.VARCHAR));
-		resultingColumns[1] = new TAPColumn("ra", new TAPType(TAPDatatype.DOUBLE), "Right ascension", "deg", "pos.eq.ra", null);
-		resultingColumns[2] = new TAPColumn("deg", new TAPType(TAPDatatype.DOUBLE), "Declination", "deg", "pos.eq.dec", null);
-		resultingColumns[3] = new TAPColumn("gmag", new TAPType(TAPDatatype.DOUBLE), "G magnitude", "mag", "phot.mag;em.opt.B", null);
+		resultingColumns[0] = new TAPColumn("ID", new DBType(DBDatatype.VARCHAR));
+		resultingColumns[1] = new TAPColumn("ra", new DBType(DBDatatype.DOUBLE), "Right ascension", "deg", "pos.eq.ra", null);
+		resultingColumns[2] = new TAPColumn("deg", new DBType(DBDatatype.DOUBLE), "Declination", "deg", "pos.eq.dec", null);
+		resultingColumns[3] = new TAPColumn("gmag", new DBType(DBDatatype.DOUBLE), "G magnitude", "mag", "phot.mag;em.opt.B", null);
 
 		if (!svFile.exists())
 			svFile.createNewFile();
@@ -217,6 +218,16 @@ public class SVFormatTest {
 			return null;
 		}
 
+		@Override
+		public Collection<String> getGeometries(){
+			return null;
+		}
+
+		@Override
+		public Collection<FunctionDef> getUDFs(){
+			return null;
+		}
+
 		@Override
 		public TAPLog getLogger(){
 			return null;
diff --git a/test/tap/formatter/TextFormatTest.java b/test/tap/formatter/TextFormatTest.java
index 959475b..b763a29 100644
--- a/test/tap/formatter/TextFormatTest.java
+++ b/test/tap/formatter/TextFormatTest.java
@@ -28,12 +28,13 @@ import tap.file.TAPFileManager;
 import tap.log.TAPLog;
 import tap.metadata.TAPColumn;
 import tap.metadata.TAPMetadata;
-import tap.metadata.TAPType;
-import tap.metadata.TAPType.TAPDatatype;
 import tap.parameters.TAPParameters;
 import testtools.CommandExecute;
 import testtools.DBTools;
 import uws.service.UserIdentifier;
+import adql.db.DBType;
+import adql.db.DBType.DBDatatype;
+import adql.db.FunctionDef;
 
 /**
  * <p>Test the TestFormat function {@link TestFormat#writeResult(TableIterator, OutputStream, TAPExecutionReport, Thread)}.</p>
@@ -56,10 +57,10 @@ public class TextFormatTest {
 		serviceConn = new ServiceConnectionTest();
 
 		resultingColumns = new TAPColumn[4];
-		resultingColumns[0] = new TAPColumn("ID", new TAPType(TAPDatatype.VARCHAR));
-		resultingColumns[1] = new TAPColumn("ra", new TAPType(TAPDatatype.DOUBLE), "Right ascension", "deg", "pos.eq.ra", null);
-		resultingColumns[2] = new TAPColumn("deg", new TAPType(TAPDatatype.DOUBLE), "Declination", "deg", "pos.eq.dec", null);
-		resultingColumns[3] = new TAPColumn("gmag", new TAPType(TAPDatatype.DOUBLE), "G magnitude", "mag", "phot.mag;em.opt.B", null);
+		resultingColumns[0] = new TAPColumn("ID", new DBType(DBDatatype.VARCHAR));
+		resultingColumns[1] = new TAPColumn("ra", new DBType(DBDatatype.DOUBLE), "Right ascension", "deg", "pos.eq.ra", null);
+		resultingColumns[2] = new TAPColumn("deg", new DBType(DBDatatype.DOUBLE), "Declination", "deg", "pos.eq.dec", null);
+		resultingColumns[3] = new TAPColumn("gmag", new DBType(DBDatatype.DOUBLE), "G magnitude", "mag", "phot.mag;em.opt.B", null);
 
 		if (!textFile.exists())
 			textFile.createNewFile();
@@ -217,6 +218,16 @@ public class TextFormatTest {
 			return null;
 		}
 
+		@Override
+		public Collection<String> getGeometries(){
+			return null;
+		}
+
+		@Override
+		public Collection<FunctionDef> getUDFs(){
+			return null;
+		}
+
 		@Override
 		public TAPLog getLogger(){
 			return null;
diff --git a/test/tap/formatter/VOTableFormatTest.java b/test/tap/formatter/VOTableFormatTest.java
index 51e9dc8..5812072 100644
--- a/test/tap/formatter/VOTableFormatTest.java
+++ b/test/tap/formatter/VOTableFormatTest.java
@@ -28,13 +28,14 @@ import tap.file.TAPFileManager;
 import tap.log.TAPLog;
 import tap.metadata.TAPColumn;
 import tap.metadata.TAPMetadata;
-import tap.metadata.TAPType;
-import tap.metadata.TAPType.TAPDatatype;
 import tap.parameters.TAPParameters;
 import testtools.CommandExecute;
 import testtools.DBTools;
 import uk.ac.starlink.votable.DataFormat;
 import uws.service.UserIdentifier;
+import adql.db.DBType;
+import adql.db.DBType.DBDatatype;
+import adql.db.FunctionDef;
 
 /**
  * <p>Test the VOTableFormat function {@link VOTableFormat#writeResult(TableIterator, OutputStream, TAPExecutionReport, Thread)}.</p>
@@ -57,10 +58,10 @@ public class VOTableFormatTest {
 		serviceConn = new ServiceConnectionTest();
 
 		resultingColumns = new TAPColumn[4];
-		resultingColumns[0] = new TAPColumn("ID", new TAPType(TAPDatatype.VARCHAR));
-		resultingColumns[1] = new TAPColumn("ra", new TAPType(TAPDatatype.DOUBLE), "Right ascension", "deg", "pos.eq.ra", null);
-		resultingColumns[2] = new TAPColumn("deg", new TAPType(TAPDatatype.DOUBLE), "Declination", "deg", "pos.eq.dec", null);
-		resultingColumns[3] = new TAPColumn("gmag", new TAPType(TAPDatatype.DOUBLE), "G magnitude", "mag", "phot.mag;em.opt.B", null);
+		resultingColumns[0] = new TAPColumn("ID", new DBType(DBDatatype.VARCHAR));
+		resultingColumns[1] = new TAPColumn("ra", new DBType(DBDatatype.DOUBLE), "Right ascension", "deg", "pos.eq.ra", null);
+		resultingColumns[2] = new TAPColumn("deg", new DBType(DBDatatype.DOUBLE), "Declination", "deg", "pos.eq.dec", null);
+		resultingColumns[3] = new TAPColumn("gmag", new DBType(DBDatatype.DOUBLE), "G magnitude", "mag", "phot.mag;em.opt.B", null);
 
 		if (!votableFile.exists())
 			votableFile.createNewFile();
@@ -222,6 +223,16 @@ public class VOTableFormatTest {
 			return null;
 		}
 
+		@Override
+		public Collection<String> getGeometries(){
+			return null;
+		}
+
+		@Override
+		public Collection<FunctionDef> getUDFs(){
+			return null;
+		}
+
 		@Override
 		public TAPLog getLogger(){
 			return null;
diff --git a/test/uws/service/UWSUrlTest.java b/test/uws/service/UWSUrlTest.java
index 6733650..a61db70 100644
--- a/test/uws/service/UWSUrlTest.java
+++ b/test/uws/service/UWSUrlTest.java
@@ -2,6 +2,7 @@ package uws.service;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 import java.io.BufferedReader;
@@ -406,39 +407,53 @@ public class UWSUrlTest {
 		// CASE 1: http://localhost:8080/tapTest/path with url-pattern = /path/*
 		try{
 			UWSUrl uu = new UWSUrl(requestFromPath2root);
-			assertEquals(uu.getBaseURI(), "/path");
-		}catch(NullPointerException e){
+			assertEquals("/path", uu.getBaseURI());
+			assertEquals("", uu.getUwsURI());
+			assertEquals("http://localhost:8080/tapTest/path/", uu.toString());
+		}catch(Exception e){
+			e.printStackTrace(System.err);
 			fail("This HTTP request is perfectly correct: " + requestFromPath2root.getRequestURL());
 		}
 
 		// CASE 2: http://localhost:8080/tapTest/path/async with url-pattern = /path/*
 		try{
 			UWSUrl uu = new UWSUrl(requestFromPath2async);
-			assertEquals(uu.getBaseURI(), "/path");
-		}catch(NullPointerException e){
+			assertEquals("/path", uu.getBaseURI());
+			assertEquals("/async", uu.getUwsURI());
+			assertEquals("http://localhost:8080/tapTest/path/async", uu.toString());
+		}catch(Exception e){
+			e.printStackTrace(System.err);
 			fail("This HTTP request is perfectly correct: " + requestFromPath2async.getRequestURL());
 		}
 
-		// CASE 3: http://localhost:8080/tapTest/async with url-pattern = /*
+		// CASE 3: http://localhost:8080/tapTest with url-pattern = /*
 		try{
 			UWSUrl uu = new UWSUrl(requestFromRoot2root);
-			assertEquals(uu.getBaseURI(), "");
-		}catch(NullPointerException e){
+			assertEquals("", uu.getBaseURI());
+			assertEquals("", uu.getUwsURI());
+			assertEquals("http://localhost:8080/tapTest/", uu.toString());
+		}catch(Exception e){
+			e.printStackTrace(System.err);
 			fail("This HTTP request is perfectly correct: " + requestFromRoot2root.getRequestURL());
 		}
 
 		// CASE 4: http://localhost:8080/tapTest/async with url-pattern = /*
 		try{
 			UWSUrl uu = new UWSUrl(requestFromRoot2async);
-			assertEquals(uu.getBaseURI(), "");
-		}catch(NullPointerException e){
+			assertEquals("", uu.getBaseURI());
+			assertEquals("/async", uu.getUwsURI());
+			assertEquals("http://localhost:8080/tapTest/async", uu.toString());
+		}catch(Exception e){
+			e.printStackTrace(System.err);
 			fail("This HTTP request is perfectly correct: " + requestFromRoot2async.getRequestURL());
 		}
 
 		// CASE 5: http://localhost:8080/tapTest/path/async with url-pattern = /path/*
 		try{
 			new UWSUrl(requestWithServletPathNull);
-		}catch(NullPointerException e){
+			fail("RequestURL with no servlet path: this test should have failed!");
+		}catch(Exception e){
+			assertTrue(e instanceof NullPointerException);
 			assertEquals(e.getMessage(), "The extracted base UWS URI is NULL!");
 		}
 	}
@@ -449,18 +464,20 @@ public class UWSUrlTest {
 		try{
 			UWSUrl uu = new UWSUrl(requestFromPath2root);
 			uu.load(requestFromPath2root);
-			assertEquals(uu.getUwsURI(), "");
-			assertEquals(uu.jobSummary("async", "123456A").toString(), "http://localhost:8080/tapTest/path/async/123456A");
-		}catch(NullPointerException e){
+			assertEquals("", uu.getUwsURI());
+			assertEquals("http://localhost:8080/tapTest/path/async/123456A", uu.jobSummary("async", "123456A").toString());
+		}catch(Exception e){
+			e.printStackTrace(System.err);
 			fail("This HTTP request is perfectly correct: " + requestFromPath2root.getRequestURL());
 		}
 		// CASE 1b: Idem while loading http://localhost:8080/tapTest/path/async
 		try{
 			UWSUrl uu = new UWSUrl(requestFromPath2root);
 			uu.load(requestFromPath2async);
-			assertEquals(uu.getUwsURI(), "/async");
-			assertEquals(uu.jobSummary("async", "123456A").toString(), "http://localhost:8080/tapTest/path/async/123456A");
-		}catch(NullPointerException e){
+			assertEquals("/async", uu.getUwsURI());
+			assertEquals("http://localhost:8080/tapTest/path/async/123456A", uu.jobSummary("async", "123456A").toString());
+		}catch(Exception e){
+			e.printStackTrace(System.err);
 			fail("This HTTP request is perfectly correct: " + requestFromPath2async.getRequestURL());
 		}
 
@@ -468,9 +485,10 @@ public class UWSUrlTest {
 		try{
 			UWSUrl uu = new UWSUrl(requestFromPath2async);
 			uu.load(requestFromPath2async);
-			assertEquals(uu.getUwsURI(), "/async");
-			assertEquals(uu.jobSummary("async", "123456A").toString(), "http://localhost:8080/tapTest/path/async/123456A");
-		}catch(NullPointerException e){
+			assertEquals("/async", uu.getUwsURI());
+			assertEquals("http://localhost:8080/tapTest/path/async/123456A", uu.jobSummary("async", "123456A").toString());
+		}catch(Exception e){
+			e.printStackTrace(System.err);
 			fail("This HTTP request is perfectly correct: " + requestFromPath2async.getRequestURL());
 		}
 
@@ -478,9 +496,10 @@ public class UWSUrlTest {
 		try{
 			UWSUrl uu = new UWSUrl(requestFromPath2async);
 			uu.load(requestFromPath2root);
-			assertEquals(uu.getUwsURI(), "");
-			assertEquals(uu.jobSummary("async", "123456A").toString(), "http://localhost:8080/tapTest/path/async/123456A");
-		}catch(NullPointerException e){
+			assertEquals("", uu.getUwsURI());
+			assertEquals("http://localhost:8080/tapTest/path/async/123456A", uu.jobSummary("async", "123456A").toString());
+		}catch(Exception e){
+			e.printStackTrace(System.err);
 			fail("This HTTP request is perfectly correct: " + requestFromPath2root.getRequestURL());
 		}
 
@@ -488,8 +507,8 @@ public class UWSUrlTest {
 		try{
 			UWSUrl uu = new UWSUrl(requestFromRoot2root);
 			uu.load(requestFromRoot2root);
-			assertEquals(uu.getUwsURI(), "");
-			assertEquals(uu.jobSummary("async", "123456A").toString(), "http://localhost:8080/tapTest/async/123456A");
+			assertEquals("", uu.getUwsURI());
+			assertEquals("http://localhost:8080/tapTest/async/123456A", uu.jobSummary("async", "123456A").toString());
 		}catch(NullPointerException e){
 			fail("This HTTP request is perfectly correct: " + requestFromRoot2root.getRequestURL());
 		}
@@ -498,9 +517,10 @@ public class UWSUrlTest {
 		try{
 			UWSUrl uu = new UWSUrl(requestFromRoot2root);
 			uu.load(requestFromRoot2async);
-			assertEquals(uu.getUwsURI(), "/async");
-			assertEquals(uu.jobSummary("async", "123456A").toString(), "http://localhost:8080/tapTest/async/123456A");
-		}catch(NullPointerException e){
+			assertEquals("/async", uu.getUwsURI());
+			assertEquals("http://localhost:8080/tapTest/async/123456A", uu.jobSummary("async", "123456A").toString());
+		}catch(Exception e){
+			e.printStackTrace(System.err);
 			fail("This HTTP request is perfectly correct: " + requestFromRoot2async.getRequestURL());
 		}
 
@@ -508,9 +528,10 @@ public class UWSUrlTest {
 		try{
 			UWSUrl uu = new UWSUrl(requestFromRoot2async);
 			uu.load(requestFromRoot2async);
-			assertEquals(uu.getUwsURI(), "/async");
-			assertEquals(uu.jobSummary("async", "123456A").toString(), "http://localhost:8080/tapTest/async/123456A");
-		}catch(NullPointerException e){
+			assertEquals("/async", uu.getUwsURI());
+			assertEquals("http://localhost:8080/tapTest/async/123456A", uu.jobSummary("async", "123456A").toString());
+		}catch(Exception e){
+			e.printStackTrace(System.err);
 			fail("This HTTP request is perfectly correct: " + requestFromRoot2async.getRequestURL());
 		}
 
@@ -518,9 +539,10 @@ public class UWSUrlTest {
 		try{
 			UWSUrl uu = new UWSUrl(requestFromRoot2async);
 			uu.load(requestFromRoot2root);
-			assertEquals(uu.getUwsURI(), "");
-			assertEquals(uu.jobSummary("async", "123456A").toString(), "http://localhost:8080/tapTest/async/123456A");
-		}catch(NullPointerException e){
+			assertEquals("", uu.getUwsURI());
+			assertEquals("http://localhost:8080/tapTest/async/123456A", uu.jobSummary("async", "123456A").toString());
+		}catch(Exception e){
+			e.printStackTrace(System.err);
 			fail("This HTTP request is perfectly correct: " + requestFromRoot2root.getRequestURL());
 		}
 
@@ -529,7 +551,8 @@ public class UWSUrlTest {
 			UWSUrl uu = new UWSUrl(requestFromRoot2async);
 			uu.load(requestFromPath2async);
 			assertFalse(uu.getUwsURI().equals(""));
-		}catch(NullPointerException e){
+		}catch(Exception e){
+			e.printStackTrace(System.err);
 			fail("This HTTP request is perfectly correct: " + requestFromRoot2root.getRequestURL());
 		}
 	}
-- 
GitLab