Skip to content
Snippets Groups Projects
Commit 89418d13 authored by Grégory Mantelet's avatar Grégory Mantelet
Browse files

[ADQL,TAP] New parser for ADQL-2.1.

- Now, `ADQLParserFactory.createParser(...)` should be used to create a parser
- Only the new function `LOWER` is supported for the moment
- Not yet possible to manage the optional features _(next dev to come)_
=> 1st step for ADQL-Lib v2.0

- TAP adapted so that using the last stable version of the ADQL language
  (i.e. 2.0 for the moment)
  - but not yet possible to set the ADQL version to use in the configuration
    file
parent 6beb5a94
No related branches found
No related tags found
No related merge requests found
Showing
with 3767 additions and 15831 deletions
......@@ -4,11 +4,17 @@ README
Preambule
---------
This GitHub repository contains the sources of 3 libraries implementing [IVOA](http://www.ivoa.net/ "International Virtual Observatory Alliance") standards and protocols:
This GitHub repository contains the sources of the library-set named VOLLT. It
contains 3 libraries implementing [IVOA](http://www.ivoa.net/ "International Virtual Observatory Alliance")
standards and protocols:
* [ADQL](http://www.ivoa.net/documents/latest/ADQL.html "Astronomical Data Query Language")
* [UWS-1.1](http://www.ivoa.net/documents/UWS/20161024/index.html "Universal Worker Service pattern")
* [TAP](http://www.ivoa.net/documents/TAP/ "Table Access Protocol")
_**NOTE:** Support of ADQL-2.1 currently under development. For the moment, TAP
is still using ADQL-2.0 by default._
### Documentation
For a complete documentation/tutorial and a demo of the 3 libraries you should visit the following websites: [ADQLTuto](http://cdsportal.u-strasbg.fr/adqltuto), [UWSTuto](http://cdsportal.u-strasbg.fr/uwstuto) and [TAPTuto](http://cdsportal.u-strasbg.fr/taptuto).
......@@ -40,7 +46,7 @@ Below are summed up the dependencies of each library:
| Package `cds.utils` | X | | X |
| Postgres JDBC Driver | X | | X |
| Package `uws` | | X | X |
| JSON library | | X | X |
| JSON library | | X | X |
| HTTP Servlet API | | X | X |
| HTTP Multipart Library | | X | X |
| Packages `cds.*` | | | X |
......
......@@ -2,7 +2,7 @@
<!DOCTYPE project>
<project name="adql" basedir="." default="buildLib">
<property name="version" value="1.5" />
<property name="version" value="2.0" />
<property name="srcDir" value="src" />
<property name="testDir" value="test" />
......
This diff is collapsed.
/* 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 SQL_RESERVED_WORD = 2;
/** RegularExpression Id. */
int LEFT_PAR = 3;
/** RegularExpression Id. */
int RIGHT_PAR = 4;
/** RegularExpression Id. */
int DOT = 5;
/** RegularExpression Id. */
int COMMA = 6;
/** RegularExpression Id. */
int EOQ = 7;
/** RegularExpression Id. */
int CONCAT = 8;
/** RegularExpression Id. */
int PLUS = 9;
/** RegularExpression Id. */
int MINUS = 10;
/** RegularExpression Id. */
int ASTERISK = 11;
/** RegularExpression Id. */
int DIVIDE = 12;
/** RegularExpression Id. */
int EQUAL = 13;
/** RegularExpression Id. */
int NOT_EQUAL = 14;
/** RegularExpression Id. */
int LESS_THAN = 15;
/** RegularExpression Id. */
int LESS_EQUAL_THAN = 16;
/** RegularExpression Id. */
int GREATER_THAN = 17;
/** RegularExpression Id. */
int GREATER_EQUAL_THAN = 18;
/** RegularExpression Id. */
int SELECT = 19;
/** RegularExpression Id. */
int QUANTIFIER = 20;
/** RegularExpression Id. */
int TOP = 21;
/** RegularExpression Id. */
int FROM = 22;
/** RegularExpression Id. */
int AS = 23;
/** RegularExpression Id. */
int NATURAL = 24;
/** RegularExpression Id. */
int INNER = 25;
/** RegularExpression Id. */
int OUTER = 26;
/** RegularExpression Id. */
int RIGHT = 27;
/** RegularExpression Id. */
int LEFT = 28;
/** RegularExpression Id. */
int FULL = 29;
/** RegularExpression Id. */
int JOIN = 30;
/** RegularExpression Id. */
int ON = 31;
/** RegularExpression Id. */
int USING = 32;
/** RegularExpression Id. */
int WHERE = 33;
/** RegularExpression Id. */
int AND = 34;
/** RegularExpression Id. */
int OR = 35;
/** RegularExpression Id. */
int NOT = 36;
/** RegularExpression Id. */
int IS = 37;
/** RegularExpression Id. */
int NULL = 38;
/** RegularExpression Id. */
int BETWEEN = 39;
/** RegularExpression Id. */
int LIKE = 40;
/** RegularExpression Id. */
int IN = 41;
/** RegularExpression Id. */
int EXISTS = 42;
/** RegularExpression Id. */
int BY = 43;
/** RegularExpression Id. */
int GROUP = 44;
/** RegularExpression Id. */
int HAVING = 45;
/** RegularExpression Id. */
int ORDER = 46;
/** RegularExpression Id. */
int ASC = 47;
/** RegularExpression Id. */
int DESC = 48;
/** RegularExpression Id. */
int AVG = 49;
/** RegularExpression Id. */
int MAX = 50;
/** RegularExpression Id. */
int MIN = 51;
/** RegularExpression Id. */
int SUM = 52;
/** RegularExpression Id. */
int COUNT = 53;
/** RegularExpression Id. */
int BOX = 54;
/** RegularExpression Id. */
int CENTROID = 55;
/** RegularExpression Id. */
int CIRCLE = 56;
/** RegularExpression Id. */
int POINT = 57;
/** RegularExpression Id. */
int POLYGON = 58;
/** RegularExpression Id. */
int REGION = 59;
/** RegularExpression Id. */
int CONTAINS = 60;
/** RegularExpression Id. */
int INTERSECTS = 61;
/** RegularExpression Id. */
int AREA = 62;
/** RegularExpression Id. */
int COORD1 = 63;
/** RegularExpression Id. */
int COORD2 = 64;
/** RegularExpression Id. */
int COORDSYS = 65;
/** RegularExpression Id. */
int DISTANCE = 66;
/** RegularExpression Id. */
int ABS = 67;
/** RegularExpression Id. */
int CEILING = 68;
/** RegularExpression Id. */
int DEGREES = 69;
/** RegularExpression Id. */
int EXP = 70;
/** RegularExpression Id. */
int FLOOR = 71;
/** RegularExpression Id. */
int LOG = 72;
/** RegularExpression Id. */
int LOG10 = 73;
/** RegularExpression Id. */
int MOD = 74;
/** RegularExpression Id. */
int PI = 75;
/** RegularExpression Id. */
int POWER = 76;
/** RegularExpression Id. */
int RADIANS = 77;
/** RegularExpression Id. */
int RAND = 78;
/** RegularExpression Id. */
int ROUND = 79;
/** RegularExpression Id. */
int SQRT = 80;
/** RegularExpression Id. */
int TRUNCATE = 81;
/** RegularExpression Id. */
int ACOS = 82;
/** RegularExpression Id. */
int ASIN = 83;
/** RegularExpression Id. */
int ATAN = 84;
/** RegularExpression Id. */
int ATAN2 = 85;
/** RegularExpression Id. */
int COS = 86;
/** RegularExpression Id. */
int COT = 87;
/** RegularExpression Id. */
int SIN = 88;
/** RegularExpression Id. */
int TAN = 89;
/** RegularExpression Id. */
int STRING_LITERAL = 93;
/** RegularExpression Id. */
int SCIENTIFIC_NUMBER = 94;
/** RegularExpression Id. */
int UNSIGNED_FLOAT = 95;
/** RegularExpression Id. */
int UNSIGNED_INTEGER = 96;
/** RegularExpression Id. */
int DIGIT = 97;
/** RegularExpression Id. */
int DELIMITED_IDENTIFIER = 100;
/** RegularExpression Id. */
int REGULAR_IDENTIFIER_CANDIDATE = 101;
/** RegularExpression Id. */
int Letter = 102;
/** Lexical state. */
int DEFAULT = 0;
/** Lexical state. */
int WithinString = 1;
/** Lexical state. */
int WithinDelimitedId = 2;
/** Literal token values. */
String[] tokenImage = {
"<EOF>",
"<token of kind 1>",
"<SQL_RESERVED_WORD>",
"\"(\"",
"\")\"",
"\".\"",
"\",\"",
"\";\"",
"\"||\"",
"\"+\"",
"\"-\"",
"\"*\"",
"\"/\"",
"\"=\"",
"<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\"",
"\"BY\"",
"\"GROUP\"",
"\"HAVING\"",
"\"ORDER\"",
"\"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 90>",
"\"\\\'\"",
"<token of kind 92>",
"\"\\\'\"",
"<SCIENTIFIC_NUMBER>",
"<UNSIGNED_FLOAT>",
"<UNSIGNED_INTEGER>",
"<DIGIT>",
"\"\\\"\"",
"<token of kind 99>",
"\"\\\"\"",
"<REGULAR_IDENTIFIER_CANDIDATE>",
"<Letter>",
};
}
package adql.parser;
import java.io.IOException;
import java.io.InputStream;
import adql.db.exception.UnresolvedIdentifiersException;
import adql.query.ADQLQuery;
import adql.translator.PostgreSQLTranslator;
import adql.translator.TranslationException;
/**
* Factory of ADQL parsers.
*
* <h3>ADQL versions</h3>
*
* <p>
* It is able to deal with all versions of the ADQL grammar supported by this
* library. All these versions are listed in the enumeration
* {@link ADQLVersion}.
* </p>
*
* <p>
* To create a such factory, an ADQL version must be provided. If none is
* given, the default one will be used (<i>see {@link #DEFAULT_VERSION}</i>).
* </p>
*
* <h3>Runnable class</h3>
*
* <p>
* This class includes a main function and thus, it can be executed directly.
* Its execution allows to parse an ADQL query. Then, in function of the passed
* parameters, it is possible to just check its syntax, translate it into SQL
* or try to fix the query.
* </p>
* <p><i>
* To get help about this program, just run it with the argument
* <code>-h</code> or <code>--help</code>.
* </i></p>
*
* @author Gr&eacute;gory Mantelet (CDS)
* @version 2.0 (04/2019)
* @since 2.0
*/
public class ADQLParserFactory {
/* **********************************************************************
* VERSION MANAGEMENT
* ********************************************************************** */
/**
* Enumeration of all supported versions of the ADQL grammar.
*
* @author Gr&eacute;gory Mantelet (CDS)
* @version 2.0 (04/2019)
* @since 2.0
*/
public static enum ADQLVersion {
/** Version REC-2.0 - <a href="http://www.ivoa.net/documents/cover/ADQL-20081030.html">http://www.ivoa.net/documents/cover/ADQL-20081030.html</a>. */
V2_0,
/** Version PR-2.1 - <a href="http://www.ivoa.net/documents/ADQL/20180112/index.html">http://www.ivoa.net/documents/ADQL/20180112/index.html</a>. */
V2_1; // TODO Move 2.1 as default when it becomes REC
@Override
public String toString() {
return name().toLowerCase().replace('_', '.');
}
/** TODO JUnit for ADQLVersion.parse(String)
* Parse the given string as an ADQL version number.
*
* <p>This function should work with the following syntaxes:</p>
* <ul>
* <li><code>2.0</code></li>
* <li><code>2_0</code></li>
* <li><code>v2.0</code> or <code>V2.0</code></li>
* <li><code>v2_0</code> or <code>V2_0</code></li>
* </ul>
*
* @param str String to parse as an ADQL version specification.
*
* @return The identified ADQL version.
*/
public static ADQLVersion parse(String str) {
if (str == null)
return null;
str = str.trim().toUpperCase();
if (str.isEmpty())
return null;
if (str.charAt(0) != 'V')
str = 'V' + str;
try {
return ADQLVersion.valueOf(str.replaceAll("\\.", "_"));
} catch(IllegalArgumentException iae) {
return null;
}
}
}
/** Version of the ADQL grammar to use when none is specified:
* {@link ADQLVersion#V2_0 2.0}. */
public final static ADQLVersion DEFAULT_VERSION = ADQLVersion.V2_0; // TODO Move 2.1 as default when it becomes REC
/**
* Get the list of all supported ADQL grammar versions.
*
* @return List of all supported ADQL versions.
*
* @see ADQLVersion#values()
*/
public static ADQLVersion[] getSupportedVersions() {
return ADQLVersion.values();
}
/**
* Build on the fly a human list of all supported ADQL grammar versions.
*
* <p><i><b>Example:</b> <code>v2.0</code>, <code>v2.1</code>.</i></p>
*
* @return List of all supported ADQL versions.
*/
public static String getSupportedVersionsAsString() {
StringBuilder buf = new StringBuilder();
for(ADQLVersion v : ADQLVersion.values()) {
if (buf.length() > 0)
buf.append(", ");
buf.append(v.toString());
if (v == DEFAULT_VERSION)
buf.append(" (default)");
}
return buf.toString();
}
/* **********************************************************************
* PARSER CREATION
* ********************************************************************** */
/**
* Builds a parser whose the query to parse will have to be given as a
* String in parameter of
* {@link ADQLParser#parseQuery(java.lang.String) parseQuery(String)}.
*/
public final ADQLParser createParser() {
return createParser(DEFAULT_VERSION);
}
/**
* Builds a parser whose the query to parse will have to be given as a
* String in parameter of
* {@link ADQLParser#parseQuery(java.lang.String) parseQuery(String)}.
*
* @param version Version of the ADQL grammar that the parser must
* implement.
* <i>If NULL, the {@link #DEFAULT_VERSION} will be used.</i>
*/
public ADQLParser createParser(ADQLVersion version) {
// Prevent the NULL value by setting the default version if necessary:
if (version == null)
version = DEFAULT_VERSION;
// Create the appropriate parser in function of the specified version:
switch(version) {
case V2_0:
return new ADQLParser200();
case V2_1:
default:
return new ADQLParser201();
}
}
/* **********************************************************************
* STATIC PARSER CREATION
* ********************************************************************** */
/** Factory to use only when a default parser is asked without any
* {@link ADQLParserFactory} instance.
* @see #createDefaultParser() */
private static volatile ADQLParserFactory defaultFactory = null;
/**
* Create an ADQL parser with the default ADQL grammar version (see
* {@link #DEFAULT_VERSION}).
*
* @return A new parser implementing the default version supported by this
* library.
*/
public final static ADQLParser createDefaultParser() {
// Create the default factory, if not already done:
if (defaultFactory == null) {
synchronized (ADQLParserFactory.class) {
if (defaultFactory == null)
defaultFactory = new ADQLParserFactory();
}
}
// Create a parser implementing the default version of the ADQL grammar:
return defaultFactory.createParser(DEFAULT_VERSION);
}
/* **********************************************************************
* MAIN FUNCTION
* ********************************************************************** */
/**
* Parses the given ADQL query.
*
* <p>The result of the parsing depends of the parameters:</p>
*
* <p>
* <b>ONLY the syntax is checked: the query is NOT EXECUTED !</b>
* </p>
*/
public static final void main(String[] args) throws Exception {
final String USAGE = "Usage:\n adqlParser.jar [-version=...] [-h] [-d] [-v] [-e] [-a|-s] [-f] [<FILE>|<URL>]\n\nNOTE: If no file or URL is given, the ADQL query is expected in the standard\n input. This query must end with a ';' or <Ctrl+D>!\n\nParameters:\n -version=... : Set the version of the ADQL grammar to follow.\n It must be one among: " + getSupportedVersionsAsString() + "\n -h or --help : Display this help.\n -v or --verbose : Print the main steps of the parsing\n -d or --debug : Print stack traces when a grave error occurs\n -e or --explain : Explain the ADQL parsing (or Expand the parsing tree)\n -a or --adql : Display the understood ADQL query\n -s or --sql : Ask the SQL translation of the given ADQL query\n (SQL compatible with PostgreSQL)\n -f or --try-fix : Try fixing the most common ADQL query issues before\n attempting to parse the query.\n\nReturn:\n By default: nothing if the query is correct. Otherwise a message explaining\n why the query is not correct is displayed.\n With the -s option, the SQL translation of the given ADQL query will be\n returned.\n With the -a option, the ADQL query is returned as it has been understood.\n\nExit status:\n 0 OK !\n 1 Parameter error (missing or incorrect parameter)\n 2 File error (incorrect file/url, reading error, ...)\n 3 Parsing error (syntactic or semantic error)\n 4 Translation error (a problem has occurred during the translation of the\n given ADQL query in SQL).";
final String NEED_HELP_MSG = "Try -h or --help to get more help about the usage of this program.";
final String urlRegex = "^(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]";
ADQLParser parser;
short mode = -1;
String file = null;
ADQLVersion version = DEFAULT_VERSION;
boolean verbose = false, debug = false, explain = false, tryFix = false;
// Parameters reading:
for(int i = 0; i < args.length; i++) {
if (args[i].startsWith("-version=")) {
String[] parts = args[i].split("=");
if (parts.length <= 1) {
System.err.println("((!)) Missing ADQL version! It must be one among: " + getSupportedVersionsAsString() + ". ((!))\n" + NEED_HELP_MSG);
System.exit(1);
}
String strVersion = parts[1].replaceAll("\\.", "_");
version = ADQLVersion.parse(strVersion);
if (version == null) {
System.err.println("((!)) Incorrect ADQL version: \"" + args[i].split("=")[1] + "\"! It must be one among: " + getSupportedVersionsAsString() + ". ((!))\n" + NEED_HELP_MSG);
System.exit(1);
}
} else 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" + NEED_HELP_MSG);
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" + NEED_HELP_MSG);
System.exit(1);
} else
mode = 2;
} else if (args[i].equalsIgnoreCase("-f") || args[i].equalsIgnoreCase("--try-fix"))
tryFix = true;
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" + NEED_HELP_MSG);
System.exit(1);
} else
file = args[i].trim();
}
try {
// Get the parser for the specified ADQL version:
parser = (new ADQLParserFactory()).createParser(version);
// Try fixing the query, if asked:
InputStream in = null;
if (tryFix) {
if (verbose)
System.out.println("((i)) Trying to automatically fix the query...");
String query;
try {
// get the input stream...
if (file == null || file.length() == 0)
in = System.in;
else if (file.matches(urlRegex))
in = (new java.net.URL(file)).openStream();
else
in = new java.io.FileInputStream(file);
// ...and try fixing the query:
query = parser.tryQuickFix(in);
} finally {
// close the stream (if opened):
if (in != null)
in.close();
in = null;
}
if (verbose)
System.out.println("((i)) SUGGESTED QUERY:\n" + query);
// Initialise the parser with this fixed query:
in = new java.io.ByteArrayInputStream(query.getBytes());
}
// Otherwise, take the query as provided:
else {
// Initialise the parser with the specified input:
if (file == null || file.length() == 0)
in = System.in;
else if (file.matches(urlRegex))
in = (new java.net.URL(file)).openStream();
else
in = new java.io.FileInputStream(file);
}
// Enable/Disable the debugging in function of the parameters:
parser.setDebug(explain);
// Query parsing:
try {
if (verbose)
System.out.print("((i)) Parsing ADQL query...");
ADQLQuery q = parser.parseQuery(in);
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);
}
}
}
This diff is collapsed.
This diff is collapsed.
......@@ -20,6 +20,11 @@
* - addition of a constructor with a TokenMgrError which adds a piece of
* advice to fix the token issue (see buildExpandedMessage(...))
*
* Modified by Gr&eacute;gory Mantelet (ARI), on April 2019
* Modifications:
* - change the way ADQL and SQL reserved keywords are identified for the
* generation of the appropriate HINT in the error message
*
* /!\ DO NOT RE-GENERATE THIS FILE /!\
* In case of re-generation, replace it by ParseException.java.backup (but maybe
* after a diff in case of significant modifications have been done by a new
......@@ -53,7 +58,7 @@ public class ParseException extends Exception {
* a new object of this type with the fields "currentToken",
* "expectedTokenSequences", and "tokenImage" set.
*/
public ParseException(Token currentTokenVal, int[][] expectedTokenSequencesVal, String[] tokenImageVal){
public ParseException(Token currentTokenVal, int[][] expectedTokenSequencesVal, String[] tokenImageVal) {
super(initialise(currentTokenVal, expectedTokenSequencesVal, tokenImageVal));
currentToken = currentTokenVal;
expectedTokenSequences = expectedTokenSequencesVal;
......@@ -72,25 +77,25 @@ public class ParseException extends Exception {
* these constructors.
*/
public ParseException(){
public ParseException() {
super();
}
/** Constructor with message. */
public ParseException(String message){
public ParseException(String message) {
super(message);
}
public ParseException(String message, TextPosition errorPosition){
public ParseException(String message, TextPosition errorPosition) {
this(message);
position = errorPosition;
}
public ParseException(TokenMgrError err){
public ParseException(TokenMgrError err) {
this(buildExpandedMessage(err), new TextPosition(err.getErrorLine(), err.getErrorColumn()));
}
private final static String buildExpandedMessage(final TokenMgrError err){
private final static String buildExpandedMessage(final TokenMgrError err) {
if (err.getMessage().indexOf("<EOF>") > 0)
return err.getMessage() + "! Possible cause: a string between single or double quotes which is never closed (solution: well...just close it!).";
else
......@@ -121,44 +126,12 @@ public class ParseException extends Exception {
/** Line in the ADQL query where the exception occurs. */
protected TextPosition position = null;
/** Regular expression listing all ADQL reserved words.
*
* <p><i>Note 1:
* This list is built NOT from the list given in the ADQL-2.0 standard,
* but from the collation of all words potentially used in an ADQL query
* (including standard function names).
* </i></p>
*
* <p><i>Note 2:
* This regular expression is only used to display an appropriate hint
* to the user in the error message if a such word is at the origin of
* the error. (see {@link #initialise(Token, int[][], String[])} for more
* details).
* </i></p> */
private final static String ADQL_RESERVED_WORDS_REGEX = "(ABS|ACOS|AREA|ASIN|ATAN|ATAN2|BOX|CEILING|CENTROID|CIRCLE|CONTAINS|COORD1|COORD2|COORDSYS|COS|DEGREES|DISTANCE|EXP|FLOOR|INTERSECTS|LOG|LOG10|MOD|PI|POINT|POLYGON|POWER|RADIANS|REGION|RAND|ROUND|SIN|SQRT|TOP|TAN|TRUNCATE|SELECT|TOP|DISTINCT|ALL|AS|COUNT|AVG|MAX|MIN|SUM|FROM|JOIN|CROSS|INNER|OUTER|LEFT|RIGHT|FULL|NATURAL|USING|ON|WHERE|IS|NOT|AND|OR|EXISTS|IN|LIKE|NULL|BETWEEN|ORDER|ASC|DESC|GROUP|BY|HAVING)";
/** Regular expression listing all SQL reserved words.
*
* <p><i>Note 1:
* This list is built from the list given in the ADQL-2.0 standard,
* after removal of all words potentially used in an ADQL query
* (see {@link #ADQL_RESERVED_WORDS_REGEX}).
* </i></p>
*
* <p><i>Note 2:
* This regular expression is only used to display an appropriate hint
* to the user in the error message if a such word is at the origin of
* the error. (see {@link #initialise(Token, int[][], String[])} for more
* details).
* </i></p> */
private final static String SQL_RESERVED_WORDS_REGEX = "(ABSOLUTE|ACTION|ADD|ALLOCATE|ALTER|ANY|ARE|ASSERTION|AT|AUTHORIZATION|BEGIN|BIT|BIT_LENGTH|BOTH|CASCADE|CASCADED|CASE|CAST|CATALOG|CHAR|CHARACTER|CHAR_LENGTH|CHARACTER_LENGTH|CHECK|CLOSE|COALESCE|COLLATE|COLLATION|COLUMN|COMMIT|CONNECT|CONNECTION|CONSTRAINT|CONSTRAINTS|CONTINUE|CONVERT|CORRESPONDING|CREATE|CURRENT|CURRENT_DATE|CURRENT_TIME|CURRENT_TIMESTAMP|CURRENT_USER|CURSOR|DATE|DAY|DEALLOCATE|DECIMAL|DECLARE|DEFAULT|DEFERRABLE|DEFERRED|DELETE|DESCRIBE|DESCRIPTOR|DIAGNOSTICS|DISCONNECT|DOMAIN|DOUBLE|DROP|ELSE|END|END-EXEC|ESCAPE|EXCEPT|EXCEPTION|EXEC|EXECUTE|EXTERNAL|EXTRACT|FALSE|FETCH|FIRST|FLOAT|FOR|FOREIGN|FOUND|GET|GLOBAL|GO|GOTO|GRANT|HOUR|IDENTITY|IMMEDIATE|INDICATOR|INITIALLY|INPUT|INSENSITIVE|INSERT|INT|INTEGER|INTERSECT|INTERVAL|INTO|ISOLATION|KEY|LANGUAGE|LAST|LEADING|LEVEL|LOCAL|LOWER|MATCH|MINUTE|MODULE|MONTH|NAMES|NATIONAL|NCHAR|NEXT|NO|NULLIF|NUMERIC|OCTET_LENGTH|OF|ONLY|OPEN|OPTION|OUTPUT|OVERLAPS|PAD|PARTIAL|POSITION|PRECISION|PREPARE|PRESERVE|PRIMARY|PRIOR|PRIVILEGES|PROCEDURE|PUBLIC|READ|REAL|REFERENCES|RELATIVE|RESTRICT|REVOKE|ROLLBACK|ROWS|SCHEMA|SCROLL|SECOND|SECTION|SESSION|SESSION_USER|SET|SIZE|SMALLINT|SOME|SPACE|SQL|SQLCODE|SQLERROR|SQLSTATE|SUBSTRING|SYSTEM_USER|TABLE|TEMPORARY|THEN|TIME|TIMESTAMP|TIMEZONE_HOUR|TIMEZONE_MINUTE|TO|TRAILING|TRANSACTION|TRANSLATE|TRANSLATION|TRIM|TRUE|UNION|UNIQUE|UNKNOWN|UPDATE|UPPER|USAGE|USER|VALUE|VALUES|VARCHAR|VARYING|VIEW|WHEN|WHENEVER|WITH|WORK|WRITE|YEAR|ZONE)";
/**
* Gets the position in the ADQL query of the token which generates this exception.
*
* @return Position or <code>null</code> if unknown.
*/
public final TextPosition getPosition(){
public final TextPosition getPosition() {
return position;
}
......@@ -169,16 +142,16 @@ public class ParseException extends Exception {
* from the parser) the correct error message
* gets displayed.
*/
private static String initialise(Token currentToken, int[][] expectedTokenSequences, String[] tokenImage){
private static String initialise(Token currentToken, int[][] expectedTokenSequences, String[] tokenImage) {
int maxSize = 0;
// Build the list of expected tokens:
StringBuffer expected = new StringBuffer();
for(int i = 0; i < expectedTokenSequences.length; i++){
if (maxSize < expectedTokenSequences[i].length){
for(int i = 0; i < expectedTokenSequences.length; i++) {
if (maxSize < expectedTokenSequences[i].length) {
maxSize = expectedTokenSequences[i].length;
}
for(int j = 0; j < expectedTokenSequences[i].length; j++){
for(int j = 0; j < expectedTokenSequences[i].length; j++) {
expected.append(tokenImage[expectedTokenSequences[i][j]]);
}
expected.append(" ");
......@@ -189,10 +162,10 @@ public class ParseException extends Exception {
msg.append(" Encountered \"");
Token tok = currentToken.next;
StringBuffer tokenName = new StringBuffer();
for(int i = 0; i < maxSize; i++){
for(int i = 0; i < maxSize; i++) {
if (i != 0)
tokenName.append(' ');
if (tok.kind == 0){
if (tok.kind == 0) {
tokenName.append(tokenImage[0]);
break;
}
......@@ -202,19 +175,21 @@ public class ParseException extends Exception {
msg.append(tokenName.toString()).append("\".");
// Append the expected tokens list:
if (expectedTokenSequences.length == 1){
if (expectedTokenSequences.length == 1) {
msg.append(" Was expecting: ");
}else{
} else {
msg.append(" Was expecting one of: ");
}
msg.append(expected);
// Append a hint about reserved words if it is one:
String word = tokenName.toString().trim();
if (word.toUpperCase().matches(ADQL_RESERVED_WORDS_REGEX))
msg.append(System.getProperty("line.separator", "\n")).append("(HINT: \"").append(word).append("\" is a reserved ADQL word. To use it as a column/table/schema name/alias, write it between double quotes.)");
else if (word.toUpperCase().matches(SQL_RESERVED_WORDS_REGEX))
msg.append(System.getProperty("line.separator", "\n")).append("(HINT: \"").append(word).append("\" is not supported in ADQL, but is however a reserved word. To use it as a column/table/schema name/alias, write it between double quotes.)");
if (maxSize == 1) {
tok = currentToken.next;
if (tok.adqlReserved)
msg.append(System.getProperty("line.separator", "\n")).append("(HINT: \"").append(tok.image).append("\" is a reserved ADQL word in " + currentToken.next.adqlVersion + ". To use it as a column/table/schema name/alias, write it between double quotes.)");
else if (tok.sqlReserved)
msg.append(System.getProperty("line.separator", "\n")).append("(HINT: \"").append(tok.image).append("\" is not supported in ADQL " + currentToken.next.adqlVersion + ", but is however a reserved word. To use it as a column/table/schema name/alias, write it between double quotes.)");
}
return msg.toString();
/*String eol = System.getProperty("line.separator", "\n");
......@@ -267,11 +242,11 @@ public class ParseException extends Exception {
* when these raw version cannot be used as part of an ASCII
* string literal.
*/
static String add_escapes(String str){
static String add_escapes(String str) {
StringBuffer retval = new StringBuffer();
char ch;
for(int i = 0; i < str.length(); i++){
switch(str.charAt(i)){
for(int i = 0; i < str.length(); i++) {
switch(str.charAt(i)) {
case 0:
continue;
case '\b':
......@@ -299,10 +274,10 @@ public class ParseException extends Exception {
retval.append("\\\\");
continue;
default:
if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e){
if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) {
String s = "0000" + Integer.toString(ch, 16);
retval.append("\\u" + s.substring(s.length() - 4, s.length()));
}else{
} else {
retval.append(ch);
}
continue;
......
......@@ -20,6 +20,11 @@
* - addition of a constructor with a TokenMgrError which adds a piece of
* advice to fix the token issue (see buildExpandedMessage(...))
*
* Modified by Gr&eacute;gory Mantelet (ARI), on April 2019
* Modifications:
* - change the way ADQL and SQL reserved keywords are identified for the
* generation of the appropriate HINT in the error message
*
* /!\ DO NOT RE-GENERATE THIS FILE /!\
* In case of re-generation, replace it by ParseException.java.backup (but maybe
* after a diff in case of significant modifications have been done by a new
......@@ -53,7 +58,7 @@ public class ParseException extends Exception {
* a new object of this type with the fields "currentToken",
* "expectedTokenSequences", and "tokenImage" set.
*/
public ParseException(Token currentTokenVal, int[][] expectedTokenSequencesVal, String[] tokenImageVal){
public ParseException(Token currentTokenVal, int[][] expectedTokenSequencesVal, String[] tokenImageVal) {
super(initialise(currentTokenVal, expectedTokenSequencesVal, tokenImageVal));
currentToken = currentTokenVal;
expectedTokenSequences = expectedTokenSequencesVal;
......@@ -72,25 +77,25 @@ public class ParseException extends Exception {
* these constructors.
*/
public ParseException(){
public ParseException() {
super();
}
/** Constructor with message. */
public ParseException(String message){
public ParseException(String message) {
super(message);
}
public ParseException(String message, TextPosition errorPosition){
public ParseException(String message, TextPosition errorPosition) {
this(message);
position = errorPosition;
}
public ParseException(TokenMgrError err){
public ParseException(TokenMgrError err) {
this(buildExpandedMessage(err), new TextPosition(err.getErrorLine(), err.getErrorColumn()));
}
private final static String buildExpandedMessage(final TokenMgrError err){
private final static String buildExpandedMessage(final TokenMgrError err) {
if (err.getMessage().indexOf("<EOF>") > 0)
return err.getMessage() + "! Possible cause: a string between single or double quotes which is never closed (solution: well...just close it!).";
else
......@@ -121,44 +126,12 @@ public class ParseException extends Exception {
/** Line in the ADQL query where the exception occurs. */
protected TextPosition position = null;
/** Regular expression listing all ADQL reserved words.
*
* <p><i>Note 1:
* This list is built NOT from the list given in the ADQL-2.0 standard,
* but from the collation of all words potentially used in an ADQL query
* (including standard function names).
* </i></p>
*
* <p><i>Note 2:
* This regular expression is only used to display an appropriate hint
* to the user in the error message if a such word is at the origin of
* the error. (see {@link #initialise(Token, int[][], String[])} for more
* details).
* </i></p> */
private final static String ADQL_RESERVED_WORDS_REGEX = "(ABS|ACOS|AREA|ASIN|ATAN|ATAN2|BOX|CEILING|CENTROID|CIRCLE|CONTAINS|COORD1|COORD2|COORDSYS|COS|DEGREES|DISTANCE|EXP|FLOOR|INTERSECTS|LOG|LOG10|MOD|PI|POINT|POLYGON|POWER|RADIANS|REGION|RAND|ROUND|SIN|SQRT|TOP|TAN|TRUNCATE|SELECT|TOP|DISTINCT|ALL|AS|COUNT|AVG|MAX|MIN|SUM|FROM|JOIN|CROSS|INNER|OUTER|LEFT|RIGHT|FULL|NATURAL|USING|ON|WHERE|IS|NOT|AND|OR|EXISTS|IN|LIKE|NULL|BETWEEN|ORDER|ASC|DESC|GROUP|BY|HAVING)";
/** Regular expression listing all SQL reserved words.
*
* <p><i>Note 1:
* This list is built from the list given in the ADQL-2.0 standard,
* after removal of all words potentially used in an ADQL query
* (see {@link #ADQL_RESERVED_WORDS_REGEX}).
* </i></p>
*
* <p><i>Note 2:
* This regular expression is only used to display an appropriate hint
* to the user in the error message if a such word is at the origin of
* the error. (see {@link #initialise(Token, int[][], String[])} for more
* details).
* </i></p> */
private final static String SQL_RESERVED_WORDS_REGEX = "(ABSOLUTE|ACTION|ADD|ALLOCATE|ALTER|ANY|ARE|ASSERTION|AT|AUTHORIZATION|BEGIN|BIT|BIT_LENGTH|BOTH|CASCADE|CASCADED|CASE|CAST|CATALOG|CHAR|CHARACTER|CHAR_LENGTH|CHARACTER_LENGTH|CHECK|CLOSE|COALESCE|COLLATE|COLLATION|COLUMN|COMMIT|CONNECT|CONNECTION|CONSTRAINT|CONSTRAINTS|CONTINUE|CONVERT|CORRESPONDING|CREATE|CURRENT|CURRENT_DATE|CURRENT_TIME|CURRENT_TIMESTAMP|CURRENT_USER|CURSOR|DATE|DAY|DEALLOCATE|DECIMAL|DECLARE|DEFAULT|DEFERRABLE|DEFERRED|DELETE|DESCRIBE|DESCRIPTOR|DIAGNOSTICS|DISCONNECT|DOMAIN|DOUBLE|DROP|ELSE|END|END-EXEC|ESCAPE|EXCEPT|EXCEPTION|EXEC|EXECUTE|EXTERNAL|EXTRACT|FALSE|FETCH|FIRST|FLOAT|FOR|FOREIGN|FOUND|GET|GLOBAL|GO|GOTO|GRANT|HOUR|IDENTITY|IMMEDIATE|INDICATOR|INITIALLY|INPUT|INSENSITIVE|INSERT|INT|INTEGER|INTERSECT|INTERVAL|INTO|ISOLATION|KEY|LANGUAGE|LAST|LEADING|LEVEL|LOCAL|LOWER|MATCH|MINUTE|MODULE|MONTH|NAMES|NATIONAL|NCHAR|NEXT|NO|NULLIF|NUMERIC|OCTET_LENGTH|OF|ONLY|OPEN|OPTION|OUTPUT|OVERLAPS|PAD|PARTIAL|POSITION|PRECISION|PREPARE|PRESERVE|PRIMARY|PRIOR|PRIVILEGES|PROCEDURE|PUBLIC|READ|REAL|REFERENCES|RELATIVE|RESTRICT|REVOKE|ROLLBACK|ROWS|SCHEMA|SCROLL|SECOND|SECTION|SESSION|SESSION_USER|SET|SIZE|SMALLINT|SOME|SPACE|SQL|SQLCODE|SQLERROR|SQLSTATE|SUBSTRING|SYSTEM_USER|TABLE|TEMPORARY|THEN|TIME|TIMESTAMP|TIMEZONE_HOUR|TIMEZONE_MINUTE|TO|TRAILING|TRANSACTION|TRANSLATE|TRANSLATION|TRIM|TRUE|UNION|UNIQUE|UNKNOWN|UPDATE|UPPER|USAGE|USER|VALUE|VALUES|VARCHAR|VARYING|VIEW|WHEN|WHENEVER|WITH|WORK|WRITE|YEAR|ZONE)";
/**
* Gets the position in the ADQL query of the token which generates this exception.
*
* @return Position or <code>null</code> if unknown.
*/
public final TextPosition getPosition(){
public final TextPosition getPosition() {
return position;
}
......@@ -169,16 +142,16 @@ public class ParseException extends Exception {
* from the parser) the correct error message
* gets displayed.
*/
private static String initialise(Token currentToken, int[][] expectedTokenSequences, String[] tokenImage){
private static String initialise(Token currentToken, int[][] expectedTokenSequences, String[] tokenImage) {
int maxSize = 0;
// Build the list of expected tokens:
StringBuffer expected = new StringBuffer();
for(int i = 0; i < expectedTokenSequences.length; i++){
if (maxSize < expectedTokenSequences[i].length){
for(int i = 0; i < expectedTokenSequences.length; i++) {
if (maxSize < expectedTokenSequences[i].length) {
maxSize = expectedTokenSequences[i].length;
}
for(int j = 0; j < expectedTokenSequences[i].length; j++){
for(int j = 0; j < expectedTokenSequences[i].length; j++) {
expected.append(tokenImage[expectedTokenSequences[i][j]]);
}
expected.append(" ");
......@@ -189,10 +162,10 @@ public class ParseException extends Exception {
msg.append(" Encountered \"");
Token tok = currentToken.next;
StringBuffer tokenName = new StringBuffer();
for(int i = 0; i < maxSize; i++){
for(int i = 0; i < maxSize; i++) {
if (i != 0)
tokenName.append(' ');
if (tok.kind == 0){
if (tok.kind == 0) {
tokenName.append(tokenImage[0]);
break;
}
......@@ -202,19 +175,21 @@ public class ParseException extends Exception {
msg.append(tokenName.toString()).append("\".");
// Append the expected tokens list:
if (expectedTokenSequences.length == 1){
if (expectedTokenSequences.length == 1) {
msg.append(" Was expecting: ");
}else{
} else {
msg.append(" Was expecting one of: ");
}
msg.append(expected);
// Append a hint about reserved words if it is one:
String word = tokenName.toString().trim();
if (word.toUpperCase().matches(ADQL_RESERVED_WORDS_REGEX))
msg.append(System.getProperty("line.separator", "\n")).append("(HINT: \"").append(word).append("\" is a reserved ADQL word. To use it as a column/table/schema name/alias, write it between double quotes.)");
else if (word.toUpperCase().matches(SQL_RESERVED_WORDS_REGEX))
msg.append(System.getProperty("line.separator", "\n")).append("(HINT: \"").append(word).append("\" is not supported in ADQL, but is however a reserved word. To use it as a column/table/schema name/alias, write it between double quotes.)");
if (maxSize == 1) {
tok = currentToken.next;
if (tok.adqlReserved)
msg.append(System.getProperty("line.separator", "\n")).append("(HINT: \"").append(tok.image).append("\" is a reserved ADQL word in " + currentToken.next.adqlVersion + ". To use it as a column/table/schema name/alias, write it between double quotes.)");
else if (tok.sqlReserved)
msg.append(System.getProperty("line.separator", "\n")).append("(HINT: \"").append(tok.image).append("\" is not supported in ADQL " + currentToken.next.adqlVersion + ", but is however a reserved word. To use it as a column/table/schema name/alias, write it between double quotes.)");
}
return msg.toString();
/*String eol = System.getProperty("line.separator", "\n");
......@@ -267,11 +242,11 @@ public class ParseException extends Exception {
* when these raw version cannot be used as part of an ASCII
* string literal.
*/
static String add_escapes(String str){
static String add_escapes(String str) {
StringBuffer retval = new StringBuffer();
char ch;
for(int i = 0; i < str.length(); i++){
switch(str.charAt(i)){
for(int i = 0; i < str.length(); i++) {
switch(str.charAt(i)) {
case 0:
continue;
case '\b':
......@@ -299,10 +274,10 @@ public class ParseException extends Exception {
retval.append("\\\\");
continue;
default:
if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e){
if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) {
String s = "0000" + Integer.toString(ch, 16);
retval.append("\\u" + s.substring(s.length() - 4, s.length()));
}else{
} else {
retval.append(ch);
}
continue;
......
/* Generated By:JavaCC: Do not edit this line. Token.java Version 6.0 */
/* JavaCCOptions:TOKEN_EXTENDS=,KEEP_LINE_COL=null,SUPPORT_CLASS_VISIBILITY_PUBLIC=true
*
* Modified by Gr&eacute;gory Mantelet (CDS), on March 2017
* Modification: addition of position (line and column) in the original text.
* /!\ DO NOT RE-GENERATE THIS FILE /!\
* In case of re-generation, replace it by
* Token.java.backup (but maybe after a diff
* in case of significant modifications have been done
* by a new version of JavaCC).
*
* Modified by Gr&eacute;gory Mantelet (CDS), on April 2019
* Modifications:
* - addition of the attributes: `adqlReserved`, `sqlReserved`,
* `adqlVersion` and `isFunctionName`
*
* /!\ DO NOT RE-GENERATE THIS FILE /!\
* In case of re-generation, replace it by Token.java.backup (but maybe
* after a diff in case of significant modifications have been done by a new
* version of JavaCC).
*/
package adql.parser;
import adql.parser.ADQLParserFactory.ADQLVersion;
/**
* Describes the input token stream.
*/
public class Token implements java.io.Serializable {
/**
......@@ -69,6 +72,57 @@ public class Token implements java.io.Serializable {
*/
public Token specialToken;
/**
* Indicate whether this token is a reserved ADQL function name.
*
* <p><i><b>Implementation note:</b>
* This piece of information is just used when attempting to fix an ADQL
* query.
* </i></p>
*
* @since 2.0
*/
public boolean isFunctionName = false;
/**
* Indicate whether this token is a reserved keyword in the current version
* of the ADQL language.
*
* <p><i><b>Implementation note:</b>
* This piece of information is just used to add a hint in
* ParseException's messages.
* </i></p>
*
* @since 2.0
*/
public boolean adqlReserved = false;
/**
* Indicate whether this token is a reserved keyword in the current version
* of the ADQL language because it is a valid keyword in SQL (and may have
* unexpected behavior if used because not explicitly defined in ADQL).
*
* <p><i><b>Implementation note:</b>
* This piece of information is just used to add a hint in
* ParseException's messages.
* </i></p>
*
* @since 2.0
*/
public boolean sqlReserved = false;
/**
* Give the version of the ADQL language.
*
* <p><i><b>Implementation note:</b>
* This piece of information is used just for more clarity in
* ParseException's messages.
* </i></p>
*
* @since 2.0
*/
public ADQLVersion adqlVersion = null;
/**
* An optional attribute value of the Token.
* Tokens which are not used as syntactic sugar will often contain
......@@ -77,26 +131,27 @@ public class Token implements java.io.Serializable {
* Any subclass of Token that actually wants to return a non-null value can
* override this method as appropriate.
*/
public Object getValue(){
public Object getValue() {
return null;
}
/**
* No-argument constructor
*/
public Token(){}
public Token() {
}
/**
* Constructs a new token for the specified Image.
*/
public Token(int kind){
public Token(int kind) {
this(kind, null);
}
/**
* Constructs a new token for the specified Image and Kind.
*/
public Token(int kind, String image){
public Token(int kind, String image) {
this.kind = kind;
this.image = image;
}
......@@ -105,7 +160,7 @@ public class Token implements java.io.Serializable {
* Returns the image.
*/
@Override
public String toString(){
public String toString() {
return image;
}
......@@ -121,14 +176,14 @@ public class Token implements java.io.Serializable {
* to the following switch statement. Then you can cast matchedToken
* variable to the appropriate type and use sit in your lexical actions.
*/
public static Token newToken(int ofKind, String image){
switch(ofKind){
public static Token newToken(int ofKind, String image) {
switch(ofKind) {
default:
return new Token(ofKind, image);
}
}
public static Token newToken(int ofKind){
public static Token newToken(int ofKind) {
return newToken(ofKind, null);
}
......
/* Generated By:JavaCC: Do not edit this line. Token.java Version 6.0 */
/* JavaCCOptions:TOKEN_EXTENDS=,KEEP_LINE_COL=null,SUPPORT_CLASS_VISIBILITY_PUBLIC=true
*
* Modified by Gr&eacute;gory Mantelet (CDS), on March 2017
* Modification: addition of position (line and column) in the original text.
* /!\ DO NOT RE-GENERATE THIS FILE /!\
* In case of re-generation, replace it by
* Token.java.backup (but maybe after a diff
* in case of significant modifications have been done
* by a new version of JavaCC).
*
* Modified by Gr&eacute;gory Mantelet (CDS), on April 2019
* Modifications:
* - addition of the attributes: `adqlReserved`, `sqlReserved`,
* `adqlVersion` and `isFunctionName`
*
* /!\ DO NOT RE-GENERATE THIS FILE /!\
* In case of re-generation, replace it by Token.java.backup (but maybe
* after a diff in case of significant modifications have been done by a new
* version of JavaCC).
*/
package adql.parser;
import adql.parser.ADQLParserFactory.ADQLVersion;
/**
* Describes the input token stream.
*/
public class Token implements java.io.Serializable {
/**
......@@ -69,6 +72,57 @@ public class Token implements java.io.Serializable {
*/
public Token specialToken;
/**
* Indicate whether this token is a reserved ADQL function name.
*
* <p><i><b>Implementation note:</b>
* This piece of information is just used when attempting to fix an ADQL
* query.
* </i></p>
*
* @since 2.0
*/
public boolean isFunctionName = false;
/**
* Indicate whether this token is a reserved keyword in the current version
* of the ADQL language.
*
* <p><i><b>Implementation note:</b>
* This piece of information is just used to add a hint in
* ParseException's messages.
* </i></p>
*
* @since 2.0
*/
public boolean adqlReserved = false;
/**
* Indicate whether this token is a reserved keyword in the current version
* of the ADQL language because it is a valid keyword in SQL (and may have
* unexpected behavior if used because not explicitly defined in ADQL).
*
* <p><i><b>Implementation note:</b>
* This piece of information is just used to add a hint in
* ParseException's messages.
* </i></p>
*
* @since 2.0
*/
public boolean sqlReserved = false;
/**
* Give the version of the ADQL language.
*
* <p><i><b>Implementation note:</b>
* This piece of information is used just for more clarity in
* ParseException's messages.
* </i></p>
*
* @since 2.0
*/
public ADQLVersion adqlVersion = null;
/**
* An optional attribute value of the Token.
* Tokens which are not used as syntactic sugar will often contain
......@@ -77,26 +131,27 @@ public class Token implements java.io.Serializable {
* Any subclass of Token that actually wants to return a non-null value can
* override this method as appropriate.
*/
public Object getValue(){
public Object getValue() {
return null;
}
/**
* No-argument constructor
*/
public Token(){}
public Token() {
}
/**
* Constructs a new token for the specified Image.
*/
public Token(int kind){
public Token(int kind) {
this(kind, null);
}
/**
* Constructs a new token for the specified Image and Kind.
*/
public Token(int kind, String image){
public Token(int kind, String image) {
this.kind = kind;
this.image = image;
}
......@@ -105,7 +160,7 @@ public class Token implements java.io.Serializable {
* Returns the image.
*/
@Override
public String toString(){
public String toString() {
return image;
}
......@@ -121,14 +176,14 @@ public class Token implements java.io.Serializable {
* to the following switch statement. Then you can cast matchedToken
* variable to the appropriate type and use sit in your lexical actions.
*/
public static Token newToken(int ofKind, String image){
switch(ofKind){
public static Token newToken(int ofKind, String image) {
switch(ofKind) {
default:
return new Token(ofKind, image);
}
}
public static Token newToken(int ofKind){
public static Token newToken(int ofKind) {
return newToken(ofKind, null);
}
......
......@@ -4,7 +4,9 @@
* Modified by Gr&eacute;gory Mantelet (CDS), on Sept. 2017
* Modifications:
* - addition of position (line and column) in the original text
* - display the incorrect character as a character instead of an integer
* - adapt the error message so that being more explicit for humans
* and display the incorrect character as a character instead of an
* integer value
*
* /!\ DO NOT RE-GENERATE THIS FILE /!\
* In case of re-generation, replace it by TokenMgrError.java.backup (but maybe
......
This diff is collapsed.
package adql.query.operand.function.string;
/*
* 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 2019 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
*/
import adql.query.ADQLObject;
import adql.query.operand.ADQLOperand;
import adql.query.operand.function.ADQLFunction;
/**
* It represents the LOWER function of ADQL.
*
* <p>This function put the given text in lower case.</p>
*
* <p><i><u>Example:</u><br/>
* <code>LOWER('Francis Albert Augustus Charles Emmanuel')</code><br/>
* which should return:<br/>
* <code>francis albert augustus charles emmanuel</code>
* </i></p>
*
* @author Gr&eacute;gory Mantelet (CDS)
* @version 2.0 (04/2019)
* @since 2.0
*/
public class LowerFunction extends ADQLFunction {
/** Constant name of this function. */
protected final String FCT_NAME = "LOWER";
/** The only parameter of this function. */
protected ADQLOperand strParam;
/**
* Builds a LOWER function with its parameter.
*
* @param param Parameter of LOWER.
* @throws NullPointerException If the given operand is <i>null</i>
* or if it's not a string parameter.
*/
public LowerFunction(final ADQLOperand strParam) {
if (strParam == null)
throw new NullPointerException("The function " + FCT_NAME + " must have one non-NULL parameter!");
if (!strParam.isString())
throw new NullPointerException("The ADQL function " + FCT_NAME + " must have one parameter of type VARCHAR (i.e. a String)!");
this.strParam = strParam;
}
@Override
public final boolean isNumeric() {
return false;
}
@Override
public final boolean isString() {
return true;
}
@Override
public final boolean isGeometry() {
return false;
}
@Override
public final String getName() {
return FCT_NAME;
}
@Override
public ADQLObject getCopy() throws Exception {
return new LowerFunction((ADQLOperand)(strParam.getCopy()));
}
@Override
public int getNbParameters() {
return 1;
}
@Override
public final ADQLOperand[] getParameters() {
return new ADQLOperand[]{ strParam };
}
@Override
public ADQLOperand getParameter(final int index) throws ArrayIndexOutOfBoundsException {
if (index == 0)
return strParam;
else
throw new ArrayIndexOutOfBoundsException("No " + index + "-th parameter for the function \"" + FCT_NAME + "\" !");
}
@Override
public ADQLOperand setParameter(final int index, final ADQLOperand replacer) throws ArrayIndexOutOfBoundsException, NullPointerException, Exception {
if (index == 0) {
ADQLOperand replaced = strParam;
if (replacer == null)
throw new NullPointerException("Missing the new parameter of the function \"" + toADQL() + "\"!");
else if (replacer.isString())
strParam = replacer;
else
throw new Exception("Impossible to replace a String parameter by a " + replacer.getClass().getName() + " (" + replacer.toADQL() + ") !");
setPosition(null);
return replaced;
} else
throw new ArrayIndexOutOfBoundsException("No " + index + "-th parameter for the function \"" + FCT_NAME + "\" !");
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment