package adql.example.parse;

import java.util.HashSet;

import adql.db.DBChecker;
import adql.db.DBTable;
import adql.db.exception.UnresolvedIdentifiersException;
import adql.parser.ADQLParser;
import adql.parser.ADQLParser.ADQLVersion;
import adql.parser.feature.FeatureSet;
import adql.parser.grammar.ParseException;
import adql.query.TextPosition;

/**
 * Examples and explanations on how to handle with different kinds of parsing
 * error.
 *
 * @author Gr&eacute;gory Mantelet (CDS)
 * @version 12/2023
 */
public class C_HandleParseException {

	public static void main(final String[] args) {

		////////////////////////////////////////////////////////////////////////
		//                    CASE 1/3: Simple error                          //
		////////////////////////////////////////////////////////////////////////

		// Input query (i.e. SELECT keyword missing):
		String QUERY = "name, ra || ' - ' || dec as \"Position\"\nFrom data\nWhere Contains(Point('ICRS', ra, dec), Circle('ICRS', 10, 5, 1)) = 1\nOrder By name;";
		System.out.println("\n##### QUERY #1 #####\n\n    " + QUERY.replaceAll("\n", "\n    "));

		try {

			// Parse the query:
			(new ADQLParser()).parseQuery(QUERY);

			System.err.println("((X)) This message should never be visible! ((X)");

		} catch(ParseException ex) {

			// Full name of the caught exception:
			final String CLASS = ex.getClass().getName();
			/*
			 * NOTES:
			 *   ParseException is the root type of all possible exceptions that
			 *   can possibly be generated by an ADQLParser. Special types of
			 *   ParseException are going to be demonstrated with other examples
			 *   below.
			 */

			// Position of the error inside the input ADQL query:
			final TextPosition POSITION = ex.getPosition();
			/*
			 * NOTES:
			 *   A TextPosition keeps the begin and end line and column numbers.
			 *   They can be got individually directly with the attributes:
			 *     - TextPosition.beginLine
			 *     - TextPosition.endLine
			 *     - TextPosition.beginColumn
			 *     - TextPosition.endColumn
			 *
			 *   TextPosition.toString() will conveniently print all these
			 *   information in a string like: `[l.1 c.5 - l.1 c.9]`.
			 */

			// Error message
			final String MESSAGE = ex.getMessage();
			/*
			 * NOTES:
			 *   A ParseException is still an error. So a message with
			 *   a human description of the error is also available.
			 */

			System.out.println("\n((X)) INCORRECT QUERY! " + CLASS + " ((X))\n" + POSITION + " " + MESSAGE);

		}

		////////////////////////////////////////////////////////////////////////
		//                  CASE 2/3: Unresolved identifiers                  //
		////////////////////////////////////////////////////////////////////////

		// Input query (i.e. SELECT keyword missing):
		QUERY = "SELECT name, ra || ' - ' || dec as \"Position\"\nFrom data\nWhere Contains(Point('ICRS', ra, dec), Circle('ICRS', 10, 5, 1)) = 1\nOrder By name;";
		System.out.println("\n##### QUERY #2 #####\n\n    " + QUERY.replaceAll("\n", "\n    "));

		try {

			/* Parse the query (with an empty list of DB tables
			 *  => no table/column can be resolved in any parsed query): */
			(new ADQLParser(null, new DBChecker(new HashSet<DBTable>(0)), null, null)).parseQuery(QUERY);

			System.err.println("\n((X)) This message should never be visible! ((X)");

		} catch(UnresolvedIdentifiersException ex) {

			/*
			 * NOTES:
			 *   UnresolvedIdentifiersException is the only extension of
			 *   ParseException able to represent more than one error at a time.
			 *
			 *   It is possible to count and to list these errors with:
			 *
			 *     - UnresolvedIdentifiersException.getNbErrors()
			 *
			 *     - UnresolvedIdentifiersException.getErrors()
			 *       or UnresolvedIdentifiersException.iterator()
			 *
			 *   As suggested by its name, this exception is very often used to
			 *   list unresolved table/column names.
			 */

			System.out.println("\n((X))) INCORRECT QUERY! Cause: " + ex.getNbErrors() + " unresolved identifiers! ((X))");
			for(ParseException pe : ex) {
				System.out.println("  - " + pe.getPosition() + " " + pe.getMessage());
			}

		} catch(ParseException ex) {
			System.out.println("\n((X)) INCORRECT QUERY! " + ex.getClass().getSimpleName() + " ((X))\n" + ex.getPosition() + " " + ex.getMessage());
		}

		////////////////////////////////////////////////////////////////////////
		//                  CASE 3/3: Unsupported features                    //
		////////////////////////////////////////////////////////////////////////

		// Input query:
		QUERY = "Select LOWER(name), MY_UDF('foo')\nFrom data;";
		System.out.println("\n##### QUERY #3 #####\n\n    " + QUERY.replaceAll("\n", "\n    "));

		try {

			/* Parse the query (as above, with an empty list of DB tables
			 *  BUT ALSO with absolutely no optional feature supported): */
			final ADQLParser parser = new ADQLParser(ADQLVersion.V2_1, new DBChecker(new HashSet<DBTable>(0)), null, new FeatureSet(false));
			parser.allowAnyUdf(false);
			parser.parseQuery(QUERY);

			System.err.println("\n((X)) This message should never be visible! ((X)");

		} catch(UnresolvedIdentifiersException ex) {

			/*
			 * NOTES:
			 *   Since ADQL-2.1, there is the notion of optional language
			 *   features.
			 *
			 *   Because several unsupported features may be used in a same
			 *   ADQL query, UnresolvedIdentifiersException is now also used to
			 *   list all of them.
			 *
			 *   However, unresolved identifiers and unsupported features can
			 *   not be raised in the same time. First, supported features are
			 *   tested. If no unsupported features are detected, then,
			 *   identifiers resolution are tested. So, if unsupported features
			 *   are detected, an UnresolvedIdentifiersException listing all of
			 *   them is immediately thrown, which prevent testing the
			 *   identifiers.
			 *
			 *   So, errors about support of language features always have the
			 *   priority (as illustrated in this example).
			 */

			System.out.println("\n((X))) INCORRECT QUERY! Cause: " + ex.getNbErrors() + " unsupported features! ((X))");
			for(ParseException pe : ex) {
				System.out.println("  - " + pe.getPosition() + " " + pe.getMessage());
			}

		} catch(ParseException ex) {
			System.out.println("\n((X)) INCORRECT QUERY! " + ex.getClass().getSimpleName() + " ((X))\n" + ex.getPosition() + " " + ex.getMessage());
		}

	}

}
