Commit c7bede57 authored by Grégory Mantelet's avatar Grégory Mantelet
Browse files

[ADQL] Support hexadecimal values (as numeric values).

parent 827554c6
Loading
Loading
Loading
Loading
+9 −2
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ package adql.parser.grammar;
import java.io.InputStream;
import java.util.Stack;

import adql.parser.ADQLParser.ADQLVersion;
import adql.parser.ADQLQueryFactory;
import adql.query.ADQLQuery;
import adql.query.TextPosition;
@@ -106,8 +107,14 @@ public abstract class ADQLGrammarBase implements ADQLGrammar {
	public final void testRegularIdentifier(final Token token) throws ParseException {
		if (token == null)
			throw new ParseException("Impossible to test whether NULL is a valid ADQL regular identifier!");
		else if (!isRegularIdentifier(token.image))
			throw new ParseException("Invalid ADQL regular identifier: \u005c"" + token.image + "\u005c"! If it aims to be a column/table name/alias, you should write it between double quotes.", new TextPosition(token));
		else if (!isRegularIdentifier(token.image)) {
			String message = "Invalid ADQL regular identifier: \u005c"" + token.image + "\u005c"!";
			if (getVersion() == ADQLVersion.V2_0 && token.image.matches("0[Xx][0-9a-fA-F]+"))
				message += " HINT: hexadecimal values are not supported in ADQL-2.0. You should change the grammar version of the ADQL parser to at least ADQL-2.1.";
			else
				message += " HINT: If it aims to be a column/table name/alias, you should write it between double quotes.";
			throw new ParseException(message, new TextPosition(token));
		}
	}

	/* **********************************************************************
+9 −5
Original line number Diff line number Diff line
@@ -396,6 +396,7 @@ 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>)+ >
|	< UNSIGNED_HEXADECIMAL: ("0""x" (<DIGIT> | ["a"-"f","A"-"F"])+) >
|	< #DIGIT: ["0"-"9"] >
}

@@ -783,8 +784,10 @@ StringConstant String(): {Token t, start=null; String str=""; StringConstant cst
NumericConstant UnsignedNumeric(): {Token t; NumericConstant cst;} {
	(t=<SCIENTIFIC_NUMBER>
	| t=<UNSIGNED_FLOAT>
	| t=<UNSIGNED_INTEGER>)
	{		try{
	| t=<UNSIGNED_INTEGER>
	| t=<UNSIGNED_HEXADECIMAL>)
	{
		try{
		  	cst = queryFactory.createNumericConstant(t.image);
			cst.setPosition(new TextPosition(t));
			return cst;
@@ -796,6 +799,7 @@ NumericConstant UnsignedNumeric(): {Token t; NumericConstant cst;} {

NumericConstant UnsignedFloat(): {Token t; NumericConstant cst;} {
	(t=<UNSIGNED_INTEGER>
	| t=<UNSIGNED_HEXADECIMAL>
	| t=<UNSIGNED_FLOAT>)
	{
		try{
+37 −4
Original line number Diff line number Diff line
@@ -30,7 +30,7 @@ import adql.query.TextPosition;
 * A numeric (integer, double, ...) constant.
 *
 * @author Gr&eacute;gory Mantelet (CDS;ARI)
 * @version 2.0 (07/2019)
 * @version 2.0 (08/2019)
 */
public class NumericConstant implements ADQLOperand {

@@ -117,8 +117,37 @@ public class NumericConstant implements ADQLOperand {
		return value;
	}

	/**
	 * Tell whether this numeric constant is written in an hexadecimal form.
	 *
	 * @return	<code>true</code> if written in hexadecimal,
	 *        	<code>false</code> otherwise.
	 *
	 * @since 2.0
	 */
	public final boolean isHexadecimal() {
		return isHexadecimal(value);
	}

	/**
	 * Tell whether the given string is an hexadecimal numeric.
	 *
	 * @param val	The string to test.
	 *
	 * @return	<code>true</code> if the given string is an hexadecimal value,
	 *        	<code>false</code> otherwise.
	 *
	 * @since 2.0
	 */
	protected boolean isHexadecimal(final String val) {
		return val.matches("0[Xx][0-9a-fA-F]+");
	}

	public double getNumericValue() {
		try {
			if (isHexadecimal())
				return Long.parseLong(value.substring(2), 16);
			else
				return Double.parseDouble(value);
		} catch(NumberFormatException nfe) {
			return Double.NaN;
@@ -175,8 +204,12 @@ public class NumericConstant implements ADQLOperand {
	 *                              	in a Double.
	 */
	public void setValue(String value, boolean checkNumeric) throws NumberFormatException {
		if (checkNumeric)
		if (checkNumeric) {
			if (isHexadecimal(value))
				Long.parseLong(value.substring(2), 16);
			else
				Double.parseDouble(value);
		}

		this.value = value;
	}
+8 −1
Original line number Diff line number Diff line
@@ -729,6 +729,13 @@ public abstract class JDBCTranslator implements ADQLTranslator {

	@Override
	public String translate(NumericConstant numConst) throws TranslationException {
		if (numConst.isHexadecimal()) {
			try {
				return "" + Long.parseLong(numConst.getValue().substring(2), 16);
			} catch(NumberFormatException nfe) {
				throw new TranslationException("Impossible to evaluate the given hexadecimal expression: \"" + numConst.getValue() + "\"!", nfe);
			}
		} else
			return numConst.getValue();
	}

+25 −0
Original line number Diff line number Diff line
@@ -51,6 +51,31 @@ public class TestADQLParser {
	public void tearDown() throws Exception {
	}

	@Test
	public void testHexadecimal() {

		// CASE: No hexadecimal in ADQL-2.0
		ADQLParser parser = new ADQLParser(ADQLVersion.V2_0);
		try {
			parser.parseQuery("SELECT 0xF FROM foo");
			fail("Hexadecimal values should not be allowed with ADQL-2.0!");
		} catch(Exception ex) {
			assertEquals(ParseException.class, ex.getClass());
			assertEquals("Invalid ADQL regular identifier: \"0xF\"! HINT: hexadecimal values are not supported in ADQL-2.0. You should change the grammar version of the ADQL parser to at least ADQL-2.1.", ex.getMessage());
		}

		// CASE: Hexadecimal allowed in ADQL-2.1
		parser = new ADQLParser(ADQLVersion.V2_1);
		try {
			assertEquals("SELECT 0xF\nFROM foo", parser.parseQuery("SELECT 0xF FROM foo").toADQL());
			assertEquals("SELECT 0xF*2\nFROM foo", parser.parseQuery("SELECT 0xF*2 FROM foo").toADQL());
			assertEquals("SELECT -0xF\nFROM foo", parser.parseQuery("SELECT -0xF FROM foo").toADQL());
		} catch(Exception ex) {
			ex.printStackTrace();
			fail("Unexpected error with valid hexadecimal values! (see console for more details)");
		}
	}

	@Test
	public void testOffset() {

Loading