Skip to content
adqlGrammar.jj 54.1 KiB
Newer Older
/*
 * This file is part of ADQLLibrary.
 * 
 * ADQLLibrary is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * ADQLLibrary is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with ADQLLibrary.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * Copyright 2012-2017 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
 *                       Astronomisches Rechen Institute (ARI)
 */

/*
*  This JavaCC file implements the BNF definition of ADQL v2.0 (IVOA Recommendation 30 Oct 2008 - http://www.ivoa.net/Documents/cover/ADQL-20081030.html).
*  To generate the parser with this file use JavaCC. This .jj file has been successfully tested with JavaCC 6.0.
*  
*  The generated parser checks the syntax of the given ADQL query and generates an object representation but no coherence with any database is done.
*  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.4 (09/2017)
*/

							/* ########### */
							/* # OPTIONS # */
							/* ########### */
options {
	STATIC = false;
	IGNORE_CASE = true;
}

							/* ########## */
							/* # PARSER # */
							/* ########## */
PARSER_BEGIN(ADQLParser)

package adql.parser;

import java.util.Stack;
import java.util.Vector;
import java.util.ArrayList;
import java.util.Collection;

import java.io.FileReader;
import java.io.IOException;

import adql.db.exception.UnresolvedIdentifiersException;

import adql.parser.IdentifierItems.IdentifierItem;

import adql.parser.ADQLQueryFactory.JoinType;

import adql.query.*;
import adql.query.from.*;
import adql.query.constraint.*;

import adql.query.operand.*;

import adql.query.operand.function.*;

import adql.query.operand.function.geometry.*;
import adql.query.operand.function.geometry.GeometryFunction.GeometryValue;

import adql.translator.PostgreSQLTranslator;
import adql.translator.TranslationException;

/**
* <p>Parses an ADQL query thanks to the {@link ADQLParser#Query()} function. </p>
* 
* <p>This parser is able, thanks to a {@link QueryChecker} object, to check each ADQLQuery just after its generation.
* It could be used to check the consistency between the ADQL query to parse and the "database" on which the query must be executed.
* By default, there is no {@link QueryChecker}. Thus you must extend {@link QueryChecker} to check semantically all generated ADQLQuery objects.</p>
* 
* <p>To create an object representation of the given ADQL query, this parser uses a {@link ADQLQueryFactory} object. So if you want customize some object (ie. CONTAINS) of this representation
* you just have to extend the corresponding default object (ie. ContainsFunction) and to extend the corresponding function of {@link ADQLQueryFactory} (ie. createContains(...)).</p>
* 
* <p><b><u>WARNING:</u> To modify this class it's strongly encouraged to modify the .jj file in the section between <i>PARSER_BEGIN</i> and <i>PARSER_END</i> and to re-compile it with JavaCC.</b></p>
*
* @see QueryChecker
* @see ADQLQueryFactory
*
* @author Gr&eacute;gory Mantelet (CDS;ARI) - gmantele@ari.uni-heidelberg.de
* @version 1.4 (09/2017)
*/
public class ADQLParser {
	
	/** Tools to build the object representation of the ADQL query. */
	private ADQLQueryFactory queryFactory = new ADQLQueryFactory();
	
	/** The stack of queries (because there may be some sub-queries). */
	private Stack<ADQLQuery> stackQuery = new Stack<ADQLQuery>();
	
	/** The object representation of the ADQL query to parse. (ONLY USED DURING THE PARSING, else it is always <i>null</i>). */
	private ADQLQuery query = null;
	
	/** Checks each ADQLQuery (sub-query or not) just after their generation. */
	private QueryChecker queryChecker = null;
	
	/** The first token of a table/column name. This token is extracted by {@link #Identifier()}. */
	private Token currentIdentifierToken = null;
	
	/**
	* Builds an ADQL parser without a query to parse.
	*/
	public ADQLParser(){
		this(new java.io.ByteArrayInputStream("".getBytes()));
	}
	
	/**
	* Builds an ADQL parser without a query to parse but with a QueryChecker and a ADQLQueryFactory.
	*
	* @param checker	The object to use to check each ADQLQuery.
	* @param factory	The object to use to build an object representation of the given ADQL query.
	*/
	public ADQLParser(QueryChecker checker, ADQLQueryFactory factory) {
		this();
		
		queryChecker = checker;
			
		if (factory != null)
			queryFactory = factory;
	}
	
	/**
	* Builds an ADQL parser without a query to parse but with a QueryChecker.
	*
	* @param checker	The object to use to check each ADQLQuery.
	*/
	public ADQLParser(QueryChecker checker) {
		this(checker, null);
	}
	
	/**
	* Builds an ADQL parser without a query to parse but with a ADQLQueryFactory.
	*
	* @param factory	The object to use to build an object representation of the given ADQL query.
	*/
	public ADQLParser(ADQLQueryFactory factory) {
		this((QueryChecker)null, factory);
	}
	
	/**
	* Builds a parser with a stream containing the query to parse.
	*
	* @param stream		The stream in which the ADQL query to parse is given.
	* @param checker	The object to use to check each ADQLQuery.
	* @param factory	The object to use to build an object representation of the given ADQL query.
	*/
	public ADQLParser(java.io.InputStream stream, QueryChecker checker, ADQLQueryFactory factory) {
		this(stream);
		queryChecker = checker;
		
		if (factory != null)
			queryFactory = factory;
	}
	
	/**
	* Builds a parser with a stream containing the query to parse.
	*
	* @param stream		The stream in which the ADQL query to parse is given.
	* @param checker	The object to use to check each ADQLQuery.
	*/
	public ADQLParser(java.io.InputStream stream, QueryChecker checker) {
		this(stream, checker, null);
	}
	
	/**
	* Builds a parser with a stream containing the query to parse.
	*
	* @param stream		The stream in which the ADQL query to parse is given.
	* @param factory	The object to use to build an object representation of the given ADQL query.
	*/
	public ADQLParser(java.io.InputStream stream, ADQLQueryFactory factory) {
		this(stream, (QueryChecker)null, factory);
	}
	
	/**
	* Builds a parser with a stream containing the query to parse.
	*
	* @param stream			The stream in which the ADQL query to parse is given.
	* @param encoding		The supplied encoding.
	* @param checker		The object to use to check each ADQLQuery.
	* @param factory		The object to use to build an object representation of the given ADQL query.
	*/
	public ADQLParser(java.io.InputStream stream, String encoding, QueryChecker checker, ADQLQueryFactory factory) {
		this(stream, encoding);
		
		queryChecker = checker;
		
		if (factory != null)
			queryFactory = factory;
	}
	
	/**
	* Builds a parser with a stream containing the query to parse.
	*
	* @param stream			The stream in which the ADQL query to parse is given.
	* @param encoding		The supplied encoding.
	* @param checker		The object to use to check each ADQLQuery.
	*/
	public ADQLParser(java.io.InputStream stream, String encoding, QueryChecker checker) {
		this(stream, encoding, checker, null);
	}
	
	/**
	* Builds a parser with a stream containing the query to parse.
	*
	* @param stream			The stream in which the ADQL query to parse is given.
	* @param encoding		The supplied encoding.
	* @param factory		The object to use to build an object representation of the given ADQL query.
	*/
	public ADQLParser(java.io.InputStream stream, String encoding, ADQLQueryFactory factory) {
		this(stream, encoding, null, factory);
	}
	
	/**
	* Builds a parser with a reader containing the query to parse.
	*
	* @param reader			The reader in which the ADQL query to parse is given.
	* @param checker		The object to use to check each ADQLQuery.
	* @param factory		The object to use to build an object representation of the given ADQL query.
	*/
	public ADQLParser(java.io.Reader reader, QueryChecker checker, ADQLQueryFactory factory) {
		this(reader);
		queryChecker = checker;

		if (factory != null)
			queryFactory = factory;
	}
	
	/**
	* Builds a parser with a reader containing the query to parse.
	*
	* @param reader			The reader in which the ADQL query to parse is given.
	* @param checker		The object to use to check each ADQLQuery.
	*/
	public ADQLParser(java.io.Reader reader, QueryChecker checker) {
		this(reader, checker, null);
	}
	
	/**
	* Builds a parser with a reader containing the query to parse.
	*
	* @param reader		The reader in which the ADQL query to parse is given.
	* @param factory		The object to use to build an object representation of the given ADQL query.
	*/
	public ADQLParser(java.io.Reader reader, ADQLQueryFactory factory) {
		this(reader, null, factory);
	}
	
	/**
	* Builds a parser with another token manager.
	*
	* @param tm				The manager which associates a token to a numeric code.
	* @param checker		The object to use to check each ADQLQuery.
	* @param factory		The object to use to build an object representation of the given ADQL query.
	*/
	public ADQLParser(ADQLParserTokenManager tm, QueryChecker checker, ADQLQueryFactory factory) {
		this(tm);
		queryChecker = checker;

		if (factory != null)
			queryFactory = factory;
	}
	
	/**
	* Builds a parser with another token manager.
	*
	* @param tm				The manager which associates a token to a numeric code.
	* @param checker		The object to use to check each ADQLQuery.
	*/
	public ADQLParser(ADQLParserTokenManager tm, QueryChecker checker) {
		this(tm, checker, null);
	}
	
	/**
	* Builds a parser with another token manager.
	*
	* @param tm				The manager which associates a token to a numeric code.
	* @param factory		The object to use to build an object representation of the given ADQL query.
	*/
	public ADQLParser(ADQLParserTokenManager tm, ADQLQueryFactory factory) {
		this(tm, null, factory);
	}
	
	/**
	* Parses the query given at the creation of this parser or in the <i>ReInit</i> functions.
	*
	* @return 					The object representation of the given ADQL query.
	* @throws ParseException	If there is at least one syntactic error.
	*
	* @see ADQLParser#Query()
	*/
	public final ADQLQuery parseQuery() throws ParseException {
		stackQuery.clear();
		query = null;
		try { 
			return Query();
		}catch(TokenMgrError tme) {
			throw new ParseException(tme.getMessage(), new TextPosition(tme.getErrorLine(), tme.getErrorColumn()));
		}
	}
	
	/**
	* Parses the query given in parameter.
	*
	* @param q					The ADQL query to parse.
	* @return 					The object representation of the given ADQL query.
	* @throws ParseException	If there is at least one syntactic error.
	*
	* @see ADQLParser#ReInit(java.io.InputStream)
	* @see ADQLParser#setDebug(boolean)
	* @see ADQLParser#Query()
	*/
	public final ADQLQuery parseQuery(String q) throws ParseException {
		stackQuery.clear();
		query = null;
		ReInit(new java.io.ByteArrayInputStream(q.getBytes()));
		try { 
			return Query();
		}catch(TokenMgrError tme) {
			throw new ParseException(tme.getMessage(), new TextPosition(tme.getErrorLine(), tme.getErrorColumn()));
		}
	}
	
	/**
	* Parses the query contained in the stream given in parameter.
	*
	* @param stream				The stream which contains the ADQL query to parse.
	* @return 					The object representation of the given ADQL query.
	* @throws ParseException	If there is at least one syntactic error.
	*
	* @see ADQLParser#ReInit(java.io.InputStream)
	* @see ADQLParser#setDebug(boolean)
	* @see ADQLParser#Query()
	*/
	public final ADQLQuery parseQuery(java.io.InputStream stream) throws ParseException {
		stackQuery.clear();
		query = null;
		ReInit(stream);
		try { 
			return Query();
		}catch(TokenMgrError tme) {
			throw new ParseException(tme.getMessage(), new TextPosition(tme.getErrorLine(), tme.getErrorColumn()));
		}
	}
	
	public final void setDebug(boolean debug){
		if (debug) enable_tracing();
		else       disable_tracing();
	}
	
	public final QueryChecker getQueryChecker(){
		return queryChecker;
	}
	
	public final void setQueryChecker(QueryChecker checker){
		queryChecker = checker;
	}
	
	public final ADQLQueryFactory getQueryFactory(){
		return queryFactory;
	}
	
	public final void setQueryFactory(ADQLQueryFactory factory){
		queryFactory = (factory!=null)?factory:(new ADQLQueryFactory());
	}
	
	private final ParseException generateParseException(Exception ex){
		if (!(ex instanceof ParseException)){
			ParseException pex = new ParseException("["+ex.getClass().getName()+"] "+ex.getMessage());
			pex.setStackTrace(ex.getStackTrace());
			return pex;
		}else
			return (ParseException)ex;
	}

	/**
	* <p>Gets the specified ADQL query and parses the given ADQL query. The SQL translation is then printed if the syntax is correct.</p>
	* <p><b>ONLY the syntax is checked: the query is NOT EXECUTED !</b></p>
	* <p>Supplied parameters are: <ul><li>[-debug] -url http://...</li><li>[-debug] -file ...</li><li>[-debug] -query SELECT...</li></ul></p>
	*
	* @param args
	* @throws Exception
	*/
	public static final void main(String[] args) throws Exception {
		final String USAGE = "Usage:\n\tadqlParser.jar [-d] [-v] [-e] [-a|-s] [<FILE>|<URL>]\n\nNOTE: If no file or URL is given, the ADQL query is expected in the standard input. This query must end with a ';' !\n\nParameters:\n\t-v or --verbose : Print the main steps of the parsing\n\t-d or --debug   : Print stack traces when a grave error occurs\n\t-e or --explain : Explain the ADQL parsing (or Expand the parsing tree)\n\t-a or --adql    : Display the understood ADQL query\n\t-s or --sql     : Ask the SQL translation of the given ADQL query (SQL compatible with PostgreSQL)\n\nReturn:\n\tBy default: nothing if the query is correct. Otherwise a message explaining why the query is not correct is displayed.\n\tWith the -s option, the SQL translation of the given ADQL query will be returned.\n\tWith the -a option, the ADQL query is returned as it has been understood.\n\nExit status:\n\t0\tOK !\n\t1\tParameter error (missing or incorrect parameter)\n\t2\tFile error (incorrect file/url, reading error, ...)\n\t3\tParsing error (syntactic or semantic error)\n\t4\tTranslation error (a problem has occurred during the translation of the given ADQL query in SQL).";

		ADQLParser parser;

		final String urlRegex = "^(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]";

		String file = null, metaFile = null;
		short mode = -1;
		boolean verbose=false, debug=false, explain=false;

		// Parameters reading:
		for(int i=0; i<args.length; i++){
			if (args[i].equalsIgnoreCase("-d") || args[i].equalsIgnoreCase("--debug"))
				debug = true;
			else if (args[i].equalsIgnoreCase("-v") || args[i].equalsIgnoreCase("--verbose"))
				verbose = true;
			else if (args[i].equalsIgnoreCase("-e") || args[i].equalsIgnoreCase("--explain"))
				explain = true;
			else if (args[i].equalsIgnoreCase("-a") || args[i].equalsIgnoreCase("--adql")){
				if (mode != -1){
					System.err.println("((!)) Too much parameter: you must choose between -s, -c, -a or nothing ((!))\n"+USAGE);
					System.exit(1);
				}else
					mode = 1;
			}else if (args[i].equalsIgnoreCase("-s") || args[i].equalsIgnoreCase("--sql")){
				if (mode != -1){
					System.err.println("((!)) Too much parameter: you must choose between -s, -c, -a or nothing ((!))\n"+USAGE);
					System.exit(1);
				}else
					mode = 2;
			}else if (args[i].equalsIgnoreCase("-h") || args[i].equalsIgnoreCase("--help")){
				System.out.println(USAGE);
				System.exit(0);
			}else if (args[i].startsWith("-")){
				System.err.println("((!)) Unknown parameter: \""+args[i]+"\" ((!))\u005cn"+USAGE);
				System.exit(1);
			}else
				file = args[i].trim();
		}

		try{

			if (file == null || file.length()==0)
				parser = new ADQLParser(System.in);
			else if (file.matches(urlRegex))
				parser = new ADQLParser((new java.net.URL(file)).openStream());
			else
				parser = new ADQLParser(new FileReader(file));

			parser.setDebug(explain);

			// Query parsing:
			try{
				if (verbose)	System.out.print("((i)) Parsing ADQL query...");
				ADQLQuery q = parser.parseQuery();
				if (verbose)	System.out.println("((i)) CORRECT ADQL QUERY ((i))");
				if (mode==2){
					PostgreSQLTranslator translator = new PostgreSQLTranslator();
					if (verbose)	System.out.print("((i)) Translating in SQL...");
					String sql = translator.translate(q);
					if (verbose)	System.out.println("ok");
					System.out.println(sql);
				}else if (mode==1){
					System.out.println(q.toADQL());
				}
			}catch(UnresolvedIdentifiersException uie){
				System.err.println("((X)) "+uie.getNbErrors()+" unresolved identifiers:");
				for(ParseException pe : uie)
					System.err.println("\t - at "+pe.getPosition()+": "+uie.getMessage());
				if (debug)		uie.printStackTrace(System.err);
				System.exit(3);
			}catch(ParseException pe){
				System.err.println("((X)) Syntax error: "+pe.getMessage()+" ((X))");
				if (debug)		pe.printStackTrace(System.err);
				System.exit(3);
			}catch(TranslationException te){
				if (verbose)	System.out.println("error");
				System.err.println("((X)) Translation error: "+te.getMessage()+" ((X))");
				if (debug)		te.printStackTrace(System.err);
				System.exit(4);
			}

		}catch(IOException ioe){
			System.err.println("\n((X)) Error while reading the file \""+file+"\": "+ioe.getMessage()+" ((X))");
			if (debug)		ioe.printStackTrace(System.err);
			System.exit(2);
		}
    	
    }
}

PARSER_END(ADQLParser)


							/* ########### */
							/* # GRAMMAR # */
							/* ########### */
/* ******************** */
/* Characters to ignore */
/* ******************** */
SKIP : { < " " | "\t" | "\n" | "\r" | "\r\n" > }

/* *********** */
/* Punctuation */
/* *********** */
TOKEN : {
	< LEFT_PAR: "(" >
|	< RIGHT_PAR: ")" > 
|	< DOT: "." >
|	< COMMA: "," >
|	< EOQ: ";">
|	< CONCAT: "||" >
}

/* ******************** */
/* Arithmetic operators */
/* ******************** */
TOKEN : {
	< PLUS: "+" >
|	< MINUS: "-" >
|	< ASTERISK: "*" >
|	< DIVIDE: "/" >
}

/* ******************** */
/* Comparison operators */
/* ******************** */
TOKEN : {
	< EQUAL: "=" >
|	< NOT_EQUAL: "<>" | "!=" >
|	< LESS_THAN: "<" >
|	< LESS_EQUAL_THAN: "<=" >
|	< GREATER_THAN: ">" >
|	< GREATER_EQUAL_THAN: ">=" >
}

/* *************** */
/* SELECT's tokens */
/* *************** */
TOKEN : {
	< SELECT: "SELECT" >
|	< QUANTIFIER: "DISTINCT" | "ALL" >
|	< TOP: "TOP" >
}

/* ************* */
/* FROM's tokens */
/* ************* */
TOKEN : {
	< FROM: "FROM" >
|	< AS: "AS" >
|	< NATURAL: "NATURAL" >
|	< INNER: "INNER" >
|	< OUTER: "OUTER" >
|	< RIGHT: "RIGHT" >
|	< LEFT: "LEFT" >
|	< FULL: "FULL" >
|	< JOIN: "JOIN" >
|	< ON: "ON" >
|	< USING: "USING" >
}

/* ************** */
/* WHERE's tokens */
/* ************** */
TOKEN : {
	< WHERE: "WHERE" >
|	< AND: "AND" >
|	< OR: "OR" >
|	< NOT: "NOT" >
|	< IS: "IS" >
|	< NULL: "NULL" >
|	< BETWEEN: "BETWEEN" >
|	< LIKE: "LIKE" >
|	< IN: "IN" >
|	< EXISTS: "EXISTS" >
}

/* ********************* */
/* Other clauses' tokens */
/* ********************* */
TOKEN : {
	< GROUP_BY: "GROUP BY" >
|	< HAVING: "HAVING" >
|	< ORDER_BY: "ORDER BY" >
|	< ASC: "ASC" >
|	< DESC: "DESC" >
}

/* ************* */
/* SQL functions */
/* ************* */
TOKEN : {
	< AVG: "AVG" >
|	< MAX: "MAX" >
|	< MIN: "MIN" >
|	< SUM: "SUM" >
|	< COUNT: "COUNT" >
}

/* ************** */
/* ADQL functions */
/* ************** */
TOKEN : {
	< BOX: "BOX" >
|	< CENTROID: "CENTROID" >
|	< CIRCLE: "CIRCLE" >
|	< POINT: "POINT" >
|	< POLYGON: "POLYGON" >
|	< REGION: "REGION" >

|	< CONTAINS: "CONTAINS" >
|	< INTERSECTS: "INTERSECTS" >
|	< AREA: "AREA" >
|	< COORD1: "COORD1" >
|	< COORD2: "COORD2" >
|	< COORDSYS: "COORDSYS" >
|	< DISTANCE: "DISTANCE" >
}

/* ********************** */
/* Mathematical functions */
/* ********************** */
TOKEN : {
	< ABS: "ABS" >
|	< CEILING: "CEILING" >
|	< DEGREES: "DEGREES" >
|	< EXP: "EXP" >
|	< FLOOR: "FLOOR" >
|	< LOG: "LOG" >
|	< LOG10: "LOG10" >
|	< MOD: "MOD" >
|	< PI: "PI" >
|	< POWER: "POWER" >
|	< RADIANS: "RADIANS" >
|	< RAND: "RAND" >
|	< ROUND: "ROUND" >
|	< SQRT: "SQRT" >
|	< TRUNCATE: "TRUNCATE" >
}

/* ************************* */
/* Trigonometrical functions */
/* ************************* */
TOKEN : {
	< ACOS: "ACOS" >
|	< ASIN: "ASIN" >
|	< ATAN: "ATAN" >
|	< ATAN2: "ATAN2" >
|	< COS: "COS" >
|	< COT: "COT" >
|	< SIN: "SIN" >
|	< TAN: "TAN" >
}

/* ******* */
/* Comment */
/* ******* */
SKIP : { < <MINUS><MINUS> (~["\n","\r"])* ("\n"|"\r"|"\r\n")? > }

/* ****** */
/* String */
/* ****** */
<DEFAULT> MORE : { "'" : WithinString }
<WithinString> MORE : { < ~["'"] | ("''") > }
<WithinString> TOKEN : { < STRING_LITERAL: "'" >: DEFAULT }

/* ************************************************* */
/* Identifier (column, tables, ...) */
/* ************************************************* */
<DEFAULT> MORE : { "\"" : WithinDelimitedId }
<WithinDelimitedId> MORE : { < ~["\""] | ("\"\"") > }
<WithinDelimitedId> TOKEN : { < DELIMITED_IDENTIFIER: "\"" >: DEFAULT }

TOKEN : {
	< REGULAR_IDENTIFIER: (<Letter>)+ (<DIGIT> | <Letter> | "_")* >
|	< #Letter: ["a"-"z","A"-"Z"] >
}

/* *************** */
/* Primary numbers */
/* *************** */
TOKEN : {
	< SCIENTIFIC_NUMBER: (<UNSIGNED_FLOAT>|<UNSIGNED_INTEGER>) "E" (<PLUS>|<MINUS>)? <UNSIGNED_INTEGER> >
|	< UNSIGNED_FLOAT: (<UNSIGNED_INTEGER> <DOT> (<UNSIGNED_INTEGER>)?) | (<DOT> <UNSIGNED_INTEGER>) >
|	< UNSIGNED_INTEGER: (<DIGIT>)+ >
|	< #DIGIT: ["0"-"9"] >
}

							/* ########## */
							/* # SYNTAX # */
							/* ########## */
							
/* ******************* */
/* GENERAL ADQL SYNTAX */
/* ******************* */
/**
* Parses the ADQL query given at the parser creation or in the {@link ADQLParser#ReInit(java.io.InputStream)}
* or in the <i>parseQuery</i> functions.
*
* @return					The object representation of the query.
* @throws ParseException	If the query syntax is incorrect.
*/
ADQLQuery Query(): {ADQLQuery q = null;}{
	q=QueryExpression() (<EOF> | <EOQ>)
	{
		// check the query:
		if (queryChecker != null)
			queryChecker.check(q);
			
		return q;
	}
ADQLQuery QueryExpression(): {TextPosition endPos = null;} {
	{
		try{
			// create the query:
			query = queryFactory.createQuery();
			stackQuery.push(query);
		}catch(Exception ex){
			throw generateParseException(ex);
		}
	}
	Select()
	From()     {endPos = query.getFrom().getPosition();}
	[Where()   {endPos = query.getWhere().getPosition();}]
	[GroupBy() {endPos = query.getGroupBy().getPosition();}]
	[Having()  {endPos = query.getHaving().getPosition();}]
	[OrderBy() {endPos = query.getOrderBy().getPosition();}]
		// set the position of the query:
		query.setPosition(new TextPosition(query.getSelect().getPosition(), endPos));
		
		// 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();
			
		return previousQuery;
	}
}

ADQLQuery SubQueryExpression(): {ADQLQuery q = null; Token start, end;} {
	start=<LEFT_PAR> q=QueryExpression() end=<RIGHT_PAR>
	{
		q.setPosition(new TextPosition(start, end));
		return q;
	}
void Select(): {ClauseSelect select = query.getSelect(); SelectItem item=null; Token start,t = null;} {
	start=<SELECT>
	[t=<QUANTIFIER> {select.setDistinctColumns(t.image.equalsIgnoreCase("DISTINCT"));}]
	[<TOP> t=<UNSIGNED_INTEGER>
	 {
	  try{
	  	select.setLimit(Integer.parseInt(t.image));
	  }catch(NumberFormatException nfe){
	  	throw new ParseException("[l."+t.beginLine+";c."+t.beginColumn+"] The TOP limit (\""+t.image+"\") isn't a regular unsigned integer !");
	  }
	 }
	]
	
	item=SelectItem() {select.add(item);}
	(<COMMA> item=SelectItem() {select.add(item);})*
	{
		TextPosition lastItemPos = query.getSelect().get(query.getSelect().size()-1).getPosition();
		select.setPosition(new TextPosition(start.beginLine, start.beginColumn, lastItemPos.endLine, lastItemPos.endColumn));
	}
SelectItem SelectItem(): {IdentifierItems identifiers = new IdentifierItems(true); IdentifierItem id = null, label = null; ADQLOperand op = null; SelectItem item; Token starToken;} {
		( starToken=<ASTERISK>
		  {
		    item = new SelectAllColumns(query);
		    item.setPosition(new TextPosition(starToken));
		    return item;
		  }
		)
	|LOOKAHEAD(7)
		(
			id=Identifier() <DOT> { identifiers.append(id); }
			(
				id=Identifier() <DOT> { identifiers.append(id); }
				(
					id=Identifier() <DOT> { identifiers.append(id); }
				)?
			)?
				try{
					item = new SelectAllColumns( queryFactory.createTable(identifiers, null) );
					TextPosition firstPos = identifiers.get(0).position;
					item.setPosition(new TextPosition(firstPos.beginLine, firstPos.beginColumn, starToken.endLine, (starToken.endColumn < 0) ? -1 : (starToken.endColumn + 1)));
					return item;
				}catch(Exception ex) {
					throw generateParseException(ex);
				}
			}
		)
		
	| 
		(op=ValueExpression()[[<AS>] label=Identifier()])
			item = queryFactory.createSelectItem(op, (label==null)?null:label.identifier);
			if (label != null){
				item.setCaseSensitive(label.caseSensitivity);
				item.setPosition(new TextPosition(op.getPosition(), label.position));
			}else
				item.setPosition(new TextPosition(op.getPosition()));
			return item;
		}catch(Exception ex){
			throw generateParseException(ex);
		}
	}
}

void From():{FromContent content = null, content2 = null;}{
	try{
		<FROM> content=TableRef()
		(<COMMA> content2=TableRef()
		 {
		   TextPosition startPos = content.getPosition(), endPos = content2.getPosition();
		   content = queryFactory.createJoin(JoinType.CROSS, content, content2);
		   content.setPosition(new TextPosition(startPos, endPos));
		 }
		)*
		{ query.setFrom(content); }
	}catch(Exception ex){
		throw generateParseException(ex);
	}
}

void Where(): {ClauseConstraints where = query.getWhere(); ADQLConstraint condition; Token start;} {
	start=<WHERE> ConditionsList(where)
	{
	  TextPosition endPosition = where.getPosition();
	  where.setPosition(new TextPosition(start.beginLine, start.beginColumn, endPosition.endLine, endPosition.endColumn));
	}
void GroupBy(): {ClauseADQL<ADQLColumn> groupBy = query.getGroupBy(); ADQLColumn colRef = null; Token start;} {
	start=<GROUP_BY> colRef=Column() { groupBy.add(colRef); }
	( <COMMA> colRef=Column() { groupBy.add(colRef); } )*
	{ groupBy.setPosition(new TextPosition(start.beginLine, start.beginColumn, colRef.getPosition().endLine, colRef.getPosition().endColumn)); }
void Having(): {ClauseConstraints having = query.getHaving(); Token start;} {
	start=<HAVING> ConditionsList(having)
	{
	  TextPosition endPosition = having.getPosition();
	  having.setPosition(new TextPosition(start.beginLine, start.beginColumn, endPosition.endLine, endPosition.endColumn));
	}
void OrderBy(): {ClauseADQL<ADQLOrder> orderBy = query.getOrderBy(); ADQLOrder order = null; Token start;} {
	start=<ORDER_BY> order=OrderItem() {orderBy.add(order);}
	( <COMMA> order=OrderItem() {orderBy.add(order);} )*
	{ orderBy.setPosition(new TextPosition(start, token)); }

/* *************************** */
/* COLUMN AND TABLE REFERENCES */
/* *************************** */
IdentifierItem Identifier(): {Token t;} {
	(
		t=<REGULAR_IDENTIFIER>
		{ return new IdentifierItem(t, false); }
	|
		t=<DELIMITED_IDENTIFIER>
		{ return new IdentifierItem(t, true); }
	)
}

/**
 * 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.
 */
IdentifierItems TableName(): {IdentifierItems identifiers=new IdentifierItems(true); IdentifierItem id=null;} {
	(
		id=Identifier() {identifiers.append(id);}						// catalog
		(LOOKAHEAD(1) <DOT> id=Identifier() {identifiers.append(id);})?	// schema
		(LOOKAHEAD(1) <DOT> id=Identifier() {identifiers.append(id);})?	// table
	)
	{ return identifiers; }
}

/**
 * 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.
 */
IdentifierItems ColumnName(): {IdentifierItem id; IdentifierItems table=null, identifiers=new IdentifierItems(false);} {
	( id=Identifier() (LOOKAHEAD(1) <DOT> table=TableName())? )
	{
		identifiers.append(id);
		if (table != null){
			for(int i=0; i<table.size(); i++)
				identifiers.append(table.get(i));
		}
		return identifiers;
	}
}

ADQLColumn Column(): {IdentifierItems identifiers;} {
	identifiers = ColumnName()
	{
		try{
			return queryFactory.createColumn(identifiers);
		}catch(Exception ex){
			throw generateParseException(ex);
		}
	}
}

ADQLOrder OrderItem(): {IdentifierItem identifier = null; Token ind = null, desc = null;}{
	(identifier=Identifier() | ind=<UNSIGNED_INTEGER>) (<ASC> | desc=<DESC>)?
			if (identifier != null){
				order = queryFactory.createOrder(identifier, desc!=null);
				order.setPosition(identifier.position);
			}else{
				order = queryFactory.createOrder(Integer.parseInt(ind.image), desc!=null);
				order.setPosition(new TextPosition(ind));
			}
			return order;
		}catch(Exception ex){
			throw generateParseException(ex);
		}
	}
}

FromContent SimpleTableRef(): {IdentifierItem alias = null; IdentifierItems identifiers = null; ADQLQuery subQuery = null; FromContent content = null; Token start,end;} {
	try{
		(
			identifiers=TableName() [[<AS>] alias=Identifier()]
			{
			  content = queryFactory.createTable(identifiers, alias);
			  if (alias == null)
			  	content.setPosition(new TextPosition(identifiers.get(0).position, identifiers.get(identifiers.size()-1).position));
			  else
			  	content.setPosition(new TextPosition(identifiers.get(0).position, alias.position));
			  return content;
			}
		|LOOKAHEAD(2)
			subQuery=SubQueryExpression() [<AS>] alias=Identifier()
			{
			  content = queryFactory.createTable(subQuery, alias);
			  if (alias == null)
			  	content.setPosition(new TextPosition(subQuery.getPosition()));
			  else
			  	content.setPosition(new TextPosition(subQuery.getPosition(), alias.position));
			  return content;
			}
			start=<LEFT_PAR> content=JoinedTable() end=<RIGHT_PAR>
			{
			  content.setPosition(new TextPosition(start, end));
			  return content;
			}
		)
	}catch(Exception ex){
		throw generateParseException(ex);
	}
}

FromContent TableRef(): { FromContent content; } {
	content=SimpleTableRef()
	( LOOKAHEAD(2) content=JoinSpecification(content) )*
	{ return content; }
}

FromContent JoinedTable(): { FromContent content; } {
	content=SimpleTableRef()
	( content=JoinSpecification(content) )+
	{ return content; }
}


ADQLJoin JoinSpecification(FromContent leftTable): { boolean natural = false; JoinType type = JoinType.INNER;  ClauseConstraints condition = new ClauseConstraints("ON"); ArrayList<ADQLColumn> lstColumns=new ArrayList<ADQLColumn>(); IdentifierItem id; FromContent rightTable; ADQLJoin join; Token lastPar;} {
			<NATURAL> {natural=true;} [<INNER> | ((<LEFT> {type = JoinType.OUTER_LEFT;}|<RIGHT> {type = JoinType.OUTER_RIGHT;}|<FULL> {type = JoinType.OUTER_FULL;}) [<OUTER>])] <JOIN> rightTable=SimpleTableRef()