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

[ADQL] Add a mechanism to declare ADQL features and to set which optional ones

are supported or not. All ADQLObject instances now have a new function allowing
to return a LanguageFeature describing the corresponding ADQL feature.

Only all geometric functions (e.g. POINT, CONTAINS) and the new ADQL-2.1
function `LOWER` are optional. By default, the ADQL-2.1 parser declare them as
supported. But their support can be changed at anytime thanks
ADQLParser.getSupportedFeatures() and ADQLParser.setSupportedFeatures(...).

In the current state, UDF declaration does not work anymore with the ADQL-2.1
parser. This will be fixed in a further commit. Besides, TAP has not yet been
adapted to use/configure the FeatureSet of the parser to use.

Anyway, the Javadoc of all geometric functions has been updated (so that being
conform to their description in ADQL-2.1) as well as the formatting of all
ADQLObjects (e.g. lines fitting on 80 characters, ...).
parent b20793a9
Loading
Loading
Loading
Loading
+64 −32
Original line number Diff line number Diff line
@@ -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 2012,2015 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
 * Copyright 2012-2019 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
 *                       Astronomisches Rechen Institut (ARI)
 */

@@ -27,10 +27,12 @@ import adql.db.DBChecker;
import adql.parser.ParseException;

/**
 * <p>
 * 	This exception is thrown by {@link DBChecker} when several columns or tables do not exist.
 * 	It lists several {@link ParseException} (either {@link UnresolvedColumnException} or {@link UnresolvedTableException}).
 * </p>
 * This exception is thrown by {@link DBChecker} when several columns, tables,
 * functions or even ADQL features do not exist. It lists several
 * {@link ParseException} (either {@link UnresolvedColumnException},
 * {@link UnresolvedTableException}, {@link UnresolvedFunctionException},
 * {@link UnresolvedJoinException} or {@link UnsupportedFeatureException}).
 *
 * <p>
 * 	Its message only tells the number of unresolved identifiers.
 * 	If you want to have more details about the position and the exact message of each exception, you just have to iterate
@@ -38,21 +40,51 @@ import adql.parser.ParseException;
 * </p>
 *
 * @author Gr&eacute;gory Mantelet (CDS;ARI)
 * @version 1.4 (06/2015)
 * @version 2.0 (07/2019)
 *
 * @see DBChecker
 */
public class UnresolvedIdentifiersException extends ParseException implements Iterable<ParseException> {
	private static final long serialVersionUID = 1L;

	/**
	 * Type of the unresolved/unsupported items listed in this exception.
	 *
	 * <i>
	 * <p><b>Examples:</b></p>
	 * <ul>
	 * 	<li><code>unresolved identifier</code> (default),</li>
	 * 	<li><code>unsupported expression</code></li>
	 * </ul>
	 * </i>
	 *
	 * @since 2.0 */
	protected final String itemNature;

	/** List of exceptions (one per unresolved identifier). */
	protected ArrayList<ParseException> exceptions;
	private String unresolvedIdentifiers = null;

	/**
	 * Build an empty {@link UnresolvedIdentifiersException} (that's to say: there is no unresolved identifier).
	 * Build an empty {@link UnresolvedIdentifiersException} (that's to say:
	 * there is no unresolved identifier).
	 */
	public UnresolvedIdentifiersException() {
		this(null);
	}

	/**
	 * Build an empty {@link UnresolvedIdentifiersException} (that's to say:
	 * there is no unresolved identifier).
	 *
	 * @param itemNature	Type of the unresolved items listed in this
	 *                     	exception. <i>By default:
	 *                     	<code>unresolved identifier</code>.</i>
	 *
	 * @since 2.0
	 */
	public UnresolvedIdentifiersException(final String itemNature) {
		this.itemNature = (itemNature == null || itemNature.trim().isEmpty() ? "unresolved identifier" : itemNature);
		exceptions = new ArrayList<ParseException>();
	}

@@ -127,7 +159,7 @@ public class UnresolvedIdentifiersException extends ParseException implements It
	@Override
	public String getMessage() {
		StringBuffer buf = new StringBuffer();
		buf.append(exceptions.size()).append(" unresolved identifiers").append(((unresolvedIdentifiers != null) ? (": " + unresolvedIdentifiers) : "")).append('!');
		buf.append(exceptions.size()).append(" " + itemNature + "s").append(((unresolvedIdentifiers != null) ? (": " + unresolvedIdentifiers) : "")).append('!');
		for(ParseException pe : exceptions)
			buf.append("\n  - ").append(pe.getMessage());
		return buf.toString();
+90 −0
Original line number Diff line number Diff line
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 2019 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
 */

import adql.parser.ParseException;
import adql.parser.feature.LanguageFeature;
import adql.query.ADQLObject;

/**
 * Exception thrown when an ADQL language feature is used while declared as not
 * supported.
 *
 * @author Gr&eacute;gory Mantelet (CDS)
 * @version 2.0 (07/2019)
 * @since 2.0
 */
public class UnsupportedFeatureException extends ParseException {
	private static final long serialVersionUID = 1L;

	/** Function which can not be resolved. */
	protected final ADQLObject unsupportedExpression;

	/**
	 * Build the exception with the given unsupported expression.
	 *
	 * <p>The error message will be automatically generated.</p>
	 *
	 * @param obj	[REQUIRED] The unsupported expression.
	 */
	public UnsupportedFeatureException(final ADQLObject obj) {
		this(obj, null);
	}

	/**
	 * Build the exception with the given message and the given unsupported
	 * expression.
	 *
	 * <p>
	 * 	If no message is provided, a default one will be generated by using the
	 * 	given ADQL expression.
	 * </p>
	 *
	 * @param obj		[REQUIRED] The unsupported expression.
	 * @param message	[OPTIONAL] Custom error message.
	 */
	public UnsupportedFeatureException(final ADQLObject obj, final String message) {
		super((message == null ? buildDefaultMessage(obj) : message), obj.getPosition());
		unsupportedExpression = obj;
	}

	protected static String buildDefaultMessage(final ADQLObject obj) {
		return "Unsupported ADQL feature: \"" + obj.getFeatureDescription().form + "\"" + (obj.getFeatureDescription().type == null ? "" : " (of type '" + obj.getFeatureDescription().type + "')") + "!";
	}

	/**
	 * Get the unsupported expression.
	 *
	 * @return	The unsupported expression
	 */
	public final ADQLObject getUnsupportedExpression() {
		return unsupportedExpression;
	}

	/**
	 * Get the description of the unsupported language feature.
	 *
	 * @return	The unresolved language feature.
	 */
	public final LanguageFeature getUnsupportedLanguageFeature() {
		return unsupportedExpression.getFeatureDescription();
	}

}
+8 −0
Original line number Diff line number Diff line
@@ -23,6 +23,8 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;

import adql.parser.ADQLParserFactory.ADQLVersion;
import adql.parser.feature.FeatureSet;
import adql.query.ADQLOrder;
import adql.query.ADQLQuery;
import adql.query.ClauseADQL;
@@ -45,6 +47,8 @@ public interface ADQLParser {
	 *                           GETTERS & SETTERS
	 * ********************************************************************** */

	public ADQLVersion getADQLVersion();

	public QueryChecker getQueryChecker();

	public void setQueryChecker(final QueryChecker checker);
@@ -53,6 +57,10 @@ public interface ADQLParser {

	public void setQueryFactory(final ADQLQueryFactory factory);

	public FeatureSet getSupportedFeatures();

	public void setSupportedFeatures(final FeatureSet features);

	/* **********************************************************************
	 *                         PARSING DEBUG FUNCTIONS
	 * ********************************************************************** */
+83 −63
Original line number Diff line number Diff line
@@ -26,8 +26,10 @@ import java.util.ArrayList;
import java.util.Stack;
import java.util.Vector;

import adql.parser.ADQLParserFactory.ADQLVersion;
import adql.parser.ADQLQueryFactory.JoinType;
import adql.parser.IdentifierItems.IdentifierItem;
import adql.parser.feature.FeatureSet;
import adql.query.ADQLOrder;
import adql.query.ADQLQuery;
import adql.query.ClauseADQL;
@@ -107,7 +109,7 @@ import adql.query.operand.function.geometry.PointFunction;
* @see ADQLQueryFactory
*
* @author Gr&eacute;gory Mantelet (CDS)
* @version 2.0 (04/2019)
* @version 2.0 (07/2019)
* @since 2.0
*/
public class ADQLParser200 implements ADQLParser, ADQLParser200Constants {
@@ -343,6 +345,11 @@ public class ADQLParser200 implements ADQLParser, ADQLParser200Constants {

	/* ADDITIONAL GETTERS & SETTERS */

	@Override
	public final ADQLVersion getADQLVersion() {
		return ADQLVersion.V2_0;
	}

	@Override
	public final void setDebug(boolean debug) {
		if (debug)
@@ -371,6 +378,19 @@ public class ADQLParser200 implements ADQLParser, ADQLParser200Constants {
		queryFactory = (factory != null) ? factory : (new ADQLQueryFactory());
	}

	@Override
	public final FeatureSet getSupportedFeatures() {
		FeatureSet languageFeatures = new FeatureSet();
		languageFeatures.unsupportAll();
		return languageFeatures;
	}

	@Override
	public final void setSupportedFeatures(final FeatureSet languageFeatures) {
		/* Nothing to do here for ADQL-2.0 because no optional feature can be
		* supported)! */
	}

	/* EXCEPTION HELPER FUNCTION */

	private final ParseException generateParseException(Exception ex) {
@@ -4408,60 +4428,6 @@ public class ADQLParser200 implements ADQLParser, ADQLParser200Constants {
		}
	}

	private boolean jj_3R_54() {
		Token xsp;
		xsp = jj_scanpos;
		if (jj_scan_token(25)) {
			jj_scanpos = xsp;
			if (jj_3R_73())
				return true;
		}
		return false;
	}

	private boolean jj_3R_44() {
		if (jj_scan_token(STRING_LITERAL))
			return true;
		return false;
	}

	private boolean jj_3R_23() {
		Token xsp;
		if (jj_3R_44())
			return true;
		while(true) {
			xsp = jj_scanpos;
			if (jj_3R_44()) {
				jj_scanpos = xsp;
				break;
			}
		}
		return false;
	}

	private boolean jj_3R_116() {
		if (jj_scan_token(LEFT))
			return true;
		return false;
	}

	private boolean jj_3R_74() {
		Token xsp;
		xsp = jj_scanpos;
		if (jj_3R_116()) {
			jj_scanpos = xsp;
			if (jj_3R_117()) {
				jj_scanpos = xsp;
				if (jj_3R_118())
					return true;
			}
		}
		xsp = jj_scanpos;
		if (jj_scan_token(26))
			jj_scanpos = xsp;
		return false;
	}

	private boolean jj_3_18() {
		if (jj_3R_16())
			return true;
@@ -5091,6 +5057,14 @@ public class ADQLParser200 implements ADQLParser, ADQLParser200Constants {
		return false;
	}

	private boolean jj_3R_52() {
		if (jj_scan_token(CONCAT))
			return true;
		if (jj_3R_36())
			return true;
		return false;
	}

	private boolean jj_3R_86() {
		if (jj_scan_token(DEGREES))
			return true;
@@ -5103,14 +5077,6 @@ public class ADQLParser200 implements ADQLParser, ADQLParser200Constants {
		return false;
	}

	private boolean jj_3R_52() {
		if (jj_scan_token(CONCAT))
			return true;
		if (jj_3R_36())
			return true;
		return false;
	}

	private boolean jj_3R_85() {
		if (jj_scan_token(CEILING))
			return true;
@@ -6135,6 +6101,60 @@ public class ADQLParser200 implements ADQLParser, ADQLParser200Constants {
		return false;
	}

	private boolean jj_3R_54() {
		Token xsp;
		xsp = jj_scanpos;
		if (jj_scan_token(25)) {
			jj_scanpos = xsp;
			if (jj_3R_73())
				return true;
		}
		return false;
	}

	private boolean jj_3R_44() {
		if (jj_scan_token(STRING_LITERAL))
			return true;
		return false;
	}

	private boolean jj_3R_23() {
		Token xsp;
		if (jj_3R_44())
			return true;
		while(true) {
			xsp = jj_scanpos;
			if (jj_3R_44()) {
				jj_scanpos = xsp;
				break;
			}
		}
		return false;
	}

	private boolean jj_3R_116() {
		if (jj_scan_token(LEFT))
			return true;
		return false;
	}

	private boolean jj_3R_74() {
		Token xsp;
		xsp = jj_scanpos;
		if (jj_3R_116()) {
			jj_scanpos = xsp;
			if (jj_3R_117()) {
				jj_scanpos = xsp;
				if (jj_3R_118())
					return true;
			}
		}
		xsp = jj_scanpos;
		if (jj_scan_token(26))
			jj_scanpos = xsp;
		return false;
	}

	/** Generated Token Manager. */
	public ADQLParser200TokenManager token_source;
	SimpleCharStream jj_input_stream;
+2 −0
Original line number Diff line number Diff line
@@ -27,8 +27,10 @@ import java.util.Collection;
import java.io.FileReader;
import java.io.IOException;
import adql.db.exception.UnresolvedIdentifiersException;
import adql.parser.ADQLParserFactory.ADQLVersion;
import adql.parser.IdentifierItems.IdentifierItem;
import adql.parser.ADQLQueryFactory.JoinType;
import adql.parser.feature.FeatureSet;
import adql.query.*;
import adql.query.from.*;
import adql.query.constraint.*;
Loading