META-INF/0000755000175000017500000000000013245022474010707 5ustar olesolesMETA-INF/MANIFEST.MF0000644000175000017500000000015013245022472012333 0ustar olesolesManifest-Version: 1.0 Ant-Version: Apache Ant 1.9.6 Created-By: 1.7.0_151-b01 (Oracle Corporation) src/0000755000175000017500000000000013244760406010341 5ustar olesolessrc/COPYING.LESSER0000644000175000017500000001674313177122310012372 0ustar olesoles GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. src/cds/0000755000175000017500000000000013177122310011101 5ustar olesolessrc/cds/utils/0000755000175000017500000000000013177122310012241 5ustar olesolessrc/cds/utils/TextualSearchList.java0000644000175000017500000003717513177122310016531 0ustar olesolespackage cds.utils; /* * 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 . * * Copyright 2012-2017 - UDS/Centre de Données astronomiques de Strasbourg (CDS), * Astronomisches Rechen Institut (ARI) */ import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; /** *

A TextualSearchList is an {@link ArrayList} with a textual search capability.

*

* The interest of this class lies in the fact that objects can be searched with * or without case sensitivity on their textual key thanks to {@link #get(String, boolean)}. *

*

* The textual key is extracted by an object implementing the {@link KeyExtractor} instance. * If no {@link KeyExtractor} instance is given at initialization, the string returned * by the {@link Object#toString() toString()} function will be used as key. *

*

WARNING: The extracted key MUST be CASE-SENSITIVE and UNIQUE !

* * @param Type of object to manage in this list. * @author Grégory Mantelet (CDS;ARI) * @version 1.4 (09/2017) */ public class TextualSearchList< E > extends ArrayList { private static final long serialVersionUID = 1L; /** Object to use to extract an unique textual string. */ public final KeyExtractor keyExtractor; /** Map which associates objects of type E with its textual string (case-sensitive). */ protected final HashMap> csMap; /** Map which associates objects of type E with their lower-case textual string. */ protected final HashMap> ncsMap; /* ************ */ /* CONSTRUCTORS */ /* ************ */ /** *

Builds an empty TextualSearchList.

* *

Note: * the key of inserted objects will be the string returned by their {@link Object#toString() toString()} function. *

* * @see #TextualSearchList(KeyExtractor) */ public TextualSearchList(){ this(new DefaultKeyExtractor()); } /** * Builds an empty TextualSearchList. * * @param keyExtractor The object to use to extract a textual key from objects to insert. * * @see ArrayList#ArrayList() */ public TextualSearchList(final KeyExtractor keyExtractor){ super(); this.keyExtractor = keyExtractor; csMap = new HashMap>(); ncsMap = new HashMap>(); } /** *

Builds an empty TextualSearchList with an initial capacity.

* *

Note: * the key of inserted objects will be the string returned by their {@link Object#toString() toString()} function. *

* * @param initialCapacity Initial capacity of this list. * * @see #TextualSearchList(int, KeyExtractor) */ public TextualSearchList(int initialCapacity){ this(initialCapacity, new DefaultKeyExtractor()); } /** * Builds an empty TextualSearchList with an initial capacity. * * @param initialCapacity Initial capacity of this list. * @param keyExtractor The object to use to extract a textual key from objects to insert. * * @see ArrayList#ArrayList(int) */ public TextualSearchList(final int initialCapacity, final KeyExtractor keyExtractor){ super(initialCapacity); this.keyExtractor = keyExtractor; csMap = new HashMap>(initialCapacity); ncsMap = new HashMap>(initialCapacity); } /** *

Builds a TextualSearchList filled with the objects of the given collection.

* *

Note: * the key of inserted objects will be the string returned by their {@link Object#toString() toString()} function. *

* * @param c Collection to copy into this list. */ public TextualSearchList(Collection c){ this(c, new DefaultKeyExtractor()); } /** * Builds a TextualSearchList filled with the objects of the given collection. * * @param c Collection to copy into this list. * @param keyExtractor The object object to use to extract a textual key from objects to insert. * * @see #addAll(Collection) */ public TextualSearchList(Collection c, final KeyExtractor keyExtractor){ this.keyExtractor = keyExtractor; csMap = new HashMap>(c.size()); ncsMap = new HashMap>(c.size()); addAll(c); } /** * Returns true if this list contains the specified element. * More formally, returns true if and only if this list contains at least one element * e such that (keyExtractor.getKey(o).equals(keyExtractor.getKey(e))). * * @see java.util.ArrayList#contains(java.lang.Object) * @see #getKey(Object) * * @since 1.1 */ @SuppressWarnings("unchecked") @Override public boolean contains(Object o){ try{ if (o == null) return false; else{ E object = (E)o; return !get(getKey(object)).isEmpty(); } }catch(Exception e){ return false; } } /** * Searches (CASE-INSENSITIVE) the object which has the given key. * * @param key Textual key of the object to search. * * @return The corresponding object or null. */ public final List get(final String key){ return get(key, false); } /** * Searches of all the object which has the given key. * * @param key Textual key of the object to search. * @param caseSensitive true to consider the case of the key, false otherwise. * * @return All the objects whose the key is the same as the given one. */ @SuppressWarnings("unchecked") public List get(final String key, final boolean caseSensitive){ if (key == null) return new ArrayList(0); ArrayList founds = caseSensitive ? csMap.get(key) : ncsMap.get(key.toLowerCase()); if (founds == null) return new ArrayList(0); else return (ArrayList)founds.clone(); } /** * Generates and checks the key of the given object. * * @param value The object whose the key must be generated. * * @return Its corresponding key or null if this object already exist in this list. * * @throws NullPointerException If the given object or its extracted key is null. * @throws IllegalArgumentException If the extracted key is already used by another object in this list. */ private final String getKey(final E value) throws NullPointerException, IllegalArgumentException{ String key = keyExtractor.getKey(value); if (key == null) throw new NullPointerException("Null keys are not allowed in a TextualSearchList !"); return key; } /** * Adds the given object in the two maps with the given key. * * @param key The key with which the given object must be associated. * @param value The object to add. */ private final void putIntoMaps(final String key, final E value){ // update the case-sensitive map: putIntoMap(csMap, key, value); // update the case-INsensitive map: putIntoMap(ncsMap, key.toLowerCase(), value); } /** * Adds the given object in the given map with the given key. * * @param map The map in which the given value must be added. * @param key The key with which the given object must be associated. * @param value The object to add. * * @param The type of objects managed in the given map. */ private static final < E > void putIntoMap(final HashMap> map, final String key, final E value){ ArrayList lst = map.get(key); if (lst == null){ lst = new ArrayList(); lst.add(value); map.put(key, lst); }else lst.add(value); } /** * Adds the given object at the end of this list. * * @param obj Object to add (different from NULL). * * @throws NullPointerException If the given object or its extracted key is null. * @throws IllegalArgumentException If the extracted key is already used by another object in this list. * * @see java.util.ArrayList#add(java.lang.Object) */ @Override public boolean add(E obj) throws NullPointerException, IllegalArgumentException{ if (obj == null) throw new NullPointerException("Null objects are not allowed in a TextualSearchList !"); String key = getKey(obj); if (key == null) return false; if (super.add(obj)){ putIntoMaps(key, obj); return true; }else return false; } /** * Adds the given object at the given position in this list. * * @param index Index at which the given object must be added. * @param obj Object to add (different from NULL). * * @throws NullPointerException If the given object or its extracted key is null. * @throws IllegalArgumentException If the extracted key is already used by another object in this list. * @throws IndexOutOfBoundsException If the given index is negative or greater than the size of this list. * * @see java.util.ArrayList#add(int, java.lang.Object) */ @Override public void add(int index, E obj) throws NullPointerException, IllegalArgumentException, IndexOutOfBoundsException{ if (obj == null) throw new NullPointerException("Null objects are not allowed in a TextualSearchList !"); String key = getKey(obj); if (key == null) return; super.add(index, obj); if (get(index).equals(obj)) putIntoMaps(key, obj); } /** * Appends all the objects of the given collection in this list. * * @param c Collection of objects to add. * * @return true if this list changed as a result of the call, false otherwise. * * @throws NullPointerException If an object to add or its extracted key is null. * @throws IllegalArgumentException If the extracted key is already used by another object in this list. * * @see java.util.ArrayList#addAll(java.util.Collection) * @see #add(Object) */ @Override public boolean addAll(Collection c) throws NullPointerException, IllegalArgumentException{ if (c == null) return false; boolean modified = false; for(E obj : c) modified = add(obj) || modified; return modified; } /** * Appends all the objects of the given collection in this list after the given position. * * @param index Position from which objects of the given collection must be added. * @param c Collection of objects to add. * * @return true if this list changed as a result of the call, false otherwise. * * @throws NullPointerException If an object to add or its extracted key is null. * @throws IllegalArgumentException If the extracted key is already used by another object in this list. * @throws IndexOutOfBoundsException If the given index is negative or greater than the size of this list. * * @see java.util.ArrayList#addAll(int, java.util.Collection) * @see #add(int, Object) */ @Override public boolean addAll(int index, Collection c) throws NullPointerException, IllegalArgumentException, IndexOutOfBoundsException{ if (c == null) return false; boolean modified = false; int ind = index; for(E obj : c){ add(ind++, obj); modified = get(ind).equals(obj); } return modified; } /** * Replaces the element at the specified position in this list with the specified element. * * @param index Position of the object to replace. * @param obj Object to be stored at the given position (different from NULL). * * @return Replaced object. * * @throws NullPointerException If the object to add or its extracted key is null. * @throws IllegalArgumentException If the extracted key is already used by another object in this list. * @throws IndexOutOfBoundsException If the given index is negative or greater than the size of this list. * * @see java.util.ArrayList#set(int, java.lang.Object) */ @Override public E set(int index, E obj) throws NullPointerException, IllegalArgumentException{ if (obj == null) throw new NullPointerException("Null objects are not allowed in a TextualSearchList !"); if (get(index).equals(obj)) return obj; String key = getKey(obj); E old = super.set(index, obj); // Removes the old object from the index: String oldKey = keyExtractor.getKey(old); removeFromMaps(oldKey, old); // Adds the new object into the index: putIntoMaps(key, obj); return old; } @Override public void clear(){ super.clear(); csMap.clear(); ncsMap.clear(); } /** * Removes the given object associated with the given key from the two maps. * * @param key The key associated with the given object. * @param value The object to remove. */ private final void removeFromMaps(final String key, final E value){ // update the case-sensitive map: removeFromMap(csMap, key, value); // update the case-insensitive map: removeFromMap(ncsMap, key.toLowerCase(), value); } /** * Removes the given object associated with the given key from the given map. * * @param map The map from which the given object must be removed. * @param key The key associated with the given object. * @param value The object to remove. * * @param The type of objects managed in the given map. */ private static final < E > void removeFromMap(final HashMap> map, final String key, final E value){ List lst = map.get(key); if (lst != null){ lst.remove(value); if (lst.isEmpty()) map.remove(key); } } @Override public E remove(int index){ E removed = super.remove(index); if (removed != null){ String key = keyExtractor.getKey(removed); removeFromMaps(key, removed); } return removed; } @SuppressWarnings("unchecked") @Override public boolean remove(Object obj){ boolean removed = super.remove(obj); if (removed){ String key = keyExtractor.getKey((E)obj); removeFromMaps(key, (E)obj); } return removed; } @Override protected void removeRange(int fromIndex, int toIndex) throws IndexOutOfBoundsException{ if (fromIndex < 0 || fromIndex >= size() || toIndex < 0 || toIndex >= size() || fromIndex > toIndex) throw new IndexOutOfBoundsException("Incorrect range indexes: from " + fromIndex + " to " + toIndex + " !"); for(int i = fromIndex; i < toIndex; i++) remove(i); } /* ************************************************ */ /* KEY_EXTRACTOR INTERFACE & DEFAULT IMPLEMENTATION */ /* ************************************************ */ /** * Lets extract an unique textual key (case-sensitive) from a given type of object. * * @author Gégory Mantelet (CDS) * @param Type of object from which a textual key must be extracted. * @see TextualSearchList */ public static interface KeyExtractor< E > { /** * Extracts an UNIQUE textual key (case-sensitive) from the given object. * @param obj Object from which a textual key must be extracted. * @return Its textual key (case-sensitive). */ public String getKey(final E obj); } /** * Default implementation of {@link KeyExtractor}. * The extracted key is the string returned by the {@link Object#toString() toString()} function. * * @author Grégory Mantelet (CDS) * @param Type of object from which a textual key must be extracted. */ protected static class DefaultKeyExtractor< E > implements KeyExtractor { @Override public String getKey(final E obj){ return obj.toString(); } } } src/adql/0000755000175000017500000000000013177122310011251 5ustar olesolessrc/adql/db/0000755000175000017500000000000013210016772011640 5ustar olesolessrc/adql/db/exception/0000755000175000017500000000000013177122310013634 5ustar olesolessrc/adql/db/exception/UnresolvedColumnException.java0000644000175000017500000000604713177122310021671 0ustar olesolespackage 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 . * * Copyright 2012 - UDS/Centre de Données astronomiques de Strasbourg (CDS) */ import adql.db.DBChecker; import adql.parser.ParseException; import adql.query.operand.ADQLColumn; /** * This exception is thrown by {@link DBChecker} when a column does not exist * or whose the table reference is ambiguous. * * @author Grégory Mantelet (CDS) * @version 08/2011 * * @see DBChecker */ public class UnresolvedColumnException extends ParseException { private static final long serialVersionUID = 1L; private final String columnName; /* ************ */ /* CONSTRUCTORS */ /* ************ */ /** *

UNKNOWN COLUMN

* *

Builds the exception with an {@link ADQLColumn} which does not exist.

* * @param c The unresolved {@link ADQLColumn}. */ public UnresolvedColumnException(ADQLColumn c){ super(buildMessage("Unknown column", c)); initPosition(c); columnName = (c != null) ? c.getColumnName() : null; } /** *

AMBIGUOUS COLUMN NAME

* *

* Builds the exception with an {@link ADQLColumn} which does not have a table reference AND which may come from more than one table * OR with an {@link ADQLColumn} which may reference more than one column in the table. *

* * @param c The ambiguous {@link ADQLColumn}. * @param col1 First possibility. * @param col2 A second possibility. */ public UnresolvedColumnException(ADQLColumn c, String col1, String col2){ super(buildMessage("Ambiguous column name", c, col1, col2)); initPosition(c); columnName = (c != null) ? c.getColumnName() : null; } protected final void initPosition(final ADQLColumn c){ position = c.getPosition(); } public final String getColumnName(){ return columnName; } private static final String buildMessage(String msgStart, ADQLColumn c){ StringBuffer msg = new StringBuffer(); msg.append(msgStart).append(" \"").append(c.getFullColumnName()).append("\" !"); return msg.toString(); } private static final String buildMessage(String msgStart, ADQLColumn c, String col1, String col2){ if (col1 != null && col2 != null){ StringBuffer msg = new StringBuffer(buildMessage(msgStart, c)); msg.append(" It may be (at least) \"").append(col1).append("\" or \"").append(col2).append("\"."); return msg.toString(); }else return buildMessage(msgStart, c); } } src/adql/db/exception/UnresolvedJoinException.java0000644000175000017500000000416413177122310021331 0ustar olesolespackage 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 . * * Copyright 2013-2015 - Astronomisches Rechen Institut (ARI) */ import adql.parser.ParseException; import adql.query.TextPosition; /** * This exception is thrown when a table between 2 tables can not be resolved, * and particularly because of the join condition (i.e. column names not found, ...). * * @author Grégory Mantelet (ARI) * @version 1.4 (06/2015) * @since 1.2 */ public class UnresolvedJoinException extends ParseException { private static final long serialVersionUID = 1L; /** * Build a simple UnresolvedJoin. * It is generally used when a column can not be resolved (linked to one of the joined tables). * * @param message Message to display explaining why the join can't be resolved. */ public UnresolvedJoinException(String message){ super(message); } /** * Build an UnresolvedJoin and specify, in addition of the error message, the position of the column not resolved. * * @param message Message to display explaining why the join can't be resolved. * @param errorPosition Position of the wrong part of the join. */ public UnresolvedJoinException(String message, TextPosition errorPosition){ super(message, errorPosition); } /** * Set the position of the invalid JOIN. * * @param pos Position of the concerned JOIN inside the ADQL query. * * @since 1.4 */ public void setPosition(final TextPosition pos){ this.position = pos; } } src/adql/db/exception/UnresolvedIdentifiersException.java0000644000175000017500000001117013177122310022672 0ustar olesolespackage 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 . * * Copyright 2012,2015 - UDS/Centre de Données astronomiques de Strasbourg (CDS), * Astronomisches Rechen Institut (ARI) */ import java.util.ArrayList; import java.util.Iterator; import adql.db.DBChecker; import adql.parser.ParseException; /** *

* 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}). *

*

* 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 * on this {@link UnresolvedIdentifiersException} (method {@link #iterator()}). *

* * @author Grégory Mantelet (CDS;ARI) * @version 1.4 (06/2015) * * @see DBChecker */ public class UnresolvedIdentifiersException extends ParseException implements Iterable { private static final long serialVersionUID = 1L; /** List of exceptions (one per unresolved identifier). */ protected ArrayList exceptions; private String unresolvedIdentifiers = null; /** * Build an empty {@link UnresolvedIdentifiersException} (that's to say: there is no unresolved identifier). */ public UnresolvedIdentifiersException(){ exceptions = new ArrayList(); } /** * Adds a {@link ParseException} (supposed to be either an {@link UnresolvedColumnException} or an {@link UnresolvedTableException}). * * @param pe An exception. */ public final void addException(final ParseException pe){ if (pe != null){ exceptions.add(pe); if (pe instanceof UnresolvedColumnException){ String colName = ((UnresolvedColumnException)pe).getColumnName(); if (colName != null && colName.trim().length() > 0) addIdentifierName(colName + " " + pe.getPosition()); }else if (pe instanceof UnresolvedTableException){ String tableName = ((UnresolvedTableException)pe).getTableName(); if (tableName != null && tableName.trim().length() > 0) addIdentifierName(tableName + " " + pe.getPosition()); }else if (pe instanceof UnresolvedFunctionException){ String fctName = (((UnresolvedFunctionException)pe).getFunction() == null) ? null : ((UnresolvedFunctionException)pe).getFunction().getName() + "(...)"; if (fctName != null && fctName.trim().length() > 0) addIdentifierName(fctName + " " + pe.getPosition()); }else if (pe instanceof UnresolvedIdentifiersException) addIdentifierName(((UnresolvedIdentifiersException)pe).unresolvedIdentifiers); } } /** * Adds the name (or the description) into the string list of all the unresolved identifiers. * * @param name Name (or description) of the identifier to add. */ private final void addIdentifierName(final String name){ if (name != null && name.trim().length() > 0){ if (unresolvedIdentifiers == null) unresolvedIdentifiers = ""; else unresolvedIdentifiers += ", "; unresolvedIdentifiers += name; } } /** * Gets the number of unresolved identifiers. * * @return The number of unresolved identifiers. */ public final int getNbErrors(){ return exceptions.size(); } /** * Gets the list of all errors. * * @return Errors list. */ public final Iterator getErrors(){ return exceptions.iterator(); } @Override public final Iterator iterator(){ return getErrors(); } /** * Only tells how many identifiers have not been resolved. * * @see java.lang.Throwable#getMessage() */ @Override public String getMessage(){ StringBuffer buf = new StringBuffer(); buf.append(exceptions.size()).append(" unresolved identifiers").append(((unresolvedIdentifiers != null) ? (": " + unresolvedIdentifiers) : "")).append('!'); for(ParseException pe : exceptions) buf.append("\n - ").append(pe.getMessage()); return buf.toString(); } } src/adql/db/exception/UnresolvedFunctionException.java0000644000175000017500000001064513177122310022220 0ustar olesolespackage 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 . * * Copyright 2014-2015 - Astronomisches Rechen Institut (ARI) */ import adql.parser.ParseException; import adql.query.TextPosition; import adql.query.operand.function.ADQLFunction; /** * Exception thrown when a function can not be resolved by the library. * * @author Grégory Mantelet (ARI) * @version 1.4 (08/2015) * @since 1.3 */ public class UnresolvedFunctionException extends ParseException { private static final long serialVersionUID = 1L; /** Function which can not be resolved. */ protected final ADQLFunction functionInError; /** * Build the exception with just a message. * * @param message Description of the error. */ public UnresolvedFunctionException(final String message){ this(message, (TextPosition)null); } /** * Build the exception with just a message. * * @param message Description of the error. * @param pos Position of the unresolved function inside the ADQL query. * * @since 1.4 */ public UnresolvedFunctionException(final String message, final TextPosition pos){ super(message, pos); functionInError = null; } /** * Build the exception with the unresolved function in parameter. * The position of this function in the ADQL query can be retrieved and used afterwards. * * @param fct The unresolved function. */ public UnresolvedFunctionException(final ADQLFunction fct){ super("Unresolved function: \"" + fct.toADQL() + "\"! No UDF has been defined or found with the signature: " + getFctSignature(fct) + ".", fct.getPosition()); functionInError = fct; } /** * Build the exception with a message but also with the unresolved function in parameter. * The position of this function in the ADQL query can be retrieved and used afterwards. * * @param message Description of the error. * @param fct The unresolved function. */ public UnresolvedFunctionException(final String message, final ADQLFunction fct){ super(message, (fct == null) ? null : fct.getPosition()); functionInError = fct; } /** * Get the unresolved function at the origin of this exception. * * @return The unresolved function. Note: MAY be NULL */ public final ADQLFunction getFunction(){ return functionInError; } /** *

Get the signature of the function given in parameter.

* *

* In this signature, just the name and the type of all the parameters are written. * The return type is never part of a function signature. *

* *

Note 1: * A parameter type can be either "NUMERIC", "STRING" or "GEOMETRY". In order to be the most generic has possible, * no more precision about a type is returned here. If the parameter is none of these type kinds, "param" suffixed * by the parameter index (e.g. "param1") is returned. *

* *

Note 2: * If the given object is NULL, an empty string is returned. *

* * @param fct Function whose the signature must be returned. * * @return The corresponding signature. */ public static String getFctSignature(final ADQLFunction fct){ if (fct == null) return ""; StringBuffer buf = new StringBuffer(fct.getName().toLowerCase()); buf.append('('); for(int i = 0; i < fct.getNbParameters(); i++){ if (fct.getParameter(i).isNumeric() && fct.getParameter(i).isString() && fct.getParameter(i).isGeometry()) buf.append("param").append(i + 1); else if (fct.getParameter(i).isNumeric()) buf.append("NUMERIC"); else if (fct.getParameter(i).isString()) buf.append("STRING"); else if (fct.getParameter(i).isGeometry()) buf.append("GEOMETRY"); else buf.append("param").append(i + 1); if ((i + 1) < fct.getNbParameters()) buf.append(", "); } buf.append(')'); return buf.toString(); } } src/adql/db/exception/UnresolvedTableException.java0000644000175000017500000001255713177122310021466 0ustar olesolespackage 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 . * * Copyright 2012 - UDS/Centre de Données astronomiques de Strasbourg (CDS) */ import adql.db.DBChecker; import adql.parser.ParseException; import adql.query.from.ADQLTable; import adql.query.operand.ADQLColumn; /** * This exception is thrown by {@link DBChecker} when a table does not exist * or whose the schema reference is ambiguous. * * @author Grégory Mantelet (CDS) * @version 08/2011 * * @see DBChecker */ public class UnresolvedTableException extends ParseException { private static final long serialVersionUID = 1L; private final String tableName; /* ************ */ /* CONSTRUCTORS */ /* ************ */ /** *

UNKNOWN TABLE

* *

Builds the exception with an {@link ADQLTable} which does not exist.

* * @param table The unresolved {@link ADQLTable}. */ public UnresolvedTableException(ADQLTable table){ super(buildMessage("Unknown table", table)); initPosition(table); tableName = (table != null) ? table.getTableName() : null; } /** *

AMBIGUOUS TABLE NAME

* *

* Builds the exception with an {@link ADQLTable} which does not have a schema reference AND which may come from more than one schema. * The two given schema names are schemas which contain a table with the same name as the given one. *

* * @param table The ambiguous {@link ADQLTable} (no schema reference). * @param t1 First possibility. * @param t2 A second possibility. */ public UnresolvedTableException(ADQLTable table, String t1, String t2){ super(buildMessage("Ambiguous table name", table, t1, t2)); initPosition(table); tableName = (table != null) ? table.getTableName() : null; } /** * Initializes the position at which this exception occurs. * * @param table The unresolved table. */ protected final void initPosition(ADQLTable table){ position = table.getPosition(); } /** *

UNKNOWN TABLE REFERENCE

* *

Builds the exception with an {@link ADQLColumn} whose the table reference is unknown.

* * @param column The {@link ADQLColumn} whose the table reference is unresolved. */ public UnresolvedTableException(ADQLColumn column){ super(buildMessage("Unknown table reference", column)); initPosition(column); tableName = (column != null) ? column.getTableName() : null; } /** *

AMBIGUOUS TABLE REFERENCE

* *

* Builds the exception with an {@link ADQLColumn} which has an ambiguous table reference. * The two given table correspond to tables which match with the table reference of the given {@link ADQLColumn}. *

* * @param column The {@link ADQLColumn} whose the table reference is ambiguous. * @param table1 A table whose the name match with the table reference of the column. * @param table2 Another table whose the name match with the table reference of the column. */ public UnresolvedTableException(ADQLColumn column, String table1, String table2){ super(buildMessage("Ambiguous table reference", column, table1, table2)); initPosition(column); tableName = (column != null) ? column.getTableName() : null; } protected final void initPosition(ADQLColumn column){ position = column.getPosition(); } private static final String buildMessage(String msgStart, ADQLTable t){ StringBuffer msg = new StringBuffer(); msg.append(msgStart).append(" \""); if (t.isSubQuery()) msg.append(t.getAlias()).append("\" !"); else msg.append(t.getFullTableName()).append("\"").append(t.hasAlias() ? (" (alias " + t.getAlias() + ")") : "").append(" !"); return msg.toString(); } private static final String buildMessage(String msgStart, ADQLTable t, String t1, String t2){ if (t1 != null && t2 != null){ StringBuffer msg = new StringBuffer(buildMessage(msgStart, t)); msg.append(" It may be (at least) \"").append(t1).append("\" or \"").append(t2).append("\"."); return msg.toString(); }else return buildMessage(msgStart, t); } private static final String buildMessage(String msgStart, ADQLColumn c){ StringBuffer msg = new StringBuffer(); msg.append(msgStart); msg.append(" \"").append(c.getFullColumnPrefix()).append("\" in \"").append(c.getFullColumnName()).append("\" !"); return msg.toString(); } private static final String buildMessage(String msgStart, ADQLColumn c, String table1, String table2){ if (table1 != null && table2 != null){ StringBuffer msg = new StringBuffer(buildMessage(msgStart, c)); msg.append(" It may come (at least) from \"").append(table1).append("\" or from \"").append(table2).append("\"."); return msg.toString(); }else return buildMessage(msgStart, c); } public final String getTableName(){ return tableName; } } src/adql/db/STCS.java0000644000175000017500000017451413177122310013271 0ustar olesolespackage adql.db; /* * 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 . * * Copyright 2014-2017 - Astronomisches Rechen Institut (ARI) */ import java.util.ArrayList; import java.util.regex.Matcher; import java.util.regex.Pattern; import adql.parser.ADQLQueryFactory; import adql.parser.ParseException; import adql.query.TextPosition; import adql.query.operand.ADQLOperand; import adql.query.operand.NegativeOperand; import adql.query.operand.NumericConstant; import adql.query.operand.StringConstant; import adql.query.operand.function.ADQLFunction; import adql.query.operand.function.geometry.BoxFunction; import adql.query.operand.function.geometry.CircleFunction; import adql.query.operand.function.geometry.GeometryFunction; import adql.query.operand.function.geometry.PointFunction; import adql.query.operand.function.geometry.PolygonFunction; import adql.query.operand.function.geometry.RegionFunction; /** *

This class helps dealing with the subset of STC-S expressions described by the section "6 Use of STC-S in TAP (informative)" * of the TAP Recommendation 1.0 (27th March 2010). This subset is limited to the most common coordinate systems and regions.

* *

Note: * No instance of this class can be created. Its usage is only limited to its static functions and classes. *

* *

Coordinate system

*

* The function {@link #parseCoordSys(String)} is able to parse a string containing only the STC-S expression of a coordinate system * (or an empty string or null which would be interpreted as the default coordinate system - UNKNOWNFRAME UNKNOWNREFPOS SPHERICAL2). * When successful, this parsing returns an object representation of the coordinate system: {@link CoordSys}. *

*

* To serialize into STC-S a coordinate system, you have to create a {@link CoordSys} instance with the desired values * and to call the function {@link CoordSys#toSTCS()}. The static function {@link #toSTCS(CoordSys)} is just calling the * {@link CoordSys#toSTCS()} on the given coordinate system. *

* *

Geometrical region

*

* As for the coordinate system, there is a static function to parse the STC-S representation of a geometrical region: {@link #parseRegion(String)}. * Here again, when the parsing is successful an object representation is returned: {@link Region}. *

*

* This class lets also serializing into STC-S a region. The procedure is the same as with a coordinate system: create a {@link Region} and then * call {@link Region#toString()}. *

*

* The class {@link Region} lets also dealing with the {@link ADQLFunction} implementing a region. It is then possible to create a {@link Region} * object from a such {@link ADQLFunction} and to get the corresponding STC-S representation. The static function {@link #toSTCS(GeometryFunction)} * is a helpful function which do these both actions in once. *

*

Note: * The conversion from {@link ADQLFunction} to {@link Region} or STC-S is possible only if the {@link ADQLFunction} contains constants as parameter. * Thus, a such function using a column, a concatenation, a math operation or using another function can not be converted into STC-S using this class. *

* * @author Grégory Mantelet (ARI) * @version 1.4 (04/2017) * @since 1.3 */ public final class STCS { /** * Empty private constructor ; in order to prevent any instance creation. */ private STCS(){} /* ***************** */ /* COORDINATE SYSTEM */ /* ***************** */ /** Regular expression for a STC-S representation of a coordinate system. It takes into account the fact that each part of * a coordinate system is optional and so that a full coordinate system expression can be reduced to an empty string. */ private final static String coordSysRegExp = Frame.regexp + "?\\s*" + RefPos.regexp + "?\\s*" + Flavor.regexp + "?"; /** Regular expression of an expression exclusively limited to a coordinate system. */ private final static String onlyCoordSysRegExp = "^\\s*" + coordSysRegExp + "\\s*$"; /** Regular expression of a default coordinate system: either an empty string or a string containing only default values. */ private final static String defaultCoordSysRegExp = "^\\s*" + Frame.DEFAULT + "?\\s*" + RefPos.DEFAULT + "?\\s*" + Flavor.DEFAULT + "?\\s*$"; /** Regular expression of a pattern describing a set of allowed coordinate systems. See {@link #buildAllowedRegExp(String)} for more details. */ /* With this regular expression, we get the following matching groups: * 0: All the expression * 1+(6*N): The N-th part of the coordinate system (N is an unsigned integer between 0 and 2 (included) ; it is reduced to '*' if the two following groups are NULL * 2+(6*N): A single value for the N-th part * 3+(6*N): A list of values for the N-th part * 4+(6*N): First value of the list for the N-th part * 5+(6*N): All the other values (starting with a |) of the list for the N-th part * 6+(6*N): Last value of the list for the N-th part. */ private final static String allowedCoordSysRegExp = "^\\s*" + buildAllowedRegExp(Frame.regexp) + "\\s+" + buildAllowedRegExp(RefPos.regexp) + "\\s+" + buildAllowedRegExp(Flavor.regexp) + "\\s*$"; /** Pattern of an allowed coordinate system pattern. This object has been compiled with {@link #allowedCoordSysRegExp}. */ private final static Pattern allowedCoordSysPattern = Pattern.compile(allowedCoordSysRegExp); /** Human description of the syntax of a full coordinate system expression. */ private final static String COORD_SYS_SYNTAX = "\"[" + Frame.regexp + "] [" + RefPos.regexp + "] [" + Flavor.regexp + "]\" ; an empty string is also allowed and will be interpreted as the coordinate system locally used"; /** * Build the regular expression of a string defining the allowed values for one part of the whole coordinate system. * * @param rootRegExp All allowed part values. * * @return The corresponding regular expression. */ private static String buildAllowedRegExp(final String rootRegExp){ return "(" + rootRegExp + "|\\*|(\\(\\s*" + rootRegExp + "\\s*(\\|\\s*" + rootRegExp + "\\s*)*\\)))"; } /** *

List of all possible frames in an STC expression.

* *

* When no value is specified, the default one is {@link #UNKNOWNFRAME}. * The default value is also accessible through the attribute {@link #DEFAULT} * and it is possible to test whether a frame is the default with the function {@link #isDefault()}. *

* *

Note: * The possible values listed in this enumeration are limited to the subset of STC-S described by the section "6 Use of STC-S in TAP (informative)" * of the TAP Recommendation 1.0 (27th March 2010). *

* * @author Grégory Mantelet (ARI) * @version 1.4 (04/2017) * @since 1.3 */ public static enum Frame{ ECLIPTIC, FK4, FK5, J2000, GALACTIC, ICRS, UNKNOWNFRAME; /** Default value for a frame: {@link #UNKNOWNFRAME}. */ public static final Frame DEFAULT = UNKNOWNFRAME; /** Regular expression to test whether a string is a valid frame or not. This regular expression does not take into account * the case of an empty string (which means "default frame"). */ public static final String regexp = buildRegexp(Frame.class); /** * Tell whether this frame is the default one. * * @return true if this is the default frame, false */ public final boolean isDefault(){ return this == DEFAULT; } } /** *

List of all possible reference positions in an STC expression.

* *

* When no value is specified, the default one is {@link #UNKNOWNREFPOS}. * The default value is also accessible through the attribute {@link #DEFAULT} * and it is possible to test whether a reference position is the default with the function {@link #isDefault()}. *

* *

Note: * The possible values listed in this enumeration are limited to the subset of STC-S described by the section "6 Use of STC-S in TAP (informative)" * of the TAP Recommendation 1.0 (27th March 2010). *

* * @author Grégory Mantelet (ARI) * @version 1.3 (10/2014) * @since 1.3 */ public static enum RefPos{ BARYCENTER, GEOCENTER, HELIOCENTER, LSR, TOPOCENTER, RELOCATABLE, UNKNOWNREFPOS; /** Default value for a reference position: {@link #UNKNOWNREFPOS}. */ public static final RefPos DEFAULT = UNKNOWNREFPOS; /** Regular expression to test whether a string is a valid reference position or not. This regular expression does not take into account * the case of an empty string (which means "default reference position"). */ public static final String regexp = buildRegexp(RefPos.class); /** * Tell whether this reference position is the default one. * * @return true if this is the default reference position, false */ public final boolean isDefault(){ return this == DEFAULT; } } /** *

List of all possible flavors in an STC expression.

* *

* When no value is specified, the default one is {@link #SPHERICAL2}. * The default value is also accessible through the attribute {@link #DEFAULT} * and it is possible to test whether a flavor is the default with the function {@link #isDefault()}. *

* *

Note: * The possible values listed in this enumeration are limited to the subset of STC-S described by the section "6 Use of STC-S in TAP (informative)" * of the TAP Recommendation 1.0 (27th March 2010). *

* * @author Grégory Mantelet (ARI) * @version 1.3 (10/2014) * @since 1.3 */ public static enum Flavor{ CARTESIAN2, CARTESIAN3, SPHERICAL2; /** Default value for a flavor: {@link #SPHERICAL2}. */ public static final Flavor DEFAULT = SPHERICAL2; /** Regular expression to test whether a string is a valid flavor or not. This regular expression does not take into account * the case of an empty string (which means "default flavor"). */ public static final String regexp = buildRegexp(Flavor.class); /** * Tell whether this flavor is the default one. * * @return true if this is the default flavor, false */ public final boolean isDefault(){ return this == DEFAULT; } } /** * Build a regular expression covering all possible values of the given enumeration. * * @param enumType Class of an enumeration type. * * @return The build regular expression or "\s*" if the given enumeration contains no constants/values. * * @throws IllegalArgumentException If the given class is not an enumeration type. */ private static String buildRegexp(final Class enumType) throws IllegalArgumentException{ // The given class must be an enumeration type: if (!enumType.isEnum()) throw new IllegalArgumentException("An enum class was expected, but a " + enumType.getName() + " has been given!"); // Get the enumeration constants/values: Object[] constants = enumType.getEnumConstants(); if (constants == null || constants.length == 0) return "\\s*"; // Concatenate all constants with pipe to build a choice regular expression: StringBuffer buf = new StringBuffer("("); for(int i = 0; i < constants.length; i++){ buf.append(constants[i]); if ((i + 1) < constants.length) buf.append('|'); } return buf.append(')').toString(); } /** *

Object representation of an STC coordinate system.

* *

* A coordinate system is composed of three parts: a frame ({@link #frame}), * a reference position ({@link #refpos}) and a flavor ({@link #flavor}). *

* *

* The default value - also corresponding to an empty string - should be: * {@link Frame#UNKNOWNFRAME} {@link RefPos#UNKNOWNREFPOS} {@link Flavor#SPHERICAL2}. * Once built, it is possible to know whether the coordinate system is the default one * or not thanks to function {@link #isDefault()}. *

* *

* An instance of this class can be easily serialized into STC-S using {@link #toSTCS()}, {@link #toFullSTCS()} * or {@link #toString()}. {@link #toFullSTCS()} will display default values explicitly * on the contrary to {@link #toSTCS()} which will replace them by empty strings. *

* *

Important note: * The flavors CARTESIAN2 and CARTESIAN3 can not be used with other frame and reference position than * UNKNOWNFRAME and UNKNOWNREFPOS. In the contrary case an {@link IllegalArgumentException} is throw. *

* * @author Grégory Mantelet (ARI) * @version 1.3 (10/2014) * @since 1.3 */ public static class CoordSys { /** First item of a coordinate system expression: the frame. */ public final Frame frame; /** Second item of a coordinate system expression: the reference position. */ public final RefPos refpos; /** Third and last item of a coordinate system expression: the flavor. */ public final Flavor flavor; /** Indicate whether all parts of the coordinate system are set to their default value. */ private final boolean isDefault; /** STC-S representation of this coordinate system. Default items are not written (that's to say, they are replaced by an empty string). */ private final String stcs; /** STC-S representation of this coordinate system. Default items are explicitly written. */ private final String fullStcs; /** * Build a default coordinate system (UNKNOWNFRAME UNKNOWNREFPOS SPHERICAL2). */ public CoordSys(){ this(null, null, null); } /** * Build a coordinate system with the given parts. * * @param fr Frame part. * @param rp Reference position part. * @param fl Flavor part. * * @throws IllegalArgumentException If a cartesian flavor is used with a frame and reference position other than UNKNOWNFRAME and UNKNOWNREFPOS. */ public CoordSys(final Frame fr, final RefPos rp, final Flavor fl) throws IllegalArgumentException{ frame = (fr == null) ? Frame.DEFAULT : fr; refpos = (rp == null) ? RefPos.DEFAULT : rp; flavor = (fl == null) ? Flavor.DEFAULT : fl; if (flavor != Flavor.SPHERICAL2 && (frame != Frame.UNKNOWNFRAME || refpos != RefPos.UNKNOWNREFPOS)) throw new IllegalArgumentException("a coordinate system expressed with a cartesian flavor MUST have an UNKNOWNFRAME and UNKNOWNREFPOS!"); isDefault = frame.isDefault() && refpos.isDefault() && flavor.isDefault(); stcs = ((!frame.isDefault() ? frame + " " : "") + (!refpos.isDefault() ? refpos + " " : "") + (!flavor.isDefault() ? flavor : "")).trim(); fullStcs = frame + " " + refpos + " " + flavor; } /** * Build a coordinate system by parsing the given STC-S expression. * * @param coordsys STC-S expression representing a coordinate system. Empty string and NULL are allowed values ; they correspond to a default coordinate system. * * @throws ParseException If the syntax of the given STC-S expression is wrong or if it is not a coordinate system only. */ public CoordSys(final String coordsys) throws ParseException{ CoordSys tmp = new STCSParser().parseCoordSys(coordsys); frame = tmp.frame; refpos = tmp.refpos; flavor = tmp.flavor; isDefault = tmp.isDefault; stcs = tmp.stcs; fullStcs = tmp.fullStcs; } /** * Tell whether this is the default coordinate system (UNKNOWNFRAME UNKNOWNREFPOS SPHERICAL2). * * @return true if it is the default coordinate system, false otherwise. */ public final boolean isDefault(){ return isDefault; } /** * Get the STC-S expression of this coordinate system, * in which default values are not written (they are replaced by empty strings). * * @return STC-S representation of this coordinate system. */ public String toSTCS(){ return stcs; } /** * Get the STC-S expression of this coordinate system, * in which default values are explicitly written. * * @return STC-S representation of this coordinate system. */ public String toFullSTCS(){ return fullStcs; } /** * Convert this coordinate system into a STC-S expression. * * @see java.lang.Object#toString() * @see #toSTCS() */ @Override public String toString(){ return stcs; } } /** * Parse the given STC-S representation of a coordinate system. * * @param stcs STC-S expression of a coordinate system. Note: a NULL or empty string will be interpreted as a default coordinate system. * * @return The object representation of the specified coordinate system. * * @throws ParseException If the given expression has a wrong STC-S syntax. */ public static CoordSys parseCoordSys(final String stcs) throws ParseException{ return (new STCSParser().parseCoordSys(stcs)); } /** *

Convert an object representation of a coordinate system into an STC-S expression.

* *

Note: * A NULL object will be interpreted as the default coordinate system and so an empty string will be returned. * Otherwise, this function is equivalent to {@link CoordSys#toSTCS()} (in which default values for each * coordinate system part is not displayed). *

* * @param coordSys The object representation of the coordinate system to convert into STC-S. * * @return The corresponding STC-S expression. * * @see CoordSys#toSTCS() * @see CoordSys#toFullSTCS() */ public static String toSTCS(final CoordSys coordSys){ if (coordSys == null) return ""; else return coordSys.toSTCS(); } /** *

Build a big regular expression gathering all of the given coordinate system syntaxes.

* *

* Each item of the given list must respect a strict syntax. Each part of the coordinate system * may be a single value, a list of values or a '*' (meaning all values are allowed). * A list of values must have the following syntax: ({value1}|{value2}|...). * An empty string is NOT here accepted. *

* *

Example: * (ICRS|FK4|FK5) * SPHERICAL2 is OK, * but (ICRS|FK4|FK5) * is not valid because the flavor value is not defined. *

* *

* Since the default value of each part of a coordinate system should always be possible, * this function ensure these default values are always possible in the returned regular expression. * Thus, if some values except the default one are specified, the default value is automatically appended. *

* *

Note: * If the given array is NULL, all coordinate systems are allowed. * But if the given array is empty, none except an empty string or the default value will be allowed. *

* * @param allowedCoordSys List of all coordinate systems that are allowed. * * @return The corresponding regular expression. * * @throws ParseException If the syntax of one of the given allowed coordinate system is wrong. */ public static String buildCoordSysRegExp(final String[] allowedCoordSys) throws ParseException{ // NULL array => all coordinate systems are allowed: if (allowedCoordSys == null) return onlyCoordSysRegExp; // Empty array => no coordinate system (except the default one) is allowed: else if (allowedCoordSys.length == 0) return defaultCoordSysRegExp; // The final regular expression must be reduced to a coordinate system and nothing else before: StringBuffer finalRegExp = new StringBuffer("^\\s*("); // For each allowed coordinate system: Matcher m; int nbCoordSys = 0; for(int i = 0; i < allowedCoordSys.length; i++){ // NULL item => skipped! if (allowedCoordSys[i] == null) continue; else{ if (nbCoordSys > 0) finalRegExp.append('|'); nbCoordSys++; } // Check its syntax and identify all of its parts: m = allowedCoordSysPattern.matcher(allowedCoordSys[i].toUpperCase()); if (m.matches()){ finalRegExp.append('('); for(int g = 0; g < 3; g++){ // See the comment after the Javadoc of #allowedCoordSysRegExp for a complete list of available groups returned by the pattern. // SINGLE VALUE: if (m.group(2 + (6 * g)) != null) finalRegExp.append('(').append(defaultChoice(g, m.group(2 + (6 * g)))).append(m.group(2 + (6 * g))).append(')'); // LIST OF VALUES: else if (m.group(3 + (6 * g)) != null) finalRegExp.append('(').append(defaultChoice(g, m.group(3 + (6 * g)))).append(m.group(3 + (6 * g)).replaceAll("\\s", "").substring(1)); // JOKER (*): else{ switch(g){ case 0: finalRegExp.append(Frame.regexp); break; case 1: finalRegExp.append(RefPos.regexp); break; case 2: finalRegExp.append(Flavor.regexp); break; } finalRegExp.append('?'); } finalRegExp.append("\\s*"); } finalRegExp.append(')'); }else throw new ParseException("Wrong allowed coordinate system syntax for the " + (i + 1) + "-th item: \"" + allowedCoordSys[i] + "\"! Expected: \"frameRegExp refposRegExp flavorRegExp\" ; where each xxxRegExp = (xxx | '*' | '('xxx ('|' xxx)*')'), frame=\"" + Frame.regexp + "\", refpos=\"" + RefPos.regexp + "\" and flavor=\"" + Flavor.regexp + "\" ; an empty string is also allowed and will be interpreted as '*' (so all possible values)."); } // The final regular expression must be reduced to a coordinate system and nothing else after: finalRegExp.append(")\\s*$"); return (nbCoordSys > 0) ? finalRegExp.toString() : defaultCoordSysRegExp; } /** * Get the default value appended by a '|' character, ONLY IF the given value does not already contain the default value. * * @param g Index of the coordinate system part (0: Frame, 1: RefPos, 2: Flavor, another value will return an empty string). * @param value Value in which the default value must prefix. * * @return A prefix for the given value (the default value and a '|' if the default value is not already in the given value, "" otherwise). */ private static String defaultChoice(final int g, final String value){ switch(g){ case 0: return value.contains(Frame.DEFAULT.toString()) ? "" : Frame.DEFAULT + "|"; case 1: return value.contains(RefPos.DEFAULT.toString()) ? "" : RefPos.DEFAULT + "|"; case 2: return value.contains(Flavor.DEFAULT.toString()) ? "" : Flavor.DEFAULT + "|"; default: return ""; } } /* ****** */ /* REGION */ /* ****** */ /** *

List all possible region types allowed in an STC-S expression.

* *

Note: * The possible values listed in this enumeration are limited to the subset of STC-S described by the section "6 Use of STC-S in TAP (informative)" * of the TAP Recommendation 1.0 (27th March 2010). *

* * @author Grégory Mantelet (ARI) * @version 1.3 (10/2014) * @since 1.3 */ public static enum RegionType{ POSITION, CIRCLE, BOX, POLYGON, UNION, INTERSECTION, NOT; } /** *

Object representation of an STC region.

* *

* This class contains a field for each possible parameter of a region. Depending of the region type * some are not used. In such case, these unused fields are set to NULL. *

* *

* An instance of this class can be easily serialized into STC-S using {@link #toSTCS()}, {@link #toFullSTCS()} * or {@link #toString()}. {@link #toFullSTCS()} will display default value explicit * on the contrary to {@link #toSTCS()} which will replace them by empty strings. *

* * @author Grégory Mantelet (ARI) * @version 1.3 (10/2014) * @since 1.3 */ public static class Region { /** Type of the region. */ public final RegionType type; /** Coordinate system used by this region. * Note: only the NOT region does not declare a coordinate system ; so only for this region this field is NULL. */ public final CoordSys coordSys; /** List of coordinates' pairs. The second dimension of this array represents a pair of coordinates ; it is then an array of two elements. * Note: this field is used by POINT, BOX, CIRCLE and POLYGON. */ public final double[][] coordinates; /** Width of the BOX region. */ public final double width; /** Height of the BOX region. */ public final double height; /** Radius of the CIRCLE region. */ public final double radius; /** List of regions unified (UNION), intersected (INTERSECTION) or avoided (NOT). */ public final Region[] regions; /** STC-S representation of this region, in which default values of the coordinate system (if any) are not written (they are replaced by empty strings). * Note: This attribute is NULL until the first call of the function {@link #toSTCS()} where it is built. */ private String stcs = null; /** STC-S representation of this region, in which default values of the coordinate system (if any) are explicitly written. * Note: This attribute is NULL until the first call of the function {@link #toFullSTCS()} where it is built. */ private String fullStcs = null; /** The ADQL function object representing this region. * Note: this attribute is NULL until the first call of the function {@link #toGeometry()} or {@link #toGeometry(ADQLQueryFactory)}. */ private GeometryFunction geometry = null; /** *

Constructor for a POINT/POSITION region.

* *

Important note: * The array of coordinates is used like that. No copy is done. *

* * @param coordSys Coordinate system. note: It MAY BE null ; if so, the default coordinate system will be chosen * @param coordinates A pair of coordinates ; coordinates[0] and coordinates[1]. */ public Region(final CoordSys coordSys, final double[] coordinates){ this(coordSys, new double[][]{coordinates}); } /** *

Constructor for a POINT/POSITION or a POLYGON region.

* *

Whether it is a polygon or a point depends on the number of given coordinates:

*
    *
  • 1 item => POINT/POSITION
  • *
  • more items => POLYGON
  • *
* *

Important note: * The array of coordinates is used like that. No copy is done. *

* * @param coordSys Coordinate system. note: It MAY BE null ; if so, the default coordinate system will be chosen * @param coordinates List of coordinates' pairs ; coordinates[n] = 1 pair = 2 items (coordinates[n][0] and coordinates[n][1]) ; if 1 pair, it is a POINT/POSITION, but if more, it is a POLYGON. */ public Region(final CoordSys coordSys, final double[][] coordinates){ // Check roughly the coordinates: if (coordinates == null || coordinates.length == 0) throw new NullPointerException("Missing coordinates!"); else if (coordinates[0].length != 2) throw new IllegalArgumentException("Wrong number of coordinates! Expected at least 2 pairs of coordinates (so coordinates[0], coordinates[1] and coordinates[n].length = 2)."); // Decide of the region type in function of the number of coordinates' pairs: type = (coordinates.length > 1) ? RegionType.POLYGON : RegionType.POSITION; // Set the coordinate system (if NULL, choose the default one): this.coordSys = (coordSys == null ? new CoordSys() : coordSys); // Set the coordinates: this.coordinates = coordinates; // Set the other fields as not used: width = Double.NaN; height = Double.NaN; radius = Double.NaN; regions = null; } /** *

Constructor for a CIRCLE region.

* *

Important note: * The array of coordinates is used like that. No copy is done. *

* * @param coordSys Coordinate system. note: It MAY BE null ; if so, the default coordinate system will be chosen * @param coordinates A pair of coordinates ; coordinates[0] and coordinates[1]. * @param radius The circle radius. */ public Region(final CoordSys coordSys, final double[] coordinates, final double radius){ // Check roughly the coordinates: if (coordinates == null || coordinates.length == 0) throw new NullPointerException("Missing coordinates!"); else if (coordinates.length != 2) throw new IllegalArgumentException("Wrong number of coordinates! Expected exactly 2 values."); // Set the region type: type = RegionType.CIRCLE; // Set the coordinate system (if NULL, choose the default one): this.coordSys = (coordSys == null ? new CoordSys() : coordSys); // Set the coordinates: this.coordinates = new double[][]{coordinates}; // Set the radius: this.radius = radius; // Set the other fields as not used: width = Double.NaN; height = Double.NaN; regions = null; } /** *

Constructor for a BOX region.

* *

Important note: * The array of coordinates is used like that. No copy is done. *

* * @param coordSys Coordinate system. note: It MAY BE null ; if so, the default coordinate system will be chosen * @param coordinates A pair of coordinates ; coordinates[0] and coordinates[1]. * @param width Width of the box. * @param height Height of the box. */ public Region(final CoordSys coordSys, final double[] coordinates, final double width, final double height){ // Check roughly the coordinates: if (coordinates == null || coordinates.length == 0) throw new NullPointerException("Missing coordinates!"); else if (coordinates.length != 2) throw new IllegalArgumentException("Wrong number of coordinates! Expected exactly 2 values."); // Set the region type: type = RegionType.BOX; // Set the coordinate system (if NULL, choose the default one): this.coordSys = (coordSys == null ? new CoordSys() : coordSys); // Set the coordinates: this.coordinates = new double[][]{coordinates}; // Set the size of the box: this.width = width; this.height = height; // Set the other fields as not used: radius = Double.NaN; regions = null; } /** *

Constructor for a UNION or INTERSECTION region.

* *

Important note: * The array of regions is used like that. No copy is done. *

* * @param unionOrIntersection Type of the region to create. Note: It can be ONLY a UNION or INTERSECTION. Another value will throw an IllegalArgumentException). * @param coordSys Coordinate system. note: It MAY BE null ; if so, the default coordinate system will be chosen * @param regions Regions to unite or to intersect. Note: At least two regions must be provided. */ public Region(final RegionType unionOrIntersection, final CoordSys coordSys, final Region[] regions){ // Check the type: if (unionOrIntersection == null) throw new NullPointerException("Missing type of region (UNION or INTERSECTION here)!"); else if (unionOrIntersection != RegionType.UNION && unionOrIntersection != RegionType.INTERSECTION) throw new IllegalArgumentException("Wrong region type: \"" + unionOrIntersection + "\"! This constructor lets create only an UNION or INTERSECTION region."); // Check the list of regions: if (regions == null || regions.length == 0) throw new NullPointerException("Missing regions to " + (unionOrIntersection == RegionType.UNION ? "unite" : "intersect") + "!"); else if (regions.length < 2) throw new IllegalArgumentException("Wrong number of regions! Expected at least 2 regions."); // Set the region type: type = unionOrIntersection; // Set the coordinate system (if NULL, choose the default one): this.coordSys = (coordSys == null ? new CoordSys() : coordSys); // Set the regions: this.regions = regions; // Set the other fields as not used: coordinates = null; radius = Double.NaN; width = Double.NaN; height = Double.NaN; } /** * Constructor for a NOT region. * * @param region Any region to not select. */ public Region(final Region region){ // Check the region parameter: if (region == null) throw new NullPointerException("Missing region to NOT select!"); // Set the region type: type = RegionType.NOT; // Set the regions: this.regions = new Region[]{region}; // Set the other fields as not used: coordSys = null; coordinates = null; radius = Double.NaN; width = Double.NaN; height = Double.NaN; } /** *

Build a Region from the given ADQL representation.

* *

Note: * Only {@link PointFunction}, {@link CircleFunction}, {@link BoxFunction}, {@link PolygonFunction} and {@link RegionFunction} * are accepted here. Other extensions of {@link GeometryFunction} will throw an {@link IllegalArgumentException}. *

* * @param geometry The ADQL representation of the region to create here. * * @throws IllegalArgumentException If the given geometry is neither of {@link PointFunction}, {@link BoxFunction}, {@link PolygonFunction} and {@link RegionFunction}. * @throws ParseException If the declared coordinate system, the coordinates or the STC-S definition has a wrong syntax. */ public Region(final GeometryFunction geometry) throws IllegalArgumentException, ParseException{ if (geometry == null) throw new NullPointerException("Missing geometry to convert into STCS.Region!"); if (geometry instanceof PointFunction){ type = RegionType.POSITION; coordSys = STCS.parseCoordSys(extractString(geometry.getCoordinateSystem())); coordinates = new double[][]{{extractNumeric(((PointFunction)geometry).getCoord1()),extractNumeric(((PointFunction)geometry).getCoord2())}}; width = Double.NaN; height = Double.NaN; radius = Double.NaN; regions = null; }else if (geometry instanceof CircleFunction){ type = RegionType.CIRCLE; coordSys = STCS.parseCoordSys(extractString(geometry.getCoordinateSystem())); coordinates = new double[][]{{extractNumeric(((CircleFunction)geometry).getCoord1()),extractNumeric(((CircleFunction)geometry).getCoord2())}}; radius = extractNumeric(((CircleFunction)geometry).getRadius()); width = Double.NaN; height = Double.NaN; regions = null; }else if (geometry instanceof BoxFunction){ type = RegionType.BOX; coordSys = STCS.parseCoordSys(extractString(geometry.getCoordinateSystem())); coordinates = new double[][]{{extractNumeric(((BoxFunction)geometry).getCoord1()),extractNumeric(((BoxFunction)geometry).getCoord2())}}; width = extractNumeric(((BoxFunction)geometry).getWidth()); height = extractNumeric(((BoxFunction)geometry).getHeight()); radius = Double.NaN; regions = null; }else if (geometry instanceof PolygonFunction){ PolygonFunction poly = (PolygonFunction)geometry; type = RegionType.POLYGON; coordSys = STCS.parseCoordSys(extractString(poly.getCoordinateSystem())); coordinates = new double[(poly.getNbParameters() - 1) / 2][2]; for(int i = 0; i < coordinates.length; i++) coordinates[i] = new double[]{extractNumeric(poly.getParameter(1 + i * 2)),extractNumeric(poly.getParameter(2 + i * 2))}; width = Double.NaN; height = Double.NaN; radius = Double.NaN; regions = null; }else if (geometry instanceof RegionFunction){ Region r = STCS.parseRegion(extractString(((RegionFunction)geometry).getParameter(0))); type = r.type; coordSys = r.coordSys; coordinates = r.coordinates; width = r.width; height = r.height; radius = r.radius; regions = r.regions; }else throw new IllegalArgumentException("Unknown region type! Only geometrical function PointFunction, CircleFunction, BoxFunction, PolygonFunction and RegionFunction are allowed."); } /** * Extract a string value from the given {@link ADQLOperand} * which is expected to be a {@link StringConstant} instance. * * @param op A string operand. * * @return The string value embedded in the given operand. * * @throws ParseException If the given operand is not an instance of {@link StringConstant}. */ private static String extractString(final ADQLOperand op) throws ParseException{ if (op == null) throw new NullPointerException("Missing operand!"); else if (op instanceof StringConstant) return ((StringConstant)op).getValue(); else throw new ParseException("Can not convert into STC-S a non string argument (including ADQLColumn and Concatenation)!"); } /** * Extract a numeric value from the given {@link ADQLOperand} * which is expected to be a {@link NumericConstant} instance * or a {@link NegativeOperand} embedding a {@link NumericConstant}. * * @param op A numeric operand. * * @return The numeric value embedded in the given operand. * * @throws ParseException If the given operand is not an instance of {@link NumericConstant} or a {@link NegativeOperand}. */ private static double extractNumeric(final ADQLOperand op) throws ParseException{ if (op == null) throw new NullPointerException("Missing operand!"); else if (op instanceof NumericConstant) return Double.parseDouble(((NumericConstant)op).getValue()); else if (op instanceof NegativeOperand) return extractNumeric(((NegativeOperand)op).getOperand()) * -1; else throw new ParseException("Can not convert into STC-S a non numeric argument (including ADQLColumn and Operation)!"); } /** *

Get the STC-S representation of this region (in which default values * of the coordinate system are not written ; they are replaced by empty strings).

* *

Note: * This function build the STC-S just once and store it in a class attribute. * The value of this attribute is then returned at next calls of this function. *

* * @return Its STC-S representation. */ public String toSTCS(){ if (stcs != null) return stcs; else{ // Write the region type: StringBuffer buf = new StringBuffer(type.toString()); // Write the coordinate system (except for NOT): if (type != RegionType.NOT){ String coordSysStr = coordSys.toSTCS(); if (coordSysStr != null && coordSysStr.length() > 0) buf.append(' ').append(coordSysStr); buf.append(' '); } // Write the other parameters (coordinates, regions, ...): switch(type){ case POSITION: case POLYGON: appendCoordinates(buf, coordinates); break; case CIRCLE: appendCoordinates(buf, coordinates); buf.append(' ').append(radius); break; case BOX: appendCoordinates(buf, coordinates); buf.append(' ').append(width).append(' ').append(height); break; case UNION: case INTERSECTION: case NOT: buf.append('('); appendRegions(buf, regions, false); buf.append(')'); break; } // Return the built STC-S: return (stcs = buf.toString()); } } /** *

Get the STC-S representation of this region (in which default values * of the coordinate system are explicitly written).

* *

Note: * This function build the STC-S just once and store it in a class attribute. * The value of this attribute is then returned at next calls of this function. *

* * @return Its STC-S representation. */ public String toFullSTCS(){ if (fullStcs != null) return fullStcs; else{ // Write the region type: StringBuffer buf = new StringBuffer(type.toString()); // Write the coordinate system (except for NOT): if (type != RegionType.NOT){ String coordSysStr = coordSys.toFullSTCS(); if (coordSysStr != null && coordSysStr.length() > 0) buf.append(' ').append(coordSysStr); buf.append(' '); } // Write the other parameters (coordinates, regions, ...): switch(type){ case POSITION: case POLYGON: appendCoordinates(buf, coordinates); break; case CIRCLE: appendCoordinates(buf, coordinates); buf.append(' ').append(radius); break; case BOX: appendCoordinates(buf, coordinates); buf.append(' ').append(width).append(' ').append(height); break; case UNION: case INTERSECTION: case NOT: buf.append('('); appendRegions(buf, regions, true); buf.append(')'); break; } // Return the built STC-S: return (fullStcs = buf.toString()); } } /** * Append all the given coordinates to the given buffer. * * @param buf Buffer in which coordinates must be appended. * @param coords Coordinates to append. */ private static void appendCoordinates(final StringBuffer buf, final double[][] coords){ for(int i = 0; i < coords.length; i++){ if (i > 0) buf.append(' '); buf.append(coords[i][0]).append(' ').append(coords[i][1]); } } /** * Append all the given regions in the given buffer. * * @param buf Buffer in which regions must be appended. * @param regions Regions to append. * @param fullCoordSys Indicate whether the coordinate system of the regions must explicitly display the default values. */ private static void appendRegions(final StringBuffer buf, final Region[] regions, final boolean fullCoordSys){ for(int i = 0; i < regions.length; i++){ if (i > 0) buf.append(' '); if (fullCoordSys) buf.append(regions[i].toFullSTCS()); else buf.append(regions[i].toSTCS()); } } @Override public String toString(){ return toSTCS(); } /** *

Convert this region into its corresponding ADQL representation.

* *
    *
  • POSITION: {@link PointFunction}
  • *
  • CIRCLE: {@link CircleFunction}
  • *
  • BOX: {@link BoxFunction}
  • *
  • POLYGON: {@link PolygonFunction}
  • *
  • UNION, INTERSECTION, NOT: {@link RegionFunction}
  • *
* *

Note: * This function is using the default ADQL factory, built using {@link ADQLQueryFactory#ADQLQueryFactory()}. *

* * @return The corresponding ADQL representation. * * @see #toGeometry(ADQLQueryFactory) */ public GeometryFunction toGeometry(){ return toGeometry(null); } /** *

Convert this region into its corresponding ADQL representation.

* *
    *
  • POSITION: {@link PointFunction}
  • *
  • CIRCLE: {@link CircleFunction}
  • *
  • BOX: {@link BoxFunction}
  • *
  • POLYGON: {@link PolygonFunction}
  • *
  • UNION, INTERSECTION, NOT: {@link RegionFunction}
  • *
* *

Note: * This function build the ADQL representation just once and store it in a class attribute. * The value of this attribute is then returned at next calls of this function. *

* * @param factory The factory of ADQL objects to use. * * @return The corresponding ADQL representation. */ public GeometryFunction toGeometry(ADQLQueryFactory factory){ if (factory == null) factory = new ADQLQueryFactory(); try{ if (geometry != null) return geometry; else{ StringConstant coordSysObj = factory.createStringConstant(coordSys == null ? "" : coordSys.toString()); switch(type){ case POSITION: return (geometry = factory.createPoint(coordSysObj, toNumericObj(coordinates[0][0], factory), toNumericObj(coordinates[0][1], factory))); case CIRCLE: return (geometry = factory.createCircle(coordSysObj, toNumericObj(coordinates[0][0], factory), toNumericObj(coordinates[0][1], factory), toNumericObj(radius, factory))); case BOX: return (geometry = factory.createBox(coordSysObj, toNumericObj(coordinates[0][0], factory), toNumericObj(coordinates[0][1], factory), toNumericObj(width, factory), toNumericObj(height, factory))); case POLYGON: ArrayList coords = new ArrayList(coordinates.length * 2); for(int i = 0; i < coordinates.length; i++){ coords.add(toNumericObj(coordinates[i][0], factory)); coords.add(toNumericObj(coordinates[i][1], factory)); } return (geometry = factory.createPolygon(coordSysObj, coords)); default: return (geometry = factory.createRegion(factory.createStringConstant(toString()))); } } }catch(Exception pe){ return null; } } /** *

Convert a numeric value into an ADQL representation:

* *
    *
  • If negative: NegativeOperand(NumericConstant(val))
  • *
  • Otherwise: NumericConstant(val)
  • *
* * @param val The value to embed in an ADQL object. * @param factory The factory to use to created ADQL objects. * * @return The representing ADQL representation. * * @throws Exception If an error occurs while creating the ADQL object. */ private ADQLOperand toNumericObj(final double val, final ADQLQueryFactory factory) throws Exception{ if (val >= 0) return factory.createNumericConstant("" + val); else return factory.createNegativeOperand(factory.createNumericConstant("" + (val * -1))); } } /** * Parse the given STC-S expression representing a geometrical region. * * @param stcsRegion STC-S expression of a region. Note: MUST be different from NULL. * * @return The object representation of the specified geometrical region. * * @throws ParseException If the given expression is NULL, empty string or if the STC-S syntax is wrong. */ public static Region parseRegion(final String stcsRegion) throws ParseException{ if (stcsRegion == null || stcsRegion.trim().length() == 0) throw new ParseException("Missing STC-S expression to parse!"); return (new STCSParser().parseRegion(stcsRegion)); } /** * Convert into STC-S the given object representation of a geometrical region. * * @param region Region to convert into STC-S. * * @return The corresponding STC-S expression. */ public static String toSTCS(final Region region){ if (region == null) throw new NullPointerException("Missing region to serialize into STC-S!"); return region.toSTCS(); } /** *

Convert into STC-S the given ADQL representation of a geometrical function.

* *

Important note: * Only {@link PointFunction}, {@link CircleFunction}, {@link BoxFunction}, {@link PolygonFunction} * and {@link RegionFunction} are accepted here. Other extensions of {@link GeometryFunction} will * throw an {@link IllegalArgumentException}. *

* * @param region ADQL representation of the region to convert into STC-S. * * @return The corresponding STC-S expression. * * @throws ParseException If the given object is NULL or not of the good type. */ public static String toSTCS(final GeometryFunction region) throws ParseException{ if (region == null) throw new NullPointerException("Missing region to serialize into STC-S!"); return (new Region(region)).toSTCS(); } /* *************************** */ /* PARSER OF STC-S EXPRESSIONS */ /* *************************** */ /** * Let parse any STC-S expression. * * @author Grégory Mantelet (ARI) * @version 1.3 (11/2014) * @since 1.3 */ private static class STCSParser { /** Regular expression of a numerical value. */ private final static String numericRegExp = "(\\+|-)?(\\d+(\\.\\d*)?|\\.\\d+)([Ee](\\+|-)?\\d+)?"; /** Position of the next characters to read in the STC-S expression to parse. */ private int pos; /** Full STC-S expression to parse. */ private String stcs; /** Last read token (can be a numeric, a string, a region type, ...). */ private String token; /** Buffer used to read tokens. */ private StringBuffer buffer; /** * Exception sent when the end of the expression * (EOE = End Of Expression) is reached. * * @author Grégory Mantelet (ARI) * @version 1.3 (10/2014) * @since 1.3 */ private static class EOEException extends ParseException { private static final long serialVersionUID = 1L; /** Build a simple EOEException. */ public EOEException(){ super("Unexpected End Of Expression!"); } } /** * Build the STC-S parser. */ public STCSParser(){} /** * Parse the given STC-S expression, expected as a coordinate system. * * @param stcs The STC-S expression to parse. * * @return The corresponding object representation of the specified coordinate system. * * @throws ParseException If the syntax of the given STC-S expression is wrong or if it is not a coordinate system. */ public CoordSys parseCoordSys(final String stcs) throws ParseException{ init(stcs); CoordSys coordsys = null; try{ coordsys = coordSys(); end(COORD_SYS_SYNTAX); return coordsys; }catch(EOEException ex){ ex.printStackTrace(); return new CoordSys(); } } /** * Parse the given STC-S expression, expected as a geometrical region. * * @param stcs The STC-S expression to parse. * * @return The corresponding object representation of the specified geometrical region. * * @throws ParseException If the syntax of the given STC-S expression is wrong or if it is not a geometrical region. */ public Region parseRegion(final String stcs) throws ParseException{ init(stcs); Region region = region(); end("\"POSITION \", \"CIRCLE \", \"BOX \", \"POLYGON [ ...]\", \"UNION ( [ ...] )\", \"INTERSECTION [] ( [ ...] )\" or \"NOT ( )\""); return region; } /** * Prepare the parser in order to read the given STC-S expression. * * @param newStcs New STC-S expression to parse from now. */ private void init(final String newStcs){ stcs = (newStcs == null) ? "" : newStcs; token = null; buffer = new StringBuffer(); pos = 0; } /** * Finalize the parsing. * No more characters (except eventually some space characters) should remain in the STC-S expression to parse. * * @param expectedSyntax Description of the good syntax expected. This description is used only to write the * {@link ParseException} in case other non-space characters are found among the remaining characters. * * @throws ParseException If other non-space characters remains. */ private void end(final String expectedSyntax) throws ParseException{ // Skip all spaces: skipSpaces(); // If there is still some characters, they are not expected, and so throw an exception: if (stcs.length() > 0 && pos < stcs.length()) throw new ParseException("Incorrect syntax: \"" + stcs.substring(pos) + "\" was unexpected! Expected syntax: " + expectedSyntax + ".", new TextPosition(1, pos, 1, stcs.length())); // Reset the buffer, token and the STC-S expression to parse: buffer = null; stcs = null; token = null; } /** * Tool function which skip all next space characters until the next meaningful characters. */ private void skipSpaces(){ while(pos < stcs.length() && Character.isWhitespace(stcs.charAt(pos))) pos++; } /** *

Get the next meaningful word. This word can be a numeric, any string constant or a region type.

* *

* In case the end of the expression is reached before getting any meaningful character, an {@link EOEException} is thrown. *

* * @return The full read word/token. * * @throws EOEException If the end of the STC-S expression is reached before getting any meaningful character. */ private String nextToken() throws EOEException{ // Skip all spaces: skipSpaces(); // Fetch all characters until word separator (a space or a open/close parenthesis): while(pos < stcs.length() && !Character.isWhitespace(stcs.charAt(pos)) && stcs.charAt(pos) != '(' && stcs.charAt(pos) != ')') buffer.append(stcs.charAt(pos++)); // If no character has been fetched while at least one was expected, throw an exception: if (buffer.length() == 0) throw new EOEException(); // Save the read token and reset the buffer: token = buffer.toString(); buffer.delete(0, token.length()); return token; } /** * Read the next token as a numeric. * If not a numeric, a {@link ParseException} is thrown. * * @return The read numerical value. * * @throws ParseException If the next token is not a numerical expression. */ private double numeric() throws ParseException{ if (nextToken().matches(numericRegExp)) return Double.parseDouble(token); else throw new ParseException("a numeric was expected!", new TextPosition(1, pos - token.length(), 1, pos)); } /** * Read the next 2 tokens as a coordinate pairs (so as 2 numerical values). * If not 2 numeric, a {@link ParseException} is thrown. * * @return The read coordinate pairs. * * @throws ParseException If the next 2 tokens are not 2 numerical expressions. */ private double[] coordPair() throws ParseException{ skipSpaces(); int startPos = pos; try{ return new double[]{numeric(),numeric()}; }catch(ParseException pe){ if (pe instanceof EOEException) throw pe; else throw new ParseException("a coordinates pair (2 numerics separated by one or more spaces) was expected!", new TextPosition(1, startPos, 1, pos)); } } /** * Read and parse the next tokens as a coordinate system expression. * If they do not match, a {@link ParseException} is thrown. * * @return The object representation of the read coordinate system. * * @throws ParseException If the next tokens are not representing a valid coordinate system. */ private CoordSys coordSys() throws ParseException{ // Skip all spaces: skipSpaces(); // Backup the current position: /* (because every parts of a coordinate system are optional ; * like this, it will be possible to go back in the expression * to parse if optional parts are not written) */ String oldToken = token; int startPos = pos; Frame fr = null; RefPos rp = null; Flavor fl = null; try{ // Read the token: nextToken(); // Try to parse it as a frame: if ((fr = frame()) != null){ // if success, go the next token: startPos = pos; oldToken = token; nextToken(); } // Try to parse the last read token as a reference position: if ((rp = refpos()) != null){ // if success, go the next token: startPos = pos; oldToken = token; nextToken(); } // Try to parse the last read token as a flavor: if ((fl = flavor()) == null){ // if NOT a success, go back "in time" (go back to the position before reading the token): pos = startPos; token = oldToken; } }catch(EOEException ex){ /* End Of Expression may happen here since all parts of a coordinate system are optional. * So, there is no need to treat the error. */ } // Build the object representation of the read coordinate system: /* Note: if nothing has been read for one or all parts of the coordinate system, * the NULL value will be replaced automatically in the constructor * by the default value of the corresponding part(s). */ try{ return new CoordSys(fr, rp, fl); }catch(IllegalArgumentException iae){ throw new ParseException(iae.getMessage(), new TextPosition(1, startPos, 1, pos)); } } /** * Parse the last read token as FRAME. * * @return The corresponding enumeration item, or NULL if the last token is not a valid FRAME item. */ private Frame frame(){ try{ return Frame.valueOf(token.toUpperCase()); }catch(IllegalArgumentException iae){ return null; } } /** * Parse the last read token as REFERENCE POSITION. * * @return The corresponding enumeration item, or NULL if the last token is not a valid REFERENCE POSITION item. */ private RefPos refpos(){ try{ return RefPos.valueOf(token.toUpperCase()); }catch(IllegalArgumentException iae){ return null; } } /** * Parse the last read token as FLAVOR. * * @return The corresponding enumeration item, or NULL if the last token is not a valid FLAVOR item. */ private Flavor flavor(){ try{ return Flavor.valueOf(token.toUpperCase()); }catch(IllegalArgumentException iae){ return null; } } /** * Read and parse the next tokens as a geometrical region. * If they do not match, a {@link ParseException} is thrown. * * @return The object representation of the read geometrical region. * * @throws ParseException If the next tokens are not representing a valid geometrical region. */ private Region region() throws ParseException{ // Skip all spaces: skipSpaces(); // Read the next token (it should be the region type): int startPos = pos; token = nextToken().toUpperCase(); /* Identify the region type, next the expected parameters and finally build the corresponding object representation */ // POSITION case: if (token.equals("POSITION")){ try{ CoordSys coordSys = coordSys(); double[] coords = coordPair(); return new Region(coordSys, coords); }catch(Exception e){ throw buildException(e, "\"POSITION \", where coordPair=\" \" and coordSys=" + COORD_SYS_SYNTAX, startPos); } } // CIRCLE case: else if (token.equals("CIRCLE")){ try{ CoordSys coordSys = coordSys(); double[] coords = coordPair(); double radius = numeric(); return new Region(coordSys, coords, radius); }catch(Exception e){ throw buildException(e, "\"CIRCLE \", where coordPair=\" \", radius=\"\" and coordSys=" + COORD_SYS_SYNTAX, startPos); } } // BOX case: else if (token.equals("BOX")){ try{ CoordSys coordSys = coordSys(); double[] coords = coordPair(); double width = numeric(), height = numeric(); return new Region(coordSys, coords, width, height); }catch(Exception e){ throw buildException(e, "\"BOX \", where coordPair=\" \", width and height=\"\" and coordSys=" + COORD_SYS_SYNTAX, startPos); } } // POLYGON case: else if (token.equals("POLYGON")){ try{ CoordSys coordSys = coordSys(); ArrayList coordinates = new ArrayList(6); double[] coords; for(int i = 0; i < 3; i++){ coords = coordPair(); coordinates.add(coords[0]); coordinates.add(coords[1]); } boolean moreCoord = true; int posBackup; do{ posBackup = pos; try{ coords = coordPair(); coordinates.add(coords[0]); coordinates.add(coords[1]); }catch(ParseException pe){ moreCoord = false; pos = posBackup; } }while(moreCoord); double[][] allCoords = new double[coordinates.size() / 2][2]; for(int i = 0; i < coordinates.size() && i + 1 < coordinates.size(); i += 2) allCoords[i / 2] = new double[]{coordinates.get(i),coordinates.get(i + 1)}; return new Region(coordSys, allCoords); }catch(Exception e){ throw buildException(e, "\"POLYGON [ ...]\", where coordPair=\" \" and coordSys=" + COORD_SYS_SYNTAX, startPos); } } // UNION & INTERSECTION cases: else if (token.equals("UNION") || token.equals("INTERSECTION")){ RegionType type = (token.equals("UNION") ? RegionType.UNION : RegionType.INTERSECTION); try{ CoordSys coordSys = coordSys(); ArrayList regions = new ArrayList(2); skipSpaces(); if (stcs.charAt(pos) != '(') throw buildException(new ParseException("a opening parenthesis - ( - was expected!", new TextPosition(1, pos, 1, pos + 1)), "\"" + type + " ( [ ...] )\", where coordSys=" + COORD_SYS_SYNTAX, startPos); else pos++; // parse and add the FIRST region: regions.add(region()); // parse and add the SECOND region: regions.add(region()); skipSpaces(); while(stcs.charAt(pos) != ')'){ regions.add(region()); skipSpaces(); } pos++; return new Region(type, coordSys, regions.toArray(new Region[regions.size()])); }catch(Exception e){ if (e instanceof ParseException && e.getMessage().startsWith("Incorrect syntax: \"")) throw (ParseException)e; else throw buildException(e, "\"" + type + " ( [ ...] )\", where coordSys=" + COORD_SYS_SYNTAX, startPos); } } // NOT case: else if (token.equals("NOT")){ try{ skipSpaces(); if (stcs.charAt(pos) != '(') throw buildException(new ParseException("an opening parenthesis - ( - was expected!", new TextPosition(1, pos, 1, pos + 1)), "\"NOT ( )\"", startPos); else pos++; Region region = region(); skipSpaces(); if (stcs.charAt(pos) != ')') throw buildException(new ParseException("a closing parenthesis - ) - was expected!", new TextPosition(1, pos, 1, pos + 1)), "\"NOT ( )\"", startPos); else pos++; return new Region(region); }catch(Exception e){ if (e instanceof ParseException && e.getMessage().startsWith("Incorrect syntax: ")) throw (ParseException)e; else throw buildException(e, "\"NOT ( )\"", startPos); } } // Otherwise, the region type is not known and so a ParseException is thrown: else throw new ParseException("Unknown STC region type: \"" + token + "\"!", new TextPosition(1, startPos, 1, pos)); } /** * Build a {@link ParseException} based on the given one and by adding the human description of what was expected, if needed. * * @param ex Root exception. * @param expectedSyntax Human description of what was expected. * @param startPos Position of the first character of the wrong part of expression. * * @return The build exception. */ private ParseException buildException(final Exception ex, final String expectedSyntax, int startPos){ if (ex instanceof EOEException) return new ParseException("Unexpected End Of Expression! Expected syntax: " + expectedSyntax + ".", new TextPosition(1, startPos, 1, pos)); else if (ex instanceof ParseException) return new ParseException("Incorrect syntax: " + ex.getMessage() + " Expected syntax: " + expectedSyntax + ".", (((ParseException)ex).getPosition() != null ? ((ParseException)ex).getPosition() : new TextPosition(1, startPos, 1, pos))); else return new ParseException(ex.getMessage(), new TextPosition(1, startPos, 1, pos)); } } } src/adql/db/DBTable.java0000644000175000017500000001001713177122310013735 0ustar olesolespackage adql.db; /* * 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 . * * Copyright 2012-2016 - UDS/Centre de Données astronomiques de Strasbourg (CDS), * Astronomisches Rechen Institut (ARI) */ /** *

Definition of a valid target table.

* *

* This table can be used in an ADQL query with its ADQL name ({@link #getADQLName()}) * and corresponds to a real table in the "database" with its DB name ({@link #getDBName()}). *

* * @author Grégory Mantelet (CDS;ARI) * @version 1.4 (07/2016) */ public interface DBTable extends Iterable { /** * Gets the name of this table (without any prefix and double-quotes). * * @return Its ADQL name. */ public String getADQLName(); /** * Gets the name of this table in the "database". * * @return Its DB name. */ public String getDBName(); /** * Gets the ADQL name of the schema which contains this table. * * @return ADQL name of its schema. */ public String getADQLSchemaName(); /** * Gets the DB name of the schema which contains this table. * * @return DB name of its schema. */ public String getDBSchemaName(); /** * Gets the ADQL name of the catalog which contains this table. * * @return ADQL name of its catalog. */ public String getADQLCatalogName(); /** * Gets the DB name of the catalog which contains this table. * * @return DB name of its catalog. */ public String getDBCatalogName(); /** * Gets the definition of the specified column if it exists in this table. * * @param colName Name of the column (may be the ADQL or DB name depending of the second parameter). * @param adqlName true means the given name is the ADQL name of the column and that the research must be done on the ADQL name of columns, * false means the same thing but with the DB name. * * @return The corresponding column, or null if the specified column had not been found. */ public DBColumn getColumn(String colName, boolean adqlName); /** *

Makes a copy of this instance of {@link DBTable}, with the possibility to change the DB and ADQL names.

* *

IMPORTANT: * The given DB and ADQL name may be NULL. If NULL, the copy will contain exactly the same full name (DB and/or ADQL).
* And they may be qualified (that's to say: prefixed by the schema name or by the catalog and schema name). It means that it is possible to * change the catalog, schema and table name in the copy.
* For instance: *

*
    *
  • .copy(null, "foo") => a copy with the same full DB name, but with no ADQL catalog and schema name and with an ADQL table name equals to "foo"
  • *
  • .copy("schema.table", ) => a copy with the same full ADQL name, but with no DB catalog name, with a DB schema name equals to "schema" and with a DB table name equals to "table"
  • *
* * @param dbName Its new DB name. * It may be qualified. * It may also be NULL ; if so, the full DB name won't be different in the copy. * @param adqlName Its new ADQL name. * It may be qualified. * It may also be NULL ; if so, the full DB name won't be different in the copy. * * @return A modified copy of this {@link DBTable}. */ public DBTable copy(final String dbName, final String adqlName); } src/adql/db/DBCommonColumn.java0000644000175000017500000001444413177122310015324 0ustar olesolespackage adql.db; /* * 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 . * * Copyright 2014-2015 - Astronomisches Rechen Institut (ARI) */ import java.util.ArrayList; import java.util.Iterator; import adql.db.exception.UnresolvedJoinException; import adql.query.ADQLQuery; /** * This is a special column which exists only after a NATURAL JOIN or a JOIN ... USING between two tables. * It lets unify several columns of the joined tables in a single column. * * Thus, the writer of an ADQL query can use the column name without table prefix (since after the join there will be only one) * or with a prefix table of the joined tables. The list of all covered tables is stored in this object and can be extended * in case of several JOINs. * * @author Grégory Mantelet (ARI) - gmantele@ari.uni-heidelberg.de * @version 1.3 (05/2015) * @since 1.2 */ public class DBCommonColumn implements DBColumn { protected DBColumn generalColumnDesc; protected ArrayList lstCoveredTables = new ArrayList(); /** * Create a column which merges both of the given columns. * * This special {@link DBColumn} implementation is not associated with one table, * and can be listed in a {@link DBTable} ONLY IF the latter is the result of a sub-query (see {@link ADQLQuery#getResultingColumns()}). * * A column resulting from a tables join is common only to the joined tables. That's why a list of all tables covered * by this column is created or update at each merge. It can be accessed thanks to {@link #getCoveredTables()}. * * Note: In the case one or both of the columns to join are {@link DBCommonColumn}, the list of their covered tables are also merged. * * @param leftCol Column of the left join table. May be a {@link DBCommonColumn}. * @param rightCol Column of the right join table. May be a {@link DBCommonColumn}. * * @throws UnresolvedJoinException If the type of the two given columns are not roughly (just testing numeric, string or geometry) compatible. */ public DBCommonColumn(final DBColumn leftCol, final DBColumn rightCol) throws UnresolvedJoinException{ // Test whether type of both columns are compatible: if (leftCol.getDatatype() != null && rightCol.getDatatype() != null && !leftCol.getDatatype().isCompatible(rightCol.getDatatype())) throw new UnresolvedJoinException("JOIN impossible: incompatible column types when trying to join the columns " + leftCol.getADQLName() + " (" + leftCol.getDatatype() + ") and " + rightCol.getADQLName() + " (" + rightCol.getDatatype() + ")!"); // LEFT COLUMN: if (leftCol instanceof DBCommonColumn){ // set the general column description: generalColumnDesc = ((DBCommonColumn)leftCol).generalColumnDesc; // add all covered tables of the left common column: Iterator it = ((DBCommonColumn)leftCol).getCoveredTables(); while(it.hasNext()) addCoveredTable(it.next()); }else{ // set the general column description: generalColumnDesc = leftCol.copy(leftCol.getDBName(), leftCol.getADQLName(), null); // add the table to cover: addCoveredTable(leftCol.getTable()); } // RIGHT COLUMN: if (rightCol instanceof DBCommonColumn){ // add all covered tables of the left common column: Iterator it = ((DBCommonColumn)rightCol).getCoveredTables(); while(it.hasNext()) addCoveredTable(it.next()); }else{ // add the table to cover: addCoveredTable(rightCol.getTable()); } } /** * Constructor by copy. * It returns a copy of this instance of {@link DBCommonColumn}. * * Note: The list of covered tables is NOT deeply copied! * * @param toCopy The {@link DBCommonColumn} to copy. * @param dbName The new DB name of this {@link DBCommonColumn}. * @param adqlName The new ADQL name of this {@link DBCommonColumn}. */ @SuppressWarnings("unchecked") public DBCommonColumn(final DBCommonColumn toCopy, final String dbName, final String adqlName){ generalColumnDesc = toCopy.generalColumnDesc.copy(dbName, adqlName, null); lstCoveredTables = (ArrayList)toCopy.lstCoveredTables.clone(); } @Override public final String getADQLName(){ return generalColumnDesc.getADQLName(); } @Override public final String getDBName(){ return generalColumnDesc.getDBName(); } @Override public final DBType getDatatype(){ return generalColumnDesc.getDatatype(); } @Override public final DBTable getTable(){ return null; } /** * Get an iterator over the list of all tables covered by this common column. * * @return Iterator over all covered tables. */ public final Iterator getCoveredTables(){ return lstCoveredTables.iterator(); } /** * Add a table that this common column must cover from now. * * Warning: no unicity check is never done ! * * @param table Table to add in the covered tables list. */ protected void addCoveredTable(final DBTable table){ if (table != null) lstCoveredTables.add(table); } /** * WARNING: This copy function does not make a real copy of this DBCommonColumn ! * It returns a modified copy of the general column description it contains. * * Note: To make a real copy of this DBCommonColumn use the Constructor by copy {@link #DBCommonColumn(DBCommonColumn, String, String)}. * * @param dbName Its new DB name. * @param adqlName Its new ADQL name. * @param dbTable Its new DBTable * * @return A modified copy of the general column description this common column represents. * * @see adql.db.DBColumn#copy(java.lang.String, java.lang.String, adql.db.DBTable) */ @Override public DBColumn copy(final String dbName, final String adqlName, final DBTable dbTable){ return generalColumnDesc.copy(dbName, adqlName, dbTable); } } src/adql/db/DBType.java0000644000175000017500000002353013177122310013633 0ustar olesolespackage adql.db; /* * 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 TAPLibrary. If not, see . * * Copyright 2014-2016 - Astronomisches Rechen Institut (ARI) */ /** *

* Describe a full column type as it is described in the IVOA document of TAP. * Thus, this object contains 2 attributes: type (or datatype) and length (or size). *

* *

The length/size may be not defined ; in this case, its value is set to {@link #NO_LENGTH} or is negative or null.

* *

All datatypes declared in the IVOA recommendation document of TAP are listed in an enumeration type: {@link DBDatatype}. * It is used to set the attribute type/datatype of this class.

* * @author Grégory Mantelet (ARI) * @version 1.4 (07/2016) * @since 1.3 */ public class DBType { /** * List of all datatypes declared in the IVOA recommendation of TAP (in the section UPLOAD). * * @author Grégory Mantelet (ARI) * @version 1.4 (07/2016) * @since 1.3 */ public static enum DBDatatype{ SMALLINT, INTEGER, BIGINT, REAL, DOUBLE, BINARY, VARBINARY, CHAR, VARCHAR, BLOB, CLOB, TIMESTAMP, POINT, REGION, /** Type to use when the precise datatype is unknown. * @since 1.4 */ UNKNOWN, /**

Type to use when the type is known as numeric but there is no precise datatype * (e.g. double, float, integer, ...).

*

It is particularly used when creating a {@link DefaultDBColumn} from an ADQL function * or operation while listing resulting columns of a sub-query.

*

This type is similar to {@link #UNKNOWN}.

* @since 1.4 */ UNKNOWN_NUMERIC; /** String to return when {@link #toString()} is called. * @since 1.4*/ private String strExp = this.name(); @Override public String toString(){ return strExp; } /** *

This function lets define the name of the type as provided * ONLY FOR {@link #UNKNOWN} and {@link #UNKNOWN_NUMERIC} {@link DBDatatype}s.

* *

Important: * If this {@link DBDatatype} is not {@link #UNKNOWN} or {@link #UNKNOWN_NUMERIC} this function has no effect. * But if the given name is NULL or empty, no custom type will be set ; instead the default value (i.e. name of * the unknown enum item) will be returned. *

* * @param typeName User type name. * * @since 1.4 */ public void setCustomType(final String typeName){ if ((this == UNKNOWN || this == UNKNOWN_NUMERIC)){ if (typeName != null && typeName.trim().length() > 0) strExp = "?" + typeName.trim() + "?"; else strExp = this.name(); } } } /** Special value in case no length/size is specified. */ public static final int NO_LENGTH = -1; /** Datatype of a column. */ public final DBDatatype type; /** The length parameter (only few datatypes need this parameter: char, varchar, binary and varbinary). */ public final int length; /** * Build a TAP column type by specifying a datatype. * * @param datatype Column datatype. */ public DBType(final DBDatatype datatype){ this(datatype, NO_LENGTH); } /** * Build a TAP column type by specifying a datatype and a length (needed only for datatypes like char, varchar, binary and varbinary). * * @param datatype Column datatype. * @param length Length of the column value (needed only for datatypes like char, varchar, binary and varbinary). */ public DBType(final DBDatatype datatype, final int length){ if (datatype == null) throw new NullPointerException("Missing TAP column datatype !"); this.type = datatype; this.length = length; } /** *

Tells whether this type is a numeric.

* *

Concerned types: * {@link DBDatatype#SMALLINT SMALLINT}, {@link DBDatatype#INTEGER INTEGER}, {@link DBDatatype#BIGINT BIGINT}, * {@link DBDatatype#REAL REAL}, {@link DBDatatype#DOUBLE DOUBLE}, {@link DBDatatype#BINARY BINARY}, * {@link DBDatatype#VARBINARY VARBINARY} and {@link DBDatatype#BLOB BLOB}. *

* *

Important note: * Since {@link DBDatatype#UNKNOWN UNKNOWN} is an unresolved type, it can potentially be anything. * But, in order to avoid incorrect operation while expecting a numeric although the type is unknown * and is in fact not really a numeric, this function will return false if the type is * {@link DBDatatype#UNKNOWN UNKNOWN} BUT true if * {@link DBDatatype#UNKNOWN_NUMERIC UNKNOWN_NUMERIC}. *

* * @return true if this type is a numeric, false otherwise. */ public boolean isNumeric(){ switch(type){ case SMALLINT: case INTEGER: case BIGINT: case REAL: case DOUBLE: /* Note: binaries are also included here because they can also be considered as Numeric, * but not for JOINs. */ case BINARY: case VARBINARY: case BLOB: case UNKNOWN_NUMERIC: return true; default: return false; } } /** *

Tells whether this type is a list of bytes.

* *

Concerned types: * {@link DBDatatype#BINARY BINARY}, {@link DBDatatype#VARBINARY VARBINARY} and {@link DBDatatype#BLOB BLOB}. *

* *

Important note: * Since {@link DBDatatype#UNKNOWN UNKNOWN} is an unresolved type, it can potentially be anything. * But, in order to avoid incorrect operation while expecting a binary although the type is unknown * and is in fact not really a binary, this function will return false if the type is * {@link DBDatatype#UNKNOWN UNKNOWN} or {@link DBDatatype#UNKNOWN_NUMERIC UNKNOWN_NUMERIC}. *

* * @return true if this type is a binary, false otherwise. */ public boolean isBinary(){ switch(type){ case BINARY: case VARBINARY: case BLOB: return true; default: return false; } } /** *

Tells whether this type is about characters.

* *

Concerned types: * {@link DBDatatype#CHAR CHAR}, {@link DBDatatype#VARCHAR VARCHAR}, {@link DBDatatype#CLOB CLOB} * and {@link DBDatatype#TIMESTAMP TIMESTAMP}. *

* *

Important note: * Since {@link DBDatatype#UNKNOWN UNKNOWN} is an unresolved type, it can potentially be anything. * But, in order to avoid incorrect operation while expecting a string although the type is unknown * and is in fact not really a string, this function will return false if the type is * {@link DBDatatype#UNKNOWN UNKNOWN} or {@link DBDatatype#UNKNOWN_NUMERIC UNKNOWN_NUMERIC} *

* * @return true if this type is a string, false otherwise. */ public boolean isString(){ switch(type){ case CHAR: case VARCHAR: case CLOB: case TIMESTAMP: return true; default: return false; } } /** *

Tells whether this type is a geometrical region.

* *

Concerned types: * {@link DBDatatype#POINT POINT} and {@link DBDatatype#REGION REGION}. *

* *

Important note: * Since {@link DBDatatype#UNKNOWN UNKNOWN} is an unresolved type, it can potentially be anything. * But, in order to avoid incorrect operation while expecting a geometry although the type is unknown * and is in fact not really a geometry, this function will return false if the type is * {@link DBDatatype#UNKNOWN UNKNOWN} or {@link DBDatatype#UNKNOWN_NUMERIC UNKNOWN_NUMERIC}. *

* * @return true if this type is a geometry, false otherwise. */ public boolean isGeometry(){ return (type == DBDatatype.POINT || type == DBDatatype.REGION); } /** *

Tell whether this type has been resolved or not.

* *

Concerned types: * {@link DBDatatype#UNKNOWN UNKNOWN} and {@link DBDatatype#UNKNOWN_NUMERIC UNKNOWN_NUMERIC}. *

* * @return true if this type has NOT been resolved, false otherwise. * * @since 1.4 */ public boolean isUnknown(){ return type == DBDatatype.UNKNOWN || type == DBDatatype.UNKNOWN_NUMERIC; } /** *

Tell whether this {@link DBType} is compatible with the given one.

* *

* Two {@link DBType}s are said compatible if they are both binary, numeric, geometric or string. * If one of the two types is {@link DBDatatype#UNKNOWN unknown} or {@link DBDatatype#UNKNOWN_NUMERIC unknown_numeric}, * this function will consider them as compatible and will return true. *

* * @param t The type to compare to. * * @return true if this type is compatible with the given one, false otherwise. */ public boolean isCompatible(final DBType t){ if (t == null) return false; else if (isUnknown() || t.isUnknown()) return true; else if (isBinary() == t.isBinary()) return (type == DBDatatype.BLOB && t.type == DBDatatype.BLOB) || (type != DBDatatype.BLOB && t.type != DBDatatype.BLOB); else if (isNumeric() == t.isNumeric()) return true; else if (isGeometry() == t.isGeometry()) return (type == t.type); else if (isString()) return (type == DBDatatype.CLOB && t.type == DBDatatype.CLOB) || (type != DBDatatype.CLOB && t.type != DBDatatype.CLOB); else return (type == t.type); } @Override public String toString(){ if (length > 0) return type + "(" + length + ")"; else return type.toString(); } } src/adql/db/SearchColumnList.java0000644000175000017500000003313213177122310015722 0ustar olesolespackage adql.db; /* * 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 . * * Copyright 2012-2017 - UDS/Centre de Données astronomiques de Strasbourg (CDS), * Astronomisches Rechen Institut (ARI) */ import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import adql.query.IdentifierField; import adql.query.from.ADQLJoin; import adql.query.operand.ADQLColumn; import cds.utils.TextualSearchList; /** *

A list of {@link DBColumn} elements ordered by their ADQL name in an ascending manner.

* *

* In addition to an ADQL name, {@link DBColumn} elements can be searched by specifying their table, schema and catalog. * These last information will be used only if the ADQL column name is ambiguous, otherwise all matching elements are returned. *

* *

* Note: * Table aliases can be listed here with their corresponding table name. Consequently, a table alias can be given as table name in the search parameters. *

* * @author Grégory Mantelet (CDS;ARI) * @version 1.4 (09/2017) */ public class SearchColumnList extends TextualSearchList { private static final long serialVersionUID = 1L; /** Indicates whether multiple occurrences are allowed. */ private boolean distinct = false; /** Case-sensitive dictionary of table aliases. (tableAlias <-> TableName) */ private final Map tableAliases = new HashMap(); /** Case-insensitive dictionary of table aliases. (tablealias <-> List<TableName>) */ private final Map> mapAliases = new HashMap>(); /* ************ */ /* CONSTRUCTORS */ /* ************ */ /** * Void constructor. */ public SearchColumnList(){ super(new DBColumnKeyExtractor()); } /** * Constructor by copy: all the elements of the given collection of {@link DBColumn} are copied ordered into this list. * * @param collection Collection of {@link DBColumn} to copy. */ public SearchColumnList(final Collection collection){ super(collection, new DBColumnKeyExtractor()); } /** * Constructor with the initial capacity. * * @param initialCapacity Initial capacity of this list. */ public SearchColumnList(final int initialCapacity){ super(initialCapacity, new DBColumnKeyExtractor()); } /* ******* */ /* GETTERS */ /* ******* */ /** * Tells whether multiple occurrences are allowed. * * @return true means that multiple occurrences are allowed, false otherwise. */ public final boolean isDistinct(){ return distinct; } /** * Lets indicating that multiple occurrences are allowed. * * @param distinct true means that multiple occurrences are allowed, false otherwise. */ public final void setDistinct(final boolean distinct){ this.distinct = distinct; } /* ********************** */ /* TABLE ALIAS MANAGEMENT */ /* ********************** */ /** * Adds the given association between a table name and its alias in a query. * * @param tableAlias Table alias. * @param tableName Table name. */ public final void putTableAlias(final String tableAlias, final String tableName){ if (tableAlias != null && tableName != null){ tableAliases.put(tableAlias, tableName); List aliases = mapAliases.get(tableAlias.toLowerCase()); if (aliases == null){ aliases = new ArrayList(); mapAliases.put(tableAlias.toLowerCase(), aliases); } aliases.add(tableAlias); } } /** * Removes the given alias from this list. * * @param tableAlias The table alias which must be removed. */ public final void removeTableAlias(final String tableAlias){ tableAliases.remove(tableAlias); List aliases = mapAliases.get(tableAlias.toLowerCase()); if (aliases != null){ aliases.remove(tableAlias); if (aliases.isEmpty()) mapAliases.remove(tableAlias.toLowerCase()); } } /** * Removes all table name/alias associations. */ public final void removeAllTableAliases(){ tableAliases.clear(); mapAliases.clear(); } public final int getNbTableAliases(){ return tableAliases.size(); } /* ************** */ /* SEARCH METHODS */ /* ************** */ /** * Searches all {@link DBColumn} elements which has the given name (case insensitive). * * @param columnName ADQL name of {@link DBColumn} to search for. * * @return The corresponding {@link DBColumn} elements. * * @see TextualSearchList#get(String) */ public List search(final String columnName){ return get(columnName); } /** * Searches all {@link DBColumn} elements which have the given catalog, schema, table and column name (case insensitive). * * @param catalog Catalog name. * @param schema Schema name. * @param table Table name. * @param column Column name. * * @return The list of all matching {@link DBColumn} elements. * * @see #search(String, String, String, String, byte) */ public final List search(final String catalog, final String schema, final String table, final String column){ return search(catalog, schema, table, column, (byte)0); } /** * Searches all {@link DBColumn} elements corresponding to the given {@link ADQLColumn} (case insensitive). * * @param column An {@link ADQLColumn}. * * @return The list of all corresponding {@link DBColumn} elements. * * @see #search(String, String, String, String, byte) */ public List search(final ADQLColumn column){ return search(column.getCatalogName(), column.getSchemaName(), column.getTableName(), column.getColumnName(), column.getCaseSensitive()); } /** * Searches all {@link DBColumn} elements which have the given catalog, schema, table and column name, with the specified case sensitivity. * * @param catalog Catalog name. * @param schema Schema name. * @param table Table name. * @param column Column name. * @param caseSensitivity Case sensitivity for each column parts (one bit by part ; 0=sensitive,1=insensitive ; see {@link IdentifierField} for more details). * * @return The list of all matching {@link DBColumn} elements. * * @see IdentifierField */ public List search(final String catalog, final String schema, final String table, final String column, final byte caseSensitivity){ List tmpResult = get(column, IdentifierField.COLUMN.isCaseSensitive(caseSensitivity)); /* WITH TABLE PREFIX */ if (table != null){ /* 1. Figure out the table alias */ String tableName = null; List aliasMatches = null; // Case sensitive => tableName is set , aliasMatches = null if (IdentifierField.TABLE.isCaseSensitive(caseSensitivity)){ tableName = tableAliases.get(table); if (tableName == null) tableName = table; } // Case INsensitive // a) Alias is found => tableName = null , aliasMatches contains the list of all tables matching the alias // b) No alias => tableName = table , aliasMatches = null else{ aliasMatches = mapAliases.get(table.toLowerCase()); if (aliasMatches == null || aliasMatches.isEmpty()) tableName = table; } /* 2. For each found column, test whether its table, schema and catalog names match. * If it matches, keep the column aside. */ ArrayList result = new ArrayList(); for(DBColumn match : tmpResult){ // Get the list of all tables covered by this column: // - only 1 if it is a normal column // - several if it is a common column (= result of table join) Iterator itMatchTables; if (ADQLJoin.isCommonColumn(match)) itMatchTables = ((DBCommonColumn)match).getCoveredTables(); else itMatchTables = new SingleIterator(match.getTable()); // Test the matching with every covered tables: DBTable matchTable; while(itMatchTables.hasNext()){ // get the table: matchTable = itMatchTables.next(); // test the table name: if (aliasMatches == null){ // case table name is (sensitive) or (INsensitive with no alias found) if (IdentifierField.TABLE.isCaseSensitive(caseSensitivity)){ if (!matchTable.getADQLName().equals(tableName)) continue; }else{ if (!matchTable.getADQLName().equalsIgnoreCase(tableName)) continue; } }else{ // case INsensitive with at least one alias found boolean foundAlias = false; String temp; for(int a = 0; !foundAlias && a < aliasMatches.size(); a++){ temp = tableAliases.get(aliasMatches.get(a)); if (temp != null) foundAlias = matchTable.getADQLName().equalsIgnoreCase(temp); } if (!foundAlias) continue; } // test the schema name: if (schema != null){ // No schema name (<=> no schema), then this table can not be a good match: if (matchTable.getADQLSchemaName() == null) continue; if (IdentifierField.SCHEMA.isCaseSensitive(caseSensitivity)){ if (!matchTable.getADQLSchemaName().equals(schema)) continue; }else{ if (!matchTable.getADQLSchemaName().equalsIgnoreCase(schema)) continue; } // test the catalog name: if (catalog != null){ // No catalog name (<=> no catalog), then this table can not be a good match: if (matchTable.getADQLCatalogName() == null) continue; if (IdentifierField.CATALOG.isCaseSensitive(caseSensitivity)){ if (!matchTable.getADQLCatalogName().equals(catalog)) continue; }else{ if (!matchTable.getADQLCatalogName().equalsIgnoreCase(catalog)) continue; } } } // if here, all prefixes are matching and so the column is a good match: DBColumn goodMatch = matchTable.getColumn(match.getADQLName(), true); result.add(goodMatch); } } return result; } /* NO TABLE PREFIX */ else{ // Special case: the columns merged by a NATURAL JOIN or a USING may have no table reference: if (tmpResult.size() > 1){ // List all common columns. If there are several, only the list of matching normal columns must be returned. // This list must not contain common columns. // Instead, it must contains all normal columns covered by the common columns. ArrayList result = new ArrayList(tmpResult.size()); for(int i = 0; i < tmpResult.size(); i++){ if (ADQLJoin.isCommonColumn(tmpResult.get(i))){ // this common column is a good match // => add it into the list of matching common columns // AND remove it from the normal columns list DBCommonColumn commonColumn = (DBCommonColumn)tmpResult.remove(i); result.add(commonColumn); // then, add all normal columns covered by this common columns: Iterator itCoveredTables = commonColumn.getCoveredTables(); while(itCoveredTables.hasNext()) tmpResult.add(itCoveredTables.next().getColumn(column, true)); } } if (result.size() == 1) return result; } return tmpResult; } } /* ***************** */ /* INHERITED METHODS */ /* ***************** */ @Override public boolean add(final DBColumn item){ if (distinct && contains(item)) return false; else return super.add(item); } @Override public boolean addAll(final Collection c){ boolean changed = super.addAll(c); if (changed){ if (c instanceof SearchColumnList){ SearchColumnList list = (SearchColumnList)c; for(Map.Entry entry : list.tableAliases.entrySet()) putTableAlias(entry.getKey(), entry.getValue()); } } return changed; } @Override public boolean removeAll(final Collection c){ boolean changed = super.removeAll(c); if (changed){ if (c instanceof SearchColumnList){ SearchColumnList list = (SearchColumnList)c; for(String key : list.tableAliases.keySet()) removeTableAlias(key); } } return changed; } /** * Lets extracting the key to associate with a given {@link DBColumn} instance. * * @author Grégory Mantelet (CDS) * @version 09/2011 */ private static class DBColumnKeyExtractor implements KeyExtractor { @Override public String getKey(DBColumn obj){ return obj.getADQLName(); } } /** * Iterator that iterates over only one item, given in the constructor. * * @param Type of the item that this Iterator must return. * * @author Grégory Mantelet (ARI) - gmantele@ari.uni-heidelberg.de * @version 1.2 (11/2013) * @since 1.2 */ private static class SingleIterator< E > implements Iterator { private final E item; private boolean done = false; public SingleIterator(final E singleItem){ item = singleItem; } @Override public boolean hasNext(){ return !done; } @Override public E next(){ if (!done){ done = true; return item; }else throw new NoSuchElementException(); } @Override public void remove(){ throw new UnsupportedOperationException(); } } } src/adql/db/DefaultDBColumn.java0000644000175000017500000001411113177122310015447 0ustar olesolespackage adql.db; /* * 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 . * * Copyright 2012,2015 - UDS/Centre de Données astronomiques de Strasbourg (CDS), * Astronomisches Rechen Institut (ARI) */ /** * Default implementation of {@link DBColumn}. * * @author Grégory Mantelet (CDS;ARI) * @version 1.4 (08/2015) */ public class DefaultDBColumn implements DBColumn { /** Name of the column in the "database". */ protected String dbName; /** Type of the column in the "database". * Note: This should be one of the types listed by the IVOA in the TAP description. * @since 1.3 */ protected DBType type; /** Table in which this column exists. */ protected DBTable table; /** Name that this column must have in ADQL queries. */ protected String adqlName = null; /** * Builds a default {@link DBColumn} with the given DB name and DB table. * * @param dbName Database column name (it will be also used for the ADQL name). * Only the column name is expected. Contrary to {@link DefaultDBTable}, * if a whole column reference is given, no split will be done. * @param table DB table which contains this column. * * @see #DefaultDBColumn(String, String, DBType, DBTable) */ public DefaultDBColumn(final String dbName, final DBTable table){ this(dbName, dbName, null, table); } /** * Builds a default {@link DBColumn} with the given DB name and DB table. * * @param dbName Database column name (it will be also used for the ADQL name). * Only the column name is expected. Contrary to {@link DefaultDBTable}, * if a whole column reference is given, no split will be done. * @param type Type of the column. * Note: there is no default value. Consequently if this parameter is NULL, * the type should be considered as unknown. It means that any comparison with * any type will always return 'true'. * @param table DB table which contains this column. * * @see #DefaultDBColumn(String, String, DBType, DBTable) * * @since 1.3 */ public DefaultDBColumn(final String dbName, final DBType type, final DBTable table){ this(dbName, dbName, type, table); } /** * Builds a default {@link DBColumn} with the given DB name, DB table and ADQL name. * * @param dbName Database column name. * Only the column name is expected. Contrary to {@link DefaultDBTable}, * if a whole column reference is given, no split will be done. * @param adqlName Column name used in ADQL queries. * Only the column name is expected. Contrary to {@link DefaultDBTable}, * if a whole column reference is given, no split will be done. * @param table DB table which contains this column. * * @see #DefaultDBColumn(String, String, DBType, DBTable) */ public DefaultDBColumn(final String dbName, final String adqlName, final DBTable table){ this(dbName, adqlName, null, table); } /** * Builds a default {@link DBColumn} with the given DB name, DB table and ADQL name. * * @param dbName Database column name. * Only the column name is expected. Contrary to {@link DefaultDBTable}, * if a whole column reference is given, no split will be done. * REQUIRED parameter: it must be not NULL. * @param adqlName Column name used in ADQL queries. * Only the column name is expected. Contrary to {@link DefaultDBTable}, * if a whole column reference is given, no split will be done. * If NULL, it will be set to dbName. * @param type Type of the column. * Note: there is no default value. Consequently if this parameter is NULL, * the type should be considered as unknown. It means that any comparison with * any type will always return 'true'. * @param table DB table which contains this column. * * @since 1.3 */ public DefaultDBColumn(final String dbName, final String adqlName, final DBType type, final DBTable table){ if (dbName == null || dbName.length() == 0) throw new NullPointerException("Missing DB name!"); this.dbName = dbName; this.adqlName = (adqlName == null) ? dbName : adqlName; this.type = type; this.table = table; } @Override public final String getADQLName(){ return adqlName; } public final void setADQLName(final String adqlName){ if (adqlName != null) this.adqlName = adqlName; } @Override public final DBType getDatatype(){ return type; } /** *

Set the type of this column.

* *

Note 1: * The given type should be as closed as possible from a type listed by the IVOA in the TAP protocol description into the section UPLOAD. *

* *

Note 2: * there is no default value. Consequently if this parameter is NULL, * the type should be considered as unknown. It means that any comparison with * any type will always return 'true'. *

* * @param type New type of this column. * * @since 1.3 */ public final void setDatatype(final DBType type){ this.type = type; } @Override public final String getDBName(){ return dbName; } @Override public final DBTable getTable(){ return table; } public final void setTable(final DBTable table){ this.table = table; } @Override public DBColumn copy(final String dbName, final String adqlName, final DBTable dbTable){ return new DefaultDBColumn(dbName, adqlName, type, dbTable); } } src/adql/db/SearchTableList.java0000644000175000017500000001472113177122310015517 0ustar olesolespackage adql.db; /* * 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 . * * Copyright 2012-2017 - UDS/Centre de Données astronomiques de Strasbourg (CDS) * Astronomisches Rechen Institut (ARI) */ import java.util.ArrayList; import java.util.Collection; import java.util.List; import adql.query.IdentifierField; import adql.query.from.ADQLTable; import cds.utils.TextualSearchList; /** *

A list of {@link DBTable} elements ordered by their ADQL name in an ascending manner.

* *

* In addition to an ADQL name, {@link DBTable} elements can be searched by specifying their schema and catalog. * These last information will be used only if the ADQL table name is ambiguous, otherwise all matching elements are returned. *

* * @author Grégory Mantelet (CDS;ARI) * @version 1.4 (09/2017) */ public class SearchTableList extends TextualSearchList implements SearchTableApi { private static final long serialVersionUID = 1L; /** Indicates whether multiple occurrences are allowed. */ private boolean distinct = false; /* ************ */ /* CONSTRUCTORS */ /* ************ */ /** * Void constructor. */ public SearchTableList(){ super(new DBTableKeyExtractor()); } /** * Constructor by copy: all the elements of the given collection of {@link DBTable} are copied ordered into this list. * * @param collection Collection of {@link DBTable} to copy. */ public SearchTableList(final Collection collection){ super(collection, new DBTableKeyExtractor()); } /** * Constructor with the initial capacity. * * @param initialCapacity Initial capacity of this list. */ public SearchTableList(final int initialCapacity){ super(initialCapacity, new DBTableKeyExtractor()); } /* ******* */ /* GETTERS */ /* ******* */ /** * Tells whether multiple occurrences are allowed. * * @return true means that multiple occurrences are allowed, false otherwise. */ public final boolean isDistinct(){ return distinct; } /** * Lets indicating that multiple occurrences are allowed. * * @param distinct true means that multiple occurrences are allowed, false otherwise. */ public final void setDistinct(final boolean distinct){ this.distinct = distinct; } /* ************** */ /* SEARCH METHODS */ /* ************** */ /** * Searches all {@link DBTable} elements which has the given name (case insensitive). * * @param tableName ADQL name of {@link DBTable} to search for. * * @return The corresponding {@link DBTable} elements. * * @see TextualSearchList#get(String) */ public List search(final String tableName){ return get(tableName); } /** * Searches all {@link DBTable} elements which have the given catalog, schema, and table name (case insensitive). * * @param catalog Catalog name. * @param schema Schema name. * @param table Table name. * * @return The list of all matching {@link DBTable} elements. * * @see #search(String, String, String, byte) */ public final List search(final String catalog, final String schema, final String table){ return search(catalog, schema, table, (byte)0); } /** * Searches all {@link DBTable} elements corresponding to the given {@link ADQLTable} (case insensitive). * * @param table An {@link ADQLTable}. * * @return The list of all corresponding {@link DBTable} elements. * * @see #search(String, String, String, byte) */ @Override public List search(final ADQLTable table){ return search(table.getCatalogName(), table.getSchemaName(), table.getTableName(), table.getCaseSensitive()); } /** * Searches all {@link DBTable} elements which have the given catalog, schema, and table name, with the specified case sensitivity. * * @param catalog Catalog name. * @param schema Schema name. * @param table Table name. * @param caseSensitivity Case sensitivity for each table parts (one bit by part ; 0=sensitive,1=insensitive ; see {@link IdentifierField} for more details). * * @return The list of all matching {@link DBTable} elements. * * @see IdentifierField */ public List search(final String catalog, final String schema, final String table, final byte caseSensitivity){ List tmpResult = get(table, IdentifierField.TABLE.isCaseSensitive(caseSensitivity)); if (schema != null){ List result = new ArrayList(); for(DBTable match : tmpResult){ // No schema name (<=> no schema), then this table can not be a good match: if (match.getADQLSchemaName() == null) continue; if (IdentifierField.SCHEMA.isCaseSensitive(caseSensitivity)){ if (!match.getADQLSchemaName().equals(schema)) continue; }else{ if (!match.getADQLSchemaName().equalsIgnoreCase(schema)) continue; } if (catalog != null){ // No catalog name (<=> no catalog), then this table can not be a good match: if (match.getADQLCatalogName() == null) continue; if (IdentifierField.CATALOG.isCaseSensitive(caseSensitivity)){ if (!match.getADQLCatalogName().equals(catalog)) continue; }else{ if (!match.getADQLCatalogName().equalsIgnoreCase(catalog)) continue; } } result.add(match); } return result; }else return tmpResult; } /* ***************** */ /* INHERITED METHODS */ /* ***************** */ @Override public boolean add(final DBTable item){ if (distinct && contains(item)) return false; else return super.add(item); } /** * Lets extracting a key to associate with a given {@link DBTable} instance. * * @author Grégory Mantelet (CDS) * @version 09/2011 */ private static class DBTableKeyExtractor implements KeyExtractor { @Override public String getKey(DBTable obj){ return obj.getADQLName(); } } } src/adql/db/DBTableAlias.java0000644000175000017500000000430313210016772014712 0ustar olesolespackage adql.db; /* * 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 . * * Copyright 2017 - Astronomisches Rechen Institut (ARI) */ /** * This {@link DBTable} wraps another {@link DBTable} with a different ADQL and * DB name. * *

* This wrapper aims to represent in the metadata the aliasing of a table. * This table should not be part of any schema, in ADQL but also in SQL...it is * just an alias of an existing table. *

* *

* All columns of the origin table are completely copied into this * {@link DBTable} thanks to {@link DBColumn#copy(String, String, DBTable)}, * with the same ADQL and DB name but a different parent table (this one is * used of the original one). *

* *

Note: * The origin table is still available thanks to the function * {@link #getOriginTable()}. *

* * @author Grégory Mantelet (ARI) * @version 1.4 (11/2017) * @since 1.4 */ public class DBTableAlias extends DefaultDBTable { /** Wrapped table. */ protected final DBTable originTable; /** * Wrap the given table under the given ADQL/DB name. * * @param originTable The table to wrap/alias. * @param tableAlias The alias name. */ public DBTableAlias(final DBTable originTable, final String tableAlias){ super(null, null, tableAlias); this.originTable = originTable; for(DBColumn col : originTable) addColumn(col.copy(col.getDBName(), col.getADQLName(), this)); } /** * Get the aliased/wrapped table. * * @return The aliased table. */ public DBTable getOriginTable(){ return originTable; } } src/adql/db/DBChecker.java0000644000175000017500000020640313226136374014272 0ustar olesolespackage adql.db; /* * 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 . * * Copyright 2011-2017 - UDS/Centre de Données astronomiques de Strasbourg (CDS), * Astronomisches Rechen Institut (ARI) */ import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Stack; import adql.db.STCS.CoordSys; import adql.db.STCS.Region; import adql.db.STCS.RegionType; import adql.db.exception.UnresolvedColumnException; import adql.db.exception.UnresolvedFunctionException; import adql.db.exception.UnresolvedIdentifiersException; import adql.db.exception.UnresolvedTableException; import adql.parser.ParseException; import adql.parser.QueryChecker; import adql.query.ADQLIterator; import adql.query.ADQLObject; import adql.query.ADQLQuery; import adql.query.ClauseADQL; import adql.query.ClauseSelect; import adql.query.ColumnReference; import adql.query.IdentifierField; import adql.query.SelectAllColumns; import adql.query.SelectItem; import adql.query.from.ADQLTable; import adql.query.from.FromContent; import adql.query.operand.ADQLColumn; import adql.query.operand.ADQLOperand; import adql.query.operand.StringConstant; import adql.query.operand.UnknownType; import adql.query.operand.function.ADQLFunction; import adql.query.operand.function.DefaultUDF; import adql.query.operand.function.UserDefinedFunction; import adql.query.operand.function.geometry.BoxFunction; import adql.query.operand.function.geometry.CircleFunction; import adql.query.operand.function.geometry.GeometryFunction; import adql.query.operand.function.geometry.PointFunction; import adql.query.operand.function.geometry.PolygonFunction; import adql.query.operand.function.geometry.RegionFunction; import adql.search.ISearchHandler; import adql.search.SearchColumnHandler; import adql.search.SimpleReplaceHandler; import adql.search.SimpleSearchHandler; /** * This {@link QueryChecker} implementation is able to do the following verifications on an ADQL query: *
    *
  1. Check the existence of all table and column references found in a query
  2. *
  3. Resolve all unknown functions as supported User Defined Functions (UDFs)
  4. *
  5. Check whether all used geometrical functions are supported
  6. *
  7. Check whether all used coordinate systems are supported
  8. *
  9. Check that types of columns and UDFs match with their context
  10. *
* *

Check tables and columns

*

* In addition to check the existence of tables and columns referenced in the query, * this checked will also attach database metadata on these references ({@link ADQLTable} * and {@link ADQLColumn} instances when they are resolved. *

* *

These information are:

*
    *
  • the corresponding {@link DBTable} or {@link DBColumn} (see getter and setter for DBLink in {@link ADQLTable} and {@link ADQLColumn})
  • *
  • the link between an {@link ADQLColumn} and its {@link ADQLTable}
  • *
* *

Note: * Knowing DB metadata of {@link ADQLTable} and {@link ADQLColumn} is particularly useful for the translation of the ADQL query to SQL, * because the ADQL name of columns and tables can be replaced in SQL by their DB name, if different. This mapping is done automatically * by {@link adql.translator.JDBCTranslator}. *

* * @author Grégory Mantelet (CDS;ARI) * @version 1.4 (11/2017) */ public class DBChecker implements QueryChecker { /** List of all available tables ({@link DBTable}). */ protected SearchTableApi lstTables; /**

List of all allowed geometrical functions (i.e. CONTAINS, REGION, POINT, COORD2, ...).

*

* If this list is NULL, all geometrical functions are allowed. * However, if not, all items of this list must be the only allowed geometrical functions. * So, if the list is empty, no such function is allowed. *

* @since 1.3 */ protected String[] allowedGeo = null; /**

List of all allowed coordinate systems.

*

* Each item of this list must be of the form: "{frame} {refpos} {flavor}". * Each of these 3 items can be either of value, a list of values expressed with the syntax "({value1}|{value2}|...)" * or a '*' to mean all possible values. *

*

Note: since a default value (corresponding to the empty string - '') should always be possible for each part of a coordinate system, * the checker will always add the default value (UNKNOWNFRAME, UNKNOWNREFPOS or SPHERICAL2) into the given list of possible values for each coord. sys. part.

*

* If this list is NULL, all coordinates systems are allowed. * However, if not, all items of this list must be the only allowed coordinate systems. * So, if the list is empty, none is allowed. *

* @since 1.3 */ protected String[] allowedCoordSys = null; /**

A regular expression built using the list of allowed coordinate systems. * With this regex, it is possible to known whether a coordinate system expression is allowed or not.

*

If NULL, all coordinate systems are allowed.

* @since 1.3 */ protected String coordSysRegExp = null; /**

List of all allowed User Defined Functions (UDFs).

*

* If this list is NULL, any encountered UDF will be allowed. * However, if not, all items of this list must be the only allowed UDFs. * So, if the list is empty, no UDF is allowed. *

* @since 1.3 */ protected FunctionDef[] allowedUdfs = null; /* ************ */ /* CONSTRUCTORS */ /* ************ */ /** *

Builds a {@link DBChecker} with an empty list of tables.

* *

Verifications done by this object after creation:

*
    *
  • Existence of tables and columns: NO (even unknown or fake tables and columns are allowed)
  • *
  • Existence of User Defined Functions (UDFs): NO (any "unknown" function is allowed)
  • *
  • Support of geometrical functions: NO (all valid geometrical functions are allowed)
  • *
  • Support of coordinate systems: NO (all valid coordinate systems are allowed)
  • *
*/ public DBChecker(){ this(null, null); } /** *

Builds a {@link DBChecker} with the given list of known tables.

* *

Verifications done by this object after creation:

*
    *
  • Existence of tables and columns: OK
  • *
  • Existence of User Defined Functions (UDFs): NO (any "unknown" function is allowed)
  • *
  • Support of geometrical functions: NO (all valid geometrical functions are allowed)
  • *
  • Support of coordinate systems: NO (all valid coordinate systems are allowed)
  • *
* * @param tables List of all available tables. */ public DBChecker(final Collection tables){ this(tables, null); } /** *

Builds a {@link DBChecker} with the given list of known tables and with a restricted list of user defined functions.

* *

Verifications done by this object after creation:

*
    *
  • Existence of tables and columns: OK
  • *
  • Existence of User Defined Functions (UDFs): OK
  • *
  • Support of geometrical functions: NO (all valid geometrical functions are allowed)
  • *
  • Support of coordinate systems: NO (all valid coordinate systems are allowed)
  • *
* * @param tables List of all available tables. * @param allowedUdfs List of all allowed user defined functions. * If NULL, no verification will be done (and so, all UDFs are allowed). * If empty list, no "unknown" (or UDF) is allowed. * Note: match with items of this list are done case insensitively. * * @since 1.3 */ public DBChecker(final Collection tables, final Collection allowedUdfs){ // Sort and store the given tables: setTables(tables); Object[] tmp; int cnt; // Store all allowed UDFs in a sorted array: if (allowedUdfs != null){ // Remove all NULL and empty strings: tmp = new FunctionDef[allowedUdfs.size()]; cnt = 0; for(FunctionDef udf : allowedUdfs){ if (udf != null && udf.name.trim().length() > 0) tmp[cnt++] = udf; } // make a copy of the array: this.allowedUdfs = new FunctionDef[cnt]; System.arraycopy(tmp, 0, this.allowedUdfs, 0, cnt); tmp = null; // sort the values: Arrays.sort(this.allowedUdfs); } } /** *

Builds a {@link DBChecker} with the given list of known tables and with a restricted list of user defined functions.

* *

Verifications done by this object after creation:

*
    *
  • Existence of tables and columns: OK
  • *
  • Existence of User Defined Functions (UDFs): NO (any "unknown" function is allowed)
  • *
  • Support of geometrical functions: OK
  • *
  • Support of coordinate systems: OK
  • *
* * @param tables List of all available tables. * @param allowedGeoFcts List of all allowed geometrical functions (i.e. CONTAINS, POINT, UNION, CIRCLE, COORD1). * If NULL, no verification will be done (and so, all geometries are allowed). * If empty list, no geometry function is allowed. * Note: match with items of this list are done case insensitively. * @param allowedCoordSys List of all allowed coordinate system patterns. The syntax of a such pattern is the following: * "{frame} {refpos} {flavor}" ; on the contrary to a coordinate system expression, here no part is optional. * Each part of this pattern can be one the possible values (case insensitive), a list of possible values * expressed with the syntax "({value1}|{value2}|...)", or a '*' for any valid value. * For instance: "ICRS (GEOCENTER|heliocenter) *". * If the given list is NULL, no verification will be done (and so, all coordinate systems are allowed). * If it is empty, no coordinate system is allowed (except the default values - generally expressed by an empty string: ''). * * @since 1.3 */ public DBChecker(final Collection tables, final Collection allowedGeoFcts, final Collection allowedCoordSys) throws ParseException{ this(tables, null, allowedGeoFcts, allowedCoordSys); } /** *

Builds a {@link DBChecker}.

* *

Verifications done by this object after creation:

*
    *
  • Existence of tables and columns: OK
  • *
  • Existence of User Defined Functions (UDFs): OK
  • *
  • Support of geometrical functions: OK
  • *
  • Support of coordinate systems: OK
  • *
* * @param tables List of all available tables. * @param allowedUdfs List of all allowed user defined functions. * If NULL, no verification will be done (and so, all UDFs are allowed). * If empty list, no "unknown" (or UDF) is allowed. * Note: match with items of this list are done case insensitively. * @param allowedGeoFcts List of all allowed geometrical functions (i.e. CONTAINS, POINT, UNION, CIRCLE, COORD1). * If NULL, no verification will be done (and so, all geometries are allowed). * If empty list, no geometry function is allowed. * Note: match with items of this list are done case insensitively. * @param allowedCoordSys List of all allowed coordinate system patterns. The syntax of a such pattern is the following: * "{frame} {refpos} {flavor}" ; on the contrary to a coordinate system expression, here no part is optional. * Each part of this pattern can be one the possible values (case insensitive), a list of possible values * expressed with the syntax "({value1}|{value2}|...)", or a '*' for any valid value. * For instance: "ICRS (GEOCENTER|heliocenter) *". * If the given list is NULL, no verification will be done (and so, all coordinate systems are allowed). * If it is empty, no coordinate system is allowed (except the default values - generally expressed by an empty string: ''). * * @since 1.3 */ public DBChecker(final Collection tables, final Collection allowedUdfs, final Collection allowedGeoFcts, final Collection allowedCoordSys) throws ParseException{ // Set the list of available tables + Set the list of all known UDFs: this(tables, allowedUdfs); // Set the list of allowed geometrical functions: allowedGeo = specialSort(allowedGeoFcts); // Set the list of allowed coordinate systems: this.allowedCoordSys = specialSort(allowedCoordSys); coordSysRegExp = STCS.buildCoordSysRegExp(this.allowedCoordSys); } /** * Transform the given collection of string elements in a sorted array. * Only non-NULL and non-empty strings are kept. * * @param items Items to copy and sort. * * @return A sorted array containing all - except NULL and empty strings - items of the given collection. * * @since 1.3 */ protected final static String[] specialSort(final Collection items){ // Nothing to do if the array is NULL: if (items == null) return null; // Keep only valid items (not NULL and not empty string): String[] tmp = new String[items.size()]; int cnt = 0; for(String item : items){ if (item != null && item.trim().length() > 0) tmp[cnt++] = item; } // Make an adjusted array copy: String[] copy = new String[cnt]; System.arraycopy(tmp, 0, copy, 0, cnt); // Sort the values: Arrays.sort(copy); return copy; } /* ****** */ /* SETTER */ /* ****** */ /** *

Sets the list of all available tables.

* *

Note: * Only if the given collection is NOT an implementation of * {@link SearchTableApi}, the collection will be copied inside a new * {@link SearchTableList}, otherwise it is used as provided. *

* * @param tables List of {@link DBTable}s. */ public final void setTables(final Collection tables){ if (tables == null) lstTables = new SearchTableList(); else if (tables instanceof SearchTableApi) lstTables = (SearchTableApi)tables; else lstTables = new SearchTableList(tables); } /* ************* */ /* CHECK METHODS */ /* ************* */ /** *

Check all the columns, tables and UDFs references inside the given query.

* *

* Note: This query has already been parsed ; thus it is already syntactically correct. * Only the consistency with the published tables, columns and all the defined UDFs must be checked. *

* * @param query The query to check. * * @throws ParseException An {@link UnresolvedIdentifiersException} if some tables or columns can not be resolved. * * @see #check(ADQLQuery, Stack) */ @Override public final void check(final ADQLQuery query) throws ParseException{ check(query, null); } /** *

Process several (semantic) verifications in the given ADQL query.

* *

Main verifications done in this function:

*
    *
  1. Existence of DB items (tables and columns)
  2. *
  3. Semantic verification of sub-queries
  4. *
  5. Support of every encountered User Defined Functions (UDFs - functions unknown by the syntactic parser)
  6. *
  7. Support of every encountered geometries (functions, coordinate systems and STC-S expressions)
  8. *
  9. Consistency of types still unknown (because the syntactic parser could not yet resolve them)
  10. *
* * @param query The query to check. * @param fathersList List of all columns available in the father queries and that should be accessed in sub-queries. * Each item of this stack is a list of columns available in each father-level query. * Note: this parameter is NULL if this function is called with the root/father query as parameter. * * @throws UnresolvedIdentifiersException An {@link UnresolvedIdentifiersException} if one or several of the above listed tests have detected * some semantic errors (i.e. unresolved table, columns, function). * * @since 1.2 * * @see #checkDBItems(ADQLQuery, Stack, UnresolvedIdentifiersException) * @see #checkSubQueries(ADQLQuery, Stack, SearchColumnList, UnresolvedIdentifiersException) * @see #checkUDFs(ADQLQuery, UnresolvedIdentifiersException) * @see #checkGeometries(ADQLQuery, UnresolvedIdentifiersException) * @see #checkTypes(ADQLQuery, UnresolvedIdentifiersException) */ protected void check(final ADQLQuery query, final Stack fathersList) throws UnresolvedIdentifiersException{ UnresolvedIdentifiersException errors = new UnresolvedIdentifiersException(); // A. Check DB items (tables and columns): SearchColumnList availableColumns = checkDBItems(query, fathersList, errors); // B. Check UDFs: if (allowedUdfs != null) checkUDFs(query, errors); // C. Check geometries: checkGeometries(query, errors); // D. Check types: checkTypes(query, errors); // E. Check sub-queries: checkSubQueries(query, fathersList, availableColumns, errors); // Throw all errors, if any: if (errors.getNbErrors() > 0) throw errors; } /* ************************************************ */ /* CHECKING METHODS FOR DB ITEMS (TABLES & COLUMNS) */ /* ************************************************ */ /** *

Check DB items (tables and columns) used in the given ADQL query.

* *

Operations done in this function:

*
    *
  1. Resolve all found tables
  2. *
  3. Get the whole list of all available columns Note: this list is returned by this function.
  4. *
  5. Resolve all found columns
  6. *
* * @param query Query in which the existence of DB items must be checked. * @param fathersList List of all columns available in the father queries and that should be accessed in sub-queries. * Each item of this stack is a list of columns available in each father-level query. * Note: this parameter is NULL if this function is called with the root/father query as parameter. * @param errors List of errors to complete in this function each time an unknown table or column is encountered. * * @return List of all columns available in the given query. * * @see #resolveTables(ADQLQuery, Stack, UnresolvedIdentifiersException) * @see FromContent#getDBColumns() * @see #resolveColumns(ADQLQuery, Stack, Map, SearchColumnList, UnresolvedIdentifiersException) * * @since 1.3 */ protected SearchColumnList checkDBItems(final ADQLQuery query, final Stack fathersList, final UnresolvedIdentifiersException errors){ // a. Resolve all tables: Map mapTables = resolveTables(query, fathersList, errors); // b. Get the list of all columns made available in the clause FROM: SearchColumnList availableColumns; try{ availableColumns = query.getFrom().getDBColumns(); }catch(ParseException pe){ errors.addException(pe); availableColumns = new SearchColumnList(); } // c. Resolve all columns: resolveColumns(query, fathersList, mapTables, availableColumns, errors); return availableColumns; } /** * Search all table references inside the given query, resolve them against * the available tables, and if there is only one match, attach the matching * metadata to them. * * Management of sub-query tables *

* If a table is not a DB table reference but a sub-query, this latter is * first checked (using {@link #check(ADQLQuery, Stack)} ; but the father * list must not contain tables of the given query, because on the same * level) and then corresponding table metadata are generated (using * {@link #generateDBTable(ADQLQuery, String)}) and attached to it. *

* * Management of "{table}.*" in the SELECT clause *

* For each of this SELECT item, this function tries to resolve the table * name. If only one match is found, the corresponding ADQL table object * is got from the list of resolved tables and attached to this SELECT item * (thus, the joker item will also have the good metadata, particularly if * the referenced table is a sub-query). *

* * Table alias *

* When a simple table (i.e. not a sub-query) is aliased, the metadata of * this table will be wrapped inside a {@link DBTableAlias} in order to * keep the original metadata but still declare use the table with the * alias instead of its original name. The original name will be used * only when translating the corresponding FROM item ; the rest of the time * (i.e. for references when using a column), the alias name must be used. *

*

* In order to avoid unpredictable behavior at execution of the SQL query, * the alias will be put in lower case if not defined between double * quotes. *

* * @param query Query in which the existence of tables must be * checked. * @param fathersList List of all columns available in the father queries * and that should be accessed in sub-queries. * Each item of this stack is a list of columns * available in each father-level query. * Note: this parameter is NULL if this function is * called with the root/father query as parameter. * @param errors List of errors to complete in this function each * time an unknown table or column is encountered. * * @return An associative map of all the resolved tables. */ protected Map resolveTables(final ADQLQuery query, final Stack fathersList, final UnresolvedIdentifiersException errors){ HashMap mapTables = new HashMap(); ISearchHandler sHandler; // Check the existence of all tables: sHandler = new SearchTableHandler(); sHandler.search(query.getFrom()); for(ADQLObject result : sHandler){ try{ ADQLTable table = (ADQLTable)result; // resolve the table: DBTable dbTable = null; if (table.isSubQuery()){ // check the sub-query tables: check(table.getSubQuery(), fathersList); // generate its DBTable: dbTable = generateDBTable(table.getSubQuery(), table.getAlias()); }else{ dbTable = resolveTable(table); // wrap this table metadata if an alias should be used: if (dbTable != null && table.hasAlias()) dbTable = new DBTableAlias(dbTable, (table.isCaseSensitive(IdentifierField.ALIAS) ? table.getAlias() : table.getAlias().toLowerCase())); } // link with the matched DBTable: table.setDBLink(dbTable); mapTables.put(dbTable, table); }catch(ParseException pe){ errors.addException(pe); } } // Attach table information on wildcards with the syntax "{tableName}.*" of the SELECT clause: /* Note: no need to check the table name among the father tables, because there is * no interest to select a father column in a sub-query * (which can return only one column ; besides, no aggregate is allowed * in sub-queries).*/ sHandler = new SearchWildCardHandler(); sHandler.search(query.getSelect()); for(ADQLObject result : sHandler){ try{ SelectAllColumns wildcard = (SelectAllColumns)result; ADQLTable table = wildcard.getAdqlTable(); DBTable dbTable = null; // first, try to resolve the table by table alias: if (table.getTableName() != null && table.getSchemaName() == null){ List tables = query.getFrom().getTablesByAlias(table.getTableName(), table.isCaseSensitive(IdentifierField.TABLE)); if (tables.size() == 1) dbTable = tables.get(0).getDBLink(); } // then try to resolve the table reference by table name: if (dbTable == null) dbTable = resolveTable(table); // set the corresponding tables among the list of resolved tables: wildcard.setAdqlTable(mapTables.get(dbTable)); }catch(ParseException pe){ errors.addException(pe); } } return mapTables; } /** * Resolve the given table, that's to say search for the corresponding {@link DBTable}. * * @param table The table to resolve. * * @return The corresponding {@link DBTable} if found, null otherwise. * * @throws ParseException An {@link UnresolvedTableException} if the given table can't be resolved. */ protected DBTable resolveTable(final ADQLTable table) throws ParseException{ List tables = lstTables.search(table); // good if only one table has been found: if (tables.size() == 1) return tables.get(0); // but if more than one: ambiguous table name ! else if (tables.size() > 1) throw new UnresolvedTableException(table, (tables.get(0).getADQLSchemaName() == null ? "" : tables.get(0).getADQLSchemaName() + ".") + tables.get(0).getADQLName(), (tables.get(1).getADQLSchemaName() == null ? "" : tables.get(1).getADQLSchemaName() + ".") + tables.get(1).getADQLName()); // otherwise (no match): unknown table ! else throw new UnresolvedTableException(table); } /** *

Search all column references inside the given query, resolve them thanks to the given tables' metadata, * and if there is only one match, attach the matching metadata to them.

* * Management of selected columns' references *

* A column reference is not only a direct reference to a table column using a column name. * It can also be a reference to an item of the SELECT clause (which will then call a "selected column"). * That kind of reference can be either an index (an unsigned integer starting from 1 to N, where N is the * number selected columns), or the name/alias of the column. *

*

* These references are also checked, in a second step, in this function. Thus, column metadata are * also attached to them, as common columns. *

* * @param query Query in which the existence of tables must be checked. * @param fathersList List of all columns available in the father queries and that should be accessed in sub-queries. * Each item of this stack is a list of columns available in each father-level query. * Note: this parameter is NULL if this function is called with the root/father query as parameter. * @param mapTables List of all resolved tables. * @param list List of column metadata to complete in this function each time a column reference is resolved. * @param errors List of errors to complete in this function each time an unknown table or column is encountered. */ protected void resolveColumns(final ADQLQuery query, final Stack fathersList, final Map mapTables, final SearchColumnList list, final UnresolvedIdentifiersException errors){ ISearchHandler sHandler; // Check the existence of all columns: sHandler = new SearchColumnOutsideGroupByHandler(); sHandler.search(query); for(ADQLObject result : sHandler){ try{ ADQLColumn adqlColumn = (ADQLColumn)result; // resolve the column: DBColumn dbColumn = resolveColumn(adqlColumn, list, fathersList); // link with the matched DBColumn: adqlColumn.setDBLink(dbColumn); adqlColumn.setAdqlTable(mapTables.get(dbColumn.getTable())); }catch(ParseException pe){ errors.addException(pe); } } // Check the GROUP BY items: ClauseSelect select = query.getSelect(); sHandler = new SearchColumnHandler(); sHandler.search(query.getGroupBy()); for(ADQLObject result : sHandler){ try{ ADQLColumn adqlColumn = (ADQLColumn)result; // resolve the column: DBColumn dbColumn = checkGroupByItem(adqlColumn, select, list); // link with the matched DBColumn: if (dbColumn != null){ adqlColumn.setDBLink(dbColumn); adqlColumn.setAdqlTable(mapTables.get(dbColumn.getTable())); } }catch(ParseException pe){ errors.addException(pe); } } // Check the correctness of all column references (= references to selected columns): /* Note: no need to provide the father tables when resolving column references, * because no father column can be used in ORDER BY. */ sHandler = new SearchColReferenceHandler(); sHandler.search(query); for(ADQLObject result : sHandler){ try{ ColumnReference colRef = (ColumnReference)result; // resolve the column reference: DBColumn dbColumn = checkColumnReference(colRef, select, list); // link with the matched DBColumn: colRef.setDBLink(dbColumn); if (dbColumn != null) colRef.setAdqlTable(mapTables.get(dbColumn.getTable())); }catch(ParseException pe){ errors.addException(pe); } } } /** *

Resolve the given column, that's to say search for the corresponding {@link DBColumn}.

* *

* The third parameter is used only if this function is called inside a sub-query. In this case, * the column is tried to be resolved with the first list (dbColumns). If no match is found, * the resolution is tried with the father columns list (fathersList). *

* * @param column The column to resolve. * @param dbColumns List of all available {@link DBColumn}s. * @param fathersList List of all columns available in the father queries and that should be accessed in sub-queries. * Each item of this stack is a list of columns available in each father-level query. * Note: this parameter is NULL if this function is called with the root/father query as parameter. * * @return The corresponding {@link DBColumn} if found. Otherwise an exception is thrown. * * @throws ParseException An {@link UnresolvedColumnException} if the given column can't be resolved * or an {@link UnresolvedTableException} if its table reference can't be resolved. */ protected DBColumn resolveColumn(final ADQLColumn column, final SearchColumnList dbColumns, Stack fathersList) throws ParseException{ List foundColumns = dbColumns.search(column); // good if only one column has been found: if (foundColumns.size() == 1) return foundColumns.get(0); // but if more than one: ambiguous table reference ! else if (foundColumns.size() > 1){ if (column.getTableName() == null) throw new UnresolvedColumnException(column, (foundColumns.get(0).getTable() == null) ? "" : (foundColumns.get(0).getTable().getADQLName() + "." + foundColumns.get(0).getADQLName()), (foundColumns.get(1).getTable() == null) ? "" : (foundColumns.get(1).getTable().getADQLName() + "." + foundColumns.get(1).getADQLName())); else throw new UnresolvedTableException(column, (foundColumns.get(0).getTable() == null) ? "" : foundColumns.get(0).getTable().getADQLName(), (foundColumns.get(1).getTable() == null) ? "" : foundColumns.get(1).getTable().getADQLName()); }// otherwise (no match): unknown column ! else{ if (fathersList == null || fathersList.isEmpty()) throw new UnresolvedColumnException(column); else{ Stack subStack = new Stack(); subStack.addAll(fathersList.subList(0, fathersList.size() - 1)); return resolveColumn(column, fathersList.peek(), subStack); } } } /** * Check whether the given column corresponds to a selected item's alias or to an existing column. * * @param col The column to check. * @param select The SELECT clause of the ADQL query. * @param dbColumns The list of all available columns. * * @return The corresponding {@link DBColumn} if this column corresponds to an existing column, * NULL otherwise. * * @throws ParseException An {@link UnresolvedColumnException} if the given column can't be resolved * or an {@link UnresolvedTableException} if its table reference can't be resolved. * * @see ClauseSelect#searchByAlias(String) * @see #resolveColumn(ADQLColumn, SearchColumnList, Stack) * * @since 1.4 */ protected DBColumn checkGroupByItem(final ADQLColumn col, final ClauseSelect select, final SearchColumnList dbColumns) throws ParseException{ /* If the column name is not qualified, it may be a SELECT-item's alias. * So, try resolving the name as an alias. * If it fails, perform the normal column resolution.*/ if (col.getTableName() == null){ List founds = select.searchByAlias(col.getColumnName(), col.isCaseSensitive(IdentifierField.COLUMN)); if (founds.size() == 1) return null; else if (founds.size() > 1) throw new UnresolvedColumnException(col, founds.get(0).getAlias(), founds.get(1).getAlias()); } return resolveColumn(col, dbColumns, null); } /** * Check whether the given column reference corresponds to a selected item (column or an expression with an alias) * or to an existing column. * * @param colRef The column reference which must be checked. * @param select The SELECT clause of the ADQL query. * @param dbColumns The list of all available columns. * * @return The corresponding {@link DBColumn} if this reference is actually the name of a column, null otherwise. * * @throws ParseException An {@link UnresolvedColumnException} if the given column can't be resolved * or an {@link UnresolvedTableException} if its table reference can't be resolved. * * @see ClauseSelect#searchByAlias(String) * @see #resolveColumn(ADQLColumn, SearchColumnList, Stack) */ protected DBColumn checkColumnReference(final ColumnReference colRef, final ClauseSelect select, final SearchColumnList dbColumns) throws ParseException{ if (colRef.isIndex()){ int index = colRef.getColumnIndex(); if (index > 0 && index <= select.size()){ SelectItem item = select.get(index - 1); if (item.getOperand() instanceof ADQLColumn) return ((ADQLColumn)item.getOperand()).getDBLink(); else return null; }else throw new ParseException("Column index out of bounds: " + index + " (must be between 1 and " + select.size() + ") !", colRef.getPosition()); }else{ ADQLColumn col = new ADQLColumn(null, colRef.getColumnName()); col.setCaseSensitive(colRef.isCaseSensitive()); col.setPosition(colRef.getPosition()); // search among the select_item aliases: List founds = select.searchByAlias(colRef.getColumnName(), colRef.isCaseSensitive()); if (founds.size() == 1) return null; else if (founds.size() > 1) throw new UnresolvedColumnException(col, founds.get(0).getAlias(), founds.get(1).getAlias()); // check the corresponding column: return resolveColumn(col, dbColumns, null); } } /** * Generate a {@link DBTable} corresponding to the given sub-query with the given table name. * This {@link DBTable} will contain all {@link DBColumn} returned by {@link ADQLQuery#getResultingColumns()}. * * @param subQuery Sub-query in which the specified table must be searched. * @param tableName Name of the table to search. * * @return The corresponding {@link DBTable} if the table has been found in the given sub-query, null otherwise. * * @throws ParseException Can be used to explain why the table has not been found. Note: not used by default. */ public static DBTable generateDBTable(final ADQLQuery subQuery, final String tableName) throws ParseException{ DefaultDBTable dbTable = new DefaultDBTable(tableName); DBColumn[] columns = subQuery.getResultingColumns(); for(DBColumn dbCol : columns) dbTable.addColumn(dbCol.copy(dbCol.getADQLName(), dbCol.getADQLName(), dbTable)); return dbTable; } /* ************************* */ /* CHECKING METHODS FOR UDFs */ /* ************************* */ /** *

Search all UDFs (User Defined Functions) inside the given query, and then * check their signature against the list of allowed UDFs.

* *

Note: * When more than one allowed function match, the function is considered as correct * and no error is added. * However, in case of multiple matches, the return type of matching functions could * be different and in this case, there would be an error while checking later * the types. In such case, throwing an error could make sense, but the user would * then need to cast some parameters to help the parser identifying the right function. * But the type-casting ability is not yet possible in ADQL. *

* * @param query Query in which UDFs must be checked. * @param errors List of errors to complete in this function each time a UDF does not match to any of the allowed UDFs. * * @since 1.3 */ protected void checkUDFs(final ADQLQuery query, final UnresolvedIdentifiersException errors){ // 1. Search all UDFs: ISearchHandler sHandler = new SearchUDFHandler(); sHandler.search(query); // If no UDF are allowed, throw immediately an error: if (allowedUdfs.length == 0){ for(ADQLObject result : sHandler) errors.addException(new UnresolvedFunctionException((UserDefinedFunction)result)); } // 2. Try to resolve all of them: else{ ArrayList toResolveLater = new ArrayList(); UserDefinedFunction udf; int match; BinarySearch binSearch = new BinarySearch(){ @Override protected int compare(UserDefinedFunction searchItem, FunctionDef arrayItem){ return arrayItem.compareTo(searchItem) * -1; } }; // Try to resolve all the found UDFs: /* Note: at this stage, it can happen that UDFs can not be yet resolved because the building of * their signature depends of other UDFs. That's why, these special cases should be kept * for a later resolution try. */ for(ADQLObject result : sHandler){ udf = (UserDefinedFunction)result; // if the type of not all parameters are resolved, postpone the resolution: if (!isAllParamTypesResolved(udf)) toResolveLater.add(udf); // otherwise: else{ // search for a match: match = binSearch.search(udf, allowedUdfs); // if no match... if (match < 0) errors.addException(new UnresolvedFunctionException(udf)); // if there is a match, metadata may be attached (particularly if the function is built automatically by the syntactic parser): else if (udf instanceof DefaultUDF) ((DefaultUDF)udf).setDefinition(allowedUdfs[match]); } } // Try to resolve UDFs whose some parameter types are depending of other UDFs: /* Note: we need to iterate from the end in order to resolve first the most wrapped functions * (e.g. fct1(fct2(...)) ; fct2 must be resolved before fct1). */ for(int i = toResolveLater.size() - 1; i >= 0; i--){ udf = toResolveLater.get(i); // search for a match: match = binSearch.search(udf, allowedUdfs); // if no match, add an error: if (match < 0) errors.addException(new UnresolvedFunctionException(udf)); // otherwise, metadata may be attached (particularly if the function is built automatically by the syntactic parser): else if (udf instanceof DefaultUDF) ((DefaultUDF)udf).setDefinition(allowedUdfs[match]); } // 3. Replace all the resolved DefaultUDF by an instance of the class associated with the set signature: (new ReplaceDefaultUDFHandler(errors)).searchAndReplace(query); } } /** *

Tell whether the type of all parameters of the given ADQL function * is resolved.

* *

A parameter type may not be resolved for 2 main reasons:

*
    *
  • the parameter is a column, but this column has not been successfully resolved. Thus its type is still unknown.
  • *
  • the parameter is a UDF, but this UDF has not been already resolved. Thus, as for the column, its return type is still unknown. * But it could be known later if the UDF is resolved later ; a second try should be done afterwards.
  • *
* * @param fct ADQL function whose the parameters' type should be checked. * * @return true if the type of all parameters is known, false otherwise. * * @since 1.3 */ protected final boolean isAllParamTypesResolved(final ADQLFunction fct){ for(ADQLOperand op : fct.getParameters()){ if (op.isGeometry() == op.isNumeric() && op.isNumeric() == op.isString()) return false; } return true; } /* ************************************************************************************************* */ /* METHODS CHECKING THE GEOMETRIES (geometrical functions, coordinate systems and STC-S expressions) */ /* ************************************************************************************************* */ /** *

Check all geometries.

* *

Operations done in this function:

*
    *
  1. Check that all geometrical functions are supported
  2. *
  3. Check that all explicit (string constant) coordinate system definitions are supported
  4. *
  5. Check all STC-S expressions (only in {@link RegionFunction} for the moment) and * Apply the 2 previous checks on them
  6. *
* * @param query Query in which geometries must be checked. * @param errors List of errors to complete in this function each time a geometry item is not supported. * * @see #resolveGeometryFunctions(ADQLQuery, BinarySearch, UnresolvedIdentifiersException) * @see #resolveCoordinateSystems(ADQLQuery, UnresolvedIdentifiersException) * @see #resolveSTCSExpressions(ADQLQuery, BinarySearch, UnresolvedIdentifiersException) * * @since 1.3 */ protected void checkGeometries(final ADQLQuery query, final UnresolvedIdentifiersException errors){ BinarySearch binSearch = new BinarySearch(){ @Override protected int compare(String searchItem, String arrayItem){ return searchItem.compareToIgnoreCase(arrayItem); } }; // a. Ensure that all used geometry functions are allowed: if (allowedGeo != null) resolveGeometryFunctions(query, binSearch, errors); // b. Check whether the coordinate systems are allowed: if (allowedCoordSys != null) resolveCoordinateSystems(query, errors); // c. Check all STC-S expressions (in RegionFunctions only) + the used coordinate systems (if StringConstant only): if (allowedGeo == null || (allowedGeo.length > 0 && binSearch.search("REGION", allowedGeo) >= 0)) resolveSTCSExpressions(query, binSearch, errors); } /** * Search for all geometrical functions and check whether they are allowed. * * @param query Query in which geometrical functions must be checked. * @param errors List of errors to complete in this function each time a geometrical function is not supported. * * @see #checkGeometryFunction(String, ADQLFunction, BinarySearch, UnresolvedIdentifiersException) * * @since 1.3 */ protected void resolveGeometryFunctions(final ADQLQuery query, final BinarySearch binSearch, final UnresolvedIdentifiersException errors){ ISearchHandler sHandler = new SearchGeometryHandler(); sHandler.search(query); String fctName; for(ADQLObject result : sHandler){ fctName = result.getName(); checkGeometryFunction(fctName, (ADQLFunction)result, binSearch, errors); } } /** *

Check whether the specified geometrical function is allowed by this implementation.

* *

Note: * If the list of allowed geometrical functions is empty, this function will always add an errors to the given list. * Indeed, it means that no geometrical function is allowed and so that the specified function is automatically not supported. *

* * @param fctName Name of the geometrical function to test. * @param fct The function instance being or containing the geometrical function to check. Note: this function can be the function to test or a function embedding the function under test (i.e. RegionFunction). * @param binSearch The object to use in order to search a function name inside the list of allowed functions. * It is able to perform a binary search inside a sorted array of String objects. The interest of * this object is its compare function which must be overridden and tells how to compare the item * to search and the items of the array (basically, a non-case-sensitive comparison between 2 strings). * @param errors List of errors to complete in this function each time a geometrical function is not supported. * * @since 1.3 */ protected void checkGeometryFunction(final String fctName, final ADQLFunction fct, final BinarySearch binSearch, final UnresolvedIdentifiersException errors){ int match = -1; if (allowedGeo.length != 0) match = binSearch.search(fctName, allowedGeo); if (match < 0) errors.addException(new UnresolvedFunctionException("The geometrical function \"" + fctName + "\" is not available in this implementation!", fct)); } /** *

Search all explicit coordinate system declarations, check their syntax and whether they are allowed by this implementation.

* *

Note: * "explicit" means here that all {@link StringConstant} instances. Only coordinate systems expressed as string can * be parsed and so checked. So if a coordinate system is specified by a column, no check can be done at this stage... * it will be possible to perform such test only at the execution. *

* * @param query Query in which coordinate systems must be checked. * @param errors List of errors to complete in this function each time a coordinate system has a wrong syntax or is not supported. * * @see #checkCoordinateSystem(StringConstant, UnresolvedIdentifiersException) * * @since 1.3 */ protected void resolveCoordinateSystems(final ADQLQuery query, final UnresolvedIdentifiersException errors){ ISearchHandler sHandler = new SearchCoordSysHandler(); sHandler.search(query); for(ADQLObject result : sHandler) checkCoordinateSystem((StringConstant)result, errors); } /** * Parse and then check the coordinate system contained in the given {@link StringConstant} instance. * * @param adqlCoordSys The {@link StringConstant} object containing the coordinate system to check. * @param errors List of errors to complete in this function each time a coordinate system has a wrong syntax or is not supported. * * @see STCS#parseCoordSys(String) * @see #checkCoordinateSystem(adql.db.STCS.CoordSys, ADQLOperand, UnresolvedIdentifiersException) * * @since 1.3 */ protected void checkCoordinateSystem(final StringConstant adqlCoordSys, final UnresolvedIdentifiersException errors){ String coordSysStr = adqlCoordSys.getValue(); try{ checkCoordinateSystem(STCS.parseCoordSys(coordSysStr), adqlCoordSys, errors); }catch(ParseException pe){ errors.addException(new ParseException(pe.getMessage(), adqlCoordSys.getPosition())); } } /** * Check whether the given coordinate system is allowed by this implementation. * * @param coordSys Coordinate system to test. * @param operand The operand representing or containing the coordinate system under test. * @param errors List of errors to complete in this function each time a coordinate system is not supported. * * @since 1.3 */ protected void checkCoordinateSystem(final CoordSys coordSys, final ADQLOperand operand, final UnresolvedIdentifiersException errors){ if (coordSysRegExp != null && coordSys != null && !coordSys.toFullSTCS().matches(coordSysRegExp)){ StringBuffer buf = new StringBuffer(); if (allowedCoordSys != null){ for(String cs : allowedCoordSys){ if (buf.length() > 0) buf.append(", "); buf.append(cs); } } if (buf.length() == 0) buf.append("No coordinate system is allowed!"); else buf.insert(0, "Allowed coordinate systems are: "); errors.addException(new ParseException("Coordinate system \"" + ((operand instanceof StringConstant) ? ((StringConstant)operand).getValue() : coordSys.toString()) + "\" (= \"" + coordSys.toFullSTCS() + "\") not allowed in this implementation. " + buf.toString(), operand.getPosition())); } } /** *

Search all STC-S expressions inside the given query, parse them (and so check their syntax) and then determine * whether the declared coordinate system and the expressed region are allowed in this implementation.

* *

Note: * In the current ADQL language definition, STC-S expressions can be found only as only parameter of the REGION function. *

* * @param query Query in which STC-S expressions must be checked. * @param binSearch The object to use in order to search a region name inside the list of allowed functions/regions. * It is able to perform a binary search inside a sorted array of String objects. The interest of * this object is its compare function which must be overridden and tells how to compare the item * to search and the items of the array (basically, a non-case-sensitive comparison between 2 strings). * @param errors List of errors to complete in this function each time the STC-S syntax is wrong or each time the declared coordinate system or region is not supported. * * @see STCS#parseRegion(String) * @see #checkRegion(adql.db.STCS.Region, RegionFunction, BinarySearch, UnresolvedIdentifiersException) * * @since 1.3 */ protected void resolveSTCSExpressions(final ADQLQuery query, final BinarySearch binSearch, final UnresolvedIdentifiersException errors){ // Search REGION functions: ISearchHandler sHandler = new SearchRegionHandler(); sHandler.search(query); // Parse and check their STC-S expression: String stcs; Region region; for(ADQLObject result : sHandler){ try{ // get the STC-S expression: stcs = ((StringConstant)((RegionFunction)result).getParameter(0)).getValue(); // parse the STC-S expression (and so check the syntax): region = STCS.parseRegion(stcs); // check whether the regions (this one + the possible inner ones) and the coordinate systems are allowed: checkRegion(region, (RegionFunction)result, binSearch, errors); }catch(ParseException pe){ errors.addException(new ParseException(pe.getMessage(), result.getPosition())); } } } /** *

Check the given region.

* *

The following points are checked in this function:

*
    *
  • whether the coordinate system is allowed
  • *
  • whether the type of region is allowed
  • *
  • whether the inner regions are correct (here this function is called recursively on each inner region).
  • *
* * @param r The region to check. * @param fct The REGION function containing the region to check. * @param errors List of errors to complete in this function if the given region or its inner regions are not supported. * * @see #checkCoordinateSystem(adql.db.STCS.CoordSys, ADQLOperand, UnresolvedIdentifiersException) * @see #checkGeometryFunction(String, ADQLFunction, BinarySearch, UnresolvedIdentifiersException) * @see #checkRegion(adql.db.STCS.Region, RegionFunction, BinarySearch, UnresolvedIdentifiersException) * * @since 1.3 */ protected void checkRegion(final Region r, final RegionFunction fct, final BinarySearch binSearch, final UnresolvedIdentifiersException errors){ if (r == null) return; // Check the coordinate system (if any): if (r.coordSys != null) checkCoordinateSystem(r.coordSys, fct, errors); // Check that the region type is allowed: if (allowedGeo != null){ if (allowedGeo.length == 0) errors.addException(new UnresolvedFunctionException("The region type \"" + r.type + "\" is not available in this implementation!", fct)); else checkGeometryFunction((r.type == RegionType.POSITION) ? "POINT" : r.type.toString(), fct, binSearch, errors); } // Check all the inner regions: if (r.regions != null){ for(Region innerR : r.regions) checkRegion(innerR, fct, binSearch, errors); } } /* **************************************************** */ /* METHODS CHECKING TYPES UNKNOWN WHILE CHECKING SYNTAX */ /* **************************************************** */ /** *

Search all operands whose the type is not yet known and try to resolve it now * and to check whether it matches the type expected by the syntactic parser.

* *

* Only two operands may have an unresolved type: columns and user defined functions. * Indeed, their type can be resolved only if the list of available columns and UDFs is known, * and if columns and UDFs used in the query are resolved successfully. *

* *

* When an operand type is still unknown, they will own the three kinds of type and * so this function won't raise an error: it is thus automatically on the expected type. * This behavior is perfectly correct because if the type is not resolved * that means the item/operand has not been resolved in the previous steps and so that * an error about this item has already been raised. *

* *

Important note: * This function does not check the types exactly, but just roughly by considering only three categories: * string, numeric and geometry. *

* * @param query Query in which unknown types must be resolved and checked. * @param errors List of errors to complete in this function each time a types does not match to the expected one. * * @see UnknownType * * @since 1.3 */ protected void checkTypes(final ADQLQuery query, final UnresolvedIdentifiersException errors){ // Search all unknown types: ISearchHandler sHandler = new SearchUnknownTypeHandler(); sHandler.search(query); // Check whether their type matches the expected one: UnknownType unknown; for(ADQLObject result : sHandler){ unknown = (UnknownType)result; switch(unknown.getExpectedType()){ case 'G': case 'g': if (!unknown.isGeometry()) errors.addException(new ParseException("Type mismatch! A geometry was expected instead of \"" + unknown.toADQL() + "\".", result.getPosition())); break; case 'N': case 'n': if (!unknown.isNumeric()) errors.addException(new ParseException("Type mismatch! A numeric value was expected instead of \"" + unknown.toADQL() + "\".", result.getPosition())); break; case 'S': case 's': if (!unknown.isString()) errors.addException(new ParseException("Type mismatch! A string value was expected instead of \"" + unknown.toADQL() + "\".", result.getPosition())); break; } } } /* ******************************** */ /* METHODS CHECKING THE SUB-QUERIES */ /* ******************************** */ /** *

Search all sub-queries found in the given query but not in the clause FROM. * These sub-queries are then checked using {@link #check(ADQLQuery, Stack)}.

* * Fathers stack *

* Each time a sub-query must be checked with {@link #check(ADQLQuery, Stack)}, * the list of all columns available in each of its father queries must be provided. * This function is composing itself this stack by adding the given list of available * columns (= all columns resolved in the given query) at the end of the given stack. * If this stack is given empty, then a new stack is created. *

*

* This modification of the given stack is just the execution time of this function. * Before returning, this function removes the last item of the stack. *

* * * @param query Query in which sub-queries must be checked. * @param fathersList List of all columns available in the father queries and that should be accessed in sub-queries. * Each item of this stack is a list of columns available in each father-level query. * Note: this parameter is NULL if this function is called with the root/father query as parameter. * @param availableColumns List of all columns resolved in the given query. * @param errors List of errors to complete in this function each time a semantic error is encountered. * * @since 1.3 */ protected void checkSubQueries(final ADQLQuery query, Stack fathersList, final SearchColumnList availableColumns, final UnresolvedIdentifiersException errors){ // Check sub-queries outside the clause FROM: ISearchHandler sHandler = new SearchSubQueryHandler(); sHandler.search(query); if (sHandler.getNbMatch() > 0){ // Push the list of columns into the father columns stack: if (fathersList == null) fathersList = new Stack(); fathersList.push(availableColumns); // Check each found sub-query: for(ADQLObject result : sHandler){ try{ check((ADQLQuery)result, fathersList); }catch(UnresolvedIdentifiersException uie){ Iterator itPe = uie.getErrors(); while(itPe.hasNext()) errors.addException(itPe.next()); } } // Pop the list of columns from the father columns stack: fathersList.pop(); } } /* *************** */ /* SEARCH HANDLERS */ /* *************** */ /** * Lets searching all {@link ADQLColumn} in the given object, EXCEPT in the GROUP BY clause. * *

* {@link ADQLColumn}s of the GROUP BY may be aliases and so, they can not be checked * exactly as a normal column. *

* * @author Grégory Mantelet (ARI) * @version 1.4 (05/2017) * @since 1.4 */ private static class SearchColumnOutsideGroupByHandler extends SearchColumnHandler { @Override protected boolean goInto(final ADQLObject obj){ return !(obj instanceof ClauseADQL && ((ClauseADQL)obj).getName() != null && ((ClauseADQL)obj).getName().equalsIgnoreCase("GROUP BY")) && super.goInto(obj); } } /** * Lets searching all tables. * * @author Grégory Mantelet (CDS) * @version 1.0 (07/2011) */ private static class SearchTableHandler extends SimpleSearchHandler { @Override public boolean match(final ADQLObject obj){ return obj instanceof ADQLTable; } } /** * Lets searching all wildcards. * * @author Grégory Mantelet (CDS) * @version 1.0 (09/2011) */ private static class SearchWildCardHandler extends SimpleSearchHandler { @Override public boolean match(final ADQLObject obj){ return (obj instanceof SelectAllColumns) && (((SelectAllColumns)obj).getAdqlTable() != null); } } /** * Lets searching column references. * * @author Grégory Mantelet (CDS) * @version 1.0 (11/2011) */ private static class SearchColReferenceHandler extends SimpleSearchHandler { @Override public boolean match(final ADQLObject obj){ return (obj instanceof ColumnReference); } } /** *

Lets searching subqueries in every clause except the FROM one (hence the modification of the {@link #goInto(ADQLObject)}.

* *

* Note: The function {@link #addMatch(ADQLObject, ADQLIterator)} has been modified in order to * not have the root search object (here: the main query) in the list of results. *

* * @author Grégory Mantelet (ARI) * @version 1.2 (12/2013) * @since 1.2 */ private static class SearchSubQueryHandler extends SimpleSearchHandler { @Override protected void addMatch(ADQLObject matchObj, ADQLIterator it){ if (it != null) super.addMatch(matchObj, it); } @Override protected boolean goInto(ADQLObject obj){ return super.goInto(obj) && !(obj instanceof FromContent); } @Override protected boolean match(ADQLObject obj){ return (obj instanceof ADQLQuery); } } /** * Let searching user defined functions. * * @author Grégory Mantelet (ARI) * @version 1.3 (10/2014) * @since 1.3 */ private static class SearchUDFHandler extends SimpleSearchHandler { @Override protected boolean match(ADQLObject obj){ return (obj instanceof UserDefinedFunction); } } /** *

Let replacing every {@link DefaultUDF}s whose a {@link FunctionDef} is set by their corresponding {@link UserDefinedFunction} class.

* *

Important note: * If the replacer can not be created using the class returned by {@link FunctionDef#getUDFClass()}, no replacement is performed. *

* * @author Grégory Mantelet (ARI) * @version 1.3 (02/2015) * @since 1.3 */ private static class ReplaceDefaultUDFHandler extends SimpleReplaceHandler { private final UnresolvedIdentifiersException errors; public ReplaceDefaultUDFHandler(final UnresolvedIdentifiersException errorsContainer){ errors = errorsContainer; } @Override protected boolean match(ADQLObject obj){ return (obj.getClass().getName().equals(DefaultUDF.class.getName())) && (((DefaultUDF)obj).getDefinition() != null) && (((DefaultUDF)obj).getDefinition().getUDFClass() != null); /* Note: detection of DefaultUDF is done on the exact class name rather than using "instanceof" in order to have only direct instances of DefaultUDF, * and not extensions of it. Indeed, DefaultUDFs are generally created automatically by the ADQLQueryFactory ; so, extensions of it can only be custom * UserDefinedFunctions. */ } @Override protected ADQLObject getReplacer(ADQLObject objToReplace) throws UnsupportedOperationException{ try{ // get the associated UDF class: Class udfClass = ((DefaultUDF)objToReplace).getDefinition().getUDFClass(); // get the constructor with a single parameter of type ADQLOperand[]: Constructor constructor = udfClass.getConstructor(ADQLOperand[].class); // create a new instance of this UDF class with the operands stored in the object to replace: return constructor.newInstance((Object)(((DefaultUDF)objToReplace).getParameters())); /* note: without this class, each item of the given array will be considered as a single parameter. */ }catch(Exception ex){ // IF NO INSTANCE CAN BE CREATED... // ...keep the error for further report: errors.addException(new UnresolvedFunctionException("Impossible to represent the function \"" + ((DefaultUDF)objToReplace).getName() + "\": the following error occured while creating this representation: \"" + ((ex instanceof InvocationTargetException) ? "[" + ex.getCause().getClass().getSimpleName() + "] " + ex.getCause().getMessage() : ex.getMessage()) + "\"", (DefaultUDF)objToReplace)); // ...keep the same object (i.e. no replacement): return objToReplace; } } } /** * Let searching geometrical functions. * * @author Grégory Mantelet (ARI) * @version 1.3 (10/2014) * @since 1.3 */ private static class SearchGeometryHandler extends SimpleSearchHandler { @Override protected boolean match(ADQLObject obj){ return (obj instanceof GeometryFunction); } } /** *

Let searching all ADQL objects whose the type was not known while checking the syntax of the ADQL query. * These objects are {@link ADQLColumn}s and {@link UserDefinedFunction}s.

* *

Important note: * Only {@link UnknownType} instances having an expected type equals to 'S' (or 's' ; for string) or 'N' (or 'n' ; for numeric) * are kept by this handler. Others are ignored. *

* * @author Grégory Mantelet (ARI) * @version 1.3 (10/2014) * @since 1.3 */ private static class SearchUnknownTypeHandler extends SimpleSearchHandler { @Override protected boolean match(ADQLObject obj){ if (obj instanceof UnknownType){ char expected = ((UnknownType)obj).getExpectedType(); return (expected == 'G' || expected == 'g' || expected == 'S' || expected == 's' || expected == 'N' || expected == 'n'); }else return false; } } /** * Let searching all explicit declaration of coordinate systems. * So, only {@link StringConstant} objects will be returned. * * @author Grégory Mantelet (ARI) * @version 1.3 (10/2014) * @since 1.3 */ private static class SearchCoordSysHandler extends SimpleSearchHandler { @Override protected boolean match(ADQLObject obj){ if (obj instanceof PointFunction || obj instanceof BoxFunction || obj instanceof CircleFunction || obj instanceof PolygonFunction) return (((GeometryFunction)obj).getCoordinateSystem() instanceof StringConstant); else return false; } @Override protected void addMatch(ADQLObject matchObj, ADQLIterator it){ results.add(((GeometryFunction)matchObj).getCoordinateSystem()); } } /** * Let searching all {@link RegionFunction}s. * * @author Grégory Mantelet (ARI) * @version 1.3 (10/2014) * @since 1.3 */ private static class SearchRegionHandler extends SimpleSearchHandler { @Override protected boolean match(ADQLObject obj){ if (obj instanceof RegionFunction) return (((RegionFunction)obj).getParameter(0) instanceof StringConstant); else return false; } } /** *

Implement the binary search algorithm over a sorted array.

* *

* The only difference with the standard implementation of Java is * that this object lets perform research with a different type * of object than the types of array items. *

* *

* For that reason, the "compare" function must always be implemented. *

* * @author Grégory Mantelet (ARI) * @version 1.3 (10/2014) * * @param Type of items stored in the array. * @param Type of the item to search. * * @since 1.3 */ protected static abstract class BinarySearch< T, S > { private int s, e, m, comp; /** *

Search the given item in the given array.

* *

* In case the given object matches to several items of the array, * this function will return the smallest index, pointing thus to the first * of all matches. *

* * @param searchItem Object for which a corresponding array item must be searched. * @param array Array in which the given object must be searched. * * @return The array index of the first item of all matches. */ public int search(final S searchItem, final T[] array){ s = 0; e = array.length - 1; while(s < e){ // middle of the sorted array: m = s + ((e - s) / 2); // compare the fct with the middle item of the array: comp = compare(searchItem, array[m]); // if the fct is after, trigger the inspection of the right part of the array: if (comp > 0) s = m + 1; // otherwise, the left part: else e = m; } if (s != e || compare(searchItem, array[s]) != 0) return -1; else return s; } /** * Compare the search item and the array item. * * @param searchItem Item whose a corresponding value must be found in the array. * @param arrayItem An item of the array. * * @return Negative value if searchItem is less than arrayItem, 0 if they are equals, or a positive value if searchItem is greater. */ protected abstract int compare(final S searchItem, final T arrayItem); } } src/adql/db/DBColumn.java0000644000175000017500000000447213177122310014153 0ustar olesolespackage adql.db; /* * 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 . * * Copyright 2011-2016 - UDS/Centre de Données astronomiques de Strasbourg (CDS), * Astronomisches Rechen Institut (ARI) */ /** *

Definition of a valid target column.

* *

* This column can be used in an ADQL query with its ADQL name ({@link #getADQLName()}) * and corresponds to a real column in the "database" with its DB name ({@link #getDBName()}). *

* * @author Grégory Mantelet (CDS;ARI) * @version 1.4 (07/2016) */ public interface DBColumn { /** * Gets the name of this column (without any prefix and double-quotes). * * @return Its ADQL name. */ public String getADQLName(); /** * Gets the name of this column in the "database". * * @return Its DB name. */ public String getDBName(); /** *

Get the type of this column (as closed as possible from the "database" type).

* *

Note: * The returned type should be as closed as possible from a type listed by the IVOA in the TAP protocol description into the section UPLOAD. *

* * @return Its type. * * @since 1.3 */ public DBType getDatatype(); /** * Gets the table which contains this {@link DBColumn}. * * @return Its table or null if no table is specified. */ public DBTable getTable(); /** * Makes a copy of this instance of {@link DBColumn}. * * @param dbName Its new DB name. * @param adqlName Its new ADQL name. * @param dbTable Its new table. * * @return A modified copy of this {@link DBColumn}. */ public DBColumn copy(final String dbName, final String adqlName, final DBTable dbTable); } src/adql/db/FunctionDef.java0000644000175000017500000006542513177122310014721 0ustar olesolespackage adql.db; /* * 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 . * * Copyright 2015 - Astronomisches Rechen Institut (ARI) */ import java.lang.reflect.Constructor; import java.util.regex.Matcher; import java.util.regex.Pattern; import adql.db.DBType.DBDatatype; import adql.parser.ParseException; import adql.query.operand.ADQLOperand; import adql.query.operand.function.ADQLFunction; import adql.query.operand.function.DefaultUDF; import adql.query.operand.function.UserDefinedFunction; /** *

Definition of any function that could be used in ADQL queries.

* *

* A such definition can be built manually thanks to the different constructors of this class, * or by parsing a string function definition form using the static function {@link #parse(String)}. *

* *

* The syntax of the expression expected by {@link #parse(String)} is the same as the one used to build * the string returned by {@link #toString()}. Here is this syntax: *

*
{fctName}([{param1Name} {param1Type}, ...])[ -> {returnType}]
* *

* A description of this function may be set thanks to the public class attribute {@link #description}. *

* * @author Grégory Mantelet (ARI) * @version 1.4 (08/2015) * * @since 1.3 */ public class FunctionDef implements Comparable { /** Regular expression for what should be a function or parameter name - a regular identifier. */ protected final static String regularIdentifierRegExp = "[a-zA-Z]+[0-9a-zA-Z_]*"; /** Rough regular expression for a function return type or a parameter type. * The exact type is not checked here ; just the type name syntax is tested, not its value. * This regular expression allows a type to have exactly one parameter (which is generally the length of a character or binary string. */ protected final static String typeRegExp = "([a-zA-Z_]+[ 0-9a-zA-Z_]*)(\\(\\s*([0-9]+)\\s*\\))?"; /** Rough regular expression for a function parameters' list. */ protected final static String fctParamsRegExp = "\\s*[^,]+\\s*(,\\s*[^,]+\\s*)*"; /** Rough regular expression for a function parameter: a name (see {@link #regularIdentifierRegExp}) and a type (see {@link #typeRegExp}). */ protected final static String fctParamRegExp = "\\s*(" + regularIdentifierRegExp + ")\\s+" + typeRegExp + "\\s*"; /** Rough regular expression for a whole function definition. */ protected final static String fctDefRegExp = "\\s*(" + regularIdentifierRegExp + ")\\s*\\(([a-zA-Z0-9,() \r\n\t]*)\\)(\\s*->\\s*(" + typeRegExp + "))?\\s*"; /** Pattern of a function definition. This object has been compiled with {@link #fctDefRegExp}. */ protected final static Pattern fctPattern = Pattern.compile(fctDefRegExp); /** Pattern of a single parameter definition. This object has been compiled with {@link #fctParamRegExp}. */ protected final static Pattern paramPattern = Pattern.compile(fctParamRegExp); /** Name of the function. */ public final String name; /** Description of this function. */ public String description = null; /** Type of the result returned by this function. */ public final DBType returnType; /** Indicate whether the return type is a string. */ protected final boolean isString; /** Indicate whether the return type is a numeric. */ protected final boolean isNumeric; /** Indicate whether the return type is a geometry. */ protected final boolean isGeometry; /** Indicate whether the return type is an unknown type. *

Note: * If true, {@link #isString}, {@link #isNumeric} * and {@link #isGeometry} are false. Otherwise, * at least one of these attributes is set to true. *

* @since 1.4 */ protected final boolean isUnknown; /** Total number of parameters. */ public final int nbParams; /** List of all the parameters of this function. */ protected final FunctionParam[] params; /**

String representation of this function.

*

The syntax of this representation is the following (items between brackets are optional):

*
{fctName}([{param1Name} {param1Type}, ...])[ -> {returnType}]
*/ private final String serializedForm; /**

String representation of this function dedicated to comparison with any function signature.

*

This form is different from the serialized form on the following points:

*
    *
  • the function name is always in lower case.
  • *
  • each parameter is represented by a string of 3 characters, one for each kind of type (in the order): numeric, string, geometry. * Each character is either a 0 or 1, so that indicating whether the parameter is of that kind of type.
  • *
  • no return type.
  • *
*

So the syntax of this form is the following (items between brackets are optional ; xxx is a string of 3 characters, each being either 0 or 1):

*
{fctName}([xxx, ...])
*/ private final String compareForm; /** *

Class of the {@link UserDefinedFunction} which must represent the UDF defined by this {@link FunctionDef} in the ADQL tree.

*

This class MUST have a constructor with a single parameter of type {@link ADQLOperand}[].

*

If this {@link FunctionDef} is defining an ordinary ADQL function, this attribute must be NULL. It is used only for user defined functions.

*/ private Class udfClass = null; /** *

Definition of a function parameter.

* *

This definition is composed of two items: the name and the type of the parameter.

* * @author Grégory Mantelet (ARI) * @version 1.4 (07/2015) * @since 1.3 */ public static final class FunctionParam { /** Parameter name. Ensured not null */ public final String name; /** Parameter type. Ensured not null */ public final DBType type; /** * Create a function parameter. * * @param paramName Name of the parameter to create. MUST NOT be NULL * @param paramType Type of the parameter to create. If NULL, an {@link DBDatatype#UNKNOWN UNKNOWN} type will be created and set instead. */ public FunctionParam(final String paramName, final DBType paramType){ if (paramName == null) throw new NullPointerException("Missing name! The function parameter can not be created."); this.name = paramName; this.type = (paramType == null) ? new DBType(DBDatatype.UNKNOWN) : paramType; } } /** *

Create a function definition.

* *

The created function will have no return type and no parameter.

* * @param fctName Name of the function. */ public FunctionDef(final String fctName){ this(fctName, null, null); } /** *

Create a function definition.

* *

The created function will have a return type (if the provided one is not null) and no parameter.

* * @param fctName Name of the function. * @param returnType Return type of the function. If NULL, this function will have no return type */ public FunctionDef(final String fctName, final DBType returnType){ this(fctName, returnType, null); } /** *

Create a function definition.

* *

The created function will have no return type and some parameters (except if the given array is NULL or empty).

* * @param fctName Name of the function. * @param params Parameters of this function. If NULL or empty, this function will have no parameter. */ public FunctionDef(final String fctName, final FunctionParam[] params){ this(fctName, null, params); } public FunctionDef(final String fctName, final DBType returnType, final FunctionParam[] params){ // Set the name: if (fctName == null) throw new NullPointerException("Missing name! Can not create this function definition."); this.name = fctName; // Set the parameters: this.params = (params == null || params.length == 0) ? null : params; this.nbParams = (params == null) ? 0 : params.length; // Set the return type; this.returnType = (returnType != null) ? returnType : new DBType(DBDatatype.UNKNOWN); isUnknown = this.returnType.isUnknown(); isNumeric = this.returnType.isNumeric(); isString = this.returnType.isString(); isGeometry = this.returnType.isGeometry(); // Serialize in Strings (serializedForm and compareForm) this function definition: StringBuffer bufSer = new StringBuffer(name), bufCmp = new StringBuffer(name.toLowerCase()); bufSer.append('('); for(int i = 0; i < nbParams; i++){ bufSer.append(params[i].name).append(' ').append(params[i].type); bufCmp.append(params[i].type.isNumeric() ? '1' : '0').append(params[i].type.isString() ? '1' : '0').append(params[i].type.isGeometry() ? '1' : '0'); if (i + 1 < nbParams) bufSer.append(", "); } bufSer.append(')'); if (returnType != null) bufSer.append(" -> ").append(returnType); serializedForm = bufSer.toString(); compareForm = bufCmp.toString(); } /** * Tell whether this function returns a numeric. * * @return true if this function returns a numeric, false otherwise. */ public final boolean isNumeric(){ return isNumeric; } /** * Tell whether this function returns a string. * * @return true if this function returns a string, false otherwise. */ public final boolean isString(){ return isString; } /** * Tell whether this function returns a geometry. * * @return true if this function returns a geometry, false otherwise. */ public final boolean isGeometry(){ return isGeometry; } /** *

Tell whether this function returns an unknown type.

* *

* If this function returns true, {@link #isNumeric()}, {@link #isString()} and {@link #isGeometry()} * MUST ALL return false. Otherwise, one of these 3 last functions MUST return true. *

* * @return true if this function returns an unknown/unresolved/unsupported type, false otherwise. */ public final boolean isUnknown(){ return isUnknown; } /** * Get the number of parameters required by this function. * * @return Number of required parameters. */ public final int getNbParams(){ return nbParams; } /** * Get the definition of the indParam-th parameter of this function. * * @param indParam Index of the parameter whose the definition must be returned. * * @return Definition of the specified parameter. * * @throws ArrayIndexOutOfBoundsException If the given index is negative or bigger than the number of parameters. */ public final FunctionParam getParam(final int indParam) throws ArrayIndexOutOfBoundsException{ if (indParam < 0 || indParam >= nbParams) throw new ArrayIndexOutOfBoundsException(indParam); else return params[indParam]; } /** *

Get the class of the {@link UserDefinedFunction} able to represent the function defined here in an ADQL tree.

* *

Note: * This getter should return always NULL if the function defined here is not a user defined function. *
* However, if this {@link FunctionDef} is defining a user defined function and this function returns NULL, * the library will create on the fly a {@link DefaultUDF} corresponding to this definition when needed. * Indeed this UDF class is useful only if the translation from ADQL (to SQL for instance) of the defined * function has a different signature (e.g. a different name) in the target language (e.g. SQL). *

* * @return The corresponding {@link UserDefinedFunction}. MAY BE NULL */ public final Class getUDFClass(){ return udfClass; } /** *

Set the class of the {@link UserDefinedFunction} able to represent the function defined here in an ADQL tree.

* *

Note: * If this {@link FunctionDef} defines an ordinary ADQL function - and not a user defined function - no class should be set here. *
* However, if it defines a user defined function, there is no obligation to set a UDF class. It is useful only if the translation * from ADQL (to SQL for instance) of the function has a different signature (e.g. a different name) in the target language (e.g. SQL). * If the signature is the same, there is no need to set a UDF class ; a {@link DefaultUDF} will be created on the fly by the library * when needed if it turns out that no UDF class is set. *

* * @param udfClass Class to use to represent in an ADQL tree the User Defined Function defined in this {@link FunctionDef}. * * @throws IllegalArgumentException If the given class does not provide any constructor with a single parameter of type ADQLOperand[]. */ public final < T extends UserDefinedFunction > void setUDFClass(final Class udfClass) throws IllegalArgumentException{ try{ // Ensure that, if a class is provided, it contains a constructor with a single parameter of type ADQLOperand[]: if (udfClass != null){ Constructor constructor = udfClass.getConstructor(ADQLOperand[].class); if (constructor == null) throw new IllegalArgumentException("The given class (" + udfClass.getName() + ") does not provide any constructor with a single parameter of type ADQLOperand[]!"); } // Set the new UDF class: this.udfClass = udfClass; }catch(SecurityException e){ throw new IllegalArgumentException("A security problem occurred while trying to get constructor from the class " + udfClass.getName() + ": " + e.getMessage()); }catch(NoSuchMethodException e){ throw new IllegalArgumentException("The given class (" + udfClass.getName() + ") does not provide any constructor with a single parameter of type ADQLOperand[]!"); } } /** *

Let parsing the serialized form of a function definition.

* *

The expected syntax is (items between brackets are optional):

*
{fctName}([{param1Name} {param1Type}, ...])[ -> {returnType}]
* *

* This function must be able to parse functions as defined by TAPRegExt (section 2.3). * Hence, allowed parameter types and return types should be one of the types listed by the UPLOAD section of the TAP recommendation document. * These types are listed in the enumeration object {@link DBDatatype}. * However, other types should be accepted like the common database types...but it should be better to not rely on that * since the conversion of those types to TAP types should not be exactly what is expected (because depending from the used DBMS); * a default interpretation of database types is nevertheless processed by this parser. *

* * @param strDefinition Serialized function definition to parse. * * @return The object representation of the given string definition. * * @throws ParseException If the given string has a wrong syntax or uses unknown types. */ public static FunctionDef parse(final String strDefinition) throws ParseException{ if (strDefinition == null) throw new NullPointerException("Missing string definition to build a FunctionDef!"); // Check the global syntax of the function definition: Matcher m = fctPattern.matcher(strDefinition); if (m.matches()){ // Get the function name: String fctName = m.group(1); // Parse and get the return type: DBType returnType = null; if (m.group(3) != null){ returnType = parseType(m.group(5), (m.group(7) == null) ? DBType.NO_LENGTH : Integer.parseInt(m.group(7))); if (returnType == null){ returnType = new DBType(DBDatatype.UNKNOWN); returnType.type.setCustomType(m.group(4)); } } // Get the parameters, if any: String paramsList = m.group(2); FunctionParam[] params = null; if (paramsList != null && paramsList.trim().length() > 0){ // Check the syntax of the parameters' list: if (!paramsList.matches(fctParamsRegExp)) throw new ParseException("Wrong parameters syntax! Expected syntax: \"( (, )*)\", where =\"[a-zA-Z]+[a-zA-Z0-9_]*\", should be one of the types described in the UPLOAD section of the TAP documentation. Examples of good syntax: \"()\", \"(param INTEGER)\", \"(param1 INTEGER, param2 DOUBLE)\""); // Split all the parameter definitions: String[] paramsSplit = paramsList.split(","); params = new FunctionParam[paramsSplit.length]; DBType paramType; // For each parameter definition... for(int i = 0; i < params.length; i++){ m = paramPattern.matcher(paramsSplit[i]); if (m.matches()){ // ...parse and get the parameter type: paramType = parseType(m.group(2), (m.group(4) == null) ? DBType.NO_LENGTH : Integer.parseInt(m.group(4))); // ...build the parameter definition object: if (paramType == null){ paramType = new DBType(DBDatatype.UNKNOWN); paramType.type.setCustomType(m.group(2) + ((m.group(3) == null) ? "" : m.group(3))); } params[i] = new FunctionParam(m.group(1), paramType); }else // note: should never happen because we have already check the syntax of the whole parameters list before parsing each individual parameter. throw new ParseException("Wrong syntax for the " + (i + 1) + "-th parameter: \"" + paramsSplit[i].trim() + "\"! Expected syntax: \"( (, )*)\", where =\"[a-zA-Z]+[a-zA-Z0-9_]*\", should be one of the types described in the UPLOAD section of the TAP documentation. Examples of good syntax: \"()\", \"(param INTEGER)\", \"(param1 INTEGER, param2 DOUBLE)\""); } } // Build the function definition object: return new FunctionDef(fctName, returnType, params); }else throw new ParseException("Wrong function definition syntax! Expected syntax: \"(?) ?\", where =\"[a-zA-Z]+[a-zA-Z0-9_]*\", =\" -> \", =\"( (, )*)\", should be one of the types described in the UPLOAD section of the TAP documentation. Examples of good syntax: \"foo()\", \"foo() -> VARCHAR\", \"foo(param INTEGER)\", \"foo(param1 INTEGER, param2 DOUBLE) -> DOUBLE\""); } /** * Parse the given string representation of a datatype. * * @param datatype String representation of a datatype. * Note: This string must not contain the length parameter or any other parameter. * These latter should have been separated from the datatype before calling this function. * It can however contain space(s) in first, last or intern position. * @param length Length of this datatype. * Note: This length will be used only for binary (BINARY and VARBINARY) * and character (CHAR and VARCHAR) types. * * @return The object representation of the specified datatype * or NULL if the specified datatype can not be resolved. */ private static DBType parseType(String datatype, int length){ if (datatype == null) return null; // Remove leading and trailing spaces and replace each inner serie of spaces by just one space: datatype = datatype.trim().replaceAll(" +", " "); try{ // Try to find a corresponding DBType item: DBDatatype dbDatatype = DBDatatype.valueOf(datatype.toUpperCase()); // If there's a match, build the type object representation: length = (length <= 0) ? DBType.NO_LENGTH : length; switch(dbDatatype){ case CHAR: case VARCHAR: case BINARY: case VARBINARY: return new DBType(dbDatatype, length); default: return new DBType(dbDatatype); } }catch(IllegalArgumentException iae){ // If there's no corresponding DBType item, try to find a match among the most used DB types: datatype = datatype.toLowerCase(); if (datatype.equals("bool") || datatype.equals("boolean") || datatype.equals("short") || datatype.equals("int2") || datatype.equals("smallserial") || datatype.equals("serial2")) return new DBType(DBDatatype.SMALLINT); else if (datatype.equals("int") || datatype.equals("int4") || datatype.equals("serial") || datatype.equals("serial4")) return new DBType(DBDatatype.INTEGER); else if (datatype.equals("long") || datatype.equals("number") || datatype.equals("int8") || datatype.equals("bigserial") || datatype.equals("bigserial8")) return new DBType(DBDatatype.BIGINT); else if (datatype.equals("float") || datatype.equals("float4")) return new DBType(DBDatatype.REAL); else if (datatype.equals("numeric") || datatype.equals("float8") || datatype.equals("double precision")) return new DBType(DBDatatype.DOUBLE); else if (datatype.equals("bit") || datatype.equals("byte") || datatype.equals("raw")) return new DBType(DBDatatype.BINARY, length); else if (datatype.equals("unsignedByte") || datatype.equals("bit varying") || datatype.equals("varbit")) return new DBType(DBDatatype.VARBINARY, length); else if (datatype.equals("character")) return new DBType(DBDatatype.CHAR, length); else if (datatype.equals("string") || datatype.equals("varchar2") || datatype.equals("character varying")) return new DBType(DBDatatype.VARCHAR, length); else if (datatype.equals("bytea")) return new DBType(DBDatatype.BLOB); else if (datatype.equals("text")) return new DBType(DBDatatype.CLOB); else if (datatype.equals("date") || datatype.equals("time") || datatype.equals("timetz") || datatype.equals("timestamptz")) return new DBType(DBDatatype.TIMESTAMP); else if (datatype.equals("position")) return new DBType(DBDatatype.POINT); else if (datatype.equals("polygon") || datatype.equals("box") || datatype.equals("circle")) return new DBType(DBDatatype.REGION); else return null; } } @Override public String toString(){ return serializedForm; } @Override public int compareTo(final FunctionDef def){ return compareForm.compareTo(def.compareForm); } /** *

Compare this function definition with the given ADQL function item.

* *

* The comparison is done only on the function name and on rough type of the parameters. * "Rough type" means here that just the kind of type is tested: numeric, string or geometry. * Anyway, the return type is never tested by this function, since such information is usually * not part of a function signature. *

* *

The notions of "greater" and "less" are defined here according to the three following test steps:

*
    *
  1. Name test: if the name of both function are equals, next steps are evaluated, otherwise the standard string comparison (case insensitive) result is returned.
  2. *
  3. Parameters test: parameters are compared individually. Each time parameters (at the same position in both functions) are equals the next parameter can be tested, * and so on until two parameters are different or the end of the parameters' list is reached. * Just the kind of type is used for parameter comparison. Each kind of type is tested in the following order: numeric, string and geometry. * When a kind of type is not equal for both parameters, the function exits with the appropriate value * (1 if the parameter of this function definition is of the kind of type, -1 otherwise).
  4. *
  5. Number of parameters test: in the case where this function definition has N parameters and the given ADQL function has M parameters, * and that the L (= min(N,M)) first parameters have the same type in both functions, the value returns by this function * will be N-M. Thus, if this function definition has more parameters than the given function, a positive value will be * returned. Otherwise a negative value will be returned, or 0 if the number of parameters is the same.
  6. *
* *

Note: * If one of the tested types (i.e. parameters types) is unknown, the match should return 0 (i.e. equality). * The notion of "unknown" is different in function of the tested item. A {@link DBType} is unknown if its function * {@link DBType#isUnknown()} returns true ; thus, its other functions such as {@link DBType#isNumeric()} will * return false. On the contrary, an {@link ADQLOperand} does not have any isUnknown() * function. However, when the type of a such is unknown, all its functions isNumeric(), isString() and isGeometry() return * true. *

* * @param fct ADQL function item to compare with this function definition. * * @return A positive value if this function definition is "greater" than the given {@link ADQLFunction}, * 0 if they are perfectly matching or one of the tested types (i.e. parameters types) is unknown, * or a negative value if this function definition is "less" than the given {@link ADQLFunction}. */ public int compareTo(final ADQLFunction fct){ if (fct == null) throw new NullPointerException("Missing ADQL function with which comparing this function definition!"); // Names comparison: int comp = name.compareToIgnoreCase(fct.getName()); // If equals, compare the parameters' type: if (comp == 0){ for(int i = 0; comp == 0 && i < nbParams && i < fct.getNbParameters(); i++){ // if one of the types is unknown, the comparison should return true: if (params[i].type.isUnknown() || (fct.getParameter(i).isNumeric() && fct.getParameter(i).isString() && fct.getParameter(i).isGeometry())) comp = 0; // otherwise, just compare each kind of type for an exact match: else if (params[i].type.isNumeric() == fct.getParameter(i).isNumeric()){ if (params[i].type.isString() == fct.getParameter(i).isString()){ if (params[i].type.isGeometry() == fct.getParameter(i).isGeometry()) comp = 0; else comp = params[i].type.isGeometry() ? 1 : -1; }else comp = params[i].type.isString() ? 1 : -1; }else comp = params[i].type.isNumeric() ? 1 : -1; } // If the first min(N,M) parameters are of the same type, do the last comparison on the number of parameters: if (comp == 0 && nbParams != fct.getNbParameters()) comp = nbParams - fct.getNbParameters(); } return comp; } } src/adql/db/DefaultDBTable.java0000644000175000017500000002235513177122310015252 0ustar olesolespackage adql.db; /* * 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 . * * Copyright 2012-2015 - UDS/Centre de Données astronomiques de Strasbourg (CDS), * Astronomisches Rechen Institut (ARI) */ import java.util.Collection; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; /** * Default implementation of {@link DBTable}. * * @author Grégory Mantelet (CDS;ARI) * @version 1.4 (08/2015) */ public class DefaultDBTable implements DBTable { protected String dbCatalogName; protected String dbSchemaName; protected String dbName; protected String adqlCatalogName = null; protected String adqlSchemaName = null; protected String adqlName = null; protected Map columns = new LinkedHashMap(); /** *

Builds a default {@link DBTable} with the given DB name.

* *

With this constructor: ADQL name = DB name.

* *

Note: The table name can be prefixed by a schema and a catalog: t1 or schema1.t1 or cat1.schema1.t2

* * @param dbName Database name (it will be also used as ADQL table name). * * @see #DefaultDBTable(String, String) */ public DefaultDBTable(final String dbName){ this(dbName, null); } /** *

Builds a default {@link DBTable} with the given DB and ADQL names.

* *

Note: The table names can be prefixed by a schema and a catalog: t1 or schema1.t1 or cat1.schema1.t2

* * @param dbName Database name. * @param adqlName Name used in ADQL queries. */ public DefaultDBTable(final String dbName, final String adqlName){ // DB names: String[] names = splitTableName(dbName); if (names[2] == null || names[2].length() == 0) throw new NullPointerException("Missing DB name !"); else this.dbName = names[2]; this.dbSchemaName = names[1]; this.dbCatalogName = names[0]; // ADQL names: names = splitTableName(adqlName); if (names[2] == null || names[2].length() == 0){ this.adqlName = this.dbName; this.adqlSchemaName = this.dbSchemaName; this.adqlCatalogName = this.dbCatalogName; }else{ this.adqlName = names[2]; this.adqlSchemaName = names[1]; this.adqlCatalogName = names[0]; } } /** * Builds default {@link DBTable} with a DB catalog, schema and table names. * * @param dbCatName Database catalog name (it will be also used as ADQL catalog name). * @param dbSchemName Database schema name (it will be also used as ADQL schema name). * @param dbName Database table name (it will be also used as ADQL table name). * * @see #DefaultDBTable(String, String, String, String, String, String) */ public DefaultDBTable(final String dbCatName, final String dbSchemName, final String dbName){ this(dbCatName, null, dbSchemName, null, dbName, null); } /** * Builds default {@link DBTable} with the DB and ADQL names for the catalog, schema and table. * * @param dbCatName Database catalog name. * @param adqlCatName Catalog name used in ADQL queries. * If NULL, it will be set to dbCatName. * @param dbSchemName Database schema name. * @param adqlSchemName Schema name used in ADQL queries. * If NULL, it will be set to dbSchemName. * @param dbName Database table name. * @param adqlName Table name used in ADQL queries. * If NULL, it will be set to dbName. */ public DefaultDBTable(final String dbCatName, final String adqlCatName, final String dbSchemName, final String adqlSchemName, final String dbName, final String adqlName){ if (dbName == null || dbName.length() == 0) throw new NullPointerException("Missing DB name !"); this.dbName = dbName; this.adqlName = (adqlName == null) ? dbName : adqlName; dbSchemaName = dbSchemName; adqlSchemaName = (adqlSchemName == null) ? dbSchemName : adqlSchemName; dbCatalogName = dbCatName; adqlCatalogName = (adqlCatName == null) ? dbCatName : adqlCatName; } @Override public final String getDBName(){ return dbName; } @Override public final String getDBSchemaName(){ return dbSchemaName; } @Override public final String getDBCatalogName(){ return dbCatalogName; } @Override public final String getADQLName(){ return adqlName; } public void setADQLName(final String name){ adqlName = (name != null) ? name : dbName; } @Override public final String getADQLSchemaName(){ return adqlSchemaName; } public void setADQLSchemaName(final String name){ adqlSchemaName = (name != null) ? name : dbSchemaName; } @Override public final String getADQLCatalogName(){ return adqlCatalogName; } public void setADQLCatalogName(final String name){ adqlName = (name != null) ? null : dbName; } /** *

Case sensitive !

*

Research optimized for researches by ADQL name.

* * @see adql.db.DBTable#getColumn(java.lang.String, boolean) */ @Override public DBColumn getColumn(String colName, boolean byAdqlName){ if (byAdqlName) return columns.get(colName); else{ for(DBColumn col : columns.values()){ if (col.getDBName().equals(colName)) return col; } return null; } } public boolean hasColumn(String colName, boolean byAdqlName){ return (getColumn(colName, byAdqlName) != null); } @Override public Iterator iterator(){ return columns.values().iterator(); } public void addColumn(DBColumn column){ if (column != null) columns.put(column.getADQLName(), column); } public void addAllColumns(Collection colList){ if (colList != null){ for(DBColumn column : colList) addColumn(column); } } /** * Splits the given table name in 3 parts: catalog, schema, table. * * @param table The table name to split. * * @return A String array of 3 items: [0]=catalog, [1]=schema, [0]=table. */ public static final String[] splitTableName(final String table){ String[] splitRes = new String[]{null,null,null}; if (table == null || table.trim().length() == 0) return splitRes; String[] names = table.trim().split("\\."); switch(names.length){ case 1: splitRes[2] = table.trim(); break; case 2: splitRes[2] = names[1].trim(); splitRes[1] = names[0].trim(); break; case 3: splitRes[2] = names[2].trim(); splitRes[1] = names[1].trim(); splitRes[0] = names[0].trim(); break; default: splitRes[2] = names[names.length - 1].trim(); splitRes[1] = names[names.length - 2].trim(); StringBuffer buff = new StringBuffer(names[0].trim()); for(int i = 1; i < names.length - 2; i++) buff.append('.').append(names[i].trim()); splitRes[0] = buff.toString(); } return splitRes; } /** *

Join the last 3 items of the given string array with a dot ('.'). * These three parts should be: [0]=catalog name, [1]=schema name, [2]=table name.

* *

* If the array contains less than 3 items, all the given items will be though joined. * However, if it contains more than 3 items, only the three last items will be. *

* *

A null item will be written as an empty string (string of length 0 ; "").

* *

* In the case the first and the third items are not null, but the second is null, the final string will contain in the middle two dots. * Example: if the array is {"cat", NULL, "table"}, then the joined string will be: "cat..table". *

* * @param nameParts String items to join. * * @return A string joining the 3 last string items of the given array, * or an empty string if the given array is NULL. * * @since 1.3 */ public static final String joinTableName(final String[] nameParts){ if (nameParts == null) return ""; StringBuffer str = new StringBuffer(); boolean empty = true; for(int i = (nameParts.length <= 3) ? 0 : (nameParts.length - 3); i < nameParts.length; i++){ if (!empty) str.append('.'); String part = (nameParts[i] == null) ? null : nameParts[i].trim(); if (part != null && part.length() > 0){ str.append(part); empty = false; } } return str.toString(); } @Override public DBTable copy(String dbName, String adqlName){ dbName = (dbName == null) ? joinTableName(new String[]{dbCatalogName,dbSchemaName,this.dbName}) : dbName; adqlName = (adqlName == null) ? joinTableName(new String[]{adqlCatalogName,adqlSchemaName,this.adqlName}) : adqlName; DefaultDBTable copy = new DefaultDBTable(dbName, adqlName); for(DBColumn col : this){ if (col instanceof DBCommonColumn) copy.addColumn(new DBCommonColumn((DBCommonColumn)col, col.getDBName(), col.getADQLName())); else copy.addColumn(col.copy(col.getDBName(), col.getADQLName(), copy)); } return copy; } } src/adql/db/SearchTableApi.java0000644000175000017500000000256213177122310015315 0ustar olesolespackage adql.db; /* * 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 . * * Copyright 2017 - Astronomisches Rechen Institut (ARI) */ import java.util.List; import adql.query.from.ADQLTable; /** * Simple interface about a class which allows to search for a specified * {@link ADQLTable}. * * @author Grégory Mantelet (ARI) * @version 1.4 (09/2017) * @since 1.4 * * @see SearchTableList */ public interface SearchTableApi { /** * Searches all {@link DBTable} elements corresponding to the given {@link ADQLTable} (case insensitive). * * @param table An {@link ADQLTable}. * * @return The list of all corresponding {@link DBTable} elements. */ public List search(final ADQLTable table); }src/adql/translator/0000755000175000017500000000000013177122310013442 5ustar olesolessrc/adql/translator/PostgreSQLTranslator.java0000644000175000017500000003065013226141030020361 0ustar olesolespackage adql.translator; /* * 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 . * * Copyright 2012-2016 - UDS/Centre de Données astronomiques de Strasbourg (CDS), * Astronomisches Rechen Institut (ARI) */ import adql.db.DBType; import adql.db.DBType.DBDatatype; import adql.db.STCS.Region; import adql.parser.ParseException; import adql.query.IdentifierField; import adql.query.operand.StringConstant; import adql.query.operand.function.ADQLFunction; import adql.query.operand.function.MathFunction; import adql.query.operand.function.geometry.AreaFunction; import adql.query.operand.function.geometry.BoxFunction; import adql.query.operand.function.geometry.CentroidFunction; import adql.query.operand.function.geometry.CircleFunction; import adql.query.operand.function.geometry.ContainsFunction; import adql.query.operand.function.geometry.DistanceFunction; import adql.query.operand.function.geometry.ExtractCoord; import adql.query.operand.function.geometry.ExtractCoordSys; import adql.query.operand.function.geometry.IntersectsFunction; import adql.query.operand.function.geometry.PointFunction; import adql.query.operand.function.geometry.PolygonFunction; import adql.query.operand.function.geometry.RegionFunction; /** * Translates all ADQL objects into an SQL interrogation query designed for * PostgreSQL. * *

* It overwrites the translation of mathematical functions whose some have * a different name or signature. Besides, it is also implementing the * translation of the geometrical functions. However, it does not really * translate them. It is just returning the ADQL expression (by calling * {@link #getDefaultADQLFunction(ADQLFunction)}). And so, of course, the * execution of a SQL query containing geometrical functions and translated * using this translator will not work. It is just a default implementation in * case there is no interest of these geometrical functions. *

* *

Important: * The geometrical functions are translated exactly as in ADQL. * You will probably need to extend this translator to correctly manage the * geometrical functions. An extension is already available for PgSphere: * {@link PgSphereTranslator}. *

* * @author Grégory Mantelet (CDS;ARI) * @version 1.4 (08/2016) * * @see PgSphereTranslator */ public class PostgreSQLTranslator extends JDBCTranslator { /**

Indicate the case sensitivity to apply to each SQL identifier (only SCHEMA, TABLE and COLUMN).

* *

Note: * In this implementation, this field is set by the constructor and never modified elsewhere. * It would be better to never modify it after the construction in order to keep a certain consistency. *

*/ protected byte caseSensitivity = 0x00; /** * Builds a PostgreSQLTranslator which always translates in SQL all identifiers (schema, table and column) in a case sensitive manner ; * in other words, schema, table and column names will be surrounded by double quotes in the SQL translation. */ public PostgreSQLTranslator(){ caseSensitivity = 0x0F; } /** * Builds a PostgreSQLTranslator which always translates in SQL all identifiers (schema, table and column) in the specified case sensitivity ; * in other words, schema, table and column names will all be surrounded or not by double quotes in the SQL translation. * * @param allCaseSensitive true to translate all identifiers in a case sensitive manner (surrounded by double quotes), false for case insensitivity. */ public PostgreSQLTranslator(final boolean allCaseSensitive){ caseSensitivity = allCaseSensitive ? (byte)0x0F : (byte)0x00; } /** * Builds a PostgreSQLTranslator which will always translate in SQL identifiers with the defined case sensitivity. * * @param catalog true to translate catalog names with double quotes (case sensitive in the DBMS), false otherwise. * @param schema true to translate schema names with double quotes (case sensitive in the DBMS), false otherwise. * @param table true to translate table names with double quotes (case sensitive in the DBMS), false otherwise. * @param column true to translate column names with double quotes (case sensitive in the DBMS), false otherwise. */ public PostgreSQLTranslator(final boolean catalog, final boolean schema, final boolean table, final boolean column){ caseSensitivity = IdentifierField.CATALOG.setCaseSensitive(caseSensitivity, catalog); caseSensitivity = IdentifierField.SCHEMA.setCaseSensitive(caseSensitivity, schema); caseSensitivity = IdentifierField.TABLE.setCaseSensitive(caseSensitivity, table); caseSensitivity = IdentifierField.COLUMN.setCaseSensitive(caseSensitivity, column); } @Override public boolean isCaseSensitive(final IdentifierField field){ return field == null ? false : field.isCaseSensitive(caseSensitivity); } @Override public String translate(StringConstant strConst) throws TranslationException{ // Deal with the special escaping syntax of Postgres: /* A string containing characters to escape must be prefixed by an E. * Without this prefix, Potsgres does not escape the concerned characters and * consider backslashes as normal characters. * For instance: E'foo\tfoo2'. */ if (strConst.getValue() != null && strConst.getValue().contains("\\")) return "E'" + strConst.getValue() + "'"; else return super.translate(strConst); } @Override public String translate(MathFunction fct) throws TranslationException{ switch(fct.getType()){ case LOG: return "ln(" + ((fct.getNbParameters() >= 1) ? "CAST(" + translate(fct.getParameter(0)) + " AS numeric)" : "") + ")"; case LOG10: return "log(10, " + ((fct.getNbParameters() >= 1) ? "CAST(" + translate(fct.getParameter(0)) + " AS numeric)" : "") + ")"; case RAND: return "random()"; case TRUNCATE: if (fct.getNbParameters() >= 2) return "trunc(CAST(" + translate(fct.getParameter(0)) + " AS numeric), " + translate(fct.getParameter(1)) + ")"; else if (fct.getNbParameters() >= 1) return "trunc(CAST(" + translate(fct.getParameter(0)) + " AS numeric)" + ")"; else return "trunc()"; case ROUND: if (fct.getNbParameters() >= 2) return "round(CAST(" + translate(fct.getParameter(0)) + " AS numeric), " + translate(fct.getParameter(1)) + ")"; else if (fct.getNbParameters() >= 1) return "round(CAST(" + translate(fct.getParameter(0)) + " AS numeric))"; else return "round()"; case PI: return getDefaultADQLFunction(fct); default: String sql = fct.getName() + "("; for(int i = 0; i < fct.getNbParameters(); i++) sql += ((i == 0) ? "" : ", ") + "CAST(" + translate(fct.getParameter(i)) + " AS numeric)"; return sql + ")"; } } @Override public String translate(ExtractCoord extractCoord) throws TranslationException{ return getDefaultADQLFunction(extractCoord); } @Override public String translate(ExtractCoordSys extractCoordSys) throws TranslationException{ return getDefaultADQLFunction(extractCoordSys); } @Override public String translate(AreaFunction areaFunction) throws TranslationException{ return getDefaultADQLFunction(areaFunction); } @Override public String translate(CentroidFunction centroidFunction) throws TranslationException{ return getDefaultADQLFunction(centroidFunction); } @Override public String translate(DistanceFunction fct) throws TranslationException{ return getDefaultADQLFunction(fct); } @Override public String translate(ContainsFunction fct) throws TranslationException{ return getDefaultADQLFunction(fct); } @Override public String translate(IntersectsFunction fct) throws TranslationException{ return getDefaultADQLFunction(fct); } @Override public String translate(BoxFunction box) throws TranslationException{ return getDefaultADQLFunction(box); } @Override public String translate(CircleFunction circle) throws TranslationException{ return getDefaultADQLFunction(circle); } @Override public String translate(PointFunction point) throws TranslationException{ return getDefaultADQLFunction(point); } @Override public String translate(PolygonFunction polygon) throws TranslationException{ return getDefaultADQLFunction(polygon); } @Override public String translate(RegionFunction region) throws TranslationException{ return getDefaultADQLFunction(region); } @Override public DBType convertTypeFromDB(final int dbmsType, final String rawDbmsTypeName, String dbmsTypeName, final String[] params){ // If no type is provided return VARCHAR: if (dbmsTypeName == null || dbmsTypeName.trim().length() == 0) return null; // Put the dbmsTypeName in lower case for the following comparisons: dbmsTypeName = dbmsTypeName.toLowerCase(); // Extract the length parameter (always the first one): int lengthParam = DBType.NO_LENGTH; if (params != null && params.length > 0){ try{ lengthParam = Integer.parseInt(params[0]); }catch(NumberFormatException nfe){} } // SMALLINT if (dbmsTypeName.equals("smallint") || dbmsTypeName.equals("int2") || dbmsTypeName.equals("smallserial") || dbmsTypeName.equals("serial2") || dbmsTypeName.equals("boolean") || dbmsTypeName.equals("bool")) return new DBType(DBDatatype.SMALLINT); // INTEGER else if (dbmsTypeName.equals("integer") || dbmsTypeName.equals("int") || dbmsTypeName.equals("int4") || dbmsTypeName.equals("serial") || dbmsTypeName.equals("serial4")) return new DBType(DBDatatype.INTEGER); // BIGINT else if (dbmsTypeName.equals("bigint") || dbmsTypeName.equals("int8") || dbmsTypeName.equals("bigserial") || dbmsTypeName.equals("bigserial8")) return new DBType(DBDatatype.BIGINT); // REAL else if (dbmsTypeName.equals("real") || dbmsTypeName.equals("float4")) return new DBType(DBDatatype.REAL); // DOUBLE else if (dbmsTypeName.equals("double precision") || dbmsTypeName.equals("float8") || dbmsTypeName.equals("numeric")) return new DBType(DBDatatype.DOUBLE); // BINARY else if (dbmsTypeName.equals("bit")) return new DBType(DBDatatype.BINARY, lengthParam); // VARBINARY else if (dbmsTypeName.equals("bit varying") || dbmsTypeName.equals("varbit")) return new DBType(DBDatatype.VARBINARY, lengthParam); // CHAR else if (dbmsTypeName.equals("char") || dbmsTypeName.equals("character")) return new DBType(DBDatatype.CHAR, lengthParam); // VARCHAR else if (dbmsTypeName.equals("varchar") || dbmsTypeName.equals("character varying")) return new DBType(DBDatatype.VARCHAR, lengthParam); // BLOB else if (dbmsTypeName.equals("bytea")) return new DBType(DBDatatype.BLOB); // CLOB else if (dbmsTypeName.equals("text")) return new DBType(DBDatatype.CLOB); // TIMESTAMP else if (dbmsTypeName.equals("timestamp") || dbmsTypeName.equals("timestamptz") || dbmsTypeName.equals("time") || dbmsTypeName.equals("timetz") || dbmsTypeName.equals("date")) return new DBType(DBDatatype.TIMESTAMP); // Default: else return null; } @Override public String convertTypeToDB(final DBType type){ if (type == null) return "VARCHAR"; switch(type.type){ case SMALLINT: case INTEGER: case REAL: case BIGINT: case CHAR: case VARCHAR: case TIMESTAMP: return type.type.toString(); case DOUBLE: return "DOUBLE PRECISION"; case BINARY: case VARBINARY: return "bytea"; case BLOB: return "bytea"; case CLOB: return "TEXT"; case POINT: case REGION: default: return "VARCHAR"; } } @Override public Region translateGeometryFromDB(final Object jdbcColValue) throws ParseException{ throw new ParseException("Unsupported geometrical value! The value \"" + jdbcColValue + "\" can not be parsed as a region."); } @Override public Object translateGeometryToDB(final Region region) throws ParseException{ throw new ParseException("Geometries can not be uploaded in the database in this implementation!"); } } src/adql/translator/JDBCTranslator.java0000644000175000017500000010414213226141346017070 0ustar olesolespackage adql.translator; /* * 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 . * * Copyright 2017-2018 - Astronomisches Rechen Institut (ARI) */ import java.util.Iterator; import adql.db.DBColumn; import adql.db.DBTable; import adql.db.DBTableAlias; import adql.db.DBType; import adql.db.STCS.Region; import adql.db.exception.UnresolvedJoinException; import adql.parser.ParseException; import adql.query.ADQLList; import adql.query.ADQLObject; import adql.query.ADQLOrder; import adql.query.ADQLQuery; import adql.query.ClauseConstraints; import adql.query.ClauseSelect; import adql.query.ColumnReference; import adql.query.IdentifierField; import adql.query.SelectAllColumns; import adql.query.SelectItem; import adql.query.constraint.ADQLConstraint; import adql.query.constraint.Between; import adql.query.constraint.Comparison; import adql.query.constraint.ConstraintsGroup; import adql.query.constraint.Exists; import adql.query.constraint.In; import adql.query.constraint.IsNull; import adql.query.constraint.NotConstraint; import adql.query.from.ADQLJoin; import adql.query.from.ADQLTable; import adql.query.from.FromContent; import adql.query.operand.ADQLColumn; import adql.query.operand.ADQLOperand; import adql.query.operand.Concatenation; import adql.query.operand.NegativeOperand; import adql.query.operand.NumericConstant; import adql.query.operand.Operation; import adql.query.operand.StringConstant; import adql.query.operand.WrappedOperand; import adql.query.operand.function.ADQLFunction; import adql.query.operand.function.MathFunction; import adql.query.operand.function.SQLFunction; import adql.query.operand.function.SQLFunctionType; import adql.query.operand.function.UserDefinedFunction; import adql.query.operand.function.geometry.AreaFunction; import adql.query.operand.function.geometry.BoxFunction; import adql.query.operand.function.geometry.CentroidFunction; import adql.query.operand.function.geometry.CircleFunction; import adql.query.operand.function.geometry.ContainsFunction; import adql.query.operand.function.geometry.DistanceFunction; import adql.query.operand.function.geometry.ExtractCoord; import adql.query.operand.function.geometry.ExtractCoordSys; import adql.query.operand.function.geometry.GeometryFunction; import adql.query.operand.function.geometry.GeometryFunction.GeometryValue; import adql.query.operand.function.geometry.IntersectsFunction; import adql.query.operand.function.geometry.PointFunction; import adql.query.operand.function.geometry.PolygonFunction; import adql.query.operand.function.geometry.RegionFunction; /** * Implementation of {@link ADQLTranslator} which translates ADQL queries in * SQL queries. * *

* It is already able to translate all SQL standard features, but lets abstract * the translation of all geometrical functions. So, this translator must be * extended as {@link PostgreSQLTranslator}, {@link PgSphereTranslator}, * {@link MySQLTranslator} and {@link SQLServerTranslator} are doing. *

* *

Note: * Its default implementation of the SQL syntax has been inspired by the * PostgreSQL one. However, it should work also with other DBMS, although some * translations might be needed (as it is has been done for PostgreSQL about * the mathematical functions). *

* *

SQL with or without case sensitivity?

* *

* In ADQL and in SQL, it is possible to tell the parser to respect the exact * case or not of an identifier (schema, table or column name) by surrounding * it with double quotes. However ADQL identifiers and SQL ones may be * different. In that way, the case sensitivity specified in ADQL on the * different identifiers can not be kept in SQL. That's why this translator * lets specify a general rule on which types of SQL identifier must be double * quoted. This can be done by implementing the abstract function * {@link #isCaseSensitive(IdentifierField)}. The functions translating column * and table names will call this function in order to surround the identifiers * by double quotes or not. So, be careful if you want to override the * functions translating columns and tables! *

* *

Translation of "SELECT TOP"

* *

* The default behavior of this translator is to translate the ADQL "TOP" into * the SQL "LIMIT" at the end of the query. This is ok for some DBMS, but not * all. So, if your DBMS does not know the "LIMIT" keyword, you should override * the function translating the whole query: {@link #translate(ADQLQuery)}. * Here is its current implementation: *

*
 * 	StringBuffer sql = new StringBuffer(translate(query.getSelect()));
 * 	sql.append("\nFROM ").append(translate(query.getFrom()));
 *	if (!query.getWhere().isEmpty())
 *		sql.append('\n').append(translate(query.getWhere()));
 *	if (!query.getGroupBy().isEmpty())
 *		sql.append('\n').append(translate(query.getGroupBy()));
 *	if (!query.getHaving().isEmpty())
 *		sql.append('\n').append(translate(query.getHaving()));
 *	if (!query.getOrderBy().isEmpty())
 *		sql.append('\n').append(translate(query.getOrderBy()));
 *	if (query.getSelect().hasLimit())
 *		sql.append("\nLimit ").append(query.getSelect().getLimit());
 *	return sql.toString();
 * 
* *

Translation of ADQL functions

* *

* All ADQL functions are by default not translated. Consequently, the SQL * translation is actually the ADQL expression. Generally the ADQL expression * is generic enough. However some mathematical functions may need to be * translated differently. For instance {@link PostgreSQLTranslator} is * translating differently: LOG, LOG10, RAND and TRUNC. *

* *

Note: * Geometrical regions and types have not been managed here. They stay abstract * because it is obviously impossible to have a generic translation and * conversion ; it totally depends from the database system. *

* *

Translation of "FROM" with JOINs

* *

* The FROM clause is translated into SQL as written in ADQL. There is no * differences except the identifiers that are replaced. The tables' aliases * and their case sensitivity are kept like in ADQL. *

* * @author Grégory Mantelet (ARI) * @version 1.4 (01/2018) * @since 1.4 * * @see PostgreSQLTranslator * @see PgSphereTranslator * @see MySQLTranslator * @see SQLServerTranslator */ public abstract class JDBCTranslator implements ADQLTranslator { /** *

Tell whether the specified identifier MUST be translated so that being interpreted case sensitively or not. * By default, an identifier that must be translated with case sensitivity will be surrounded by double quotes. * But, if this function returns FALSE, the SQL name will be written just as given in the metadata, without double quotes.

* *

WARNING: * An {@link IdentifierField} object can be a SCHEMA, TABLE, COLUMN and ALIAS. However, in this translator, * aliases are translated like in ADQL (so, with the same case sensitivity specification as in ADQL). * So, this function will never be used to know the case sensitivity to apply to an alias. It is then * useless to write a special behavior for the ALIAS value. *

* * @param field The identifier whose the case sensitive to apply is asked. * * @return true if the specified identifier must be translated case sensitivity, false otherwise (included if ALIAS or NULL). */ public abstract boolean isCaseSensitive(final IdentifierField field); /** *

Get the qualified DB name of the schema containing the given table.

* *

Note: * This function will, by default, add double quotes if the schema name must be case sensitive in the SQL query. * This information is provided by {@link #isCaseSensitive(IdentifierField)}. *

* * @param table A table of the schema whose the qualified DB name is asked. * * @return The qualified (with DB catalog name prefix if any, and with double quotes if needed) DB schema name, * or an empty string if there is no schema or no DB name. */ public String getQualifiedSchemaName(final DBTable table){ if (table == null || table.getDBSchemaName() == null) return ""; StringBuffer buf = new StringBuffer(); if (table.getDBCatalogName() != null) appendIdentifier(buf, table.getDBCatalogName(), IdentifierField.CATALOG).append('.'); appendIdentifier(buf, table.getDBSchemaName(), IdentifierField.SCHEMA); return buf.toString(); } /** *

Get the qualified DB name of the given table.

* *

Note: * This function will, by default, add double quotes if the table name must be case sensitive in the SQL query. * This information is provided by {@link #isCaseSensitive(IdentifierField)}. *

* * @param table The table whose the qualified DB name is asked. * * @return The qualified (with DB catalog and schema prefix if any, and with double quotes if needed) DB table name, * or an empty string if the given table is NULL or if there is no DB name. * * @see #getTableName(DBTable, boolean) */ public String getQualifiedTableName(final DBTable table){ return getTableName(table, true); } /** *

Get the DB name of the given table. * The second parameter lets specify whether the table name must be prefixed by the qualified schema name or not.

* *

Note: * This function will, by default, add double quotes if the table name must be case sensitive in the SQL query. * This information is provided by {@link #isCaseSensitive(IdentifierField)}. *

* * @param table The table whose the DB name is asked. * @param withSchema true if the qualified schema name must prefix the table name, false otherwise. * * @return The DB table name (prefixed by the qualified schema name if asked, and with double quotes if needed), * or an empty string if the given table is NULL or if there is no DB name. * * @since 2.0 */ public String getTableName(final DBTable table, final boolean withSchema){ if (table == null) return ""; StringBuffer buf = new StringBuffer(); if (withSchema){ buf.append(getQualifiedSchemaName(table)); if (buf.length() > 0) buf.append('.'); } appendIdentifier(buf, table.getDBName(), IdentifierField.TABLE); return buf.toString(); } /** *

Get the DB name of the given column

* *

Note: * This function will, by default, add double quotes if the column name must be case sensitive in the SQL query. * This information is provided by {@link #isCaseSensitive(IdentifierField)}. *

* *

Caution: * The given column may be NULL and in this case an empty string will be returned. * But if the given column is not NULL, its DB name MUST NOT BE NULL! *

* * @param column The column whose the DB name is asked. * * @return The DB column name (with double quotes if needed), * or an empty string if the given column is NULL. */ public String getColumnName(final DBColumn column){ return (column == null) ? "" : appendIdentifier(new StringBuffer(), column.getDBName(), IdentifierField.COLUMN).toString(); } /** *

Appends the given identifier in the given StringBuffer.

* *

* This function just call {@link #appendIdentifier(StringBuffer, String, boolean)} * with the same 2 first parameters. The third one is the result of: * {@link #isCaseSensitive(IdentifierField) isCaseSensitive(field)}. *

* *

Note: * In order to keep a consistent output of the appendIdentifier(...) functions, * this function can not be overwritten ; it is just a shortcut function. *

* * @param str The string buffer. * @param id The identifier to append. * @param field The type of identifier (column, table, schema, catalog or alias ?). * * @return The string buffer + identifier. * * @see #appendIdentifier(StringBuffer, String, boolean) */ public final StringBuffer appendIdentifier(final StringBuffer str, final String id, final IdentifierField field){ return appendIdentifier(str, id, isCaseSensitive(field)); } /** * Appends the given identifier to the given StringBuffer. * * @param str The string buffer. * @param id The identifier to append. * @param caseSensitive true to format the identifier so that preserving the case sensitivity, false otherwise. * * @return The string buffer + identifier. */ public StringBuffer appendIdentifier(final StringBuffer str, final String id, final boolean caseSensitive){ if (caseSensitive && !id.matches("\"[^\"]*\"")) return str.append('"').append(id).append('"'); else return str.append(id); } @Override @SuppressWarnings({"unchecked","rawtypes"}) public String translate(ADQLObject obj) throws TranslationException{ if (obj instanceof ADQLQuery) return translate((ADQLQuery)obj); else if (obj instanceof ADQLList) return translate((ADQLList)obj); else if (obj instanceof SelectItem) return translate((SelectItem)obj); else if (obj instanceof ColumnReference) return translate((ColumnReference)obj); else if (obj instanceof ADQLTable) return translate((ADQLTable)obj); else if (obj instanceof ADQLJoin) return translate((ADQLJoin)obj); else if (obj instanceof ADQLOperand) return translate((ADQLOperand)obj); else if (obj instanceof ADQLConstraint) return translate((ADQLConstraint)obj); else return obj.toADQL(); } @Override public String translate(ADQLQuery query) throws TranslationException{ StringBuffer sql = new StringBuffer(translate(query.getSelect())); sql.append("\nFROM ").append(translate(query.getFrom())); if (!query.getWhere().isEmpty()) sql.append('\n').append(translate(query.getWhere())); if (!query.getGroupBy().isEmpty()) sql.append('\n').append(translate(query.getGroupBy())); if (!query.getHaving().isEmpty()) sql.append('\n').append(translate(query.getHaving())); if (!query.getOrderBy().isEmpty()) sql.append('\n').append(translate(query.getOrderBy())); if (query.getSelect().hasLimit()) sql.append("\nLimit ").append(query.getSelect().getLimit()); return sql.toString(); } /* *************************** */ /* ****** LIST & CLAUSE ****** */ /* *************************** */ @Override public String translate(ADQLList list) throws TranslationException{ if (list instanceof ClauseSelect) return translate((ClauseSelect)list); else if (list instanceof ClauseConstraints) return translate((ClauseConstraints)list); else if (list instanceof Concatenation) return getDefaultADQLList(list, false); else return getDefaultADQLList(list); } /** * Gets the default SQL output for a list of ADQL objects. * *

Implementation note: * This function just calls {@link #getDefaultADQLList(ADQLList, boolean)} * with the given list in first parameter and true in second * one. In other words, this function always prefixes the list items by * the list name. *

* * @param list List to format into SQL. * * @return The corresponding SQL. * * @throws TranslationException If there is an error during the translation. * * @see #getDefaultADQLList(ADQLList, boolean) */ protected final String getDefaultADQLList(ADQLList list) throws TranslationException{ return getDefaultADQLList(list, true); } /** * Gets the default SQL output for a list of ADQL objects. * * @param list List to format into SQL. * @param withNamePrefix Prefix the list by its name or not. * (e.g. 'false' for a Concatenation) * * @return The corresponding SQL. * * @throws TranslationException If there is an error during the translation. * * @since 1.4 */ protected String getDefaultADQLList(ADQLList list, final boolean withNamePrefix) throws TranslationException{ String sql = (list.getName() == null || !withNamePrefix) ? "" : (list.getName() + " "); for(int i = 0; i < list.size(); i++) sql += ((i == 0) ? "" : (" " + list.getSeparator(i) + " ")) + translate(list.get(i)); return sql; } @Override public String translate(ClauseSelect clause) throws TranslationException{ String sql = null; for(int i = 0; i < clause.size(); i++){ if (i == 0){ sql = clause.getName() + (clause.distinctColumns() ? " DISTINCT" : ""); }else sql += " " + clause.getSeparator(i); sql += " " + translate(clause.get(i)); } return sql; } @Override public String translate(ClauseConstraints clause) throws TranslationException{ if (clause instanceof ConstraintsGroup) return "(" + getDefaultADQLList(clause) + ")"; else return getDefaultADQLList(clause); } @Override public String translate(SelectItem item) throws TranslationException{ if (item instanceof SelectAllColumns) return translate((SelectAllColumns)item); StringBuffer translation = new StringBuffer(translate(item.getOperand())); if (item.hasAlias()){ translation.append(" AS "); if (item.isCaseSensitive()) appendIdentifier(translation, item.getAlias(), true); else appendIdentifier(translation, item.getAlias().toLowerCase(), true); }else{ translation.append(" AS "); appendIdentifier(translation, item.getName(), true); } return translation.toString(); } @Override public String translate(SelectAllColumns item) throws TranslationException{ // Fetch the full list of columns to display: Iterable dbCols = null; if (item.getAdqlTable() != null && item.getAdqlTable().getDBLink() != null){ ADQLTable table = item.getAdqlTable(); dbCols = table.getDBLink(); }else if (item.getQuery() != null){ try{ dbCols = item.getQuery().getFrom().getDBColumns(); }catch(UnresolvedJoinException pe){ throw new TranslationException("Due to a join problem, the ADQL to SQL translation can not be completed!", pe); } } // Write the DB name of all these columns: if (dbCols != null){ StringBuffer cols = new StringBuffer(); for(DBColumn col : dbCols){ if (cols.length() > 0) cols.append(','); if (col.getTable() != null){ if (col.getTable() instanceof DBTableAlias) cols.append(getTableName(col.getTable(), false)).append('.'); else cols.append(getQualifiedTableName(col.getTable())).append('.'); } appendIdentifier(cols, col.getDBName(), IdentifierField.COLUMN); cols.append(" AS \"").append(col.getADQLName()).append('\"'); } return (cols.length() > 0) ? cols.toString() : item.toADQL(); }else{ return item.toADQL(); } } @Override public String translate(ColumnReference ref) throws TranslationException{ if (ref instanceof ADQLOrder) return translate((ADQLOrder)ref); else return getDefaultColumnReference(ref); } /** * Gets the default SQL output for a column reference. * * @param ref The column reference to format into SQL. * * @return The corresponding SQL. * * @throws TranslationException If there is an error during the translation. */ protected String getDefaultColumnReference(ColumnReference ref) throws TranslationException{ if (ref.isIndex()){ return "" + ref.getColumnIndex(); }else{ if (ref.getDBLink() == null){ return (ref.isCaseSensitive() ? ("\"" + ref.getColumnName() + "\"") : ref.getColumnName()); }else{ DBColumn dbCol = ref.getDBLink(); StringBuffer colName = new StringBuffer(); // Use the table alias if any: if (ref.getAdqlTable() != null && ref.getAdqlTable().hasAlias()) appendIdentifier(colName, ref.getAdqlTable().getAlias(), ref.getAdqlTable().isCaseSensitive(IdentifierField.ALIAS)).append('.'); // Use the DBTable if any: else if (dbCol.getTable() != null) colName.append(getQualifiedTableName(dbCol.getTable())).append('.'); appendIdentifier(colName, dbCol.getDBName(), IdentifierField.COLUMN); return colName.toString(); } } } @Override public String translate(ADQLOrder order) throws TranslationException{ return getDefaultColumnReference(order) + (order.isDescSorting() ? " DESC" : " ASC"); } /* ************************** */ /* ****** TABLE & JOIN ****** */ /* ************************** */ @Override public String translate(FromContent content) throws TranslationException{ if (content instanceof ADQLTable) return translate((ADQLTable)content); else if (content instanceof ADQLJoin) return translate((ADQLJoin)content); else return content.toADQL(); } @Override public String translate(ADQLTable table) throws TranslationException{ StringBuffer sql = new StringBuffer(); // CASE: SUB-QUERY: if (table.isSubQuery()) sql.append('(').append(translate(table.getSubQuery())).append(')'); // CASE: TABLE REFERENCE: else{ // Use the corresponding DB table, if known: if (table.getDBLink() != null){ /* Note: if the table is aliased, the aliased table is wrapped * inside a DBTableAlias. So, to get the real table name * we should get first the original table thanks to * DBTableAlias.getOriginTable(). */ if (table.getDBLink() instanceof DBTableAlias) sql.append(getQualifiedTableName(((DBTableAlias)table.getDBLink()).getOriginTable())); else sql.append(getQualifiedTableName(table.getDBLink())); } // Otherwise, use the whole table name given in the ADQL query: else sql.append(table.getFullTableName()); } // Add the table alias, if any: if (table.hasAlias()){ sql.append(" AS "); /* In case where metadata are known, the alias must always be * written case sensitively in order to ensure a translation * stability (i.e. all references clearly point toward this alias * whatever is their character case). */ if (table.getDBLink() != null){ if (table.isCaseSensitive(IdentifierField.ALIAS)) appendIdentifier(sql, table.getAlias(), true); else appendIdentifier(sql, table.getAlias().toLowerCase(), true); } /* Otherwise, just write what is written in ADQL: */ else appendIdentifier(sql, table.getAlias(), table.isCaseSensitive(IdentifierField.ALIAS)); } return sql.toString(); } @Override public String translate(ADQLJoin join) throws TranslationException{ StringBuffer sql = new StringBuffer(translate(join.getLeftTable())); if (join.isNatural()) sql.append(" NATURAL"); sql.append(' ').append(join.getJoinType()).append(' ').append(translate(join.getRightTable())).append(' '); if (!join.isNatural()){ if (join.getJoinCondition() != null) sql.append(translate(join.getJoinCondition())); else if (join.hasJoinedColumns()){ StringBuffer cols = new StringBuffer(); Iterator it = join.getJoinedColumns(); while(it.hasNext()){ ADQLColumn item = it.next(); if (cols.length() > 0) cols.append(", "); if (item.getDBLink() == null) appendIdentifier(cols, item.getColumnName(), item.isCaseSensitive(IdentifierField.COLUMN)); else appendIdentifier(cols, item.getDBLink().getDBName(), IdentifierField.COLUMN); } sql.append("USING (").append(cols).append(')'); } } return sql.toString(); } /* ********************* */ /* ****** OPERAND ****** */ /* ********************* */ @Override public String translate(ADQLOperand op) throws TranslationException{ if (op instanceof ADQLColumn) return translate((ADQLColumn)op); else if (op instanceof Concatenation) return translate((Concatenation)op); else if (op instanceof NegativeOperand) return translate((NegativeOperand)op); else if (op instanceof NumericConstant) return translate((NumericConstant)op); else if (op instanceof StringConstant) return translate((StringConstant)op); else if (op instanceof WrappedOperand) return translate((WrappedOperand)op); else if (op instanceof Operation) return translate((Operation)op); else if (op instanceof ADQLFunction) return translate((ADQLFunction)op); else return op.toADQL(); } @Override public String translate(ADQLColumn column) throws TranslationException{ // Use its DB name if known: if (column.getDBLink() != null){ DBColumn dbCol = column.getDBLink(); StringBuffer colName = new StringBuffer(); // Use the DBTable if any: if (dbCol.getTable() != null && dbCol.getTable().getDBName() != null){ /* Note: if the table is aliased, ensure no schema is prefixing * this alias thanks to getTableName(..., false). */ if (dbCol.getTable() instanceof DBTableAlias) colName.append(getTableName(dbCol.getTable(), false)).append('.'); else colName.append(getQualifiedTableName(dbCol.getTable())).append('.'); } // Otherwise, use the prefix of the column given in the ADQL query: else if (column.getTableName() != null) colName = column.getFullColumnPrefix().append('.'); appendIdentifier(colName, dbCol.getDBName(), IdentifierField.COLUMN); return colName.toString(); } // Otherwise, use the whole name given in the ADQL query: else return column.getFullColumnName(); } @Override public String translate(Concatenation concat) throws TranslationException{ return translate((ADQLList)concat); } @Override public String translate(NegativeOperand negOp) throws TranslationException{ return "-" + translate(negOp.getOperand()); } @Override public String translate(NumericConstant numConst) throws TranslationException{ return numConst.getValue(); } @Override public String translate(StringConstant strConst) throws TranslationException{ return "'" + strConst.getValue().replaceAll("'", "''") + "'"; } @Override public String translate(WrappedOperand op) throws TranslationException{ return "(" + translate(op.getOperand()) + ")"; } @Override public String translate(Operation op) throws TranslationException{ return translate(op.getLeftOperand()) + op.getOperation().toADQL() + translate(op.getRightOperand()); } /* ************************ */ /* ****** CONSTRAINT ****** */ /* ************************ */ @Override public String translate(ADQLConstraint cons) throws TranslationException{ if (cons instanceof Comparison) return translate((Comparison)cons); else if (cons instanceof Between) return translate((Between)cons); else if (cons instanceof Exists) return translate((Exists)cons); else if (cons instanceof In) return translate((In)cons); else if (cons instanceof IsNull) return translate((IsNull)cons); else if (cons instanceof NotConstraint) return translate((NotConstraint)cons); else return cons.toADQL(); } @Override public String translate(Comparison comp) throws TranslationException{ return translate(comp.getLeftOperand()) + " " + comp.getOperator().toADQL() + " " + translate(comp.getRightOperand()); } @Override public String translate(Between comp) throws TranslationException{ return translate(comp.getLeftOperand()) + " " + comp.getName() + " " + translate(comp.getMinOperand()) + " AND " + translate(comp.getMaxOperand()); } @Override public String translate(Exists exists) throws TranslationException{ return "EXISTS(" + translate(exists.getSubQuery()) + ")"; } @Override public String translate(In in) throws TranslationException{ return translate(in.getOperand()) + " " + in.getName() + " (" + (in.hasSubQuery() ? translate(in.getSubQuery()) : translate(in.getValuesList())) + ")"; } @Override public String translate(IsNull isNull) throws TranslationException{ return translate(isNull.getColumn()) + " " + isNull.getName(); } @Override public String translate(NotConstraint notCons) throws TranslationException{ return "NOT " + translate(notCons.getConstraint()); } /* *********************** */ /* ****** FUNCTIONS ****** */ /* *********************** */ @Override public String translate(ADQLFunction fct) throws TranslationException{ if (fct instanceof GeometryFunction) return translate((GeometryFunction)fct); else if (fct instanceof MathFunction) return translate((MathFunction)fct); else if (fct instanceof SQLFunction) return translate((SQLFunction)fct); else if (fct instanceof UserDefinedFunction) return translate((UserDefinedFunction)fct); else return getDefaultADQLFunction(fct); } /** * Gets the default SQL output for the given ADQL function. * * @param fct The ADQL function to format into SQL. * * @return The corresponding SQL. * * @throws TranslationException If there is an error during the translation. */ protected final String getDefaultADQLFunction(ADQLFunction fct) throws TranslationException{ String sql = fct.getName() + "("; for(int i = 0; i < fct.getNbParameters(); i++) sql += ((i == 0) ? "" : ", ") + translate(fct.getParameter(i)); return sql + ")"; } @Override public String translate(SQLFunction fct) throws TranslationException{ if (fct.getType() == SQLFunctionType.COUNT_ALL) return "COUNT(" + (fct.isDistinct() ? "DISTINCT " : "") + "*)"; else return fct.getName() + "(" + (fct.isDistinct() ? "DISTINCT " : "") + translate(fct.getParameter(0)) + ")"; } @Override public String translate(MathFunction fct) throws TranslationException{ return getDefaultADQLFunction(fct); } @Override public String translate(UserDefinedFunction fct) throws TranslationException{ return fct.translate(this); } /* *********************************** */ /* ****** GEOMETRICAL FUNCTIONS ****** */ /* *********************************** */ @Override public String translate(GeometryFunction fct) throws TranslationException{ if (fct instanceof AreaFunction) return translate((AreaFunction)fct); else if (fct instanceof BoxFunction) return translate((BoxFunction)fct); else if (fct instanceof CentroidFunction) return translate((CentroidFunction)fct); else if (fct instanceof CircleFunction) return translate((CircleFunction)fct); else if (fct instanceof ContainsFunction) return translate((ContainsFunction)fct); else if (fct instanceof DistanceFunction) return translate((DistanceFunction)fct); else if (fct instanceof ExtractCoord) return translate((ExtractCoord)fct); else if (fct instanceof ExtractCoordSys) return translate((ExtractCoordSys)fct); else if (fct instanceof IntersectsFunction) return translate((IntersectsFunction)fct); else if (fct instanceof PointFunction) return translate((PointFunction)fct); else if (fct instanceof PolygonFunction) return translate((PolygonFunction)fct); else if (fct instanceof RegionFunction) return translate((RegionFunction)fct); else return getDefaultADQLFunction(fct); } @Override public String translate(GeometryValue geomValue) throws TranslationException{ return translate(geomValue.getValue()); } /** * Convert any type provided by a JDBC driver into a type understandable by the ADQL/TAP library. * * @param dbmsType Type returned by a JDBC driver. Note: this value is returned by ResultSetMetadata.getColumnType(int) and correspond to a type of java.sql.Types * @param rawDbmsTypeName Full name of the type returned by a JDBC driver. Note: this name is returned by ResultSetMetadata.getColumnTypeName(int) ; this name may contain parameters * @param dbmsTypeName Name of type, without the eventual parameters. Note: this name is extracted from rawDbmsTypeName. * @param typeParams The eventual type parameters (e.g. char string length). Note: these parameters are extracted from rawDbmsTypeName. * * @return The corresponding ADQL/TAP type or NULL if the specified type is unknown. */ public abstract DBType convertTypeFromDB(final int dbmsType, final String rawDbmsTypeName, final String dbmsTypeName, final String[] typeParams); /** *

Convert any type provided by the ADQL/TAP library into a type understandable by a JDBC driver.

* *

Note: * The returned DBMS type may contain some parameters between brackets. *

* * @param type The ADQL/TAP library's type to convert. * * @return The corresponding DBMS type or NULL if the specified type is unknown. */ public abstract String convertTypeToDB(final DBType type); /** *

Parse the given JDBC column value as a geometry object and convert it into a {@link Region}.

* *

Note: * Generally the returned object will be used to get its STC-S expression. *

* *

Note: * If the given column value is NULL, NULL will be returned. *

* *

Important note: * This function is called ONLY for value of columns flagged as geometries by * {@link #convertTypeFromDB(int, String, String, String[])}. So the value should always * be of the expected type and format. However, if it turns out that the type is wrong * and that the conversion is finally impossible, this function SHOULD throw a * {@link ParseException}. *

* * @param jdbcColValue A JDBC column value (returned by ResultSet.getObject(int)). * * @return The corresponding {@link Region} if the given value is a geometry. * * @throws ParseException If the given object is not a geometrical object * or can not be transformed into a {@link Region} object. */ public abstract Region translateGeometryFromDB(final Object jdbcColValue) throws ParseException; /** *

Convert the given STC region into a DB column value.

* *

Note: * This function is used only by the UPLOAD feature, to import geometries provided as STC-S expression in * a VOTable document inside a DB column. *

* *

Note: * If the given region is NULL, NULL will be returned. *

* * @param region The region to store in the DB. * * @return The corresponding DB column object. * * @throws ParseException If the given STC Region can not be converted into a DB object. */ public abstract Object translateGeometryToDB(final Region region) throws ParseException; } src/adql/translator/TranslationException.java0000644000175000017500000000243013177122310020461 0ustar olesolespackage adql.translator; /* * 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 . * * Copyright 2012 - UDS/Centre de Données astronomiques de Strasbourg (CDS) */ /** * Exception thrown by an {@link ADQLTranslator}. * * @author Grégory Mantelet (CDS) * @version 01/2012 */ public class TranslationException extends Exception { private static final long serialVersionUID = 1L; public TranslationException(){ ; } public TranslationException(String msg){ super(msg); } public TranslationException(Throwable cause){ super(cause); } public TranslationException(String msg, Throwable cause){ super(msg, cause); } } src/adql/translator/PgSphereTranslator.java0000644000175000017500000006441013226141146020104 0ustar olesolespackage adql.translator; /* * 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 . * * Copyright 2012-2017 - UDS/Centre de Données astronomiques de Strasbourg (CDS), * Astronomisches Rechen Institut (ARI) */ import java.sql.SQLException; import java.util.ArrayList; import org.postgresql.util.PGobject; import adql.db.DBType; import adql.db.DBType.DBDatatype; import adql.db.STCS.Region; import adql.parser.ParseException; import adql.query.TextPosition; import adql.query.constraint.Comparison; import adql.query.constraint.ComparisonOperator; import adql.query.operand.function.geometry.AreaFunction; import adql.query.operand.function.geometry.BoxFunction; import adql.query.operand.function.geometry.CentroidFunction; import adql.query.operand.function.geometry.CircleFunction; import adql.query.operand.function.geometry.ContainsFunction; import adql.query.operand.function.geometry.DistanceFunction; import adql.query.operand.function.geometry.ExtractCoord; import adql.query.operand.function.geometry.IntersectsFunction; import adql.query.operand.function.geometry.PointFunction; import adql.query.operand.function.geometry.PolygonFunction; /** * Translates all ADQL objects into the SQL adaptation of Postgres+PgSphere. * *

* Actually only the geometrical functions and types are translated in this * class. The other functions are managed by {@link PostgreSQLTranslator}. *

* * @author Grégory Mantelet (CDS;ARI) * @version 1.4 (07/2017) */ public class PgSphereTranslator extends PostgreSQLTranslator { /** Angle between two points generated while transforming a circle into a polygon. * This angle is computed by default to get at the end a polygon of 32 points. * @see #circleToPolygon(double[], double) * @since 1.3 */ protected static double ANGLE_CIRCLE_TO_POLYGON = 2 * Math.PI / 32; /** * Builds a PgSphereTranslator which always translates in SQL all identifiers (schema, table and column) in a case sensitive manner ; * in other words, schema, table and column names will be surrounded by double quotes in the SQL translation. * * @see PostgreSQLTranslator#PostgreSQLTranslator() */ public PgSphereTranslator(){ super(); } /** * Builds a PgSphereTranslator which always translates in SQL all identifiers (schema, table and column) in the specified case sensitivity ; * in other words, schema, table and column names will all be surrounded or not by double quotes in the SQL translation. * * @param allCaseSensitive true to translate all identifiers in a case sensitive manner (surrounded by double quotes), false for case insensitivity. * * @see PostgreSQLTranslator#PostgreSQLTranslator(boolean) */ public PgSphereTranslator(boolean allCaseSensitive){ super(allCaseSensitive); } /** * Builds a PgSphereTranslator which will always translate in SQL identifiers with the defined case sensitivity. * * @param catalog true to translate catalog names with double quotes (case sensitive in the DBMS), false otherwise. * @param schema true to translate schema names with double quotes (case sensitive in the DBMS), false otherwise. * @param table true to translate table names with double quotes (case sensitive in the DBMS), false otherwise. * @param column true to translate column names with double quotes (case sensitive in the DBMS), false otherwise. * * @see PostgreSQLTranslator#PostgreSQLTranslator(boolean, boolean, boolean, boolean) */ public PgSphereTranslator(boolean catalog, boolean schema, boolean table, boolean column){ super(catalog, schema, table, column); } @Override public String translate(PointFunction point) throws TranslationException{ StringBuffer str = new StringBuffer("spoint("); str.append("radians(").append(translate(point.getCoord1())).append("),"); str.append("radians(").append(translate(point.getCoord2())).append("))"); return str.toString(); } @Override public String translate(CircleFunction circle) throws TranslationException{ StringBuffer str = new StringBuffer("scircle("); str.append("spoint(radians(").append(translate(circle.getCoord1())).append("),"); str.append("radians(").append(translate(circle.getCoord2())).append(")),"); str.append("radians(").append(translate(circle.getRadius())).append("))"); return str.toString(); } @Override public String translate(BoxFunction box) throws TranslationException{ StringBuffer str = new StringBuffer("sbox("); str.append("spoint(").append("radians(").append(translate(box.getCoord1())).append("-(").append(translate(box.getWidth())).append("/2.0)),"); str.append("radians(").append(translate(box.getCoord2())).append("-(").append(translate(box.getHeight())).append("/2.0))),"); str.append("spoint(").append("radians(").append(translate(box.getCoord1())).append("+(").append(translate(box.getWidth())).append("/2.0)),"); str.append("radians(").append(translate(box.getCoord2())).append("+(").append(translate(box.getHeight())).append("/2.0))))"); return str.toString(); } @Override public String translate(PolygonFunction polygon) throws TranslationException{ try{ StringBuffer str = new StringBuffer("spoly('{'"); if (polygon.getNbParameters() > 2){ PointFunction point = new PointFunction(polygon.getCoordinateSystem(), polygon.getParameter(1), polygon.getParameter(2)); str.append(" || ").append(translate(point)); for(int i = 3; i < polygon.getNbParameters() && i + 1 < polygon.getNbParameters(); i += 2){ point.setCoord1(polygon.getParameter(i)); point.setCoord2(polygon.getParameter(i + 1)); str.append(" || ',' || ").append(translate(point)); } } str.append(" || '}')"); return str.toString(); }catch(Exception e){ e.printStackTrace(); throw new TranslationException(e); } } @Override public String translate(ExtractCoord extractCoord) throws TranslationException{ StringBuffer str = new StringBuffer("degrees("); if (extractCoord.getName().equalsIgnoreCase("COORD1")) str.append("long("); else str.append("lat("); str.append(translate(extractCoord.getParameter(0))).append("))"); return str.toString(); } @Override public String translate(DistanceFunction fct) throws TranslationException{ StringBuffer str = new StringBuffer("degrees("); str.append(translate(fct.getP1())).append(" <-> ").append(translate(fct.getP2())).append(")"); return str.toString(); } @Override public String translate(AreaFunction areaFunction) throws TranslationException{ StringBuffer str = new StringBuffer("degrees(degrees(area("); str.append(translate(areaFunction.getParameter())).append(")))"); return str.toString(); } @Override public String translate(CentroidFunction centroidFunction) throws TranslationException{ StringBuffer str = new StringBuffer("center("); str.append(translate(centroidFunction.getParameter(0))).append(")"); return str.toString(); } @Override public String translate(ContainsFunction fct) throws TranslationException{ StringBuffer str = new StringBuffer("("); str.append(translate(fct.getLeftParam())).append(" @ ").append(translate(fct.getRightParam())).append(")"); return str.toString(); } @Override public String translate(IntersectsFunction fct) throws TranslationException{ StringBuffer str = new StringBuffer("("); str.append(translate(fct.getLeftParam())).append(" && ").append(translate(fct.getRightParam())).append(")"); return str.toString(); } @Override public String translate(Comparison comp) throws TranslationException{ if ((comp.getLeftOperand() instanceof ContainsFunction || comp.getLeftOperand() instanceof IntersectsFunction) && (comp.getOperator() == ComparisonOperator.EQUAL || comp.getOperator() == ComparisonOperator.NOT_EQUAL) && comp.getRightOperand().isNumeric()) return translate(comp.getLeftOperand()) + " " + comp.getOperator().toADQL() + " '" + translate(comp.getRightOperand()) + "'"; else if ((comp.getRightOperand() instanceof ContainsFunction || comp.getRightOperand() instanceof IntersectsFunction) && (comp.getOperator() == ComparisonOperator.EQUAL || comp.getOperator() == ComparisonOperator.NOT_EQUAL) && comp.getLeftOperand().isNumeric()) return "'" + translate(comp.getLeftOperand()) + "' " + comp.getOperator().toADQL() + " " + translate(comp.getRightOperand()); else return super.translate(comp); } @Override public DBType convertTypeFromDB(final int dbmsType, final String rawDbmsTypeName, String dbmsTypeName, final String[] params){ // If no type is provided return VARCHAR: if (dbmsTypeName == null || dbmsTypeName.trim().length() == 0) return null; // Put the dbmsTypeName in lower case for the following comparisons: dbmsTypeName = dbmsTypeName.toLowerCase(); if (dbmsTypeName.equals("spoint")) return new DBType(DBDatatype.POINT); else if (dbmsTypeName.equals("scircle") || dbmsTypeName.equals("sbox") || dbmsTypeName.equals("spoly")) return new DBType(DBDatatype.REGION); else return super.convertTypeFromDB(dbmsType, rawDbmsTypeName, dbmsTypeName, params); } @Override public String convertTypeToDB(final DBType type){ if (type != null){ if (type.type == DBDatatype.POINT) return "spoint"; else if (type.type == DBDatatype.REGION) return "spoly"; } return super.convertTypeToDB(type); } @Override public Region translateGeometryFromDB(final Object jdbcColValue) throws ParseException{ // A NULL value stays NULL: if (jdbcColValue == null) return null; // Only a special object is expected: else if (!(jdbcColValue instanceof PGobject)) throw new ParseException("Incompatible type! The column value \"" + jdbcColValue.toString() + "\" was supposed to be a geometrical object."); PGobject pgo = (PGobject)jdbcColValue; // In case one or both of the fields of the given object are NULL: if (pgo == null || pgo.getType() == null || pgo.getValue() == null || pgo.getValue().length() == 0) return null; // Extract the object type and its value: String objType = pgo.getType().toLowerCase(); String geomStr = pgo.getValue(); /* Only spoint, scircle, sbox and spoly are supported ; * these geometries are parsed and transformed in Region instances:*/ if (objType.equals("spoint")) return (new PgSphereGeometryParser()).parsePoint(geomStr); else if (objType.equals("scircle")) return (new PgSphereGeometryParser()).parseCircle(geomStr); else if (objType.equals("sbox")) return (new PgSphereGeometryParser()).parseBox(geomStr); else if (objType.equals("spoly")) return (new PgSphereGeometryParser()).parsePolygon(geomStr); else throw new ParseException("Unsupported PgSphere type: \"" + objType + "\"! Impossible to convert the column value \"" + geomStr + "\" into a Region."); } @Override public Object translateGeometryToDB(final Region region) throws ParseException{ // A NULL value stays NULL: if (region == null) return null; try{ PGobject dbRegion = new PGobject(); StringBuffer buf; // Build the PgSphere expression from the given geometry in function of its type: switch(region.type){ case POSITION: dbRegion.setType("spoint"); dbRegion.setValue("(" + region.coordinates[0][0] + "d," + region.coordinates[0][1] + "d)"); break; case POLYGON: dbRegion.setType("spoly"); buf = new StringBuffer("{"); for(int i = 0; i < region.coordinates.length; i++){ if (i > 0) buf.append(','); buf.append('(').append(region.coordinates[i][0]).append("d,").append(region.coordinates[i][1]).append("d)"); } buf.append('}'); dbRegion.setValue(buf.toString()); break; case BOX: dbRegion.setType("spoly"); buf = new StringBuffer("{"); // south west buf.append('(').append(region.coordinates[0][0] - region.width / 2).append("d,").append(region.coordinates[0][1] - region.height / 2).append("d),"); // north west buf.append('(').append(region.coordinates[0][0] - region.width / 2).append("d,").append(region.coordinates[0][1] + region.height / 2).append("d),"); // north east buf.append('(').append(region.coordinates[0][0] + region.width / 2).append("d,").append(region.coordinates[0][1] + region.height / 2).append("d),"); // south east buf.append('(').append(region.coordinates[0][0] + region.width / 2).append("d,").append(region.coordinates[0][1] - region.height / 2).append("d)"); buf.append('}'); dbRegion.setValue(buf.toString()); break; case CIRCLE: dbRegion.setType("spoly"); dbRegion.setValue(circleToPolygon(region.coordinates[0], region.radius)); break; default: throw new ParseException("Unsupported geometrical region: \"" + region.type + "\"!"); } return dbRegion; }catch(SQLException e){ /* This error could never happen! */ return null; } } /** *

Convert the specified circle into a polygon. * The generated polygon is formatted using the PgSphere syntax.

* *

Note: * The center coordinates and the radius are expected in degrees. *

* * @param center Center of the circle ([0]=ra and [1]=dec). * @param radius Radius of the circle. * * @return The PgSphere serialization of the corresponding polygon. * * @since 1.3 */ protected String circleToPolygon(final double[] center, final double radius){ double angle = 0, x, y; StringBuffer buf = new StringBuffer(); while(angle < 2 * Math.PI){ x = center[0] + radius * Math.cos(angle); y = center[1] + radius * Math.sin(angle); if (buf.length() > 0) buf.append(','); buf.append('(').append(x).append("d,").append(y).append("d)"); angle += ANGLE_CIRCLE_TO_POLYGON; } return "{" + buf + "}"; } /** *

Let parse a geometry serialized with the PgSphere syntax.

* *

* There is one function parseXxx(String) for each supported geometry. * These functions always return a {@link Region} object, * which is the object representation of an STC region. *

* *

Only the following geometries are supported:

*
    *
  • spoint => Position
  • *
  • scircle => Circle
  • *
  • sbox => Box
  • *
  • spoly => Polygon
  • *
* *

* This parser supports all the known PgSphere representations of an angle. * However, it always returns angle (coordinates, radius, width and height) in degrees. *

* * @author Grégory Mantelet (ARI) * @version 1.3 (11/2014) * @since 1.3 */ protected static class PgSphereGeometryParser { /** Position of the next characters to read in the PgSphere expression to parse. */ private int pos; /** Full PgSphere expression to parse. */ private String expr; /** Last read token (either a string/numeric or a separator). */ private String token; /** Buffer used to read tokens. */ private StringBuffer buffer; private static final char OPEN_PAR = '('; private static final char CLOSE_PAR = ')'; private static final char COMMA = ','; private static final char LESS_THAN = '<'; private static final char GREATER_THAN = '>'; private static final char OPEN_BRACE = '{'; private static final char CLOSE_BRACE = '}'; private static final char DEGREE = 'd'; private static final char HOUR = 'h'; private static final char MINUTE = 'm'; private static final char SECOND = 's'; /** * Exception sent when the end of the expression * (EOE = End Of Expression) is reached. * * @author Grégory Mantelet (ARI) * @version 1.3 (11/2014) * @since 1.3 */ private static class EOEException extends ParseException { private static final long serialVersionUID = 1L; /** Build a simple EOEException. */ public EOEException(){ super("Unexpected End Of PgSphere Expression!"); } } /** * Build the PgSphere parser. */ public PgSphereGeometryParser(){} /** * Prepare the parser in order to read the given PgSphere expression. * * @param newStcs New PgSphere expression to parse from now. */ private void init(final String newExpr){ expr = (newExpr == null) ? "" : newExpr; token = null; buffer = new StringBuffer(); pos = 0; } /** * Finalize the parsing. * No more characters (except eventually some space characters) should remain in the PgSphere expression to parse. * * @throws ParseException If other non-space characters remains. */ private void end() throws ParseException{ // Skip all spaces: skipSpaces(); // If there is still some characters, they are not expected, and so throw an exception: if (expr.length() > 0 && pos < expr.length()) throw new ParseException("Unexpected end of PgSphere region expression: \"" + expr.substring(pos) + "\" was unexpected!", new TextPosition(1, pos, 1, expr.length())); // Reset the buffer, token and the PgSphere expression to parse: buffer = null; expr = null; token = null; } /** * Tool function which skips all next space characters until the next meaningful characters. */ private void skipSpaces(){ while(pos < expr.length() && Character.isWhitespace(expr.charAt(pos))) pos++; } /** *

Get the next meaningful word. This word can be a numeric, any string constant or a separator. * This function returns this token but also stores it in the class attribute {@link #token}.

* *

* In case the end of the expression is reached before getting any meaningful character, * an {@link EOEException} is thrown. *

* * @return The full read word/token, or NULL if the end has been reached. */ private String nextToken() throws EOEException{ // Skip all spaces: skipSpaces(); if (pos >= expr.length()) throw new EOEException(); // Fetch all characters until word separator (a space or a open/close parenthesis): buffer.append(expr.charAt(pos++)); if (!isSyntaxSeparator(buffer.charAt(0))){ while(pos < expr.length() && !isSyntaxSeparator(expr.charAt(pos))){ // skip eventual white-spaces: if (!Character.isWhitespace(expr.charAt(pos))) buffer.append(expr.charAt(pos)); pos++; } } // Save the read token and reset the buffer: token = buffer.toString(); buffer.delete(0, token.length()); return token; } /** *

Tell whether the given character is a separator defined in the syntax.

* *

Here, the following characters are considered as separators/specials: * ',', 'd', 'h', 'm', 's', '(', ')', '<', '>', '{' and '}'.

* * @param c Character to test. * * @return true if the given character must be considered as a separator, false otherwise. */ private static boolean isSyntaxSeparator(final char c){ return (c == COMMA || c == DEGREE || c == HOUR || c == MINUTE || c == SECOND || c == OPEN_PAR || c == CLOSE_PAR || c == LESS_THAN || c == GREATER_THAN || c == OPEN_BRACE || c == CLOSE_BRACE); } /** * Get the next character and ensure it is the same as the character given in parameter. * If the read character is not matching the expected one, a {@link ParseException} is thrown. * * @param expected Expected character. * * @throws ParseException If the next character is not matching the given one. */ private void nextToken(final char expected) throws ParseException{ // Skip all spaces: skipSpaces(); // Test whether the end is reached: if (pos >= expr.length()) throw new EOEException(); // Fetch the next character: char t = expr.charAt(pos++); token = new String(new char[]{t}); /* Test the the fetched character with the expected one * and throw an error if they don't match: */ if (t != expected) throw new ParseException("Incorrect syntax for \"" + expr + "\"! \"" + expected + "\" was expected instead of \"" + t + "\".", new TextPosition(1, pos - 1, 1, pos)); } /** * Parse the given PgSphere geometry as a point. * * @param pgsphereExpr The PgSphere expression to parse as a point. * * @return A {@link Region} implementing a STC Position region. * * @throws ParseException If the PgSphere syntax of the given expression is wrong or does not correspond to a point. */ public Region parsePoint(final String pgsphereExpr) throws ParseException{ // Init the parser: init(pgsphereExpr); // Parse the expression: double[] coord = parsePoint(); // No more character should remain after that: end(); // Build the STC Position region: return new Region(null, coord); } /** * Internal spoint parsing function. It parses the PgSphere expression stored in this parser as a point. * * @return The ra and dec coordinates (in degrees) of the parsed point. * * @throws ParseException If the PgSphere syntax of the given expression is wrong or does not correspond to a point. * * @see #parseAngle() * @see #parsePoint(String) */ private double[] parsePoint() throws ParseException{ nextToken(OPEN_PAR); double x = parseAngle(); nextToken(COMMA); double y = parseAngle(); nextToken(CLOSE_PAR); return new double[]{x,y}; } /** * Parse the given PgSphere geometry as a circle. * * @param pgsphereExpr The PgSphere expression to parse as a circle. * * @return A {@link Region} implementing a STC Circle region. * * @throws ParseException If the PgSphere syntax of the given expression is wrong or does not correspond to a circle. */ public Region parseCircle(final String pgsphereExpr) throws ParseException{ // Init the parser: init(pgsphereExpr); // Parse the expression: nextToken(LESS_THAN); double[] center = parsePoint(); nextToken(COMMA); double radius = parseAngle(); nextToken(GREATER_THAN); // No more character should remain after that: end(); // Build the STC Circle region: return new Region(null, center, radius); } /** * Parse the given PgSphere geometry as a box. * * @param pgsphereExpr The PgSphere expression to parse as a box. * * @return A {@link Region} implementing a STC Box region. * * @throws ParseException If the PgSphere syntax of the given expression is wrong or does not correspond to a box. */ public Region parseBox(final String pgsphereExpr) throws ParseException{ // Init the parser: init(pgsphereExpr); // Parse the expression: nextToken(OPEN_PAR); double[] southwest = parsePoint(); nextToken(COMMA); double[] northeast = parsePoint(); nextToken(CLOSE_PAR); // No more character should remain after that: end(); // Build the STC Box region: double width = Math.abs(northeast[0] - southwest[0]), height = Math.abs(northeast[1] - southwest[1]); double[] center = new double[]{northeast[0] - width / 2,northeast[1] - height / 2}; return new Region(null, center, width, height); } /** * Parse the given PgSphere geometry as a point. * * @param pgsphereExpr The PgSphere expression to parse as a point. * * @return A {@link Region} implementing a STC Position region. * * @throws ParseException If the PgSphere syntax of the given expression is wrong or does not correspond to a point. */ public Region parsePolygon(final String pgsphereExpr) throws ParseException{ // Init the parser: init(pgsphereExpr); // Parse the expression: nextToken(OPEN_BRACE); ArrayList points = new ArrayList(3); // at least 3 points are expected: points.add(parsePoint()); nextToken(COMMA); points.add(parsePoint()); nextToken(COMMA); points.add(parsePoint()); // but if there are more points, parse and keep them: while(nextToken().length() == 1 && token.charAt(0) == COMMA) points.add(parsePoint()); // the expression must end with a } : if (token.length() != 1 || token.charAt(0) != CLOSE_BRACE) throw new ParseException("Incorrect syntax for \"" + expr + "\"! \"}\" was expected instead of \"" + token + "\".", new TextPosition(1, pos - token.length(), 1, pos)); // No more character should remain after that: end(); // Build the STC Polygon region: return new Region(null, points.toArray(new double[points.size()][2])); } /** *

Read the next tokens as an angle expression and returns the corresponding angle in degrees.

* *

This function supports the 4 following syntaxes:

*
    *
  • RAD: {number}
  • *
  • DEG: {number}d
  • *
  • DMS: {number}d {number}m {number}s
  • *
  • HMS: {number}h {number}m {number}s
  • *
* * @return The corresponding angle in degrees. * * @throws ParseException If the angle syntax is wrong or not supported. */ private double parseAngle() throws ParseException{ int oldPos = pos; String number = nextToken(); try{ double degrees = Double.parseDouble(number); int sign = (degrees < 0) ? -1 : 1; degrees = Math.abs(degrees); oldPos = pos; try{ if (nextToken().length() == 1 && token.charAt(0) == HOUR) sign *= 15; else if (token.length() != 1 || token.charAt(0) != DEGREE){ degrees = degrees * 180 / Math.PI; pos -= token.length(); return degrees * sign; } oldPos = pos; number = nextToken(); if (nextToken().length() == 1 && token.charAt(0) == MINUTE) degrees += Double.parseDouble(number) / 60; else if (token.length() == 1 && token.charAt(0) == SECOND){ degrees += Double.parseDouble(number) / 3600; return degrees * sign; }else{ pos = oldPos; return degrees * sign; } oldPos = pos; number = nextToken(); if (nextToken().length() == 1 && token.charAt(0) == SECOND) degrees += Double.parseDouble(number) / 3600; else pos = oldPos; }catch(EOEException ex){ pos = oldPos; } return degrees * sign; }catch(NumberFormatException nfe){ throw new ParseException("Incorrect numeric syntax: \"" + number + "\"!", new TextPosition(1, pos - token.length(), 1, pos)); } } } } src/adql/translator/SQLServerTranslator.java0000644000175000017500000004256413177122310020220 0ustar olesolespackage adql.translator; import java.util.Iterator; import adql.db.DBColumn; import adql.db.DBType; import adql.db.DBType.DBDatatype; import adql.db.STCS.Region; import adql.db.SearchColumnList; import adql.db.exception.UnresolvedJoinException; import adql.parser.ParseException; import adql.parser.SQLServer_ADQLQueryFactory; import adql.query.ADQLQuery; import adql.query.ClauseSelect; import adql.query.IdentifierField; import adql.query.from.ADQLJoin; import adql.query.from.ADQLTable; import adql.query.from.FromContent; import adql.query.operand.ADQLColumn; import adql.query.operand.function.MathFunction; import adql.query.operand.function.geometry.AreaFunction; import adql.query.operand.function.geometry.BoxFunction; import adql.query.operand.function.geometry.CentroidFunction; import adql.query.operand.function.geometry.CircleFunction; import adql.query.operand.function.geometry.ContainsFunction; import adql.query.operand.function.geometry.DistanceFunction; import adql.query.operand.function.geometry.ExtractCoord; import adql.query.operand.function.geometry.ExtractCoordSys; import adql.query.operand.function.geometry.IntersectsFunction; import adql.query.operand.function.geometry.PointFunction; import adql.query.operand.function.geometry.PolygonFunction; import adql.query.operand.function.geometry.RegionFunction; /** *

MS SQL Server translator.

* *

Important: * This translator works correctly ONLY IF {@link SQLServer_ADQLQueryFactory} has been used * to create any ADQL query this translator is asked to translate. *

* * TODO See how case sensitivity is supported by MS SQL Server and modify this translator accordingly. * * TODO Extend this class for each MS SQL Server extension supporting geometry and particularly * {@link #translateGeometryFromDB(Object)}, {@link #translateGeometryToDB(adql.db.STCS.Region)} and all this other * translate(...) functions for the ADQL's geometrical functions. * * TODO Check MS SQL Server datatypes (see {@link #convertTypeFromDB(int, String, String, String[])}, * {@link #convertTypeToDB(DBType)}). * *

Important note: * Geometrical functions are not translated ; the translation returned for them is their ADQL expression. *

* * @author Grégory Mantelet (ARI) * @version 1.4 (09/2017) * @since 1.4 * * @see SQLServer_ADQLQueryFactory */ public class SQLServerTranslator extends JDBCTranslator { /**

Indicate the case sensitivity to apply to each SQL identifier (only SCHEMA, TABLE and COLUMN).

* *

Note: * In this implementation, this field is set by the constructor and never modified elsewhere. * It would be better to never modify it after the construction in order to keep a certain consistency. *

*/ protected byte caseSensitivity = 0x00; /** * Builds an SQLServerTranslator which always translates in SQL all identifiers (schema, table and column) in a case sensitive manner ; * in other words, schema, table and column names will be surrounded by double quotes in the SQL translation. */ public SQLServerTranslator(){ caseSensitivity = 0x0F; } /** * Builds an SQLServerTranslator which always translates in SQL all identifiers (schema, table and column) in the specified case sensitivity ; * in other words, schema, table and column names will all be surrounded or not by double quotes in the SQL translation. * * @param allCaseSensitive true to translate all identifiers in a case sensitive manner (surrounded by double quotes), false for case insensitivity. */ public SQLServerTranslator(final boolean allCaseSensitive){ caseSensitivity = allCaseSensitive ? (byte)0x0F : (byte)0x00; } /** * Builds an SQLServerTranslator which will always translate in SQL identifiers with the defined case sensitivity. * * @param catalog true to translate catalog names with double quotes (case sensitive in the DBMS), false otherwise. * @param schema true to translate schema names with double quotes (case sensitive in the DBMS), false otherwise. * @param table true to translate table names with double quotes (case sensitive in the DBMS), false otherwise. * @param column true to translate column names with double quotes (case sensitive in the DBMS), false otherwise. */ public SQLServerTranslator(final boolean catalog, final boolean schema, final boolean table, final boolean column){ caseSensitivity = IdentifierField.CATALOG.setCaseSensitive(caseSensitivity, catalog); caseSensitivity = IdentifierField.SCHEMA.setCaseSensitive(caseSensitivity, schema); caseSensitivity = IdentifierField.TABLE.setCaseSensitive(caseSensitivity, table); caseSensitivity = IdentifierField.COLUMN.setCaseSensitive(caseSensitivity, column); } @Override public boolean isCaseSensitive(final IdentifierField field){ return field == null ? false : field.isCaseSensitive(caseSensitivity); } /** * For SQL Server, {@link #translate(ClauseSelect)} must be overridden for * TOP/LIMIT handling. We must not add the LIMIT at the end of the query, it * must go in the SELECT. * * @see #translate(ClauseSelect) */ @Override public String translate(ADQLQuery query) throws TranslationException{ StringBuffer sql = new StringBuffer(translate(query.getSelect())); sql.append("\nFROM ").append(translate(query.getFrom())); if (!query.getWhere().isEmpty()) sql.append('\n').append(translate(query.getWhere())); if (!query.getGroupBy().isEmpty()) sql.append('\n').append(translate(query.getGroupBy())); if (!query.getHaving().isEmpty()) sql.append('\n').append(translate(query.getHaving())); if (!query.getOrderBy().isEmpty()) sql.append('\n').append(translate(query.getOrderBy())); return sql.toString(); } @Override public String translate(ClauseSelect clause) throws TranslationException{ String sql = null; for(int i = 0; i < clause.size(); i++){ if (i == 0){ sql = clause.getName() + (clause.distinctColumns() ? " DISTINCT" : "") + (clause.hasLimit() ? " TOP " + clause.getLimit() + " " : ""); }else sql += " " + clause.getSeparator(i); sql += " " + translate(clause.get(i)); } return sql; } @Override public String translate(final ADQLJoin join) throws TranslationException{ StringBuffer sql = new StringBuffer(translate(join.getLeftTable())); sql.append(' ').append(join.getJoinType()).append(' ').append(translate(join.getRightTable())).append(' '); // CASE: NATURAL if (join.isNatural()){ try{ StringBuffer buf = new StringBuffer(); // Find duplicated items between the two lists and translate them as ON conditions: DBColumn rightCol; SearchColumnList leftList = join.getLeftTable().getDBColumns(); SearchColumnList rightList = join.getRightTable().getDBColumns(); for(DBColumn leftCol : leftList){ // search for at most one column with the same name in the RIGHT list // and throw an exception is there are several matches: rightCol = ADQLJoin.findAtMostOneColumn(leftCol.getADQLName(), (byte)0, rightList, false); // if there is one... if (rightCol != null){ // ...check there is only one column with this name in the LEFT list, // and throw an exception if it is not the case: ADQLJoin.findExactlyOneColumn(leftCol.getADQLName(), (byte)0, leftList, true); // ...append the corresponding join condition: if (buf.length() > 0) buf.append(" AND "); buf.append(translate(generateJoinColumn(join.getLeftTable(), leftCol, new ADQLColumn(leftCol.getADQLName())))); buf.append("="); buf.append(translate(generateJoinColumn(join.getRightTable(), rightCol, new ADQLColumn(rightCol.getADQLName())))); } } sql.append("ON ").append(buf.toString()); }catch(UnresolvedJoinException uje){ throw new TranslationException("Impossible to resolve the NATURAL JOIN between " + join.getLeftTable().toADQL() + " and " + join.getRightTable().toADQL() + "!", uje); } } // CASE: USING else if (join.hasJoinedColumns()){ try{ StringBuffer buf = new StringBuffer(); // For each columns of usingList, check there is in each list exactly one matching column, and then, translate it as ON condition: DBColumn leftCol, rightCol; ADQLColumn usingCol; SearchColumnList leftList = join.getLeftTable().getDBColumns(); SearchColumnList rightList = join.getRightTable().getDBColumns(); Iterator itCols = join.getJoinedColumns(); while(itCols.hasNext()){ usingCol = itCols.next(); // search for exactly one column with the same name in the LEFT list // and throw an exception if there is none, or if there are several matches: leftCol = ADQLJoin.findExactlyOneColumn(usingCol.getColumnName(), usingCol.getCaseSensitive(), leftList, true); // item in the RIGHT list: rightCol = ADQLJoin.findExactlyOneColumn(usingCol.getColumnName(), usingCol.getCaseSensitive(), rightList, false); // append the corresponding join condition: if (buf.length() > 0) buf.append(" AND "); buf.append(translate(generateJoinColumn(join.getLeftTable(), leftCol, usingCol))); buf.append("="); buf.append(translate(generateJoinColumn(join.getRightTable(), rightCol, usingCol))); } sql.append("ON ").append(buf.toString()); }catch(UnresolvedJoinException uje){ throw new TranslationException("Impossible to resolve the JOIN USING between " + join.getLeftTable().toADQL() + " and " + join.getRightTable().toADQL() + "!", uje); } } // DEFAULT CASE: else if (join.getJoinCondition() != null) sql.append(translate(join.getJoinCondition())); return sql.toString(); } /** * Generate an ADQL column of the given table and with the given metadata. * * @param table Parent table of the column to generate. * @param colMeta DB metadata of the column to generate. * @param joinedColumn The joined column (i.e. the ADQL column listed in a * USING) from which the generated column should * derive. * If NULL, an {@link ADQLColumn} instance will be * created from scratch using the ADQL name of the * given DB metadata. * * @return The generated column. */ protected ADQLColumn generateJoinColumn(final FromContent table, final DBColumn colMeta, final ADQLColumn joinedColumn){ ADQLColumn newCol = (joinedColumn == null ? new ADQLColumn(colMeta.getADQLName()) : new ADQLColumn(joinedColumn)); if (table != null){ if (table instanceof ADQLTable) newCol.setAdqlTable((ADQLTable)table); else newCol.setAdqlTable(new ADQLTable(table.getName())); } newCol.setDBLink(colMeta); return newCol; } @Override public String translate(final ExtractCoord extractCoord) throws TranslationException{ return getDefaultADQLFunction(extractCoord); } @Override public String translate(final ExtractCoordSys extractCoordSys) throws TranslationException{ return getDefaultADQLFunction(extractCoordSys); } @Override public String translate(final AreaFunction areaFunction) throws TranslationException{ return getDefaultADQLFunction(areaFunction); } @Override public String translate(final CentroidFunction centroidFunction) throws TranslationException{ return getDefaultADQLFunction(centroidFunction); } @Override public String translate(final DistanceFunction fct) throws TranslationException{ return getDefaultADQLFunction(fct); } @Override public String translate(final ContainsFunction fct) throws TranslationException{ return getDefaultADQLFunction(fct); } @Override public String translate(final IntersectsFunction fct) throws TranslationException{ return getDefaultADQLFunction(fct); } @Override public String translate(final PointFunction point) throws TranslationException{ return getDefaultADQLFunction(point); } @Override public String translate(final CircleFunction circle) throws TranslationException{ return getDefaultADQLFunction(circle); } @Override public String translate(final BoxFunction box) throws TranslationException{ return getDefaultADQLFunction(box); } @Override public String translate(final PolygonFunction polygon) throws TranslationException{ return getDefaultADQLFunction(polygon); } @Override public String translate(final RegionFunction region) throws TranslationException{ return getDefaultADQLFunction(region); } @Override public String translate(MathFunction fct) throws TranslationException{ switch(fct.getType()){ case TRUNCATE: // third argument to round nonzero means do a truncate return "round(convert(float, " + ((fct.getNbParameters() >= 2) ? (translate(fct.getParameter(0)) + ", " + translate(fct.getParameter(1))) : "") + "),1)"; case MOD: return ((fct.getNbParameters() >= 2) ? ("convert(float, " + translate(fct.getParameter(0)) + ") % convert(float, " + translate(fct.getParameter(1)) + ")") : ""); case ATAN2: return "ATN2(" + translate(fct.getParameter(0)) + ", " + translate(fct.getParameter(1)) + ")"; /* In MS-SQLServer, the following functions returns a value of the * same type as the given argument. However, ADQL requires that an * SQLServer float (so a double in ADQL) is returned. So, in order * to follow the ADQL standard, the given parameter must be * converted into a float: */ case ABS: return "abs(convert(float, " + translate(fct.getParameter(0)) + "))"; case CEILING: return "ceiling(convert(float, " + translate(fct.getParameter(0)) + "))"; case DEGREES: return "degrees(convert(float, " + translate(fct.getParameter(0)) + "))"; case FLOOR: return "floor(convert(float, " + translate(fct.getParameter(0)) + "))"; case RADIANS: return "radians(convert(float, " + translate(fct.getParameter(0)) + "))"; case ROUND: return "round(convert(float, " + translate(fct.getParameter(0)) + ")" + ", " + translate(fct.getParameter(1)) + ")"; default: return getDefaultADQLFunction(fct); } } @Override public DBType convertTypeFromDB(final int dbmsType, final String rawDbmsTypeName, String dbmsTypeName, final String[] params){ // If no type is provided return VARCHAR: if (dbmsTypeName == null || dbmsTypeName.trim().length() == 0) return null; // Put the dbmsTypeName in lower case for the following comparisons: dbmsTypeName = dbmsTypeName.toLowerCase(); // Extract the length parameter (always the first one): int lengthParam = DBType.NO_LENGTH; if (params != null && params.length > 0){ try{ lengthParam = Integer.parseInt(params[0]); }catch(NumberFormatException nfe){} } // SMALLINT if (dbmsTypeName.equals("smallint") || dbmsTypeName.equals("tinyint") || dbmsTypeName.equals("bit")) return new DBType(DBDatatype.SMALLINT); // INTEGER else if (dbmsTypeName.equals("int")) return new DBType(DBDatatype.INTEGER); // BIGINT else if (dbmsTypeName.equals("bigint")) return new DBType(DBDatatype.BIGINT); // REAL (cf https://msdn.microsoft.com/fr-fr/library/ms173773(v=sql.120).aspx) else if (dbmsTypeName.equals("real") || (dbmsTypeName.equals("float") && lengthParam >= 1 && lengthParam <= 24)) return new DBType(DBDatatype.REAL); // DOUBLE (cf https://msdn.microsoft.com/fr-fr/library/ms173773(v=sql.120).aspx) else if (dbmsTypeName.equals("float") || dbmsTypeName.equals("decimal") || dbmsTypeName.equals("numeric")) return new DBType(DBDatatype.DOUBLE); // BINARY else if (dbmsTypeName.equals("binary")) return new DBType(DBDatatype.BINARY, lengthParam); // VARBINARY else if (dbmsTypeName.equals("varbinary")) return new DBType(DBDatatype.VARBINARY, lengthParam); // CHAR else if (dbmsTypeName.equals("char") || dbmsTypeName.equals("nchar")) return new DBType(DBDatatype.CHAR, lengthParam); // VARCHAR else if (dbmsTypeName.equals("varchar") || dbmsTypeName.equals("nvarchar")) return new DBType(DBDatatype.VARCHAR, lengthParam); // BLOB else if (dbmsTypeName.equals("image")) return new DBType(DBDatatype.BLOB); // CLOB else if (dbmsTypeName.equals("text") || dbmsTypeName.equals("ntext")) return new DBType(DBDatatype.CLOB); // TIMESTAMP else if (dbmsTypeName.equals("timestamp") || dbmsTypeName.equals("datetime") || dbmsTypeName.equals("datetime2") || dbmsTypeName.equals("datetimeoffset") || dbmsTypeName.equals("smalldatetime") || dbmsTypeName.equals("time") || dbmsTypeName.equals("date") || dbmsTypeName.equals("date")) return new DBType(DBDatatype.TIMESTAMP); // Default: else return null; } @Override public String convertTypeToDB(final DBType type){ if (type == null) return "varchar"; switch(type.type){ case SMALLINT: case REAL: case BIGINT: case CHAR: case VARCHAR: case BINARY: case VARBINARY: return type.type.toString().toLowerCase(); case INTEGER: return "int"; // (cf https://msdn.microsoft.com/fr-fr/library/ms173773(v=sql.120).aspx) case DOUBLE: return "float(53)"; case TIMESTAMP: return "datetime"; case BLOB: return "image"; case CLOB: return "text"; case POINT: case REGION: default: return "varchar"; } } @Override public Region translateGeometryFromDB(final Object jdbcColValue) throws ParseException{ throw new ParseException("Unsupported geometrical value! The value \"" + jdbcColValue + "\" can not be parsed as a region."); } @Override public Object translateGeometryToDB(final Region region) throws ParseException{ throw new ParseException("Geometries can not be uploaded in the database in this implementation!"); } } src/adql/translator/ADQLTranslator.java0000644000175000017500000001520213177122310017100 0ustar olesolespackage adql.translator; /* * 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 . * * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS), * Astronomisches Rechen Institut (ARI) */ import adql.query.ADQLList; import adql.query.ADQLObject; import adql.query.ADQLOrder; import adql.query.ADQLQuery; import adql.query.ClauseConstraints; import adql.query.ClauseSelect; import adql.query.ColumnReference; import adql.query.SelectAllColumns; import adql.query.SelectItem; import adql.query.constraint.ADQLConstraint; import adql.query.constraint.Between; import adql.query.constraint.Comparison; import adql.query.constraint.Exists; import adql.query.constraint.In; import adql.query.constraint.IsNull; import adql.query.constraint.NotConstraint; import adql.query.from.ADQLJoin; import adql.query.from.ADQLTable; import adql.query.from.FromContent; import adql.query.operand.ADQLColumn; import adql.query.operand.ADQLOperand; import adql.query.operand.Concatenation; import adql.query.operand.NegativeOperand; import adql.query.operand.NumericConstant; import adql.query.operand.Operation; import adql.query.operand.StringConstant; import adql.query.operand.WrappedOperand; import adql.query.operand.function.ADQLFunction; import adql.query.operand.function.MathFunction; import adql.query.operand.function.SQLFunction; import adql.query.operand.function.UserDefinedFunction; import adql.query.operand.function.geometry.AreaFunction; import adql.query.operand.function.geometry.BoxFunction; import adql.query.operand.function.geometry.CentroidFunction; import adql.query.operand.function.geometry.CircleFunction; import adql.query.operand.function.geometry.ContainsFunction; import adql.query.operand.function.geometry.DistanceFunction; import adql.query.operand.function.geometry.ExtractCoord; import adql.query.operand.function.geometry.ExtractCoordSys; import adql.query.operand.function.geometry.GeometryFunction; import adql.query.operand.function.geometry.GeometryFunction.GeometryValue; import adql.query.operand.function.geometry.IntersectsFunction; import adql.query.operand.function.geometry.PointFunction; import adql.query.operand.function.geometry.PolygonFunction; import adql.query.operand.function.geometry.RegionFunction; /** * Translates ADQL objects into any language (i.e. SQL). * * @author Grégory Mantelet (CDS) * @version 01/2012 * * @see PostgreSQLTranslator */ public interface ADQLTranslator { public String translate(ADQLObject obj) throws TranslationException; public String translate(ADQLQuery query) throws TranslationException; /* ***** LIST & CLAUSE ***** */ public String translate(ADQLList list) throws TranslationException; public String translate(ClauseSelect clause) throws TranslationException; public String translate(ClauseConstraints clause) throws TranslationException; public String translate(SelectItem item) throws TranslationException; public String translate(SelectAllColumns item) throws TranslationException; public String translate(ColumnReference ref) throws TranslationException; public String translate(ADQLOrder order) throws TranslationException; /* ***** TABLE & JOIN ***** */ public String translate(FromContent content) throws TranslationException; public String translate(ADQLTable table) throws TranslationException; public String translate(ADQLJoin join) throws TranslationException; /* ***** OPERAND ***** */ public String translate(ADQLOperand op) throws TranslationException; public String translate(ADQLColumn column) throws TranslationException; public String translate(Concatenation concat) throws TranslationException; public String translate(NegativeOperand negOp) throws TranslationException; public String translate(NumericConstant numConst) throws TranslationException; public String translate(StringConstant strConst) throws TranslationException; public String translate(WrappedOperand op) throws TranslationException; public String translate(Operation op) throws TranslationException; /* ***** CONSTRAINT ***** */ public String translate(ADQLConstraint cons) throws TranslationException; public String translate(Comparison comp) throws TranslationException; public String translate(Between comp) throws TranslationException; public String translate(Exists exists) throws TranslationException; public String translate(In in) throws TranslationException; public String translate(IsNull isNull) throws TranslationException; public String translate(NotConstraint notCons) throws TranslationException; /* ***** FUNCTIONS ***** */ public String translate(ADQLFunction fct) throws TranslationException; public String translate(SQLFunction fct) throws TranslationException; public String translate(MathFunction fct) throws TranslationException; public String translate(UserDefinedFunction fct) throws TranslationException; /* ***** GEOMETRICAL FUNCTIONS ***** */ public String translate(GeometryFunction fct) throws TranslationException; public String translate(GeometryValue geomValue) throws TranslationException; public String translate(ExtractCoord extractCoord) throws TranslationException; public String translate(ExtractCoordSys extractCoordSys) throws TranslationException; public String translate(AreaFunction areaFunction) throws TranslationException; public String translate(CentroidFunction centroidFunction) throws TranslationException; public String translate(DistanceFunction fct) throws TranslationException; public String translate(ContainsFunction fct) throws TranslationException; public String translate(IntersectsFunction fct) throws TranslationException; public String translate(PointFunction point) throws TranslationException; public String translate(CircleFunction circle) throws TranslationException; public String translate(BoxFunction box) throws TranslationException; public String translate(PolygonFunction polygon) throws TranslationException; public String translate(RegionFunction region) throws TranslationException; } src/adql/translator/MySQLTranslator.java0000644000175000017500000002676613225633004017346 0ustar olesolespackage adql.translator; import adql.db.DBType; import adql.db.DBType.DBDatatype; import adql.db.STCS.Region; import adql.parser.ParseException; import adql.query.IdentifierField; import adql.query.operand.function.geometry.AreaFunction; import adql.query.operand.function.geometry.BoxFunction; import adql.query.operand.function.geometry.CentroidFunction; import adql.query.operand.function.geometry.CircleFunction; import adql.query.operand.function.geometry.ContainsFunction; import adql.query.operand.function.geometry.DistanceFunction; import adql.query.operand.function.geometry.ExtractCoord; import adql.query.operand.function.geometry.ExtractCoordSys; import adql.query.operand.function.geometry.IntersectsFunction; import adql.query.operand.function.geometry.PointFunction; import adql.query.operand.function.geometry.PolygonFunction; import adql.query.operand.function.geometry.RegionFunction; /* * 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 . * * Copyright 2017 - Astronomisches Rechen Institut (ARI) */ /** *

Translates all ADQL objects into an SQL interrogation query designed for MySQL.

* *

Important: * The geometrical functions are translated exactly as in ADQL. * You will probably need to extend this translator to correctly manage the geometrical functions. *

* * @author Grégory Mantelet (ARI) * @version 2.1 (08/2017) * @since 2.1 */ public class MySQLTranslator extends JDBCTranslator { /** MySQL requires a length for variable-length types such as CHAR, VARCHAR, * BINARY and VARBINARY. This static attributes is the default value set * by this translator if no length is specified. */ public static int DEFAULT_VARIABLE_LENGTH = 200; /** Indicate the case sensitivity to apply to each SQL identifier * (only SCHEMA, TABLE and COLUMN). *

Note: * In this implementation, this field is set by the constructor and never * modified elsewhere. It would be better to never modify it after the * construction in order to keep a certain consistency. *

*/ protected byte caseSensitivity = 0x00; /** * Build a MySQLTranslator which always translates in SQL all identifiers * (schema, table and column) in a case sensitive manner ; in other words, * schema, table and column names will be surrounded by back-quotes in the * SQL translation. */ public MySQLTranslator(){ caseSensitivity = 0x0F; } /** * Build a MySQLTranslator which always translates in SQL all identifiers * (schema, table and column) in the specified case sensitivity ; in other * words, schema, table and column names will all be surrounded or not by * back-quotes in the SQL translation. * * @param allCaseSensitive true to translate all identifiers in a * case sensitive manner * (surrounded by back-quotes), * false for case insensitivity. */ public MySQLTranslator(final boolean allCaseSensitive){ caseSensitivity = allCaseSensitive ? (byte)0x0F : (byte)0x00; } /** * Build a MySQLTranslator which will always translate in SQL identifiers * with the defined case sensitivity. * * @param catalog true to translate catalog names with back-quotes * (case sensitive in the DBMS), false otherwise. * @param schema true to translate schema names with back-quotes * (case sensitive in the DBMS), false otherwise. * @param table true to translate table names with back-quotes * (case sensitive in the DBMS), false otherwise. * @param column true to translate column names with back-quotes * (case sensitive in the DBMS), false otherwise. */ public MySQLTranslator(final boolean catalog, final boolean schema, final boolean table, final boolean column){ caseSensitivity = IdentifierField.CATALOG.setCaseSensitive(caseSensitivity, catalog); caseSensitivity = IdentifierField.SCHEMA.setCaseSensitive(caseSensitivity, schema); caseSensitivity = IdentifierField.TABLE.setCaseSensitive(caseSensitivity, table); caseSensitivity = IdentifierField.COLUMN.setCaseSensitive(caseSensitivity, column); } @Override public boolean isCaseSensitive(final IdentifierField field){ return field == null ? false : field.isCaseSensitive(caseSensitivity); } @Override public StringBuffer appendIdentifier(final StringBuffer str, final String id, final boolean caseSensitive){ /* Note: In MySQL the identifier quoting character is a back-quote. */ if (caseSensitive && !id.matches("\"[^\"]*\"")) return str.append('`').append(id).append('`'); else return str.append(id); } /* ********************************************************************** */ /* * * */ /* * TYPE MANAGEMENT * */ /* * * */ /* ********************************************************************** */ @Override public DBType convertTypeFromDB(final int dbmsType, final String rawDbmsTypeName, String dbmsTypeName, final String[] params){ // If no type is provided return VARCHAR: if (dbmsTypeName == null || dbmsTypeName.trim().length() == 0) return null; // Put the dbmsTypeName in lower case for the following comparisons: dbmsTypeName = dbmsTypeName.toLowerCase(); // Extract the length parameter (always the first one): int lengthParam = DBType.NO_LENGTH; if (params != null && params.length > 0){ try{ lengthParam = Integer.parseInt(params[0]); }catch(NumberFormatException nfe){} } // SMALLINT if (dbmsTypeName.equals("smallint") || dbmsTypeName.equals("tinyint") || dbmsTypeName.equals("bool") || dbmsTypeName.equals("boolean")) return new DBType(DBDatatype.SMALLINT); // INTEGER else if (dbmsTypeName.equals("integer") || dbmsTypeName.equals("int") || dbmsTypeName.equals("mediumint")) return new DBType(DBDatatype.INTEGER); // BIGINT else if (dbmsTypeName.equals("bigint")) return new DBType(DBDatatype.BIGINT); // REAL else if (dbmsTypeName.equals("float") || dbmsTypeName.equals("real")) return new DBType(DBDatatype.REAL); // DOUBLE else if (dbmsTypeName.equals("double") || dbmsTypeName.equals("double precision") || dbmsTypeName.equals("dec") || dbmsTypeName.equals("decimal") || dbmsTypeName.equals("numeric") || dbmsTypeName.equals("fixed")) return new DBType(DBDatatype.DOUBLE); // BINARY else if (dbmsTypeName.equals("bit") || dbmsTypeName.equals("binary") || dbmsTypeName.equals("char byte")) return new DBType(DBDatatype.BINARY, lengthParam); // VARBINARY else if (dbmsTypeName.equals("varbinary")) return new DBType(DBDatatype.VARBINARY, lengthParam); // CHAR else if (dbmsTypeName.equals("char") || dbmsTypeName.equals("character") || dbmsTypeName.equals("nchar") || dbmsTypeName.equals("national char")) return new DBType(DBDatatype.CHAR, lengthParam); // VARCHAR else if (dbmsTypeName.equals("varchar") || dbmsTypeName.equals("character varying") || dbmsTypeName.equals("nvarchar") || dbmsTypeName.equals("national varchar")) return new DBType(DBDatatype.VARCHAR, lengthParam); // BLOB else if (dbmsTypeName.equals("blob") || dbmsTypeName.equals("tinyblob") || dbmsTypeName.equals("mediumblob") || dbmsTypeName.equals("longblob")) return new DBType(DBDatatype.BLOB); // CLOB else if (dbmsTypeName.equals("text") || dbmsTypeName.equals("tinytext") || dbmsTypeName.equals("mediumtext") || dbmsTypeName.equals("longtext")) return new DBType(DBDatatype.CLOB); // TIMESTAMP else if (dbmsTypeName.equals("timestamp") || dbmsTypeName.equals("date") || dbmsTypeName.equals("datetime") || dbmsTypeName.equals("time") || dbmsTypeName.equals("year")) return new DBType(DBDatatype.TIMESTAMP); // Default: else return null; } @Override public String convertTypeToDB(final DBType type){ if (type == null) return "VARCHAR(" + DEFAULT_VARIABLE_LENGTH + ")"; switch(type.type){ case SMALLINT: case INTEGER: case REAL: case BIGINT: case TIMESTAMP: return type.type.toString(); case DOUBLE: return "DOUBLE PRECISION"; case CHAR: case VARCHAR: case BINARY: case VARBINARY: return type.type.toString() + "(" + (type.length > 0 ? type.length : DEFAULT_VARIABLE_LENGTH) + ")"; case BLOB: return "BLOB"; case CLOB: return "TEXT"; case POINT: case REGION: default: return "VARCHAR(" + DEFAULT_VARIABLE_LENGTH + ")"; } } @Override public Region translateGeometryFromDB(final Object jdbcColValue) throws ParseException{ throw new ParseException("Unsupported geometrical value! The value \"" + jdbcColValue + "\" can not be parsed as a region."); } @Override public Object translateGeometryToDB(final Region region) throws ParseException{ throw new ParseException("Geometries can not be uploaded in the database in this implementation!"); } /* ********************************************************************** */ /* * * */ /* * SPATIAL FUNCTIONS TRANSLATION * */ /* * * */ /* ********************************************************************** */ @Override public String translate(ExtractCoord extractCoord) throws TranslationException{ return getDefaultADQLFunction(extractCoord); } @Override public String translate(ExtractCoordSys extractCoordSys) throws TranslationException{ return getDefaultADQLFunction(extractCoordSys); } @Override public String translate(AreaFunction areaFunction) throws TranslationException{ return getDefaultADQLFunction(areaFunction); } @Override public String translate(CentroidFunction centroidFunction) throws TranslationException{ return getDefaultADQLFunction(centroidFunction); } @Override public String translate(DistanceFunction fct) throws TranslationException{ return getDefaultADQLFunction(fct); } @Override public String translate(ContainsFunction fct) throws TranslationException{ return getDefaultADQLFunction(fct); } @Override public String translate(IntersectsFunction fct) throws TranslationException{ return getDefaultADQLFunction(fct); } @Override public String translate(BoxFunction box) throws TranslationException{ return getDefaultADQLFunction(box); } @Override public String translate(CircleFunction circle) throws TranslationException{ return getDefaultADQLFunction(circle); } @Override public String translate(PointFunction point) throws TranslationException{ return getDefaultADQLFunction(point); } @Override public String translate(PolygonFunction polygon) throws TranslationException{ return getDefaultADQLFunction(polygon); } @Override public String translate(RegionFunction region) throws TranslationException{ return getDefaultADQLFunction(region); } } src/adql/parser/0000755000175000017500000000000013226134464012555 5ustar olesolessrc/adql/parser/IdentifierItems.java0000644000175000017500000001571213201352464016505 0ustar olesolespackage adql.parser; /* * 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 . * * Copyright 2012-2017 - UDS/Centre de Données astronomiques de Strasbourg (CDS), * Astronomisches Rechen Institut (ARI) */ import adql.query.IdentifierField; import adql.query.TextPosition; /** * Important: This class is designed to be filled ONLY by * {@link ADQLParser}! * *

This class is an array of maximum 4 {@link IdentifierItem}.

*

* The goal is to represent complex ADQL identifiers (column, table, ...) * which may be composed of more than only one identifier. *

*

* For instance, a table can be referenced either by only its name or by the * name of its schema and its name. So, in this last case there are 2 * identifiers. *

*

* It is possible to get one by one each identifier item (by using the * getters), or the concatenation of all (thanks to {@link #join(String)}). *

* * @author Grégory Mantelet (CDS;ARI) * @version 1.4 (11/2017) * * @see IdentifierItem */ public class IdentifierItems { /** * Represent any ADQL identifier (column name, table name or table/column * alias). * * @author Grégory Mantelet (CDS;ARI) * @version 1.4 (11/2017) */ public static class IdentifierItem { public String identifier = null; public boolean caseSensitivity = false; public TextPosition position = null; public IdentifierItem(final Token token, final boolean caseSensitive){ identifier = token.image.replaceAll("\"\"", "\""); caseSensitivity = caseSensitive; position = new TextPosition(token); } @Override public String toString(){ return identifier; } } /** All identifiers. The position of the different fields change in function of the number of elements. * If count=4: [0]=catalog, [1]=schema, [2]=table, [3]=column. */ private IdentifierItem[] identifiers = new IdentifierItem[4]; /** Number of identifier fields added. */ private int count = 0; /** Indicates whether this {@link IdentifierItems} is supposed to represent a table name (true), or a column name (false). */ private boolean tableIdent = false; /** * Builds an IdentifierItems by specifying it is a table or a column identifier. * * @param tableIdentifier true if this IdentifierItems is a table identifier, false otherwise. */ public IdentifierItems(final boolean tableIdentifier){ tableIdent = tableIdentifier; } /** *

Apppends a simple identifier, that's to say an additional field (catalog, schema, table, column).

* *

Note: This function has no effect if there are already 4 identifiers.

* * @param item Additional item (may be null). */ public void append(final IdentifierItem item){ if (count >= 4) return; identifiers[count++] = item; } /** * Gets the number of fields/identifiers stored in this {@link IdentifierItems}. * * @return The number of identifiers. */ public int size(){ return count; } /** * Gets the whole ind-th identifier/field. * * @param ind Index of the identifier/field to get. * * @return The wanted identifier/field. */ public IdentifierItem get(final int ind){ return (ind < 0 || identifiers[ind] == null) ? null : identifiers[ind]; } /** * Gets the value of the ind-th identifier/field. * * @param ind Index of the identifier/field to get. * * @return The value of the wanted identifier/field. */ public String getIdentifier(final int ind){ return (ind < 0 || identifiers[ind] == null) ? null : identifiers[ind].identifier; } public String getCatalog(){ return getIdentifier(tableIdent ? (count - 3) : (count - 4)); } public String getSchema(){ return getIdentifier(tableIdent ? (count - 2) : (count - 3)); } public String getTable(){ return getIdentifier(tableIdent ? (count - 1) : (count - 2)); } public String getColumn(){ return getIdentifier(tableIdent ? -1 : (count - 1)); } public int getBeginLine(){ return (count == 0 || identifiers[0] == null) ? -1 : identifiers[0].position.beginLine; } public int getEndLine(){ return (count == 0 || identifiers[count - 1] == null) ? -1 : identifiers[count - 1].position.endLine; } public int getBeginColumn(){ return (count == 0 || identifiers[0] == null) ? -1 : identifiers[0].position.beginColumn; } public int getEndColumn(){ return (count == 0 || identifiers[count - 1] == null) ? -1 : identifiers[count - 1].position.endColumn; } public TextPosition getPosition(){ return new TextPosition(getBeginLine(), getBeginColumn(), getEndLine(), getEndColumn()); } public byte getCaseSensitivity(){ byte sensitivity = IdentifierField.getFullCaseSensitive(false); if (count == 0) return sensitivity; int ind = count - 1; // COLUMN: if (!tableIdent){ if (identifiers[ind] != null) sensitivity = IdentifierField.COLUMN.setCaseSensitive(sensitivity, identifiers[ind].caseSensitivity); ind--; } if (ind < 0) return sensitivity; // TABLE: if (identifiers[ind] != null) sensitivity = IdentifierField.TABLE.setCaseSensitive(sensitivity, identifiers[ind].caseSensitivity); ind--; if (ind < 0) return sensitivity; // SCHEMA: if (identifiers[ind] != null) sensitivity = IdentifierField.SCHEMA.setCaseSensitive(sensitivity, identifiers[ind].caseSensitivity); ind--; if (ind < 0) return sensitivity; // CATALOG: if (identifiers[ind] != null) sensitivity = IdentifierField.CATALOG.setCaseSensitive(sensitivity, identifiers[ind].caseSensitivity); return sensitivity; } public boolean getColumnCaseSensitivity(){ if (count == 0 || tableIdent || identifiers[count - 1] == null) return false; return identifiers[count - 1].caseSensitivity; } /** * Joins all identifiers with the given delimiter. * * @param delim The string which must separate the identifiers (if null, the delimiter will be an empty string). * * @return The joint complex identifier. */ public String join(String delim){ if (count == 0) return null; if (delim == null) delim = ""; StringBuffer str = new StringBuffer(); for(int i = 0; i < count; i++){ if (identifiers[i] != null){ if (str.length() > 0) str.append(delim); str.append((identifiers[i] == null) ? "" : identifiers[i].identifier); } } return str.toString(); } @Override public String toString(){ return join("."); } } src/adql/parser/ADQLQueryFactory.java0000644000175000017500000004075013177122310016515 0ustar olesolespackage adql.parser; /* * 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 . * * Copyright 2012-2017 - UDS/Centre de Données astronomiques de Strasbourg (CDS), * Astronomisches Rechen Institut (ARI) */ import java.util.Collection; import adql.db.FunctionDef; import adql.parser.IdentifierItems.IdentifierItem; import adql.query.ADQLOrder; import adql.query.ADQLQuery; import adql.query.ClauseConstraints; import adql.query.ColumnReference; import adql.query.IdentifierField; import adql.query.SelectItem; import adql.query.TextPosition; import adql.query.constraint.ADQLConstraint; import adql.query.constraint.Between; import adql.query.constraint.Comparison; import adql.query.constraint.ComparisonOperator; import adql.query.constraint.ConstraintsGroup; import adql.query.constraint.Exists; import adql.query.constraint.In; import adql.query.constraint.IsNull; import adql.query.constraint.NotConstraint; import adql.query.from.ADQLJoin; import adql.query.from.ADQLTable; import adql.query.from.CrossJoin; import adql.query.from.FromContent; import adql.query.from.InnerJoin; import adql.query.from.OuterJoin; import adql.query.from.OuterJoin.OuterType; import adql.query.operand.ADQLColumn; import adql.query.operand.ADQLOperand; import adql.query.operand.Concatenation; import adql.query.operand.NegativeOperand; import adql.query.operand.NumericConstant; import adql.query.operand.Operation; import adql.query.operand.OperationType; import adql.query.operand.StringConstant; import adql.query.operand.WrappedOperand; import adql.query.operand.function.DefaultUDF; import adql.query.operand.function.MathFunction; import adql.query.operand.function.MathFunctionType; import adql.query.operand.function.SQLFunction; import adql.query.operand.function.SQLFunctionType; import adql.query.operand.function.UserDefinedFunction; import adql.query.operand.function.geometry.AreaFunction; import adql.query.operand.function.geometry.BoxFunction; import adql.query.operand.function.geometry.CentroidFunction; import adql.query.operand.function.geometry.CircleFunction; import adql.query.operand.function.geometry.ContainsFunction; import adql.query.operand.function.geometry.DistanceFunction; import adql.query.operand.function.geometry.ExtractCoord; import adql.query.operand.function.geometry.ExtractCoordSys; import adql.query.operand.function.geometry.GeometryFunction; import adql.query.operand.function.geometry.GeometryFunction.GeometryValue; import adql.query.operand.function.geometry.IntersectsFunction; import adql.query.operand.function.geometry.PointFunction; import adql.query.operand.function.geometry.PolygonFunction; import adql.query.operand.function.geometry.RegionFunction; /** *

This class lets the {@link ADQLParser} to build an object representation of an ADQL query.

* *

To customize the object representation you merely have to extends the appropriate functions of this class.

* * @author Grégory Mantelet (CDS;ARI) * @version 1.4 (04/2017) * * @see ADQLParser */ public class ADQLQueryFactory { /** * Type of table JOIN. * * @author Grégory Mantelet (CDS) * @version 1.0 (08/2011) */ public static enum JoinType{ CROSS, INNER, OUTER_LEFT, OUTER_RIGHT, OUTER_FULL; } /** * Create a query factory. */ public ADQLQueryFactory(){ ; } public ADQLQuery createQuery() throws Exception{ return new ADQLQuery(); } public ADQLTable createTable(final IdentifierItems idItems, final IdentifierItem alias) throws Exception{ ADQLTable t = new ADQLTable(idItems.getCatalog(), idItems.getSchema(), idItems.getTable()); // Set the table alias: if (alias != null) t.setAlias(alias.identifier); // Set the case sensitivity on the table name parts: byte caseSensitivity = idItems.getCaseSensitivity(); if (alias != null) caseSensitivity = IdentifierField.ALIAS.setCaseSensitive(caseSensitivity, alias.caseSensitivity); t.setCaseSensitive(caseSensitivity); return t; } public ADQLTable createTable(ADQLQuery query, IdentifierItem alias) throws Exception{ ADQLTable t = new ADQLTable(query); if (alias != null){ // Set the table alias: t.setAlias(alias.identifier); // Set the case sensitivity: t.setCaseSensitive(IdentifierField.ALIAS, alias.caseSensitivity); } return t; } public ADQLJoin createJoin(JoinType type, FromContent leftTable, FromContent rightTable) throws Exception{ switch(type){ case CROSS: return new CrossJoin(leftTable, rightTable); case INNER: return new InnerJoin(leftTable, rightTable); case OUTER_LEFT: return new OuterJoin(leftTable, rightTable, OuterType.LEFT); case OUTER_RIGHT: return new OuterJoin(leftTable, rightTable, OuterType.RIGHT); case OUTER_FULL: return new OuterJoin(leftTable, rightTable, OuterType.FULL); default: throw new Exception("Unknown join type: " + type); } } public ADQLJoin createJoin(JoinType type, FromContent leftTable, FromContent rightTable, ClauseConstraints condition) throws Exception{ switch(type){ case CROSS: throw new Exception("A cross join must have no condition (that's to say: no part ON) !"); default: ADQLJoin join = createJoin(type, leftTable, rightTable); join.setJoinCondition(condition); return join; } } public ADQLJoin createJoin(JoinType type, FromContent leftTable, FromContent rightTable, Collection lstColumns) throws Exception{ switch(type){ case CROSS: throw new Exception("A cross join must have no columns list (that's to say: no part USING) !"); default: ADQLJoin join = createJoin(type, leftTable, rightTable); join.setJoinedColumns(lstColumns); return join; } } public SelectItem createSelectItem(ADQLOperand operand, String alias) throws Exception{ return new SelectItem(operand, alias); } public ADQLColumn createColumn(final IdentifierItems idItems) throws Exception{ ADQLColumn col = new ADQLColumn(idItems.getCatalog(), idItems.getSchema(), idItems.getTable(), idItems.getColumn()); // Set the case sensitivity: col.setCaseSensitive(idItems.getCaseSensitivity()); // Set the position: col.setPosition(idItems.getPosition()); return col; } public ADQLColumn createColumn(final IdentifierItem columnName) throws Exception{ ADQLColumn col = new ADQLColumn(null, null, null, columnName.identifier); // Set the case sensitivity: col.setCaseSensitive(IdentifierField.COLUMN, columnName.caseSensitivity); // Set the position: col.setPosition(columnName.position); return col; } public NumericConstant createNumericConstant(String value) throws Exception{ return new NumericConstant(value, true); } public StringConstant createStringConstant(String value) throws Exception{ return new StringConstant(value); } public Operation createOperation(ADQLOperand leftOp, OperationType op, ADQLOperand rightOp) throws Exception{ return new Operation(leftOp, op, rightOp); } public NegativeOperand createNegativeOperand(ADQLOperand opToNegativate) throws Exception{ return new NegativeOperand(opToNegativate); } public Concatenation createConcatenation() throws Exception{ return new Concatenation(); } public WrappedOperand createWrappedOperand(ADQLOperand opToWrap) throws Exception{ return new WrappedOperand(opToWrap); } public ConstraintsGroup createGroupOfConstraints() throws Exception{ return new ConstraintsGroup(); } public NotConstraint createNot(ADQLConstraint constraintToNot) throws Exception{ return new NotConstraint(constraintToNot); } public Comparison createComparison(ADQLOperand leftOp, ComparisonOperator op, ADQLOperand rightOp) throws Exception{ return new Comparison(leftOp, op, rightOp); } public Between createBetween(boolean not, ADQLOperand value, ADQLOperand min, ADQLOperand max) throws Exception{ return new Between(value, min, max, not); } public IsNull createIsNull(boolean notNull, ADQLColumn column) throws Exception{ return new IsNull(column, notNull); } public Exists createExists(ADQLQuery query) throws Exception{ return new Exists(query); } public In createIn(ADQLOperand leftOp, ADQLQuery query, boolean notIn) throws Exception{ return new In(leftOp, query, notIn); } public In createIn(ADQLOperand leftOp, ADQLOperand[] valuesList, boolean notIn) throws Exception{ return new In(leftOp, valuesList, notIn); } public SQLFunction createSQLFunction(SQLFunctionType type, ADQLOperand op, boolean distinctValues) throws Exception{ return new SQLFunction(type, op, distinctValues); } public MathFunction createMathFunction(MathFunctionType type, ADQLOperand param1, ADQLOperand param2) throws Exception{ return new MathFunction(type, param1, param2); } /** *

Creates the user defined functions called as the given name and with the given parameters.

* *

* By default, this function returns a {@link DefaultUDF} instance. It is generic enough to cover every kind of functions. * But you can of course override this function in order to return your own instance of {@link UserDefinedFunction}. * In this case, you may not forget to call the super function (super.createUserDefinedFunction(name, params)) so that * all other unknown functions are still returned as {@link DefaultUDF} instances. *

* *

IMPORTANT: * The tests done to check whether a user defined function is allowed/managed in this implementation, is done later by the parser. * Only declared UDF will pass the test of the parser. For that, you should give it a list of allowed UDFs (each UDF will be then * represented by a {@link FunctionDef} object). *

* * @param name Name of the user defined function to create. * @param params Parameters of the user defined function to create. * * @return The corresponding user defined function (by default an instance of {@link DefaultUDF}). * * @throws Exception If there is a problem while creating the function. */ public UserDefinedFunction createUserDefinedFunction(String name, ADQLOperand[] params) throws Exception{ return new DefaultUDF(name, params); } public DistanceFunction createDistance(PointFunction point1, PointFunction point2) throws Exception{ return new DistanceFunction(new GeometryValue(point1), new GeometryValue(point2)); } public DistanceFunction createDistance(GeometryValue point1, GeometryValue point2) throws Exception{ return new DistanceFunction(point1, point2); } public PointFunction createPoint(ADQLOperand coordSys, ADQLOperand coords, ADQLOperand coords2) throws Exception{ return new PointFunction(coordSys, coords, coords2); } public BoxFunction createBox(ADQLOperand coordinateSystem, ADQLOperand firstCoord, ADQLOperand secondCoord, ADQLOperand boxWidth, ADQLOperand boxHeight) throws Exception{ return new BoxFunction(coordinateSystem, firstCoord, secondCoord, boxWidth, boxHeight); } public CircleFunction createCircle(ADQLOperand coordSys, ADQLOperand coord1, ADQLOperand coord2, ADQLOperand radius) throws Exception{ return new CircleFunction(coordSys, coord1, coord2, radius); } public CentroidFunction createCentroid(GeometryFunction param) throws Exception{ return new CentroidFunction(new GeometryValue(param)); } public CentroidFunction createCentroid(GeometryValue param) throws Exception{ return new CentroidFunction(param); } public RegionFunction createRegion(ADQLOperand param) throws Exception{ return new RegionFunction(param); } public PolygonFunction createPolygon(ADQLOperand coordSys, Collection coords) throws Exception{ return new PolygonFunction(coordSys, coords); } public AreaFunction createArea(GeometryFunction param) throws Exception{ return new AreaFunction(new GeometryValue(param)); } public AreaFunction createArea(GeometryValue param) throws Exception{ return new AreaFunction(param); } public ExtractCoord createCoord1(PointFunction point) throws Exception{ return new ExtractCoord(1, new GeometryValue(point)); } public ExtractCoord createCoord1(ADQLColumn point) throws Exception{ return new ExtractCoord(1, new GeometryValue(point)); } public ExtractCoord createCoord2(PointFunction point) throws Exception{ return new ExtractCoord(2, new GeometryValue(point)); } public ExtractCoord createCoord2(ADQLColumn point) throws Exception{ return new ExtractCoord(2, new GeometryValue(point)); } public ExtractCoordSys createExtractCoordSys(GeometryFunction param) throws Exception{ return new ExtractCoordSys(new GeometryValue(param)); } public ExtractCoordSys createExtractCoordSys(ADQLColumn param) throws Exception{ return new ExtractCoordSys(new GeometryValue(param)); } public ExtractCoordSys createExtractCoordSys(GeometryValue param) throws Exception{ return new ExtractCoordSys(new GeometryValue(param)); } public ContainsFunction createContains(GeometryFunction left, GeometryFunction right) throws Exception{ return new ContainsFunction(new GeometryValue(left), new GeometryValue(right)); } public ContainsFunction createContains(GeometryValue left, GeometryValue right) throws Exception{ return new ContainsFunction(left, right); } public IntersectsFunction createIntersects(GeometryFunction left, GeometryFunction right) throws Exception{ return new IntersectsFunction(new GeometryValue(left), new GeometryValue(right)); } public IntersectsFunction createIntersects(GeometryValue left, GeometryValue right) throws Exception{ return new IntersectsFunction(left, right); } /** * Replace {@link #createOrder(int, boolean, TextPosition)}. * @since 1.4 */ public ADQLOrder createOrder(final int ind, final boolean desc) throws Exception{ return new ADQLOrder(ind, desc); } /** * @deprecated since 1.4 ; Replaced by {@link #createOrder(int, boolean)} */ @Deprecated public ADQLOrder createOrder(final int ind, final boolean desc, final TextPosition position) throws Exception{ ADQLOrder order = new ADQLOrder(ind, desc); if (order != null) order.setPosition(position); return order; } public ADQLOrder createOrder(final IdentifierItem colName, final boolean desc) throws Exception{ ADQLOrder order = new ADQLOrder(colName.identifier, desc); if (order != null) order.setCaseSensitive(colName.caseSensitivity); return order; } /** * @deprecated since 1.4 ; Former version's mistake: an ORDER BY item is either a regular/delimited column name or an integer, not a qualified column name ; Replaced by {@link #createOrder(adql.parser.IdentifierItems.IdentifierItem, boolean)} ; This function is no longer used by ADQLParser. */ @Deprecated public ADQLOrder createOrder(final IdentifierItems idItems, final boolean desc) throws Exception{ ADQLOrder order = new ADQLOrder(idItems.join("."), desc); if (order != null) order.setCaseSensitive(idItems.getColumnCaseSensitivity()); return order; } public ColumnReference createColRef(final IdentifierItem idItem) throws Exception{ ColumnReference colRef = new ColumnReference(idItem.identifier); if (colRef != null){ colRef.setPosition(idItem.position); colRef.setCaseSensitive(idItem.caseSensitivity); } return colRef; } public ColumnReference createColRef(final IdentifierItems idItems) throws Exception{ ColumnReference colRef = new ColumnReference(idItems.join(".")); if (colRef != null){ colRef.setPosition(idItems.getPosition()); colRef.setCaseSensitive(idItems.getColumnCaseSensitivity()); } return colRef; } public ColumnReference createColRef(final int index, final TextPosition position) throws Exception{ ColumnReference colRef = new ColumnReference(index); if (colRef != null) colRef.setPosition(position); return colRef; } } src/adql/parser/ADQLParserConstants.java0000644000175000017500000001557013226136436017224 0ustar olesoles/* 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 DELIMITED_IDENTIFIER = 96; /** RegularExpression Id. */ int REGULAR_IDENTIFIER = 97; /** RegularExpression Id. */ int Letter = 98; /** RegularExpression Id. */ int SCIENTIFIC_NUMBER = 99; /** RegularExpression Id. */ int UNSIGNED_FLOAT = 100; /** RegularExpression Id. */ int UNSIGNED_INTEGER = 101; /** RegularExpression Id. */ int DIGIT = 102; /** Lexical state. */ int DEFAULT = 0; /** Lexical state. */ int WithinString = 1; /** Lexical state. */ int WithinDelimitedId = 2; /** Literal token values. */ String[] tokenImage = { "", "", "", "\"(\"", "\")\"", "\".\"", "\",\"", "\";\"", "\"||\"", "\"+\"", "\"-\"", "\"*\"", "\"/\"", "\"=\"", "", "\"<\"", "\"<=\"", "\">\"", "\">=\"", "\"SELECT\"", "", "\"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\"", "", "\"\\\'\"", "", "\"\\\'\"", "\"\\\"\"", "", "\"\\\"\"", "", "", "", "", "", "", }; } src/adql/parser/Token.java.backup0000644000175000017500000001044313177122310015736 0ustar olesoles/* 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é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). */ package adql.parser; /** * Describes the input token stream. */ public class Token implements java.io.Serializable { /** * The version identifier for this Serializable class. * Increment only if the serialized form of the * class changes. */ private static final long serialVersionUID = 1L; /** * An integer that describes the kind of this token. This numbering * system is determined by JavaCCParser, and a table of these numbers is * stored in the file ...Constants.java. */ public int kind; /** The line number of the first character of this Token. */ public int beginLine; /** The column number of the first character of this Token. */ public int beginColumn; /** The line number of the last character of this Token. */ public int endLine; /** The column number of the last character of this Token. */ public int endColumn; /** * The string image of the token. */ public String image; /** * A reference to the next regular (non-special) token from the input * stream. If this is the last token from the input stream, or if the * token manager has not read tokens beyond this one, this field is * set to null. This is true only if this token is also a regular * token. Otherwise, see below for a description of the contents of * this field. */ public Token next; /** * This field is used to access special tokens that occur prior to this * token, but after the immediately preceding regular (non-special) token. * If there are no such special tokens, this field is set to null. * When there are more than one such special token, this field refers * to the last of these special tokens, which in turn refers to the next * previous special token through its specialToken field, and so on * until the first special token (whose specialToken field is null). * The next fields of special tokens refer to other special tokens that * immediately follow it (without an intervening regular token). If there * is no such token, this field is null. */ public Token specialToken; /** * An optional attribute value of the Token. * Tokens which are not used as syntactic sugar will often contain * meaningful values that will be used later on by the compiler or * interpreter. This attribute value is often different from the image. * Any subclass of Token that actually wants to return a non-null value can * override this method as appropriate. */ public Object getValue(){ return null; } /** * No-argument constructor */ public Token(){} /** * Constructs a new token for the specified Image. */ public Token(int kind){ this(kind, null); } /** * Constructs a new token for the specified Image and Kind. */ public Token(int kind, String image){ this.kind = kind; this.image = image; } /** * Returns the image. */ @Override public String toString(){ return image; } /** * Returns a new Token object, by default. However, if you want, you * can create and return subclass objects based on the value of ofKind. * Simply add the cases to the switch for all those special cases. * For example, if you have a subclass of Token called IDToken that * you want to create if ofKind is ID, simply add something like : * * case MyParserConstants.ID : return new IDToken(ofKind, image); * * 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){ default: return new Token(ofKind, image); } } public static Token newToken(int ofKind){ return newToken(ofKind, null); } } /* JavaCC - OriginalChecksum=4c61cfbec4532a0e2168ba896283031a (do not edit this line) */ src/adql/parser/SimpleCharStream.java.backup0000644000175000017500000002464013177122310020065 0ustar olesoles/* Generated By:JavaCC: Do not edit this line. SimpleCharStream.java Version 6.0 */ /* JavaCCOptions:STATIC=false,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ package adql.parser; /** * An implementation of interface CharStream, where the stream is assumed to * contain only ASCII characters (without unicode processing). */ public class SimpleCharStream { /** Whether parser is static. */ public static final boolean staticFlag = false; int bufsize; int available; int tokenBegin; /** Position in buffer. */ public int bufpos = -1; protected int bufline[]; protected int bufcolumn[]; protected int column = 0; protected int line = 1; protected boolean prevCharIsCR = false; protected boolean prevCharIsLF = false; protected java.io.Reader inputStream; protected char[] buffer; protected int maxNextCharInd = 0; protected int inBuf = 0; protected int tabSize = 8; protected boolean trackLineColumn = true; public void setTabSize(int i){ tabSize = i; } public int getTabSize(){ return tabSize; } protected void ExpandBuff(boolean wrapAround){ char[] newbuffer = new char[bufsize + 2048]; int newbufline[] = new int[bufsize + 2048]; int newbufcolumn[] = new int[bufsize + 2048]; try{ if (wrapAround){ System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin); System.arraycopy(buffer, 0, newbuffer, bufsize - tokenBegin, bufpos); buffer = newbuffer; System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin); System.arraycopy(bufline, 0, newbufline, bufsize - tokenBegin, bufpos); bufline = newbufline; System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin); System.arraycopy(bufcolumn, 0, newbufcolumn, bufsize - tokenBegin, bufpos); bufcolumn = newbufcolumn; maxNextCharInd = (bufpos += (bufsize - tokenBegin)); }else{ System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin); buffer = newbuffer; System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin); bufline = newbufline; System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin); bufcolumn = newbufcolumn; maxNextCharInd = (bufpos -= tokenBegin); } }catch(Throwable t){ throw new Error(t.getMessage()); } bufsize += 2048; available = bufsize; tokenBegin = 0; } protected void FillBuff() throws java.io.IOException{ if (maxNextCharInd == available){ if (available == bufsize){ if (tokenBegin > 2048){ bufpos = maxNextCharInd = 0; available = tokenBegin; }else if (tokenBegin < 0) bufpos = maxNextCharInd = 0; else ExpandBuff(false); }else if (available > tokenBegin) available = bufsize; else if ((tokenBegin - available) < 2048) ExpandBuff(true); else available = tokenBegin; } int i; try{ if ((i = inputStream.read(buffer, maxNextCharInd, available - maxNextCharInd)) == -1){ inputStream.close(); throw new java.io.IOException(); }else maxNextCharInd += i; return; }catch(java.io.IOException e){ --bufpos; backup(0); if (tokenBegin == -1) tokenBegin = bufpos; throw e; } } /** Start. */ public char BeginToken() throws java.io.IOException{ tokenBegin = -1; char c = readChar(); tokenBegin = bufpos; return c; } protected void UpdateLineColumn(char c){ column++; if (prevCharIsLF){ prevCharIsLF = false; line += (column = 1); }else if (prevCharIsCR){ prevCharIsCR = false; if (c == '\n'){ prevCharIsLF = true; }else line += (column = 1); } switch(c){ case '\r': prevCharIsCR = true; break; case '\n': prevCharIsLF = true; break; case '\t': column--; column += (tabSize - (column % tabSize)); break; default: break; } bufline[bufpos] = line; bufcolumn[bufpos] = column; } /** Read a character. */ public char readChar() throws java.io.IOException{ if (inBuf > 0){ --inBuf; if (++bufpos == bufsize) bufpos = 0; return buffer[bufpos]; } if (++bufpos >= maxNextCharInd) FillBuff(); char c = buffer[bufpos]; UpdateLineColumn(c); return c; } @Deprecated /** * @deprecated * @see #getEndColumn */ public int getColumn(){ return bufcolumn[bufpos]; } @Deprecated /** * @deprecated * @see #getEndLine */ public int getLine(){ return bufline[bufpos]; } /** Get token end column number. */ public int getEndColumn(){ return bufcolumn[bufpos]; } /** Get token end line number. */ public int getEndLine(){ return bufline[bufpos]; } /** Get token beginning column number. */ public int getBeginColumn(){ return bufcolumn[tokenBegin]; } /** Get token beginning line number. */ public int getBeginLine(){ return bufline[tokenBegin]; } /** Backup a number of characters. */ public void backup(int amount){ inBuf += amount; if ((bufpos -= amount) < 0) bufpos += bufsize; } /** Constructor. */ public SimpleCharStream(java.io.Reader dstream, int startline, int startcolumn, int buffersize){ inputStream = dstream; line = startline; column = startcolumn - 1; available = bufsize = buffersize; buffer = new char[buffersize]; bufline = new int[buffersize]; bufcolumn = new int[buffersize]; } /** Constructor. */ public SimpleCharStream(java.io.Reader dstream, int startline, int startcolumn){ this(dstream, startline, startcolumn, 4096); } /** Constructor. */ public SimpleCharStream(java.io.Reader dstream){ this(dstream, 1, 1, 4096); } /** Reinitialise. */ public void ReInit(java.io.Reader dstream, int startline, int startcolumn, int buffersize){ inputStream = dstream; line = startline; column = startcolumn - 1; if (buffer == null || buffersize != buffer.length){ available = bufsize = buffersize; buffer = new char[buffersize]; bufline = new int[buffersize]; bufcolumn = new int[buffersize]; } prevCharIsLF = prevCharIsCR = false; tokenBegin = inBuf = maxNextCharInd = 0; bufpos = -1; } /** Reinitialise. */ public void ReInit(java.io.Reader dstream, int startline, int startcolumn){ ReInit(dstream, startline, startcolumn, 4096); } /** Reinitialise. */ public void ReInit(java.io.Reader dstream){ ReInit(dstream, 1, 1, 4096); } /** Constructor. */ public SimpleCharStream(java.io.InputStream dstream, String encoding, int startline, int startcolumn, int buffersize) throws java.io.UnsupportedEncodingException{ this(encoding == null ? new java.io.InputStreamReader(dstream) : new java.io.InputStreamReader(dstream, encoding), startline, startcolumn, buffersize); } /** Constructor. */ public SimpleCharStream(java.io.InputStream dstream, int startline, int startcolumn, int buffersize){ this(new java.io.InputStreamReader(dstream), startline, startcolumn, buffersize); } /** Constructor. */ public SimpleCharStream(java.io.InputStream dstream, String encoding, int startline, int startcolumn) throws java.io.UnsupportedEncodingException{ this(dstream, encoding, startline, startcolumn, 4096); } /** Constructor. */ public SimpleCharStream(java.io.InputStream dstream, int startline, int startcolumn){ this(dstream, startline, startcolumn, 4096); } /** Constructor. */ public SimpleCharStream(java.io.InputStream dstream, String encoding) throws java.io.UnsupportedEncodingException{ this(dstream, encoding, 1, 1, 4096); } /** Constructor. */ public SimpleCharStream(java.io.InputStream dstream){ this(dstream, 1, 1, 4096); } /** Reinitialise. */ public void ReInit(java.io.InputStream dstream, String encoding, int startline, int startcolumn, int buffersize) throws java.io.UnsupportedEncodingException{ ReInit(encoding == null ? new java.io.InputStreamReader(dstream) : new java.io.InputStreamReader(dstream, encoding), startline, startcolumn, buffersize); } /** Reinitialise. */ public void ReInit(java.io.InputStream dstream, int startline, int startcolumn, int buffersize){ ReInit(new java.io.InputStreamReader(dstream), startline, startcolumn, buffersize); } /** Reinitialise. */ public void ReInit(java.io.InputStream dstream, String encoding) throws java.io.UnsupportedEncodingException{ ReInit(dstream, encoding, 1, 1, 4096); } /** Reinitialise. */ public void ReInit(java.io.InputStream dstream){ ReInit(dstream, 1, 1, 4096); } /** Reinitialise. */ public void ReInit(java.io.InputStream dstream, String encoding, int startline, int startcolumn) throws java.io.UnsupportedEncodingException{ ReInit(dstream, encoding, startline, startcolumn, 4096); } /** Reinitialise. */ public void ReInit(java.io.InputStream dstream, int startline, int startcolumn){ ReInit(dstream, startline, startcolumn, 4096); } /** Get token literal value. */ public String GetImage(){ if (bufpos >= tokenBegin) return new String(buffer, tokenBegin, bufpos - tokenBegin + 1); else return new String(buffer, tokenBegin, bufsize - tokenBegin) + new String(buffer, 0, bufpos + 1); } /** Get the suffix. */ public char[] GetSuffix(int len){ char[] ret = new char[len]; if ((bufpos + 1) >= len) System.arraycopy(buffer, bufpos - len + 1, ret, 0, len); else{ System.arraycopy(buffer, bufsize - (len - bufpos - 1), ret, 0, len - bufpos - 1); System.arraycopy(buffer, 0, ret, len - bufpos - 1, bufpos + 1); } return ret; } /** Reset buffer when finished. */ public void Done(){ buffer = null; bufline = null; bufcolumn = null; } /** * Method to adjust line and column numbers for the start of a token. */ public void adjustBeginLineColumn(int newLine, int newCol){ int start = tokenBegin; int len; if (bufpos >= tokenBegin){ len = bufpos - tokenBegin + inBuf + 1; }else{ len = bufsize - tokenBegin + bufpos + 1 + inBuf; } int i = 0, j = 0, k = 0; int nextColDiff = 0, columnDiff = 0; while(i < len && bufline[j = start % bufsize] == bufline[k = ++start % bufsize]){ bufline[j] = newLine; nextColDiff = columnDiff + bufcolumn[k] - bufcolumn[j]; bufcolumn[j] = newCol + columnDiff; columnDiff = nextColDiff; i++; } if (i < len){ bufline[j] = newLine++; bufcolumn[j] = newCol + columnDiff; while(i++ < len){ if (bufline[j = start % bufsize] != bufline[++start % bufsize]) bufline[j] = newLine++; else bufline[j] = newLine; } } line = bufline[j]; column = bufcolumn[j]; } boolean getTrackLineColumn(){ return trackLineColumn; } void setTrackLineColumn(boolean tlc){ trackLineColumn = tlc; } } /* JavaCC - OriginalChecksum=25c201ae6a86f1f10a8af29118337505 (do not edit this line) */ src/adql/parser/TokenMgrError.java.backup0000644000175000017500000001201113177122310017407 0ustar olesoles/* Generated By:JavaCC: Do not edit this line. TokenMgrError.java Version 6.0 */ /* JavaCCOptions: * * Modified by Gré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 * * /!\ DO NOT RE-GENERATE THIS FILE /!\ * In case of re-generation, replace it by TokenMgrError.java.backup (but maybe * after a diff in case of significant modifications have been done by a new * version of JavaCC). */ package adql.parser; /** Token Manager Error. */ public class TokenMgrError extends Error { /** * The version identifier for this Serializable class. * Increment only if the serialized form of the * class changes. */ private static final long serialVersionUID = 1L; /* * Ordinals for various reasons why an Error of this type can be thrown. */ /** * Lexical error occurred. */ static final int LEXICAL_ERROR = 0; /** * An attempt was made to create a second instance of a static token manager. */ static final int STATIC_LEXER_ERROR = 1; /** * Tried to change to an invalid lexical state. */ static final int INVALID_LEXICAL_STATE = 2; /** * Detected (and bailed out of) an infinite loop in the token manager. */ static final int LOOP_DETECTED = 3; /** * Indicates the reason why the exception is thrown. It will have * one of the above 4 values. */ int errorCode; /** Indicates the line at which the error occurs. */ int errorLine = -1; /** Indicates the column at which the error occurs. */ int errorColumn = -1; /** * Replaces unprintable characters by their escaped (or unicode escaped) * equivalents in the given string */ protected static final String addEscapes(String str){ StringBuffer retval = new StringBuffer(); char ch; for(int i = 0; i < str.length(); i++){ switch(str.charAt(i)){ case 0: continue; case '\b': retval.append("\\b"); continue; case '\t': retval.append("\\t"); continue; case '\n': retval.append("\\n"); continue; case '\f': retval.append("\\f"); continue; case '\r': retval.append("\\r"); continue; case '\"': retval.append("\\\""); continue; case '\'': retval.append("\\\'"); continue; case '\\': retval.append("\\\\"); continue; default: 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{ retval.append(ch); } continue; } } return retval.toString(); } /** * Returns a detailed message for the Error when it is thrown by the * token manager to indicate a lexical error. * Parameters : * EOFSeen : indicates if EOF caused the lexical error * curLexState : lexical state in which this error occurred * errorLine : line number when the error occurred * errorColumn : column number when the error occurred * errorAfter : prefix that was seen before this error occurred * curchar : the offending character * Note: You can customize the lexical error message by modifying this method. */ protected static String LexicalError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, char curChar){ return ("Incorrect character encountered at l." + errorLine + ", c." + errorColumn + ": " + (EOFSeen ? " " : ("\"" + addEscapes(String.valueOf(curChar)) + "\"") + " ('" + curChar + "'), ") + "after : \"" + addEscapes(errorAfter) + "\""); } /** * You can also modify the body of this method to customize your error messages. * For example, cases like LOOP_DETECTED and INVALID_LEXICAL_STATE are not * of end-users concern, so you can return something like : * * "Internal Error : Please file a bug report .... " * * from this method for such cases in the release version of your parser. */ @Override public String getMessage(){ return super.getMessage(); } /** * Gets the line at which this error occurs. * * @return Error line or -1 if unknown. */ public final int getErrorLine(){ return errorLine; } /** * Gets the column at which this error occurs. * * @return Error column or -1 if unknown. */ public final int getErrorColumn(){ return errorColumn; } /* * Constructors of various flavors follow. */ /** No arg constructor. */ public TokenMgrError(){} /** Constructor with message and reason. */ public TokenMgrError(String message, int reason){ super(message); errorCode = reason; } /** Full Constructor. */ public TokenMgrError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, char curChar, int reason){ this(LexicalError(EOFSeen, lexState, errorLine, errorColumn, errorAfter, curChar), reason); this.errorLine = errorLine; this.errorColumn = errorColumn; } } /* JavaCC - OriginalChecksum=f63fc7c10226c13ff5a304e2fb1ae182 (do not edit this line) */ src/adql/parser/ParseException.java.backup0000644000175000017500000002703713177122310017616 0ustar olesoles/* Generated By:JavaCC: Do not edit this line. ParseException.java Version 6.0 */ /* JavaCCOptions:KEEP_LINE_COL=null * * Modified by Grégory Mantelet (CDS), on March 2017 * Modifications: * - addition of a getPosition() function in order to get the exact location * of the error in the original ADQL query * - generate the error message at creation instead of doing that at each * call of getMessage() * - use of StringBuffer to build the error message * - small other alterations of the generated error message * * Modified by Grégory Mantelet (ARI), on Sept. 2017 * Modifications: * - addition of a HINT in the error message when an ADQL or SQL reserved * word is at the origin of the error (see initialise(...)) * * /!\ 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 * version of JavaCC). */ package adql.parser; import adql.query.TextPosition; /** * This exception is thrown when parse errors are encountered. * You can explicitly create objects of this exception type by * calling the method generateParseException in the generated * parser. * * You can modify this class to customize your error reporting * mechanisms so long as you retain the public fields. */ public class ParseException extends Exception { /** * The version identifier for this Serializable class. * Increment only if the serialized form of the * class changes. */ private static final long serialVersionUID = 1L; /** * This constructor is used by the method "generateParseException" * in the generated parser. Calling this constructor generates * a new object of this type with the fields "currentToken", * "expectedTokenSequences", and "tokenImage" set. */ public ParseException(Token currentTokenVal, int[][] expectedTokenSequencesVal, String[] tokenImageVal){ super(initialise(currentTokenVal, expectedTokenSequencesVal, tokenImageVal)); currentToken = currentTokenVal; expectedTokenSequences = expectedTokenSequencesVal; tokenImage = tokenImageVal; position = new TextPosition(currentToken.next); } /** * The following constructors are for use by you for whatever * purpose you can think of. Constructing the exception in this * manner makes the exception behave in the normal way - i.e., as * documented in the class "Throwable". The fields "errorToken", * "expectedTokenSequences", and "tokenImage" do not contain * relevant information. The JavaCC generated code does not use * these constructors. */ public ParseException(){ super(); } /** Constructor with message. */ public ParseException(String message){ super(message); } public ParseException(String message, TextPosition errorPosition){ this(message); position = errorPosition; } /** * This is the last token that has been consumed successfully. If * this object has been created due to a parse error, the token * followng this token will (therefore) be the first error token. */ public Token currentToken; /** * Each entry in this array is an array of integers. Each array * of integers represents a sequence of tokens (by their ordinal * values) that is expected at this point of the parse. */ public int[][] expectedTokenSequences; /** * This is a reference to the "tokenImage" array of the generated * parser within which the parse error occurred. This array is * defined in the generated ...Constants interface. */ public String[] tokenImage; /** Line in the ADQL query where the exception occurs. */ protected TextPosition position = null; /** Regular expression listing all ADQL reserved words. * *

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). *

* *

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). *

*/ 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. * *

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}). *

* *

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). *

*/ 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 null if unknown. */ public final TextPosition getPosition(){ return position; } /** * It uses "currentToken" and "expectedTokenSequences" to generate a parse * error message and returns it. If this object has been created * due to a parse error, and you do not catch it (it gets thrown * from the parser) the correct error message * gets displayed. */ 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){ maxSize = expectedTokenSequences[i].length; } for(int j = 0; j < expectedTokenSequences[i].length; j++){ expected.append(tokenImage[expectedTokenSequences[i][j]]); } expected.append(" "); } // Encountered token (s list): StringBuffer msg = new StringBuffer(); msg.append(" Encountered \""); Token tok = currentToken.next; StringBuffer tokenName = new StringBuffer(); for(int i = 0; i < maxSize; i++){ if (i != 0) tokenName.append(' '); if (tok.kind == 0){ tokenName.append(tokenImage[0]); break; } tokenName.append(add_escapes(tok.image)); tok = tok.next; } msg.append(tokenName.toString()).append("\"."); // Append the expected tokens list: if (expectedTokenSequences.length == 1){ msg.append(" Was expecting: "); }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.)"); return msg.toString(); /*String eol = System.getProperty("line.separator", "\n"); StringBuffer expected = new StringBuffer(); int maxSize = 0; 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++){ expected.append(tokenImage[expectedTokenSequences[i][j]]).append(' '); } if (expectedTokenSequences[i][expectedTokenSequences[i].length - 1] != 0){ expected.append("..."); } expected.append(eol).append(" "); } String retval = "Encountered \""; Token tok = currentToken.next; for(int i = 0; i < maxSize; i++){ if (i != 0) retval += " "; if (tok.kind == 0){ retval += tokenImage[0]; break; } retval += " " + tokenImage[tok.kind]; retval += " \""; retval += add_escapes(tok.image); retval += " \""; tok = tok.next; } retval += "\" at line " + currentToken.next.beginLine + ", column " + currentToken.next.beginColumn; retval += "." + eol; if (expectedTokenSequences.length == 1){ retval += "Was expecting:" + eol + " "; }else{ retval += "Was expecting one of:" + eol + " "; } retval += expected.toString(); return retval;*/ } /** * The end of line string for this machine. */ protected String eol = System.getProperty("line.separator", "\n"); /** * Used to convert raw characters to their escaped version * when these raw version cannot be used as part of an ASCII * string literal. */ static String add_escapes(String str){ StringBuffer retval = new StringBuffer(); char ch; for(int i = 0; i < str.length(); i++){ switch(str.charAt(i)){ case 0: continue; case '\b': retval.append("\\b"); continue; case '\t': retval.append("\\t"); continue; case '\n': retval.append("\\n"); continue; case '\f': retval.append("\\f"); continue; case '\r': retval.append("\\r"); continue; case '\"': retval.append("\\\""); continue; case '\'': retval.append("\\\'"); continue; case '\\': retval.append("\\\\"); continue; default: 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{ retval.append(ch); } continue; } } return retval.toString(); } } /* JavaCC - OriginalChecksum=ffea128e805df869244c5e22c2a37cbc (do not edit this line) */ src/adql/parser/SQLServer_ADQLQueryFactory.java0000644000175000017500000000517713177122310020427 0ustar olesolespackage adql.parser; /* * 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 . * * Copyright 2016 - Astronomisches Rechen Institut (ARI) */ import adql.query.from.ADQLJoin; import adql.query.from.CrossJoin; import adql.query.from.FromContent; import adql.query.from.InnerJoin; import adql.query.from.OuterJoin; import adql.query.from.SQLServer_InnerJoin; import adql.query.from.SQLServer_OuterJoin; import adql.query.from.OuterJoin.OuterType; import adql.translator.SQLServerTranslator; /** *

Special extension of {@link ADQLQueryFactory} for MS SQL Server.

* *

Important: * This class is generally used when an ADQL translator for MS SQL Server is needed. * See {@link SQLServerTranslator} for more details. *

* *

* The only difference with {@link ADQLQueryFactory} is the creation of an * {@link ADQLJoin}. Instead of creating {@link InnerJoin} and {@link OuterJoin}, * {@link SQLServer_InnerJoin} and {@link SQLServer_OuterJoin} are respectively created. * The only difference between these last classes and the first ones is in the processing * of NATURAL JOINs and JOINs using the keyword USING. *

* * @author Grégory Mantelet (ARI) * @version 1.4 (03/2016) * @since 1.4 * * @see SQLServer_InnerJoin * @see SQLServer_OuterJoin * @see SQLServerTranslator */ public class SQLServer_ADQLQueryFactory extends ADQLQueryFactory { public ADQLJoin createJoin(JoinType type, FromContent leftTable, FromContent rightTable) throws Exception{ switch(type){ case CROSS: return new CrossJoin(leftTable, rightTable); case INNER: return new SQLServer_InnerJoin(leftTable, rightTable); case OUTER_LEFT: return new SQLServer_OuterJoin(leftTable, rightTable, OuterType.LEFT); case OUTER_RIGHT: return new SQLServer_OuterJoin(leftTable, rightTable, OuterType.RIGHT); case OUTER_FULL: return new SQLServer_OuterJoin(leftTable, rightTable, OuterType.FULL); default: throw new Exception("Unknown join type: " + type); } } }src/adql/parser/SimpleCharStream.java0000644000175000017500000002464013177122310016621 0ustar olesoles/* Generated By:JavaCC: Do not edit this line. SimpleCharStream.java Version 6.0 */ /* JavaCCOptions:STATIC=false,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ package adql.parser; /** * An implementation of interface CharStream, where the stream is assumed to * contain only ASCII characters (without unicode processing). */ public class SimpleCharStream { /** Whether parser is static. */ public static final boolean staticFlag = false; int bufsize; int available; int tokenBegin; /** Position in buffer. */ public int bufpos = -1; protected int bufline[]; protected int bufcolumn[]; protected int column = 0; protected int line = 1; protected boolean prevCharIsCR = false; protected boolean prevCharIsLF = false; protected java.io.Reader inputStream; protected char[] buffer; protected int maxNextCharInd = 0; protected int inBuf = 0; protected int tabSize = 8; protected boolean trackLineColumn = true; public void setTabSize(int i){ tabSize = i; } public int getTabSize(){ return tabSize; } protected void ExpandBuff(boolean wrapAround){ char[] newbuffer = new char[bufsize + 2048]; int newbufline[] = new int[bufsize + 2048]; int newbufcolumn[] = new int[bufsize + 2048]; try{ if (wrapAround){ System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin); System.arraycopy(buffer, 0, newbuffer, bufsize - tokenBegin, bufpos); buffer = newbuffer; System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin); System.arraycopy(bufline, 0, newbufline, bufsize - tokenBegin, bufpos); bufline = newbufline; System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin); System.arraycopy(bufcolumn, 0, newbufcolumn, bufsize - tokenBegin, bufpos); bufcolumn = newbufcolumn; maxNextCharInd = (bufpos += (bufsize - tokenBegin)); }else{ System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin); buffer = newbuffer; System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin); bufline = newbufline; System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin); bufcolumn = newbufcolumn; maxNextCharInd = (bufpos -= tokenBegin); } }catch(Throwable t){ throw new Error(t.getMessage()); } bufsize += 2048; available = bufsize; tokenBegin = 0; } protected void FillBuff() throws java.io.IOException{ if (maxNextCharInd == available){ if (available == bufsize){ if (tokenBegin > 2048){ bufpos = maxNextCharInd = 0; available = tokenBegin; }else if (tokenBegin < 0) bufpos = maxNextCharInd = 0; else ExpandBuff(false); }else if (available > tokenBegin) available = bufsize; else if ((tokenBegin - available) < 2048) ExpandBuff(true); else available = tokenBegin; } int i; try{ if ((i = inputStream.read(buffer, maxNextCharInd, available - maxNextCharInd)) == -1){ inputStream.close(); throw new java.io.IOException(); }else maxNextCharInd += i; return; }catch(java.io.IOException e){ --bufpos; backup(0); if (tokenBegin == -1) tokenBegin = bufpos; throw e; } } /** Start. */ public char BeginToken() throws java.io.IOException{ tokenBegin = -1; char c = readChar(); tokenBegin = bufpos; return c; } protected void UpdateLineColumn(char c){ column++; if (prevCharIsLF){ prevCharIsLF = false; line += (column = 1); }else if (prevCharIsCR){ prevCharIsCR = false; if (c == '\n'){ prevCharIsLF = true; }else line += (column = 1); } switch(c){ case '\r': prevCharIsCR = true; break; case '\n': prevCharIsLF = true; break; case '\t': column--; column += (tabSize - (column % tabSize)); break; default: break; } bufline[bufpos] = line; bufcolumn[bufpos] = column; } /** Read a character. */ public char readChar() throws java.io.IOException{ if (inBuf > 0){ --inBuf; if (++bufpos == bufsize) bufpos = 0; return buffer[bufpos]; } if (++bufpos >= maxNextCharInd) FillBuff(); char c = buffer[bufpos]; UpdateLineColumn(c); return c; } @Deprecated /** * @deprecated * @see #getEndColumn */ public int getColumn(){ return bufcolumn[bufpos]; } @Deprecated /** * @deprecated * @see #getEndLine */ public int getLine(){ return bufline[bufpos]; } /** Get token end column number. */ public int getEndColumn(){ return bufcolumn[bufpos]; } /** Get token end line number. */ public int getEndLine(){ return bufline[bufpos]; } /** Get token beginning column number. */ public int getBeginColumn(){ return bufcolumn[tokenBegin]; } /** Get token beginning line number. */ public int getBeginLine(){ return bufline[tokenBegin]; } /** Backup a number of characters. */ public void backup(int amount){ inBuf += amount; if ((bufpos -= amount) < 0) bufpos += bufsize; } /** Constructor. */ public SimpleCharStream(java.io.Reader dstream, int startline, int startcolumn, int buffersize){ inputStream = dstream; line = startline; column = startcolumn - 1; available = bufsize = buffersize; buffer = new char[buffersize]; bufline = new int[buffersize]; bufcolumn = new int[buffersize]; } /** Constructor. */ public SimpleCharStream(java.io.Reader dstream, int startline, int startcolumn){ this(dstream, startline, startcolumn, 4096); } /** Constructor. */ public SimpleCharStream(java.io.Reader dstream){ this(dstream, 1, 1, 4096); } /** Reinitialise. */ public void ReInit(java.io.Reader dstream, int startline, int startcolumn, int buffersize){ inputStream = dstream; line = startline; column = startcolumn - 1; if (buffer == null || buffersize != buffer.length){ available = bufsize = buffersize; buffer = new char[buffersize]; bufline = new int[buffersize]; bufcolumn = new int[buffersize]; } prevCharIsLF = prevCharIsCR = false; tokenBegin = inBuf = maxNextCharInd = 0; bufpos = -1; } /** Reinitialise. */ public void ReInit(java.io.Reader dstream, int startline, int startcolumn){ ReInit(dstream, startline, startcolumn, 4096); } /** Reinitialise. */ public void ReInit(java.io.Reader dstream){ ReInit(dstream, 1, 1, 4096); } /** Constructor. */ public SimpleCharStream(java.io.InputStream dstream, String encoding, int startline, int startcolumn, int buffersize) throws java.io.UnsupportedEncodingException{ this(encoding == null ? new java.io.InputStreamReader(dstream) : new java.io.InputStreamReader(dstream, encoding), startline, startcolumn, buffersize); } /** Constructor. */ public SimpleCharStream(java.io.InputStream dstream, int startline, int startcolumn, int buffersize){ this(new java.io.InputStreamReader(dstream), startline, startcolumn, buffersize); } /** Constructor. */ public SimpleCharStream(java.io.InputStream dstream, String encoding, int startline, int startcolumn) throws java.io.UnsupportedEncodingException{ this(dstream, encoding, startline, startcolumn, 4096); } /** Constructor. */ public SimpleCharStream(java.io.InputStream dstream, int startline, int startcolumn){ this(dstream, startline, startcolumn, 4096); } /** Constructor. */ public SimpleCharStream(java.io.InputStream dstream, String encoding) throws java.io.UnsupportedEncodingException{ this(dstream, encoding, 1, 1, 4096); } /** Constructor. */ public SimpleCharStream(java.io.InputStream dstream){ this(dstream, 1, 1, 4096); } /** Reinitialise. */ public void ReInit(java.io.InputStream dstream, String encoding, int startline, int startcolumn, int buffersize) throws java.io.UnsupportedEncodingException{ ReInit(encoding == null ? new java.io.InputStreamReader(dstream) : new java.io.InputStreamReader(dstream, encoding), startline, startcolumn, buffersize); } /** Reinitialise. */ public void ReInit(java.io.InputStream dstream, int startline, int startcolumn, int buffersize){ ReInit(new java.io.InputStreamReader(dstream), startline, startcolumn, buffersize); } /** Reinitialise. */ public void ReInit(java.io.InputStream dstream, String encoding) throws java.io.UnsupportedEncodingException{ ReInit(dstream, encoding, 1, 1, 4096); } /** Reinitialise. */ public void ReInit(java.io.InputStream dstream){ ReInit(dstream, 1, 1, 4096); } /** Reinitialise. */ public void ReInit(java.io.InputStream dstream, String encoding, int startline, int startcolumn) throws java.io.UnsupportedEncodingException{ ReInit(dstream, encoding, startline, startcolumn, 4096); } /** Reinitialise. */ public void ReInit(java.io.InputStream dstream, int startline, int startcolumn){ ReInit(dstream, startline, startcolumn, 4096); } /** Get token literal value. */ public String GetImage(){ if (bufpos >= tokenBegin) return new String(buffer, tokenBegin, bufpos - tokenBegin + 1); else return new String(buffer, tokenBegin, bufsize - tokenBegin) + new String(buffer, 0, bufpos + 1); } /** Get the suffix. */ public char[] GetSuffix(int len){ char[] ret = new char[len]; if ((bufpos + 1) >= len) System.arraycopy(buffer, bufpos - len + 1, ret, 0, len); else{ System.arraycopy(buffer, bufsize - (len - bufpos - 1), ret, 0, len - bufpos - 1); System.arraycopy(buffer, 0, ret, len - bufpos - 1, bufpos + 1); } return ret; } /** Reset buffer when finished. */ public void Done(){ buffer = null; bufline = null; bufcolumn = null; } /** * Method to adjust line and column numbers for the start of a token. */ public void adjustBeginLineColumn(int newLine, int newCol){ int start = tokenBegin; int len; if (bufpos >= tokenBegin){ len = bufpos - tokenBegin + inBuf + 1; }else{ len = bufsize - tokenBegin + bufpos + 1 + inBuf; } int i = 0, j = 0, k = 0; int nextColDiff = 0, columnDiff = 0; while(i < len && bufline[j = start % bufsize] == bufline[k = ++start % bufsize]){ bufline[j] = newLine; nextColDiff = columnDiff + bufcolumn[k] - bufcolumn[j]; bufcolumn[j] = newCol + columnDiff; columnDiff = nextColDiff; i++; } if (i < len){ bufline[j] = newLine++; bufcolumn[j] = newCol + columnDiff; while(i++ < len){ if (bufline[j = start % bufsize] != bufline[++start % bufsize]) bufline[j] = newLine++; else bufline[j] = newLine; } } line = bufline[j]; column = bufcolumn[j]; } boolean getTrackLineColumn(){ return trackLineColumn; } void setTrackLineColumn(boolean tlc){ trackLineColumn = tlc; } } /* JavaCC - OriginalChecksum=25c201ae6a86f1f10a8af29118337505 (do not edit this line) */ src/adql/parser/TokenMgrError.java0000644000175000017500000001214413177122310016152 0ustar olesoles/* Generated By:JavaCC: Do not edit this line. TokenMgrError.java Version 6.0 */ /* JavaCCOptions: * * Modified by Grégory Mantelet (CDS), on Sept. 2017 * Modifications: * - addition of position (line and column) in the original text * - 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 * after a diff in case of significant modifications have been done by a new * version of JavaCC). */ package adql.parser; /** Token Manager Error. */ public class TokenMgrError extends Error { /** * The version identifier for this Serializable class. * Increment only if the serialized form of the * class changes. */ private static final long serialVersionUID = 1L; /* * Ordinals for various reasons why an Error of this type can be thrown. */ /** * Lexical error occurred. */ static final int LEXICAL_ERROR = 0; /** * An attempt was made to create a second instance of a static token manager. */ static final int STATIC_LEXER_ERROR = 1; /** * Tried to change to an invalid lexical state. */ static final int INVALID_LEXICAL_STATE = 2; /** * Detected (and bailed out of) an infinite loop in the token manager. */ static final int LOOP_DETECTED = 3; /** * Indicates the reason why the exception is thrown. It will have * one of the above 4 values. */ int errorCode; /** Indicates the line at which the error occurs. */ int errorLine = -1; /** Indicates the column at which the error occurs. */ int errorColumn = -1; /** * Replaces unprintable characters by their escaped (or unicode escaped) * equivalents in the given string */ protected static final String addEscapes(String str){ StringBuffer retval = new StringBuffer(); char ch; for(int i = 0; i < str.length(); i++){ switch(str.charAt(i)){ case 0: continue; case '\b': retval.append("\\b"); continue; case '\t': retval.append("\\t"); continue; case '\n': retval.append("\\n"); continue; case '\f': retval.append("\\f"); continue; case '\r': retval.append("\\r"); continue; case '\"': retval.append("\\\""); continue; case '\'': retval.append("\\\'"); continue; case '\\': retval.append("\\\\"); continue; default: 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{ retval.append(ch); } continue; } } return retval.toString(); } /** * Returns a detailed message for the Error when it is thrown by the * token manager to indicate a lexical error. * Parameters : * EOFSeen : indicates if EOF caused the lexical error * curLexState : lexical state in which this error occurred * errorLine : line number when the error occurred * errorColumn : column number when the error occurred * errorAfter : prefix that was seen before this error occurred * curchar : the offending character * Note: You can customize the lexical error message by modifying this method. */ protected static String LexicalError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, char curChar){ return ("Incorrect character encountered at l." + errorLine + ", c." + errorColumn + ": " + (EOFSeen ? " " : ("\"" + addEscapes(String.valueOf(curChar)) + "\"") + " ('" + curChar + "'), ") + "after : \"" + addEscapes(errorAfter) + "\""); } /** * You can also modify the body of this method to customize your error messages. * For example, cases like LOOP_DETECTED and INVALID_LEXICAL_STATE are not * of end-users concern, so you can return something like : * * "Internal Error : Please file a bug report .... " * * from this method for such cases in the release version of your parser. */ @Override public String getMessage(){ return super.getMessage(); } /** * Gets the line at which this error occurs. * * @return Error line or -1 if unknown. */ public final int getErrorLine(){ return errorLine; } /** * Gets the column at which this error occurs. * * @return Error column or -1 if unknown. */ public final int getErrorColumn(){ return errorColumn; } /* * Constructors of various flavors follow. */ /** No arg constructor. */ public TokenMgrError(){} /** Constructor with message and reason. */ public TokenMgrError(String message, int reason){ super(message); errorCode = reason; } /** Full Constructor. */ public TokenMgrError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, char curChar, int reason){ this(LexicalError(EOFSeen, lexState, errorLine, errorColumn, errorAfter, curChar), reason); this.errorLine = errorLine; this.errorColumn = errorColumn; } } /* JavaCC - OriginalChecksum=f63fc7c10226c13ff5a304e2fb1ae182 (do not edit this line) */ src/adql/parser/ADQLParser.java0000644000175000017500000042761413226136446015336 0ustar olesoles/* ADQLParser.java */ /* Generated By:JavaCC: Do not edit this line. ADQLParser.java */ package adql.parser; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; import java.util.Stack; import java.util.Vector; import adql.db.exception.UnresolvedIdentifiersException; import adql.parser.ADQLQueryFactory.JoinType; import adql.parser.IdentifierItems.IdentifierItem; import adql.query.ADQLOrder; import adql.query.ADQLQuery; import adql.query.ClauseADQL; import adql.query.ClauseConstraints; import adql.query.ClauseSelect; import adql.query.SelectAllColumns; import adql.query.SelectItem; import adql.query.TextPosition; import adql.query.constraint.ADQLConstraint; import adql.query.constraint.Between; import adql.query.constraint.Comparison; import adql.query.constraint.ComparisonOperator; import adql.query.constraint.ConstraintsGroup; import adql.query.constraint.Exists; import adql.query.constraint.In; import adql.query.constraint.IsNull; import adql.query.constraint.NotConstraint; import adql.query.from.ADQLJoin; import adql.query.from.FromContent; import adql.query.operand.ADQLColumn; import adql.query.operand.ADQLOperand; import adql.query.operand.Concatenation; import adql.query.operand.NegativeOperand; import adql.query.operand.NumericConstant; import adql.query.operand.Operation; import adql.query.operand.OperationType; import adql.query.operand.StringConstant; import adql.query.operand.WrappedOperand; import adql.query.operand.function.ADQLFunction; import adql.query.operand.function.MathFunction; import adql.query.operand.function.MathFunctionType; import adql.query.operand.function.SQLFunction; import adql.query.operand.function.SQLFunctionType; import adql.query.operand.function.UserDefinedFunction; import adql.query.operand.function.geometry.GeometryFunction; import adql.query.operand.function.geometry.GeometryFunction.GeometryValue; import adql.query.operand.function.geometry.PointFunction; import adql.translator.PostgreSQLTranslator; import adql.translator.TranslationException; /** * Parses an ADQL query thanks to the {@link ADQLParser#Query()} function. * *

* This parser is able, thanks to a {@link QueryChecker} object, to check each * {@link 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. *

* *

* 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(...)). *

* *

WARNING: * To modify this class it's strongly encouraged to modify the .jj file in the * section between PARSER_BEGIN and PARSER_END and to re-compile * it with JavaCC. *

* * @see QueryChecker * @see ADQLQueryFactory * * @author Grégory Mantelet (CDS;ARI) - gmantele@ari.uni-heidelberg.de * @version 1.4 (01/2018) */ public class ADQLParser implements ADQLParserConstants { /** 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 stackQuery = new Stack(); /** The object representation of the ADQL query to parse. * (ONLY USED DURING THE PARSING, else it is always null). */ private ADQLQuery query = null; /** Checks each {@link 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())); setDebug(false); } /** * Builds an ADQL parser without a query to parse but with a * {@link QueryChecker} and a {@link ADQLQueryFactory}. * * @param checker The object to use to check each {@link 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 * {@link QueryChecker}. * * @param checker The object to use to check each {@link ADQLQuery}. */ public ADQLParser(QueryChecker checker){ this(checker, null); } /** * Builds an ADQL parser without a query to parse but with a * {@link 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 {@link 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); setDebug(false); setDebug(false); 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 {@link 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 {@link 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); setDebug(false); 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 {@link 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 {@link 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); setDebug(false); setDebug(false); 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 {@link 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 {@link 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); setDebug(false); setDebug(false); 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 {@link 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 * ReInit 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; } /** * Gets the specified ADQL query and parses the given ADQL query. The SQL * translation is then printed if the syntax is correct. * *

* ONLY the syntax is checked: the query is NOT EXECUTED ! *

* *

Supplied parameters are: *

    *
  • [-debug] -url http://...
  • *
  • [-debug] -file ...
  • *
  • [-debug] -query SELECT...
  • *
*

* * @param args * @throws Exception */ public static final void main(String[] args) throws Exception{ final String USAGE = "Usage:\u005cn\u005ctadqlParser.jar [-d] [-v] [-e] [-a|-s] [|]\u005cn\u005cnNOTE: If no file or URL is given, the ADQL query is expected in the standard input. This query must end with a ';' !\u005cn\u005cnParameters:\u005cn\u005ct-v or --verbose : Print the main steps of the parsing\u005cn\u005ct-d or --debug : Print stack traces when a grave error occurs\u005cn\u005ct-e or --explain : Explain the ADQL parsing (or Expand the parsing tree)\u005cn\u005ct-a or --adql : Display the understood ADQL query\u005cn\u005ct-s or --sql : Ask the SQL translation of the given ADQL query (SQL compatible with PostgreSQL)\u005cn\u005cnReturn:\u005cn\u005ctBy default: nothing if the query is correct. Otherwise a message explaining why the query is not correct is displayed.\u005cn\u005ctWith the -s option, the SQL translation of the given ADQL query will be returned.\u005cn\u005ctWith the -a option, the ADQL query is returned as it has been understood.\u005cn\u005cnExit status:\u005cn\u005ct0\u005ctOK !\u005cn\u005ct1\u005ctParameter error (missing or incorrect parameter)\u005cn\u005ct2\u005ctFile error (incorrect file/url, reading error, ...)\u005cn\u005ct3\u005ctParsing error (syntactic or semantic error)\u005cn\u005ct4\u005ctTranslation 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 ((!))\u005cn" + 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 ((!))\u005cn" + 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: \u005c"" + args[i] + "\u005c" ((!))\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("\u005ct - 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("\u005cn((X)) Error while reading the file \u005c"" + file + "\u005c": " + ioe.getMessage() + " ((X))"); if (debug) ioe.printStackTrace(System.err); System.exit(2); } } /* ########## */ /* # 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 parseQuery functions. * * @return The object representation of the query. * @throws ParseException If the query syntax is incorrect. */ final public ADQLQuery Query() throws ParseException{ trace_call("Query"); try{ ADQLQuery q = null; q = QueryExpression(); switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case 0:{ jj_consume_token(0); break; } case EOQ:{ jj_consume_token(EOQ); break; } default: jj_la1[0] = jj_gen; jj_consume_token(-1); throw new ParseException(); } // check the query: if (queryChecker != null) queryChecker.check(q); { if ("" != null) return q; } throw new Error("Missing return statement in function"); }finally{ trace_return("Query"); } } final public ADQLQuery QueryExpression() throws ParseException{ trace_call("QueryExpression"); try{ TextPosition endPos = null; try{ // create the query: query = queryFactory.createQuery(); stackQuery.push(query); }catch(Exception ex){ { if (true) throw generateParseException(ex); } } Select(); From(); endPos = query.getFrom().getPosition(); switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case WHERE:{ Where(); endPos = query.getWhere().getPosition(); break; } default: jj_la1[1] = jj_gen;; } switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case GROUP:{ GroupBy(); endPos = query.getGroupBy().getPosition(); break; } default: jj_la1[2] = jj_gen;; } switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case HAVING:{ Having(); endPos = query.getHaving().getPosition(); break; } default: jj_la1[3] = jj_gen;; } switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case ORDER:{ OrderBy(); endPos = query.getOrderBy().getPosition(); break; } default: jj_la1[4] = jj_gen;; } // 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(); { if ("" != null) return previousQuery; } throw new Error("Missing return statement in function"); }finally{ trace_return("QueryExpression"); } } final public ADQLQuery SubQueryExpression() throws ParseException{ trace_call("SubQueryExpression"); try{ ADQLQuery q = null; Token start, end; start = jj_consume_token(LEFT_PAR); q = QueryExpression(); end = jj_consume_token(RIGHT_PAR); q.setPosition(new TextPosition(start, end)); { if ("" != null) return q; } throw new Error("Missing return statement in function"); }finally{ trace_return("SubQueryExpression"); } } final public void Select() throws ParseException{ trace_call("Select"); try{ ClauseSelect select = query.getSelect(); SelectItem item = null; Token start, t = null; start = jj_consume_token(SELECT); switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case QUANTIFIER:{ t = jj_consume_token(QUANTIFIER); select.setDistinctColumns(t.image.equalsIgnoreCase("DISTINCT")); break; } default: jj_la1[5] = jj_gen;; } switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case TOP:{ jj_consume_token(TOP); t = jj_consume_token(UNSIGNED_INTEGER); try{ select.setLimit(Integer.parseInt(t.image)); }catch(NumberFormatException nfe){ { if (true) throw new ParseException("[l." + t.beginLine + ";c." + t.beginColumn + "] The TOP limit (\u005c"" + t.image + "\u005c") isn't a regular unsigned integer !"); } } break; } default: jj_la1[6] = jj_gen;; } item = SelectItem(); select.add(item); label_1: while(true){ switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case COMMA:{ ; break; } default: jj_la1[7] = jj_gen; break label_1; } jj_consume_token(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)); }finally{ trace_return("Select"); } } final public SelectItem SelectItem() throws ParseException{ trace_call("SelectItem"); try{ IdentifierItems identifiers = new IdentifierItems(true); IdentifierItem id = null, label = null; ADQLOperand op = null; SelectItem item; Token starToken; switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case ASTERISK:{ starToken = jj_consume_token(ASTERISK); item = new SelectAllColumns(query); item.setPosition(new TextPosition(starToken)); { if ("" != null) return item; } break; } default: jj_la1[12] = jj_gen; if (jj_2_1(7)){ id = Identifier(); jj_consume_token(DOT); identifiers.append(id); switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case DELIMITED_IDENTIFIER: case REGULAR_IDENTIFIER:{ id = Identifier(); jj_consume_token(DOT); identifiers.append(id); switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case DELIMITED_IDENTIFIER: case REGULAR_IDENTIFIER:{ id = Identifier(); jj_consume_token(DOT); identifiers.append(id); break; } default: jj_la1[8] = jj_gen;; } break; } default: jj_la1[9] = jj_gen;; } starToken = jj_consume_token(ASTERISK); 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))); { if ("" != null) return item; } }catch(Exception ex){ { if (true) throw generateParseException(ex); } } }else{ switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case LEFT_PAR: case PLUS: case MINUS: case AVG: case MAX: case MIN: case SUM: case COUNT: case BOX: case CENTROID: case CIRCLE: case POINT: case POLYGON: case REGION: case CONTAINS: case INTERSECTS: case AREA: case COORD1: case COORD2: case COORDSYS: case DISTANCE: case ABS: case CEILING: case DEGREES: case EXP: case FLOOR: case LOG: case LOG10: case MOD: case PI: case POWER: case RADIANS: case RAND: case ROUND: case SQRT: case TRUNCATE: case ACOS: case ASIN: case ATAN: case ATAN2: case COS: case COT: case SIN: case TAN: case STRING_LITERAL: case DELIMITED_IDENTIFIER: case REGULAR_IDENTIFIER: case SCIENTIFIC_NUMBER: case UNSIGNED_FLOAT: case UNSIGNED_INTEGER:{ op = ValueExpression(); switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case AS: case DELIMITED_IDENTIFIER: case REGULAR_IDENTIFIER:{ switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case AS:{ jj_consume_token(AS); break; } default: jj_la1[10] = jj_gen;; } label = Identifier(); break; } default: jj_la1[11] = jj_gen;; } break; } default: jj_la1[13] = jj_gen; jj_consume_token(-1); throw new ParseException(); } } } try{ 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())); { if ("" != null) return item; } }catch(Exception ex){ { if (true) throw generateParseException(ex); } } throw new Error("Missing return statement in function"); }finally{ trace_return("SelectItem"); } } final public void From() throws ParseException{ trace_call("From"); try{ FromContent content = null, content2 = null; try{ jj_consume_token(FROM); content = TableRef(); label_2: while(true){ switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case COMMA:{ ; break; } default: jj_la1[14] = jj_gen; break label_2; } jj_consume_token(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){ { if (true) throw generateParseException(ex); } } }finally{ trace_return("From"); } } final public void Where() throws ParseException{ trace_call("Where"); try{ ClauseConstraints where = query.getWhere(); ADQLConstraint condition; Token start; start = jj_consume_token(WHERE); ConditionsList(where); TextPosition endPosition = where.getPosition(); where.setPosition(new TextPosition(start.beginLine, start.beginColumn, endPosition.endLine, endPosition.endColumn)); }finally{ trace_return("Where"); } } final public void GroupBy() throws ParseException{ trace_call("GroupBy"); try{ ClauseADQL groupBy = query.getGroupBy(); ADQLColumn colRef = null; Token start; start = jj_consume_token(GROUP); jj_consume_token(BY); colRef = Column(); groupBy.add(colRef); label_3: while(true){ switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case COMMA:{ ; break; } default: jj_la1[15] = jj_gen; break label_3; } jj_consume_token(COMMA); colRef = Column(); groupBy.add(colRef); } groupBy.setPosition(new TextPosition(start.beginLine, start.beginColumn, colRef.getPosition().endLine, colRef.getPosition().endColumn)); }finally{ trace_return("GroupBy"); } } final public void Having() throws ParseException{ trace_call("Having"); try{ ClauseConstraints having = query.getHaving(); Token start; start = jj_consume_token(HAVING); ConditionsList(having); TextPosition endPosition = having.getPosition(); having.setPosition(new TextPosition(start.beginLine, start.beginColumn, endPosition.endLine, endPosition.endColumn)); }finally{ trace_return("Having"); } } final public void OrderBy() throws ParseException{ trace_call("OrderBy"); try{ ClauseADQL orderBy = query.getOrderBy(); ADQLOrder order = null; Token start; start = jj_consume_token(ORDER); jj_consume_token(BY); order = OrderItem(); orderBy.add(order); label_4: while(true){ switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case COMMA:{ ; break; } default: jj_la1[16] = jj_gen; break label_4; } jj_consume_token(COMMA); order = OrderItem(); orderBy.add(order); } orderBy.setPosition(new TextPosition(start, token)); }finally{ trace_return("OrderBy"); } } /* *************************** */ /* COLUMN AND TABLE REFERENCES */ /* *************************** */ final public IdentifierItem Identifier() throws ParseException{ trace_call("Identifier"); try{ Token t; switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case REGULAR_IDENTIFIER:{ t = jj_consume_token(REGULAR_IDENTIFIER); { if ("" != null) return new IdentifierItem(t, false); } break; } case DELIMITED_IDENTIFIER:{ t = jj_consume_token(DELIMITED_IDENTIFIER); { if ("" != null) return new IdentifierItem(t, true); } break; } default: jj_la1[17] = jj_gen; jj_consume_token(-1); throw new ParseException(); } throw new Error("Missing return statement in function"); }finally{ trace_return("Identifier"); } } /** * 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. */ final public IdentifierItems TableName() throws ParseException{ trace_call("TableName"); try{ IdentifierItems identifiers = new IdentifierItems(true); IdentifierItem id = null; id = Identifier(); identifiers.append(id); switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case DOT:{ jj_consume_token(DOT); id = Identifier(); identifiers.append(id); break; } default: jj_la1[18] = jj_gen;; } switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case DOT:{ jj_consume_token(DOT); id = Identifier(); identifiers.append(id); break; } default: jj_la1[19] = jj_gen;; } { if ("" != null) return identifiers; } throw new Error("Missing return statement in function"); }finally{ trace_return("TableName"); } } /** * 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. */ final public IdentifierItems ColumnName() throws ParseException{ trace_call("ColumnName"); try{ IdentifierItem id; IdentifierItems table = null, identifiers = new IdentifierItems(false); id = Identifier(); switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case DOT:{ jj_consume_token(DOT); table = TableName(); break; } default: jj_la1[20] = jj_gen;; } identifiers.append(id); if (table != null){ for(int i = 0; i < table.size(); i++) identifiers.append(table.get(i)); } { if ("" != null) return identifiers; } throw new Error("Missing return statement in function"); }finally{ trace_return("ColumnName"); } } final public ADQLColumn Column() throws ParseException{ trace_call("Column"); try{ IdentifierItems identifiers; identifiers = ColumnName(); try{ { if ("" != null) return queryFactory.createColumn(identifiers); } }catch(Exception ex){ { if (true) throw generateParseException(ex); } } throw new Error("Missing return statement in function"); }finally{ trace_return("Column"); } } final public ADQLOrder OrderItem() throws ParseException{ trace_call("OrderItem"); try{ IdentifierItem identifier = null; Token ind = null, desc = null; switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case DELIMITED_IDENTIFIER: case REGULAR_IDENTIFIER:{ identifier = Identifier(); break; } case UNSIGNED_INTEGER:{ ind = jj_consume_token(UNSIGNED_INTEGER); break; } default: jj_la1[21] = jj_gen; jj_consume_token(-1); throw new ParseException(); } switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case ASC: case DESC:{ switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case ASC:{ jj_consume_token(ASC); break; } case DESC:{ desc = jj_consume_token(DESC); break; } default: jj_la1[22] = jj_gen; jj_consume_token(-1); throw new ParseException(); } break; } default: jj_la1[23] = jj_gen;; } try{ ADQLOrder order = null; 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)); } { if ("" != null) return order; } }catch(Exception ex){ { if (true) throw generateParseException(ex); } } throw new Error("Missing return statement in function"); }finally{ trace_return("OrderItem"); } } final public FromContent SimpleTableRef() throws ParseException{ trace_call("SimpleTableRef"); try{ IdentifierItem alias = null; IdentifierItems identifiers = null; ADQLQuery subQuery = null; FromContent content = null; Token start, end; try{ switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case DELIMITED_IDENTIFIER: case REGULAR_IDENTIFIER:{ identifiers = TableName(); switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case AS: case DELIMITED_IDENTIFIER: case REGULAR_IDENTIFIER:{ switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case AS:{ jj_consume_token(AS); break; } default: jj_la1[24] = jj_gen;; } alias = Identifier(); break; } default: jj_la1[25] = jj_gen;; } 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)); { if ("" != null) return content; } break; } default: jj_la1[27] = jj_gen; if (jj_2_2(2)){ subQuery = SubQueryExpression(); switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case AS:{ jj_consume_token(AS); break; } default: jj_la1[26] = jj_gen;; } 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)); { if ("" != null) return content; } }else{ switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case LEFT_PAR:{ start = jj_consume_token(LEFT_PAR); content = JoinedTable(); end = jj_consume_token(RIGHT_PAR); content.setPosition(new TextPosition(start, end)); { if ("" != null) return content; } break; } default: jj_la1[28] = jj_gen; jj_consume_token(-1); throw new ParseException(); } } } }catch(Exception ex){ { if (true) throw generateParseException(ex); } } throw new Error("Missing return statement in function"); }finally{ trace_return("SimpleTableRef"); } } final public FromContent TableRef() throws ParseException{ trace_call("TableRef"); try{ FromContent content; content = SimpleTableRef(); label_5: while(true){ if (jj_2_3(2)){ ; }else{ break label_5; } content = JoinSpecification(content); } { if ("" != null) return content; } throw new Error("Missing return statement in function"); }finally{ trace_return("TableRef"); } } final public FromContent JoinedTable() throws ParseException{ trace_call("JoinedTable"); try{ FromContent content; content = SimpleTableRef(); label_6: while(true){ content = JoinSpecification(content); switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case NATURAL: case INNER: case RIGHT: case LEFT: case FULL: case JOIN:{ ; break; } default: jj_la1[29] = jj_gen; break label_6; } } { if ("" != null) return content; } throw new Error("Missing return statement in function"); }finally{ trace_return("JoinedTable"); } } final public ADQLJoin JoinSpecification(FromContent leftTable) throws ParseException{ trace_call("JoinSpecification"); try{ boolean natural = false; JoinType type = JoinType.INNER; ClauseConstraints condition = new ClauseConstraints("ON"); ArrayList lstColumns = new ArrayList(); IdentifierItem id; FromContent rightTable; ADQLJoin join; Token lastPar; try{ switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case NATURAL:{ jj_consume_token(NATURAL); natural = true; switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case INNER: case RIGHT: case LEFT: case FULL:{ switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case INNER:{ jj_consume_token(INNER); break; } case RIGHT: case LEFT: case FULL:{ switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case LEFT:{ jj_consume_token(LEFT); type = JoinType.OUTER_LEFT; break; } case RIGHT:{ jj_consume_token(RIGHT); type = JoinType.OUTER_RIGHT; break; } case FULL:{ jj_consume_token(FULL); type = JoinType.OUTER_FULL; break; } default: jj_la1[30] = jj_gen; jj_consume_token(-1); throw new ParseException(); } switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case OUTER:{ jj_consume_token(OUTER); break; } default: jj_la1[31] = jj_gen;; } break; } default: jj_la1[32] = jj_gen; jj_consume_token(-1); throw new ParseException(); } break; } default: jj_la1[33] = jj_gen;; } jj_consume_token(JOIN); rightTable = SimpleTableRef(); join = queryFactory.createJoin(type, leftTable, rightTable); join.setPosition(new TextPosition(leftTable.getPosition(), rightTable.getPosition())); { if ("" != null) return join; } break; } case INNER: case RIGHT: case LEFT: case FULL: case JOIN:{ switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case INNER: case RIGHT: case LEFT: case FULL:{ switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case INNER:{ jj_consume_token(INNER); break; } case RIGHT: case LEFT: case FULL:{ switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case LEFT:{ jj_consume_token(LEFT); type = JoinType.OUTER_LEFT; break; } case RIGHT:{ jj_consume_token(RIGHT); type = JoinType.OUTER_RIGHT; break; } case FULL:{ jj_consume_token(FULL); type = JoinType.OUTER_FULL; break; } default: jj_la1[34] = jj_gen; jj_consume_token(-1); throw new ParseException(); } switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case OUTER:{ jj_consume_token(OUTER); break; } default: jj_la1[35] = jj_gen;; } break; } default: jj_la1[36] = jj_gen; jj_consume_token(-1); throw new ParseException(); } break; } default: jj_la1[37] = jj_gen;; } jj_consume_token(JOIN); rightTable = SimpleTableRef(); switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case ON:{ jj_consume_token(ON); ConditionsList(condition); join = queryFactory.createJoin(type, leftTable, rightTable, condition); join.setPosition(new TextPosition(leftTable.getPosition(), condition.getPosition())); { if ("" != null) return join; } break; } case USING:{ jj_consume_token(USING); jj_consume_token(LEFT_PAR); id = Identifier(); lstColumns.add(queryFactory.createColumn(id)); label_7: while(true){ switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case COMMA:{ ; break; } default: jj_la1[38] = jj_gen; break label_7; } jj_consume_token(COMMA); id = Identifier(); lstColumns.add(queryFactory.createColumn(id)); } lastPar = jj_consume_token(RIGHT_PAR); join = queryFactory.createJoin(type, leftTable, rightTable, lstColumns); join.setPosition(new TextPosition(leftTable.getPosition().beginLine, leftTable.getPosition().beginColumn, lastPar.endLine, (lastPar.endColumn < 0) ? -1 : (lastPar.endColumn + 1))); { if ("" != null) return join; } break; } default: jj_la1[39] = jj_gen; jj_consume_token(-1); throw new ParseException(); } break; } default: jj_la1[40] = jj_gen; jj_consume_token(-1); throw new ParseException(); } }catch(Exception ex){ { if (true) throw generateParseException(ex); } } throw new Error("Missing return statement in function"); }finally{ trace_return("JoinSpecification"); } } /* ****** */ /* STRING */ /* ****** */ final public StringConstant String() throws ParseException{ trace_call("String"); try{ Token t, start = null; String str = ""; StringConstant cst; label_8: while(true){ t = jj_consume_token(STRING_LITERAL); str += t.image.substring(1, t.image.length() - 1).replaceAll("''", "'"); if (start == null) start = t; switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case STRING_LITERAL:{ ; break; } default: jj_la1[41] = jj_gen; break label_8; } } try{ cst = queryFactory.createStringConstant(str); cst.setPosition(new TextPosition(start, t)); { if ("" != null) return cst; } }catch(Exception ex){ { if (true) throw generateParseException(ex); } } throw new Error("Missing return statement in function"); }finally{ trace_return("String"); } } /* ************* */ /* NUMERIC TYPES */ /* ************* */ final public NumericConstant UnsignedNumeric() throws ParseException{ trace_call("UnsignedNumeric"); try{ Token t; NumericConstant cst; switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case SCIENTIFIC_NUMBER:{ t = jj_consume_token(SCIENTIFIC_NUMBER); break; } case UNSIGNED_FLOAT:{ t = jj_consume_token(UNSIGNED_FLOAT); break; } case UNSIGNED_INTEGER:{ t = jj_consume_token(UNSIGNED_INTEGER); break; } default: jj_la1[42] = jj_gen; jj_consume_token(-1); throw new ParseException(); } try{ cst = queryFactory.createNumericConstant(t.image); cst.setPosition(new TextPosition(t)); { if ("" != null) return cst; } }catch(Exception ex){ { if (true) throw generateParseException(ex); } } throw new Error("Missing return statement in function"); }finally{ trace_return("UnsignedNumeric"); } } final public NumericConstant UnsignedFloat() throws ParseException{ trace_call("UnsignedFloat"); try{ Token t; NumericConstant cst; switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case UNSIGNED_INTEGER:{ t = jj_consume_token(UNSIGNED_INTEGER); break; } case UNSIGNED_FLOAT:{ t = jj_consume_token(UNSIGNED_FLOAT); break; } default: jj_la1[43] = jj_gen; jj_consume_token(-1); throw new ParseException(); } try{ cst = queryFactory.createNumericConstant(t.image); cst.setPosition(new TextPosition(t)); { if ("" != null) return cst; } }catch(Exception ex){ { if (true) throw generateParseException(ex); } } throw new Error("Missing return statement in function"); }finally{ trace_return("UnsignedFloat"); } } final public NumericConstant SignedInteger() throws ParseException{ trace_call("SignedInteger"); try{ Token sign = null, number; NumericConstant cst; switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case PLUS: case MINUS:{ switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case PLUS:{ sign = jj_consume_token(PLUS); break; } case MINUS:{ sign = jj_consume_token(MINUS); break; } default: jj_la1[44] = jj_gen; jj_consume_token(-1); throw new ParseException(); } break; } default: jj_la1[45] = jj_gen;; } number = jj_consume_token(UNSIGNED_INTEGER); try{ if (sign == null){ cst = queryFactory.createNumericConstant(number.image); cst.setPosition(new TextPosition(number)); }else{ cst = queryFactory.createNumericConstant(sign.image + number.image); cst.setPosition(new TextPosition(sign, number)); } { if ("" != null) return cst; } }catch(Exception ex){ { if (true) throw generateParseException(ex); } } throw new Error("Missing return statement in function"); }finally{ trace_return("SignedInteger"); } } /* *********** */ /* EXPRESSIONS */ /* *********** */ final public ADQLOperand NumericValueExpressionPrimary() throws ParseException{ trace_call("NumericValueExpressionPrimary"); try{ ADQLColumn column; ADQLOperand op; Token left, right; try{ switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case SCIENTIFIC_NUMBER: case UNSIGNED_FLOAT: case UNSIGNED_INTEGER:{ // unsigned_value_specification op = UnsignedNumeric(); { if ("" != null) return op; } break; } case DELIMITED_IDENTIFIER: case REGULAR_IDENTIFIER:{ column = Column(); column.setExpectedType('N'); { if ("" != null) return column; } break; } case AVG: case MAX: case MIN: case SUM: case COUNT:{ op = SqlFunction(); { if ("" != null) return op; } break; } case LEFT_PAR:{ left = jj_consume_token(LEFT_PAR); op = NumericExpression(); right = jj_consume_token(RIGHT_PAR); WrappedOperand wop = queryFactory.createWrappedOperand(op); wop.setPosition(new TextPosition(left, right)); { if ("" != null) return wop; } break; } default: jj_la1[46] = jj_gen; jj_consume_token(-1); throw new ParseException(); } }catch(Exception ex){ { if (true) throw generateParseException(ex); } } throw new Error("Missing return statement in function"); }finally{ trace_return("NumericValueExpressionPrimary"); } } final public ADQLOperand StringValueExpressionPrimary() throws ParseException{ trace_call("StringValueExpressionPrimary"); try{ StringConstant expr; ADQLColumn column; ADQLOperand op; Token left, right; try{ switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case STRING_LITERAL:{ // string expr = String(); { if ("" != null) return expr; } break; } case SCIENTIFIC_NUMBER: case UNSIGNED_FLOAT: case UNSIGNED_INTEGER:{ op = UnsignedNumeric(); { if ("" != null) return op; } break; } case AVG: case MAX: case MIN: case SUM: case COUNT:{ op = SqlFunction(); { if ("" != null) return op; } break; } case DELIMITED_IDENTIFIER: case REGULAR_IDENTIFIER:{ column = Column(); column.setExpectedType('*'); { if ("" != null) return column; } break; } case LEFT_PAR:{ left = jj_consume_token(LEFT_PAR); op = ValueExpression(); right = jj_consume_token(RIGHT_PAR); WrappedOperand wop = queryFactory.createWrappedOperand(op); wop.setPosition(new TextPosition(left, right)); { if ("" != null) return wop; } break; } default: jj_la1[47] = jj_gen; jj_consume_token(-1); throw new ParseException(); } }catch(Exception ex){ { if (true) throw generateParseException(ex); } } throw new Error("Missing return statement in function"); }finally{ trace_return("StringValueExpressionPrimary"); } } final public ADQLOperand ValueExpression() throws ParseException{ trace_call("ValueExpression"); try{ ADQLOperand valueExpr = null; Token left, right; try{ if (jj_2_4(2147483647)){ valueExpr = NumericExpression(); }else if (jj_2_5(2147483647)){ valueExpr = StringExpression(); }else if (jj_2_6(2147483647)){ left = jj_consume_token(LEFT_PAR); valueExpr = ValueExpression(); right = jj_consume_token(RIGHT_PAR); valueExpr = queryFactory.createWrappedOperand(valueExpr); ((WrappedOperand)valueExpr).setPosition(new TextPosition(left, right)); }else if (jj_2_7(2147483647)){ valueExpr = UserDefinedFunction(); }else if (jj_2_8(2)){ valueExpr = GeometryValueFunction(); }else if (jj_2_9(2147483647)){ valueExpr = Column(); }else if (jj_2_10(2147483647)){ valueExpr = StringFactor(); }else if (jj_2_11(3)){ valueExpr = Factor(); }else{ switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case DELIMITED_IDENTIFIER: case REGULAR_IDENTIFIER:{ valueExpr = Column(); break; } default: jj_la1[48] = jj_gen; jj_consume_token(-1); throw new ParseException(); } } { if ("" != null) return valueExpr; } }catch(Exception ex){ { if (true) throw generateParseException(ex); } } throw new Error("Missing return statement in function"); }finally{ trace_return("ValueExpression"); } } final public ADQLOperand NumericExpression() throws ParseException{ trace_call("NumericExpression"); try{ Token sign = null; ADQLOperand leftOp, rightOp = null; leftOp = NumericTerm(); switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case PLUS: case MINUS:{ switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case PLUS:{ sign = jj_consume_token(PLUS); break; } case MINUS:{ sign = jj_consume_token(MINUS); break; } default: jj_la1[49] = jj_gen; jj_consume_token(-1); throw new ParseException(); } rightOp = NumericExpression(); break; } default: jj_la1[50] = jj_gen;; } if (sign == null){ if ("" != null) return leftOp; }else{ try{ Operation operation = queryFactory.createOperation(leftOp, OperationType.getOperator(sign.image), rightOp); operation.setPosition(new TextPosition(leftOp.getPosition(), rightOp.getPosition())); { if ("" != null) return operation; } }catch(Exception ex){ { if (true) throw generateParseException(ex); } } } throw new Error("Missing return statement in function"); }finally{ trace_return("NumericExpression"); } } final public ADQLOperand NumericTerm() throws ParseException{ trace_call("NumericTerm"); try{ Token sign = null; ADQLOperand leftOp, rightOp = null; leftOp = Factor(); switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case ASTERISK: case DIVIDE:{ switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case ASTERISK:{ sign = jj_consume_token(ASTERISK); break; } case DIVIDE:{ sign = jj_consume_token(DIVIDE); break; } default: jj_la1[51] = jj_gen; jj_consume_token(-1); throw new ParseException(); } rightOp = NumericTerm(); break; } default: jj_la1[52] = jj_gen;; } if (sign == null){ if ("" != null) return leftOp; }else{ try{ Operation operation = queryFactory.createOperation(leftOp, OperationType.getOperator(sign.image), rightOp); operation.setPosition(new TextPosition(leftOp.getPosition(), rightOp.getPosition())); { if ("" != null) return operation; } }catch(Exception ex){ { if (true) throw generateParseException(ex); } } } throw new Error("Missing return statement in function"); }finally{ trace_return("NumericTerm"); } } final public ADQLOperand Factor() throws ParseException{ trace_call("Factor"); try{ boolean negative = false; Token minusSign = null; ADQLOperand op; switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case PLUS: case MINUS:{ switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case PLUS:{ jj_consume_token(PLUS); break; } case MINUS:{ minusSign = jj_consume_token(MINUS); negative = true; break; } default: jj_la1[53] = jj_gen; jj_consume_token(-1); throw new ParseException(); } break; } default: jj_la1[54] = jj_gen;; } if (jj_2_12(2)){ op = NumericFunction(); }else{ switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case LEFT_PAR: case AVG: case MAX: case MIN: case SUM: case COUNT: case DELIMITED_IDENTIFIER: case REGULAR_IDENTIFIER: case SCIENTIFIC_NUMBER: case UNSIGNED_FLOAT: case UNSIGNED_INTEGER:{ op = NumericValueExpressionPrimary(); break; } default: jj_la1[55] = jj_gen; jj_consume_token(-1); throw new ParseException(); } } if (negative){ try{ TextPosition position = op.getPosition(); op = queryFactory.createNegativeOperand(op); NegativeOperand negativeOp = (NegativeOperand)op; if (minusSign != null) negativeOp.setPosition(new TextPosition(minusSign.beginLine, minusSign.beginColumn, position.endLine, position.endColumn)); else negativeOp.setPosition(position); }catch(Exception ex){ { if (true) throw generateParseException(ex); } } } { if ("" != null) return op; } throw new Error("Missing return statement in function"); }finally{ trace_return("Factor"); } } final public ADQLOperand StringExpression() throws ParseException{ trace_call("StringExpression"); try{ ADQLOperand leftOp; ADQLOperand rightOp = null; leftOp = StringFactor(); label_9: while(true){ switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case CONCAT:{ ; break; } default: jj_la1[56] = jj_gen; break label_9; } jj_consume_token(CONCAT); rightOp = StringFactor(); if (!(leftOp instanceof Concatenation)){ try{ ADQLOperand temp = leftOp; leftOp = queryFactory.createConcatenation(); ((Concatenation)leftOp).add(temp); }catch(Exception ex){ { if (true) throw generateParseException(ex); } } } ((Concatenation)leftOp).add(rightOp); } if (leftOp instanceof Concatenation){ Concatenation concat = (Concatenation)leftOp; concat.setPosition(new TextPosition(concat.get(0).getPosition(), concat.get(concat.size() - 1).getPosition())); } { if ("" != null) return leftOp; } throw new Error("Missing return statement in function"); }finally{ trace_return("StringExpression"); } } final public ADQLOperand StringFactor() throws ParseException{ trace_call("StringFactor"); try{ ADQLOperand op; switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case COORDSYS:{ op = ExtractCoordSys(); break; } default: jj_la1[57] = jj_gen; if (jj_2_13(2)){ op = UserDefinedFunction(); ((UserDefinedFunction)op).setExpectedType('S'); }else{ switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case LEFT_PAR: case AVG: case MAX: case MIN: case SUM: case COUNT: case STRING_LITERAL: case DELIMITED_IDENTIFIER: case REGULAR_IDENTIFIER: case SCIENTIFIC_NUMBER: case UNSIGNED_FLOAT: case UNSIGNED_INTEGER:{ op = StringValueExpressionPrimary(); break; } default: jj_la1[58] = jj_gen; jj_consume_token(-1); throw new ParseException(); } } } { if ("" != null) return op; } throw new Error("Missing return statement in function"); }finally{ trace_return("StringFactor"); } } final public GeometryValue GeometryExpression() throws ParseException{ trace_call("GeometryExpression"); try{ ADQLColumn col = null; GeometryFunction gf = null; switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case DELIMITED_IDENTIFIER: case REGULAR_IDENTIFIER:{ col = Column(); break; } case BOX: case CENTROID: case CIRCLE: case POINT: case POLYGON: case REGION:{ gf = GeometryValueFunction(); break; } default: jj_la1[59] = jj_gen; jj_consume_token(-1); throw new ParseException(); } if (col != null){ col.setExpectedType('G'); { if ("" != null) return new GeometryValue(col); } }else{ if ("" != null) return new GeometryValue(gf); } throw new Error("Missing return statement in function"); }finally{ trace_return("GeometryExpression"); } } /* ********************************** */ /* BOOLEAN EXPRESSIONS (WHERE clause) */ /* ********************************** */ final public ClauseConstraints ConditionsList(ClauseConstraints clause) throws ParseException{ trace_call("ConditionsList"); try{ ADQLConstraint constraint = null; Token op = null; boolean notOp = false; try{ switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case NOT:{ op = jj_consume_token(NOT); notOp = true; break; } default: jj_la1[60] = jj_gen;; } constraint = Constraint(); if (notOp){ TextPosition oldPos = constraint.getPosition(); constraint = queryFactory.createNot(constraint); ((NotConstraint)constraint).setPosition(new TextPosition(op.beginLine, op.beginColumn, oldPos.endLine, oldPos.endColumn)); } notOp = false; if (clause instanceof ADQLConstraint) clause.add(constraint); else clause.add(constraint); label_10: while(true){ switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case AND: case OR:{ ; break; } default: jj_la1[61] = jj_gen; break label_10; } switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case AND:{ op = jj_consume_token(AND); break; } case OR:{ op = jj_consume_token(OR); break; } default: jj_la1[62] = jj_gen; jj_consume_token(-1); throw new ParseException(); } switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case NOT:{ jj_consume_token(NOT); notOp = true; break; } default: jj_la1[63] = jj_gen;; } constraint = Constraint(); if (notOp){ TextPosition oldPos = constraint.getPosition(); constraint = queryFactory.createNot(constraint); ((NotConstraint)constraint).setPosition(new TextPosition(op.beginLine, op.beginColumn, oldPos.endLine, oldPos.endColumn)); } notOp = false; if (clause instanceof ADQLConstraint) clause.add(op.image, constraint); else clause.add(op.image, constraint); } }catch(Exception ex){ { if (true) throw generateParseException(ex); } } if (!clause.isEmpty()){ TextPosition start = clause.get(0).getPosition(); TextPosition end = clause.get(clause.size() - 1).getPosition(); clause.setPosition(new TextPosition(start, end)); } { if ("" != null) return clause; } throw new Error("Missing return statement in function"); }finally{ trace_return("ConditionsList"); } } final public ADQLConstraint Constraint() throws ParseException{ trace_call("Constraint"); try{ ADQLConstraint constraint = null; Token start, end; if (jj_2_14(2147483647)){ constraint = Predicate(); }else{ switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case LEFT_PAR:{ start = jj_consume_token(LEFT_PAR); try{ constraint = queryFactory.createGroupOfConstraints(); }catch(Exception ex){ { if (true) throw generateParseException(ex); } } ConditionsList((ConstraintsGroup)constraint); end = jj_consume_token(RIGHT_PAR); ((ConstraintsGroup)constraint).setPosition(new TextPosition(start, end)); break; } default: jj_la1[64] = jj_gen; jj_consume_token(-1); throw new ParseException(); } } { if ("" != null) return constraint; } throw new Error("Missing return statement in function"); }finally{ trace_return("Constraint"); } } final public ADQLConstraint Predicate() throws ParseException{ trace_call("Predicate"); try{ ADQLQuery q = null; ADQLColumn column = null; ADQLOperand strExpr1 = null, strExpr2 = null; ADQLOperand op; Token start, notToken = null, end; ADQLConstraint constraint = null; try{ switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case EXISTS:{ start = jj_consume_token(EXISTS); q = SubQueryExpression(); Exists e = queryFactory.createExists(q); e.setPosition(new TextPosition(start.beginLine, start.beginColumn, q.getPosition().endLine, q.getPosition().endColumn)); { if ("" != null) return e; } break; } default: jj_la1[69] = jj_gen; if (jj_2_16(2147483647)){ column = Column(); jj_consume_token(IS); switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case NOT:{ notToken = jj_consume_token(NOT); break; } default: jj_la1[65] = jj_gen;; } end = jj_consume_token(NULL); IsNull in = queryFactory.createIsNull((notToken != null), column); in.setPosition(new TextPosition(column.getPosition().beginLine, column.getPosition().beginColumn, end.endLine, (end.endColumn < 0) ? -1 : (end.endColumn + 1))); { if ("" != null) return in; } }else if (jj_2_17(2147483647)){ strExpr1 = StringExpression(); switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case NOT:{ notToken = jj_consume_token(NOT); break; } default: jj_la1[66] = jj_gen;; } jj_consume_token(LIKE); strExpr2 = StringExpression(); Comparison comp = queryFactory.createComparison(strExpr1, (notToken == null) ? ComparisonOperator.LIKE : ComparisonOperator.NOTLIKE, strExpr2); comp.setPosition(new TextPosition(strExpr1.getPosition(), strExpr2.getPosition())); { if ("" != null) return comp; } }else{ switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case LEFT_PAR: case PLUS: case MINUS: case AVG: case MAX: case MIN: case SUM: case COUNT: case BOX: case CENTROID: case CIRCLE: case POINT: case POLYGON: case REGION: case CONTAINS: case INTERSECTS: case AREA: case COORD1: case COORD2: case COORDSYS: case DISTANCE: case ABS: case CEILING: case DEGREES: case EXP: case FLOOR: case LOG: case LOG10: case MOD: case PI: case POWER: case RADIANS: case RAND: case ROUND: case SQRT: case TRUNCATE: case ACOS: case ASIN: case ATAN: case ATAN2: case COS: case COT: case SIN: case TAN: case STRING_LITERAL: case DELIMITED_IDENTIFIER: case REGULAR_IDENTIFIER: case SCIENTIFIC_NUMBER: case UNSIGNED_FLOAT: case UNSIGNED_INTEGER:{ op = ValueExpression(); switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case EQUAL: case NOT_EQUAL: case LESS_THAN: case LESS_EQUAL_THAN: case GREATER_THAN: case GREATER_EQUAL_THAN:{ constraint = ComparisonEnd(op); break; } default: jj_la1[67] = jj_gen; if (jj_2_15(2)){ constraint = BetweenEnd(op); }else{ switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case NOT: case IN:{ constraint = InEnd(op); break; } default: jj_la1[68] = jj_gen; jj_consume_token(-1); throw new ParseException(); } } } break; } default: jj_la1[70] = jj_gen; jj_consume_token(-1); throw new ParseException(); } } } }catch(Exception ex){ { if (true) throw generateParseException(ex); } } { if ("" != null) return constraint; } throw new Error("Missing return statement in function"); }finally{ trace_return("Predicate"); } } final public Comparison ComparisonEnd(ADQLOperand leftOp) throws ParseException{ trace_call("ComparisonEnd"); try{ Token comp; ADQLOperand rightOp; switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case EQUAL:{ comp = jj_consume_token(EQUAL); break; } case NOT_EQUAL:{ comp = jj_consume_token(NOT_EQUAL); break; } case LESS_THAN:{ comp = jj_consume_token(LESS_THAN); break; } case LESS_EQUAL_THAN:{ comp = jj_consume_token(LESS_EQUAL_THAN); break; } case GREATER_THAN:{ comp = jj_consume_token(GREATER_THAN); break; } case GREATER_EQUAL_THAN:{ comp = jj_consume_token(GREATER_EQUAL_THAN); break; } default: jj_la1[71] = jj_gen; jj_consume_token(-1); throw new ParseException(); } rightOp = ValueExpression(); try{ Comparison comparison = queryFactory.createComparison(leftOp, ComparisonOperator.getOperator(comp.image), rightOp); comparison.setPosition(new TextPosition(leftOp.getPosition(), rightOp.getPosition())); { if ("" != null) return comparison; } }catch(Exception ex){ { if (true) throw generateParseException(ex); } } throw new Error("Missing return statement in function"); }finally{ trace_return("ComparisonEnd"); } } final public Between BetweenEnd(ADQLOperand leftOp) throws ParseException{ trace_call("BetweenEnd"); try{ Token start, notToken = null; ADQLOperand min, max; switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case NOT:{ notToken = jj_consume_token(NOT); break; } default: jj_la1[72] = jj_gen;; } start = jj_consume_token(BETWEEN); min = ValueExpression(); jj_consume_token(AND); max = ValueExpression(); try{ Between bet = queryFactory.createBetween((notToken != null), leftOp, min, max); if (notToken != null) start = notToken; bet.setPosition(new TextPosition(start.beginLine, start.beginColumn, max.getPosition().endLine, max.getPosition().endColumn)); { if ("" != null) return bet; } }catch(Exception ex){ { if (true) throw generateParseException(ex); } } throw new Error("Missing return statement in function"); }finally{ trace_return("BetweenEnd"); } } final public In InEnd(ADQLOperand leftOp) throws ParseException{ trace_call("InEnd"); try{ Token not = null, start; ADQLQuery q = null; ADQLOperand item; Vector items = new Vector(); switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case NOT:{ not = jj_consume_token(NOT); break; } default: jj_la1[73] = jj_gen;; } start = jj_consume_token(IN); if (jj_2_18(2)){ q = SubQueryExpression(); }else{ switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case LEFT_PAR:{ jj_consume_token(LEFT_PAR); item = ValueExpression(); items.add(item); label_11: while(true){ switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case COMMA:{ ; break; } default: jj_la1[74] = jj_gen; break label_11; } jj_consume_token(COMMA); item = ValueExpression(); items.add(item); } jj_consume_token(RIGHT_PAR); break; } default: jj_la1[75] = jj_gen; jj_consume_token(-1); throw new ParseException(); } } try{ In in; start = (not != null) ? not : start; if (q != null){ in = queryFactory.createIn(leftOp, q, not != null); in.setPosition(new TextPosition(start.beginLine, start.beginColumn, q.getPosition().endLine, q.getPosition().endColumn)); }else{ ADQLOperand[] list = new ADQLOperand[items.size()]; int i = 0; for(ADQLOperand op : items) list[i++] = op; in = queryFactory.createIn(leftOp, list, not != null); in.setPosition(new TextPosition(start.beginLine, start.beginColumn, list[list.length - 1].getPosition().endLine, list[list.length - 1].getPosition().endColumn)); } { if ("" != null) return in; } }catch(Exception ex){ { if (true) throw generateParseException(ex); } } throw new Error("Missing return statement in function"); }finally{ trace_return("InEnd"); } } /* ************* */ /* SQL FUNCTIONS */ /* ************* */ final public SQLFunction SqlFunction() throws ParseException{ trace_call("SqlFunction"); try{ Token fct, all = null, distinct = null, end; ADQLOperand op = null; SQLFunction funct = null; try{ switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case COUNT:{ fct = jj_consume_token(COUNT); jj_consume_token(LEFT_PAR); switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case QUANTIFIER:{ distinct = jj_consume_token(QUANTIFIER); break; } default: jj_la1[76] = jj_gen;; } switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case ASTERISK:{ all = jj_consume_token(ASTERISK); break; } case LEFT_PAR: case PLUS: case MINUS: case AVG: case MAX: case MIN: case SUM: case COUNT: case BOX: case CENTROID: case CIRCLE: case POINT: case POLYGON: case REGION: case CONTAINS: case INTERSECTS: case AREA: case COORD1: case COORD2: case COORDSYS: case DISTANCE: case ABS: case CEILING: case DEGREES: case EXP: case FLOOR: case LOG: case LOG10: case MOD: case PI: case POWER: case RADIANS: case RAND: case ROUND: case SQRT: case TRUNCATE: case ACOS: case ASIN: case ATAN: case ATAN2: case COS: case COT: case SIN: case TAN: case STRING_LITERAL: case DELIMITED_IDENTIFIER: case REGULAR_IDENTIFIER: case SCIENTIFIC_NUMBER: case UNSIGNED_FLOAT: case UNSIGNED_INTEGER:{ op = ValueExpression(); break; } default: jj_la1[77] = jj_gen; jj_consume_token(-1); throw new ParseException(); } end = jj_consume_token(RIGHT_PAR); funct = queryFactory.createSQLFunction((all != null) ? SQLFunctionType.COUNT_ALL : SQLFunctionType.COUNT, op, distinct != null && distinct.image.equalsIgnoreCase("distinct")); funct.setPosition(new TextPosition(fct, end)); break; } case AVG: case MAX: case MIN: case SUM:{ switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case AVG:{ fct = jj_consume_token(AVG); break; } case MAX:{ fct = jj_consume_token(MAX); break; } case MIN:{ fct = jj_consume_token(MIN); break; } case SUM:{ fct = jj_consume_token(SUM); break; } default: jj_la1[78] = jj_gen; jj_consume_token(-1); throw new ParseException(); } jj_consume_token(LEFT_PAR); switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case QUANTIFIER:{ distinct = jj_consume_token(QUANTIFIER); break; } default: jj_la1[79] = jj_gen;; } op = ValueExpression(); end = jj_consume_token(RIGHT_PAR); funct = queryFactory.createSQLFunction(SQLFunctionType.valueOf(fct.image.toUpperCase()), op, distinct != null && distinct.image.equalsIgnoreCase("distinct")); funct.setPosition(new TextPosition(fct, end)); break; } default: jj_la1[80] = jj_gen; jj_consume_token(-1); throw new ParseException(); } }catch(Exception ex){ { if (true) throw generateParseException(ex); } } { if ("" != null) return funct; } throw new Error("Missing return statement in function"); }finally{ trace_return("SqlFunction"); } } /* ************** */ /* ADQL FUNCTIONS */ /* ************** */ final public ADQLOperand[] Coordinates() throws ParseException{ trace_call("Coordinates"); try{ ADQLOperand[] ops = new ADQLOperand[2]; ops[0] = NumericExpression(); jj_consume_token(COMMA); ops[1] = NumericExpression(); { if ("" != null) return ops; } throw new Error("Missing return statement in function"); }finally{ trace_return("Coordinates"); } } final public GeometryFunction GeometryFunction() throws ParseException{ trace_call("GeometryFunction"); try{ Token fct = null, end; GeometryValue gvf1, gvf2; GeometryValue gvp1, gvp2; GeometryFunction gf = null; PointFunction p1 = null, p2 = null; ADQLColumn col1 = null, col2 = null; try{ switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case CONTAINS: case INTERSECTS:{ switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case CONTAINS:{ fct = jj_consume_token(CONTAINS); break; } case INTERSECTS:{ fct = jj_consume_token(INTERSECTS); break; } default: jj_la1[81] = jj_gen; jj_consume_token(-1); throw new ParseException(); } jj_consume_token(LEFT_PAR); gvf1 = GeometryExpression(); jj_consume_token(COMMA); gvf2 = GeometryExpression(); end = jj_consume_token(RIGHT_PAR); if (fct.image.equalsIgnoreCase("contains")) gf = queryFactory.createContains(gvf1, gvf2); else gf = queryFactory.createIntersects(gvf1, gvf2); break; } case AREA:{ fct = jj_consume_token(AREA); jj_consume_token(LEFT_PAR); gvf1 = GeometryExpression(); end = jj_consume_token(RIGHT_PAR); gf = queryFactory.createArea(gvf1); break; } case COORD1:{ fct = jj_consume_token(COORD1); jj_consume_token(LEFT_PAR); switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case POINT:{ p1 = Point(); gf = queryFactory.createCoord1(p1); break; } case DELIMITED_IDENTIFIER: case REGULAR_IDENTIFIER:{ col1 = Column(); col1.setExpectedType('G'); gf = queryFactory.createCoord1(col1); break; } default: jj_la1[82] = jj_gen; jj_consume_token(-1); throw new ParseException(); } end = jj_consume_token(RIGHT_PAR); break; } case COORD2:{ fct = jj_consume_token(COORD2); jj_consume_token(LEFT_PAR); switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case POINT:{ p1 = Point(); gf = queryFactory.createCoord2(p1); break; } case DELIMITED_IDENTIFIER: case REGULAR_IDENTIFIER:{ col1 = Column(); col1.setExpectedType('G'); gf = queryFactory.createCoord2(col1); break; } default: jj_la1[83] = jj_gen; jj_consume_token(-1); throw new ParseException(); } end = jj_consume_token(RIGHT_PAR); break; } case DISTANCE:{ fct = jj_consume_token(DISTANCE); jj_consume_token(LEFT_PAR); switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case POINT:{ p1 = Point(); break; } case DELIMITED_IDENTIFIER: case REGULAR_IDENTIFIER:{ col1 = Column(); break; } default: jj_la1[84] = jj_gen; jj_consume_token(-1); throw new ParseException(); } if (p1 != null) gvp1 = new GeometryValue(p1); else{ col1.setExpectedType('G'); gvp1 = new GeometryValue(col1); } jj_consume_token(COMMA); switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case POINT:{ p2 = Point(); break; } case DELIMITED_IDENTIFIER: case REGULAR_IDENTIFIER:{ col2 = Column(); break; } default: jj_la1[85] = jj_gen; jj_consume_token(-1); throw new ParseException(); } if (p2 != null) gvp2 = new GeometryValue(p2); else{ col2.setExpectedType('G'); gvp2 = new GeometryValue(col2); } end = jj_consume_token(RIGHT_PAR); gf = queryFactory.createDistance(gvp1, gvp2); break; } default: jj_la1[86] = jj_gen; jj_consume_token(-1); throw new ParseException(); } }catch(Exception ex){ { if (true) throw generateParseException(ex); } } gf.setPosition(new TextPosition(fct, end)); { if ("" != null) return gf; } throw new Error("Missing return statement in function"); }finally{ trace_return("GeometryFunction"); } } final public ADQLOperand CoordinateSystem() throws ParseException{ trace_call("CoordinateSystem"); try{ ADQLOperand coordSys = null; coordSys = StringExpression(); { if ("" != null) return coordSys; } throw new Error("Missing return statement in function"); }finally{ trace_return("CoordinateSystem"); } } final public GeometryFunction GeometryValueFunction() throws ParseException{ trace_call("GeometryValueFunction"); try{ Token fct = null, end = null; ADQLOperand coordSys; ADQLOperand width, height; ADQLOperand[] coords, tmp; Vector vCoords; ADQLOperand op = null; GeometryValue gvf = null; GeometryFunction gf = null; try{ switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case BOX:{ fct = jj_consume_token(BOX); jj_consume_token(LEFT_PAR); coordSys = CoordinateSystem(); jj_consume_token(COMMA); coords = Coordinates(); jj_consume_token(COMMA); width = NumericExpression(); jj_consume_token(COMMA); height = NumericExpression(); end = jj_consume_token(RIGHT_PAR); gf = queryFactory.createBox(coordSys, coords[0], coords[1], width, height); break; } case CENTROID:{ fct = jj_consume_token(CENTROID); jj_consume_token(LEFT_PAR); gvf = GeometryExpression(); end = jj_consume_token(RIGHT_PAR); gf = queryFactory.createCentroid(gvf); break; } case CIRCLE:{ fct = jj_consume_token(CIRCLE); jj_consume_token(LEFT_PAR); coordSys = CoordinateSystem(); jj_consume_token(COMMA); coords = Coordinates(); jj_consume_token(COMMA); width = NumericExpression(); end = jj_consume_token(RIGHT_PAR); gf = queryFactory.createCircle(coordSys, coords[0], coords[1], width); break; } case POINT:{ gf = Point(); break; } case POLYGON:{ fct = jj_consume_token(POLYGON); jj_consume_token(LEFT_PAR); coordSys = CoordinateSystem(); vCoords = new Vector(); jj_consume_token(COMMA); tmp = Coordinates(); vCoords.add(tmp[0]); vCoords.add(tmp[1]); jj_consume_token(COMMA); tmp = Coordinates(); vCoords.add(tmp[0]); vCoords.add(tmp[1]); jj_consume_token(COMMA); tmp = Coordinates(); vCoords.add(tmp[0]); vCoords.add(tmp[1]); label_12: while(true){ switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case COMMA:{ ; break; } default: jj_la1[87] = jj_gen; break label_12; } jj_consume_token(COMMA); tmp = Coordinates(); vCoords.add(tmp[0]); vCoords.add(tmp[1]); } end = jj_consume_token(RIGHT_PAR); gf = queryFactory.createPolygon(coordSys, vCoords); break; } case REGION:{ fct = jj_consume_token(REGION); jj_consume_token(LEFT_PAR); op = StringExpression(); end = jj_consume_token(RIGHT_PAR); gf = queryFactory.createRegion(op); break; } default: jj_la1[88] = jj_gen; jj_consume_token(-1); throw new ParseException(); } }catch(Exception ex){ { if (true) throw generateParseException(ex); } } if (fct != null && end != null) // = !(gf instanceof Point) gf.setPosition(new TextPosition(fct, end)); { if ("" != null) return gf; } throw new Error("Missing return statement in function"); }finally{ trace_return("GeometryValueFunction"); } } final public PointFunction Point() throws ParseException{ trace_call("Point"); try{ Token start, end; ADQLOperand coordSys; ADQLOperand[] coords; start = jj_consume_token(POINT); jj_consume_token(LEFT_PAR); coordSys = CoordinateSystem(); jj_consume_token(COMMA); coords = Coordinates(); end = jj_consume_token(RIGHT_PAR); try{ PointFunction pf = queryFactory.createPoint(coordSys, coords[0], coords[1]); pf.setPosition(new TextPosition(start, end)); { if ("" != null) return pf; } }catch(Exception ex){ { if (true) throw generateParseException(ex); } } throw new Error("Missing return statement in function"); }finally{ trace_return("Point"); } } final public GeometryFunction ExtractCoordSys() throws ParseException{ trace_call("ExtractCoordSys"); try{ Token start, end; GeometryValue gvf; start = jj_consume_token(COORDSYS); jj_consume_token(LEFT_PAR); gvf = GeometryExpression(); end = jj_consume_token(RIGHT_PAR); try{ GeometryFunction gf = queryFactory.createExtractCoordSys(gvf); gf.setPosition(new TextPosition(start, end)); { if ("" != null) return gf; } }catch(Exception ex){ { if (true) throw generateParseException(ex); } } throw new Error("Missing return statement in function"); }finally{ trace_return("ExtractCoordSys"); } } /* ***************** */ /* NUMERIC FUNCTIONS */ /* ***************** */ final public ADQLFunction NumericFunction() throws ParseException{ trace_call("NumericFunction"); try{ ADQLFunction fct; switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case ABS: case CEILING: case DEGREES: case EXP: case FLOOR: case LOG: case LOG10: case MOD: case PI: case POWER: case RADIANS: case RAND: case ROUND: case SQRT: case TRUNCATE:{ fct = MathFunction(); break; } case ACOS: case ASIN: case ATAN: case ATAN2: case COS: case COT: case SIN: case TAN:{ fct = TrigFunction(); break; } case CONTAINS: case INTERSECTS: case AREA: case COORD1: case COORD2: case DISTANCE:{ fct = GeometryFunction(); break; } case REGULAR_IDENTIFIER:{ fct = UserDefinedFunction(); ((UserDefinedFunction)fct).setExpectedType('N'); break; } default: jj_la1[89] = jj_gen; jj_consume_token(-1); throw new ParseException(); } { if ("" != null) return fct; } throw new Error("Missing return statement in function"); }finally{ trace_return("NumericFunction"); } } final public MathFunction MathFunction() throws ParseException{ trace_call("MathFunction"); try{ Token fct = null, end; ADQLOperand param1 = null, param2 = null; NumericConstant integerValue = null; try{ switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case ABS:{ fct = jj_consume_token(ABS); jj_consume_token(LEFT_PAR); param1 = NumericExpression(); end = jj_consume_token(RIGHT_PAR); break; } case CEILING:{ fct = jj_consume_token(CEILING); jj_consume_token(LEFT_PAR); param1 = NumericExpression(); end = jj_consume_token(RIGHT_PAR); break; } case DEGREES:{ fct = jj_consume_token(DEGREES); jj_consume_token(LEFT_PAR); param1 = NumericExpression(); end = jj_consume_token(RIGHT_PAR); break; } case EXP:{ fct = jj_consume_token(EXP); jj_consume_token(LEFT_PAR); param1 = NumericExpression(); end = jj_consume_token(RIGHT_PAR); break; } case FLOOR:{ fct = jj_consume_token(FLOOR); jj_consume_token(LEFT_PAR); param1 = NumericExpression(); end = jj_consume_token(RIGHT_PAR); break; } case LOG:{ fct = jj_consume_token(LOG); jj_consume_token(LEFT_PAR); param1 = NumericExpression(); end = jj_consume_token(RIGHT_PAR); break; } case LOG10:{ fct = jj_consume_token(LOG10); jj_consume_token(LEFT_PAR); param1 = NumericExpression(); end = jj_consume_token(RIGHT_PAR); break; } case MOD:{ fct = jj_consume_token(MOD); jj_consume_token(LEFT_PAR); param1 = NumericExpression(); jj_consume_token(COMMA); param2 = NumericExpression(); end = jj_consume_token(RIGHT_PAR); break; } case PI:{ fct = jj_consume_token(PI); jj_consume_token(LEFT_PAR); end = jj_consume_token(RIGHT_PAR); break; } case POWER:{ fct = jj_consume_token(POWER); jj_consume_token(LEFT_PAR); param1 = NumericExpression(); jj_consume_token(COMMA); param2 = NumericExpression(); end = jj_consume_token(RIGHT_PAR); break; } case RADIANS:{ fct = jj_consume_token(RADIANS); jj_consume_token(LEFT_PAR); param1 = NumericExpression(); end = jj_consume_token(RIGHT_PAR); break; } case RAND:{ fct = jj_consume_token(RAND); jj_consume_token(LEFT_PAR); switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case LEFT_PAR: case PLUS: case MINUS: case AVG: case MAX: case MIN: case SUM: case COUNT: case CONTAINS: case INTERSECTS: case AREA: case COORD1: case COORD2: case DISTANCE: case ABS: case CEILING: case DEGREES: case EXP: case FLOOR: case LOG: case LOG10: case MOD: case PI: case POWER: case RADIANS: case RAND: case ROUND: case SQRT: case TRUNCATE: case ACOS: case ASIN: case ATAN: case ATAN2: case COS: case COT: case SIN: case TAN: case DELIMITED_IDENTIFIER: case REGULAR_IDENTIFIER: case SCIENTIFIC_NUMBER: case UNSIGNED_FLOAT: case UNSIGNED_INTEGER:{ param1 = NumericExpression(); break; } default: jj_la1[90] = jj_gen;; } end = jj_consume_token(RIGHT_PAR); break; } case ROUND:{ fct = jj_consume_token(ROUND); jj_consume_token(LEFT_PAR); param1 = NumericExpression(); switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case COMMA:{ jj_consume_token(COMMA); param2 = SignedInteger(); break; } default: jj_la1[91] = jj_gen;; } end = jj_consume_token(RIGHT_PAR); break; } case SQRT:{ fct = jj_consume_token(SQRT); jj_consume_token(LEFT_PAR); param1 = NumericExpression(); end = jj_consume_token(RIGHT_PAR); break; } case TRUNCATE:{ fct = jj_consume_token(TRUNCATE); jj_consume_token(LEFT_PAR); param1 = NumericExpression(); switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case COMMA:{ jj_consume_token(COMMA); param2 = SignedInteger(); break; } default: jj_la1[92] = jj_gen;; } end = jj_consume_token(RIGHT_PAR); break; } default: jj_la1[93] = jj_gen; jj_consume_token(-1); throw new ParseException(); } MathFunction mf = queryFactory.createMathFunction(MathFunctionType.valueOf(fct.image.toUpperCase()), param1, param2); mf.setPosition(new TextPosition(fct, end)); { if ("" != null) return mf; } }catch(Exception ex){ { if (true) throw generateParseException(ex); } } throw new Error("Missing return statement in function"); }finally{ trace_return("MathFunction"); } } final public MathFunction TrigFunction() throws ParseException{ trace_call("TrigFunction"); try{ Token fct = null, end; ADQLOperand param1 = null, param2 = null; switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case ACOS:{ fct = jj_consume_token(ACOS); jj_consume_token(LEFT_PAR); param1 = NumericExpression(); end = jj_consume_token(RIGHT_PAR); break; } case ASIN:{ fct = jj_consume_token(ASIN); jj_consume_token(LEFT_PAR); param1 = NumericExpression(); end = jj_consume_token(RIGHT_PAR); break; } case ATAN:{ fct = jj_consume_token(ATAN); jj_consume_token(LEFT_PAR); param1 = NumericExpression(); end = jj_consume_token(RIGHT_PAR); break; } case ATAN2:{ fct = jj_consume_token(ATAN2); jj_consume_token(LEFT_PAR); param1 = NumericExpression(); jj_consume_token(COMMA); param2 = NumericExpression(); end = jj_consume_token(RIGHT_PAR); break; } case COS:{ fct = jj_consume_token(COS); jj_consume_token(LEFT_PAR); param1 = NumericExpression(); end = jj_consume_token(RIGHT_PAR); break; } case COT:{ fct = jj_consume_token(COT); jj_consume_token(LEFT_PAR); param1 = NumericExpression(); end = jj_consume_token(RIGHT_PAR); break; } case SIN:{ fct = jj_consume_token(SIN); jj_consume_token(LEFT_PAR); param1 = NumericExpression(); end = jj_consume_token(RIGHT_PAR); break; } case TAN:{ fct = jj_consume_token(TAN); jj_consume_token(LEFT_PAR); param1 = NumericExpression(); end = jj_consume_token(RIGHT_PAR); break; } default: jj_la1[94] = jj_gen; jj_consume_token(-1); throw new ParseException(); } try{ MathFunction mf = queryFactory.createMathFunction(MathFunctionType.valueOf(fct.image.toUpperCase()), param1, param2); mf.setPosition(new TextPosition(fct, end)); { if ("" != null) return mf; } }catch(Exception ex){ { if (true) throw generateParseException(ex); } } throw new Error("Missing return statement in function"); }finally{ trace_return("TrigFunction"); } } final public UserDefinedFunction UserDefinedFunction() throws ParseException{ trace_call("UserDefinedFunction"); try{ Token fct, end; Vector params = new Vector(); ADQLOperand op; fct = jj_consume_token(REGULAR_IDENTIFIER); jj_consume_token(LEFT_PAR); switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case LEFT_PAR: case PLUS: case MINUS: case AVG: case MAX: case MIN: case SUM: case COUNT: case BOX: case CENTROID: case CIRCLE: case POINT: case POLYGON: case REGION: case CONTAINS: case INTERSECTS: case AREA: case COORD1: case COORD2: case COORDSYS: case DISTANCE: case ABS: case CEILING: case DEGREES: case EXP: case FLOOR: case LOG: case LOG10: case MOD: case PI: case POWER: case RADIANS: case RAND: case ROUND: case SQRT: case TRUNCATE: case ACOS: case ASIN: case ATAN: case ATAN2: case COS: case COT: case SIN: case TAN: case STRING_LITERAL: case DELIMITED_IDENTIFIER: case REGULAR_IDENTIFIER: case SCIENTIFIC_NUMBER: case UNSIGNED_FLOAT: case UNSIGNED_INTEGER:{ op = ValueExpression(); params.add(op); label_13: while(true){ switch((jj_ntk == -1) ? jj_ntk_f() : jj_ntk){ case COMMA:{ ; break; } default: jj_la1[95] = jj_gen; break label_13; } jj_consume_token(COMMA); op = ValueExpression(); params.add(op); } break; } default: jj_la1[96] = jj_gen;; } end = jj_consume_token(RIGHT_PAR); //System.out.println("INFO [ADQLParser]: \""+fct.image+"\" (from line "+fct.beginLine+" and column "+fct.beginColumn+" to line "+token.endLine+" and column "+(token.endColumn+1)+") is considered as an user defined function !"); try{ // Build the parameters list: ADQLOperand[] parameters = new ADQLOperand[params.size()]; for(int i = 0; i < params.size(); i++) parameters[i] = params.get(i); // Create the UDF function: UserDefinedFunction udf = queryFactory.createUserDefinedFunction(fct.image, parameters); udf.setPosition(new TextPosition(fct, end)); { if ("" != null) return udf; } }catch(UnsupportedOperationException uoe){ /* This catch clause is just for backward compatibility: * if the createUserDefinedFunction(...) is overridden and * the function can not be identified a such exception may be thrown). */ { if (true) throw new ParseException(uoe.getMessage(), new TextPosition(fct, token)); } }catch(Exception ex){ { if (true) throw generateParseException(ex); } } throw new Error("Missing return statement in function"); }finally{ trace_return("UserDefinedFunction"); } } private boolean jj_2_1(int xla){ jj_la = xla; jj_lastpos = jj_scanpos = token; try{ return !jj_3_1(); }catch(LookaheadSuccess ls){ return true; }finally{ jj_save(0, xla); } } private boolean jj_2_2(int xla){ jj_la = xla; jj_lastpos = jj_scanpos = token; try{ return !jj_3_2(); }catch(LookaheadSuccess ls){ return true; }finally{ jj_save(1, xla); } } private boolean jj_2_3(int xla){ jj_la = xla; jj_lastpos = jj_scanpos = token; try{ return !jj_3_3(); }catch(LookaheadSuccess ls){ return true; }finally{ jj_save(2, xla); } } private boolean jj_2_4(int xla){ jj_la = xla; jj_lastpos = jj_scanpos = token; try{ return !jj_3_4(); }catch(LookaheadSuccess ls){ return true; }finally{ jj_save(3, xla); } } private boolean jj_2_5(int xla){ jj_la = xla; jj_lastpos = jj_scanpos = token; try{ return !jj_3_5(); }catch(LookaheadSuccess ls){ return true; }finally{ jj_save(4, xla); } } private boolean jj_2_6(int xla){ jj_la = xla; jj_lastpos = jj_scanpos = token; try{ return !jj_3_6(); }catch(LookaheadSuccess ls){ return true; }finally{ jj_save(5, xla); } } private boolean jj_2_7(int xla){ jj_la = xla; jj_lastpos = jj_scanpos = token; try{ return !jj_3_7(); }catch(LookaheadSuccess ls){ return true; }finally{ jj_save(6, xla); } } private boolean jj_2_8(int xla){ jj_la = xla; jj_lastpos = jj_scanpos = token; try{ return !jj_3_8(); }catch(LookaheadSuccess ls){ return true; }finally{ jj_save(7, xla); } } private boolean jj_2_9(int xla){ jj_la = xla; jj_lastpos = jj_scanpos = token; try{ return !jj_3_9(); }catch(LookaheadSuccess ls){ return true; }finally{ jj_save(8, xla); } } private boolean jj_2_10(int xla){ jj_la = xla; jj_lastpos = jj_scanpos = token; try{ return !jj_3_10(); }catch(LookaheadSuccess ls){ return true; }finally{ jj_save(9, xla); } } private boolean jj_2_11(int xla){ jj_la = xla; jj_lastpos = jj_scanpos = token; try{ return !jj_3_11(); }catch(LookaheadSuccess ls){ return true; }finally{ jj_save(10, xla); } } private boolean jj_2_12(int xla){ jj_la = xla; jj_lastpos = jj_scanpos = token; try{ return !jj_3_12(); }catch(LookaheadSuccess ls){ return true; }finally{ jj_save(11, xla); } } private boolean jj_2_13(int xla){ jj_la = xla; jj_lastpos = jj_scanpos = token; try{ return !jj_3_13(); }catch(LookaheadSuccess ls){ return true; }finally{ jj_save(12, xla); } } private boolean jj_2_14(int xla){ jj_la = xla; jj_lastpos = jj_scanpos = token; try{ return !jj_3_14(); }catch(LookaheadSuccess ls){ return true; }finally{ jj_save(13, xla); } } private boolean jj_2_15(int xla){ jj_la = xla; jj_lastpos = jj_scanpos = token; try{ return !jj_3_15(); }catch(LookaheadSuccess ls){ return true; }finally{ jj_save(14, xla); } } private boolean jj_2_16(int xla){ jj_la = xla; jj_lastpos = jj_scanpos = token; try{ return !jj_3_16(); }catch(LookaheadSuccess ls){ return true; }finally{ jj_save(15, xla); } } private boolean jj_2_17(int xla){ jj_la = xla; jj_lastpos = jj_scanpos = token; try{ return !jj_3_17(); }catch(LookaheadSuccess ls){ return true; }finally{ jj_save(16, xla); } } private boolean jj_2_18(int xla){ jj_la = xla; jj_lastpos = jj_scanpos = token; try{ return !jj_3_18(); }catch(LookaheadSuccess ls){ return true; }finally{ jj_save(17, xla); } } private boolean jj_3R_128(){ Token xsp; xsp = jj_scanpos; if (jj_scan_token(99)){ jj_scanpos = xsp; if (jj_scan_token(100)){ jj_scanpos = xsp; if (jj_scan_token(101)) return true; } } return false; } private boolean jj_3R_129(){ Token xsp; xsp = jj_scanpos; if (jj_3R_135()){ jj_scanpos = xsp; if (jj_3R_136()) return true; } return false; } private boolean jj_3R_113(){ if (jj_scan_token(LEFT)) return true; return false; } private boolean jj_3R_73(){ Token xsp; xsp = jj_scanpos; if (jj_3R_113()){ jj_scanpos = xsp; if (jj_3R_114()){ jj_scanpos = xsp; if (jj_3R_115()) return true; } } 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; } private boolean jj_3_18(){ if (jj_3R_16()) return true; return false; } private boolean jj_3R_55(){ Token xsp; xsp = jj_scanpos; if (jj_scan_token(25)){ jj_scanpos = xsp; if (jj_3R_74()) return true; } return false; } private boolean jj_3R_35(){ Token xsp; xsp = jj_scanpos; if (jj_3R_55()) jj_scanpos = xsp; if (jj_scan_token(JOIN)) return true; if (jj_3R_56()) return true; return false; } private boolean jj_3R_34(){ if (jj_scan_token(NATURAL)) return true; Token xsp; xsp = jj_scanpos; if (jj_3R_54()) jj_scanpos = xsp; if (jj_scan_token(JOIN)) return true; return false; } private boolean jj_3R_28(){ Token xsp; xsp = jj_scanpos; if (jj_scan_token(36)) jj_scanpos = xsp; if (jj_scan_token(BETWEEN)) return true; if (jj_3R_51()) return true; return false; } private boolean jj_3_15(){ if (jj_3R_28()) return true; return false; } private boolean jj_3R_17(){ Token xsp; xsp = jj_scanpos; if (jj_3R_34()){ jj_scanpos = xsp; if (jj_3R_35()) return true; } return false; } private boolean jj_3_17(){ if (jj_3R_29()) return true; Token xsp; xsp = jj_scanpos; if (jj_scan_token(36)) jj_scanpos = xsp; if (jj_scan_token(LIKE)) return true; return false; } private boolean jj_3R_76(){ if (jj_scan_token(LEFT_PAR)) return true; return false; } private boolean jj_3_3(){ if (jj_3R_17()) return true; return false; } private boolean jj_3_16(){ if (jj_3R_22()) return true; if (jj_scan_token(IS)) return true; return false; } private boolean jj_3_2(){ if (jj_3R_16()) return true; return false; } private boolean jj_3R_75(){ if (jj_3R_79()) return true; return false; } private boolean jj_3R_134(){ if (jj_scan_token(COMMA)) return true; if (jj_3R_51()) return true; return false; } private boolean jj_3R_27(){ if (jj_3R_51()) return true; return false; } private boolean jj_3R_56(){ Token xsp; xsp = jj_scanpos; if (jj_3R_75()){ jj_scanpos = xsp; if (jj_3_2()){ jj_scanpos = xsp; if (jj_3R_76()) return true; } } return false; } private boolean jj_3_14(){ Token xsp; xsp = jj_scanpos; if (jj_scan_token(42)){ jj_scanpos = xsp; if (jj_3R_27()) return true; } return false; } private boolean jj_3R_60(){ if (jj_scan_token(DOT)) return true; if (jj_3R_79()) return true; return false; } private boolean jj_3R_120(){ if (jj_3R_51()) return true; Token xsp; while(true){ xsp = jj_scanpos; if (jj_3R_134()){ jj_scanpos = xsp; break; } } return false; } private boolean jj_3R_22(){ if (jj_3R_43()) return true; return false; } private boolean jj_3R_150(){ if (jj_scan_token(COMMA)) return true; if (jj_3R_153()) return true; return false; } private boolean jj_3R_149(){ if (jj_scan_token(COMMA)) return true; if (jj_3R_153()) return true; return false; } private boolean jj_3R_43(){ if (jj_3R_14()) return true; Token xsp; xsp = jj_scanpos; if (jj_3R_60()) jj_scanpos = xsp; return false; } private boolean jj_3R_127(){ if (jj_scan_token(DOT)) return true; if (jj_3R_14()) return true; return false; } private boolean jj_3R_126(){ if (jj_scan_token(DOT)) return true; if (jj_3R_14()) return true; return false; } private boolean jj_3R_79(){ if (jj_3R_14()) return true; Token xsp; xsp = jj_scanpos; if (jj_3R_126()) jj_scanpos = xsp; xsp = jj_scanpos; if (jj_3R_127()) jj_scanpos = xsp; return false; } private boolean jj_3R_31(){ if (jj_scan_token(DELIMITED_IDENTIFIER)) return true; return false; } private boolean jj_3R_133(){ if (jj_3R_21()) return true; return false; } private boolean jj_3R_30(){ if (jj_scan_token(REGULAR_IDENTIFIER)) return true; return false; } private boolean jj_3R_26(){ if (jj_scan_token(REGULAR_IDENTIFIER)) return true; if (jj_scan_token(LEFT_PAR)) return true; Token xsp; xsp = jj_scanpos; if (jj_3R_120()) jj_scanpos = xsp; if (jj_scan_token(RIGHT_PAR)) return true; return false; } private boolean jj_3R_141(){ if (jj_3R_112()) return true; return false; } private boolean jj_3R_14(){ Token xsp; xsp = jj_scanpos; if (jj_3R_30()){ jj_scanpos = xsp; if (jj_3R_31()) return true; } return false; } private boolean jj_3R_132(){ if (jj_3R_22()) return true; return false; } private boolean jj_3R_119(){ Token xsp; xsp = jj_scanpos; if (jj_3R_132()){ jj_scanpos = xsp; if (jj_3R_133()) return true; } return false; } private boolean jj_3R_106(){ if (jj_scan_token(TAN)) return true; if (jj_scan_token(LEFT_PAR)) return true; if (jj_3R_112()) return true; if (jj_scan_token(RIGHT_PAR)) return true; return false; } private boolean jj_3R_105(){ if (jj_scan_token(SIN)) return true; if (jj_scan_token(LEFT_PAR)) return true; if (jj_3R_112()) return true; if (jj_scan_token(RIGHT_PAR)) return true; return false; } private boolean jj_3R_104(){ if (jj_scan_token(COT)) return true; if (jj_scan_token(LEFT_PAR)) return true; if (jj_3R_112()) return true; if (jj_scan_token(RIGHT_PAR)) return true; return false; } private boolean jj_3R_58(){ if (jj_3R_78()) return true; return false; } private boolean jj_3_13(){ if (jj_3R_26()) return true; return false; } private boolean jj_3R_103(){ if (jj_scan_token(COS)) return true; if (jj_scan_token(LEFT_PAR)) return true; if (jj_3R_112()) return true; if (jj_scan_token(RIGHT_PAR)) return true; return false; } private boolean jj_3R_102(){ if (jj_scan_token(ATAN2)) return true; if (jj_scan_token(LEFT_PAR)) return true; if (jj_3R_112()) return true; if (jj_scan_token(COMMA)) return true; if (jj_3R_112()) return true; if (jj_scan_token(RIGHT_PAR)) return true; return false; } private boolean jj_3R_57(){ if (jj_3R_77()) return true; return false; } private boolean jj_3R_101(){ if (jj_scan_token(ATAN)) return true; if (jj_scan_token(LEFT_PAR)) return true; if (jj_3R_112()) return true; if (jj_scan_token(RIGHT_PAR)) return true; return false; } private boolean jj_3R_36(){ Token xsp; xsp = jj_scanpos; if (jj_3R_57()){ jj_scanpos = xsp; if (jj_3_13()){ jj_scanpos = xsp; if (jj_3R_58()) return true; } } return false; } private boolean jj_3R_100(){ if (jj_scan_token(ASIN)) return true; if (jj_scan_token(LEFT_PAR)) return true; if (jj_3R_112()) return true; if (jj_scan_token(RIGHT_PAR)) return true; return false; } private boolean jj_3R_99(){ if (jj_scan_token(ACOS)) return true; if (jj_scan_token(LEFT_PAR)) return true; if (jj_3R_112()) return true; if (jj_scan_token(RIGHT_PAR)) return true; return false; } private boolean jj_3R_64(){ Token xsp; xsp = jj_scanpos; if (jj_3R_99()){ jj_scanpos = xsp; if (jj_3R_100()){ jj_scanpos = xsp; if (jj_3R_101()){ jj_scanpos = xsp; if (jj_3R_102()){ jj_scanpos = xsp; if (jj_3R_103()){ jj_scanpos = xsp; if (jj_3R_104()){ jj_scanpos = xsp; if (jj_3R_105()){ jj_scanpos = xsp; if (jj_3R_106()) return true; } } } } } } } return false; } private boolean jj_3R_46(){ if (jj_3R_62()) return true; return false; } private boolean jj_3R_98(){ if (jj_scan_token(TRUNCATE)) return true; if (jj_scan_token(LEFT_PAR)) return true; if (jj_3R_112()) return true; Token xsp; xsp = jj_scanpos; if (jj_3R_150()) jj_scanpos = xsp; if (jj_scan_token(RIGHT_PAR)) return true; return false; } private boolean jj_3R_97(){ if (jj_scan_token(SQRT)) return true; if (jj_scan_token(LEFT_PAR)) return true; if (jj_3R_112()) return true; if (jj_scan_token(RIGHT_PAR)) return true; return false; } private boolean jj_3R_96(){ if (jj_scan_token(ROUND)) return true; if (jj_scan_token(LEFT_PAR)) return true; if (jj_3R_112()) return true; Token xsp; xsp = jj_scanpos; if (jj_3R_149()) jj_scanpos = xsp; if (jj_scan_token(RIGHT_PAR)) return true; return false; } private boolean jj_3R_95(){ if (jj_scan_token(RAND)) return true; if (jj_scan_token(LEFT_PAR)) return true; Token xsp; xsp = jj_scanpos; if (jj_3R_141()) jj_scanpos = xsp; if (jj_scan_token(RIGHT_PAR)) return true; return false; } private boolean jj_3R_94(){ if (jj_scan_token(RADIANS)) return true; if (jj_scan_token(LEFT_PAR)) return true; if (jj_3R_112()) return true; if (jj_scan_token(RIGHT_PAR)) return true; return false; } private boolean jj_3R_93(){ if (jj_scan_token(POWER)) return true; if (jj_scan_token(LEFT_PAR)) return true; if (jj_3R_112()) return true; if (jj_scan_token(COMMA)) return true; if (jj_3R_112()) return true; if (jj_scan_token(RIGHT_PAR)) return true; return false; } private boolean jj_3R_92(){ if (jj_scan_token(PI)) return true; if (jj_scan_token(LEFT_PAR)) return true; if (jj_scan_token(RIGHT_PAR)) return true; return false; } private boolean jj_3R_91(){ if (jj_scan_token(MOD)) return true; if (jj_scan_token(LEFT_PAR)) return true; if (jj_3R_112()) return true; if (jj_scan_token(COMMA)) return true; if (jj_3R_112()) return true; if (jj_scan_token(RIGHT_PAR)) return true; return false; } private boolean jj_3R_90(){ if (jj_scan_token(LOG10)) return true; if (jj_scan_token(LEFT_PAR)) return true; if (jj_3R_112()) return true; if (jj_scan_token(RIGHT_PAR)) return true; return false; } private boolean jj_3R_89(){ if (jj_scan_token(LOG)) return true; if (jj_scan_token(LEFT_PAR)) return true; if (jj_3R_112()) return true; if (jj_scan_token(RIGHT_PAR)) return true; return false; } private boolean jj_3R_88(){ if (jj_scan_token(FLOOR)) return true; if (jj_scan_token(LEFT_PAR)) return true; if (jj_3R_112()) return true; if (jj_scan_token(RIGHT_PAR)) return true; return false; } private boolean jj_3R_87(){ if (jj_scan_token(EXP)) return true; if (jj_scan_token(LEFT_PAR)) return true; if (jj_3R_112()) return true; if (jj_scan_token(RIGHT_PAR)) return true; 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; if (jj_scan_token(LEFT_PAR)) return true; if (jj_3R_112()) return true; if (jj_scan_token(RIGHT_PAR)) return true; return false; } private boolean jj_3R_85(){ if (jj_scan_token(CEILING)) return true; if (jj_scan_token(LEFT_PAR)) return true; if (jj_3R_112()) return true; if (jj_scan_token(RIGHT_PAR)) return true; return false; } private boolean jj_3R_84(){ if (jj_scan_token(ABS)) return true; if (jj_scan_token(LEFT_PAR)) return true; if (jj_3R_112()) return true; if (jj_scan_token(RIGHT_PAR)) return true; return false; } private boolean jj_3R_29(){ if (jj_3R_36()) return true; Token xsp; while(true){ xsp = jj_scanpos; if (jj_3R_52()){ jj_scanpos = xsp; break; } } return false; } private boolean jj_3R_63(){ Token xsp; xsp = jj_scanpos; if (jj_3R_84()){ jj_scanpos = xsp; if (jj_3R_85()){ jj_scanpos = xsp; if (jj_3R_86()){ jj_scanpos = xsp; if (jj_3R_87()){ jj_scanpos = xsp; if (jj_3R_88()){ jj_scanpos = xsp; if (jj_3R_89()){ jj_scanpos = xsp; if (jj_3R_90()){ jj_scanpos = xsp; if (jj_3R_91()){ jj_scanpos = xsp; if (jj_3R_92()){ jj_scanpos = xsp; if (jj_3R_93()){ jj_scanpos = xsp; if (jj_3R_94()){ jj_scanpos = xsp; if (jj_3R_95()){ jj_scanpos = xsp; if (jj_3R_96()){ jj_scanpos = xsp; if (jj_3R_97()){ jj_scanpos = xsp; if (jj_3R_98()) return true; } } } } } } } } } } } } } } return false; } private boolean jj_3R_61(){ if (jj_scan_token(MINUS)) return true; return false; } private boolean jj_3R_50(){ if (jj_3R_26()) return true; return false; } private boolean jj_3R_49(){ if (jj_3R_65()) return true; return false; } private boolean jj_3R_48(){ if (jj_3R_64()) return true; return false; } private boolean jj_3R_47(){ if (jj_3R_63()) return true; return false; } private boolean jj_3R_25(){ Token xsp; xsp = jj_scanpos; if (jj_3R_47()){ jj_scanpos = xsp; if (jj_3R_48()){ jj_scanpos = xsp; if (jj_3R_49()){ jj_scanpos = xsp; if (jj_3R_50()) return true; } } } return false; } private boolean jj_3R_32(){ if (jj_3R_14()) return true; if (jj_scan_token(DOT)) return true; return false; } private boolean jj_3_12(){ if (jj_3R_25()) return true; return false; } private boolean jj_3R_45(){ Token xsp; xsp = jj_scanpos; if (jj_scan_token(9)){ jj_scanpos = xsp; if (jj_3R_61()) return true; } return false; } private boolean jj_3R_15(){ if (jj_3R_14()) return true; if (jj_scan_token(DOT)) return true; Token xsp; xsp = jj_scanpos; if (jj_3R_32()) jj_scanpos = xsp; return false; } private boolean jj_3R_137(){ Token xsp; xsp = jj_scanpos; if (jj_scan_token(11)){ jj_scanpos = xsp; if (jj_scan_token(12)) return true; } if (jj_3R_130()) return true; return false; } private boolean jj_3R_24(){ Token xsp; xsp = jj_scanpos; if (jj_3R_45()) jj_scanpos = xsp; xsp = jj_scanpos; if (jj_3_12()){ jj_scanpos = xsp; if (jj_3R_46()) return true; } return false; } private boolean jj_3R_77(){ if (jj_scan_token(COORDSYS)) return true; if (jj_scan_token(LEFT_PAR)) return true; if (jj_3R_119()) return true; if (jj_scan_token(RIGHT_PAR)) return true; return false; } private boolean jj_3R_145(){ if (jj_3R_22()) return true; return false; } private boolean jj_3R_143(){ if (jj_3R_22()) return true; return false; } private boolean jj_3R_140(){ if (jj_scan_token(COMMA)) return true; if (jj_3R_139()) return true; return false; } private boolean jj_3R_131(){ Token xsp; xsp = jj_scanpos; if (jj_scan_token(9)){ jj_scanpos = xsp; if (jj_scan_token(10)) return true; } if (jj_3R_112()) return true; return false; } private boolean jj_3R_59(){ if (jj_scan_token(POINT)) return true; if (jj_scan_token(LEFT_PAR)) return true; if (jj_3R_138()) return true; if (jj_scan_token(COMMA)) return true; if (jj_3R_139()) return true; if (jj_scan_token(RIGHT_PAR)) return true; return false; } private boolean jj_3R_130(){ if (jj_3R_24()) return true; Token xsp; xsp = jj_scanpos; if (jj_3R_137()) jj_scanpos = xsp; return false; } private boolean jj_3R_42(){ if (jj_scan_token(REGION)) return true; if (jj_scan_token(LEFT_PAR)) return true; if (jj_3R_29()) return true; if (jj_scan_token(RIGHT_PAR)) return true; return false; } private boolean jj_3R_19(){ if (jj_3R_24()) return true; Token xsp; xsp = jj_scanpos; if (jj_scan_token(9)){ jj_scanpos = xsp; if (jj_scan_token(10)){ jj_scanpos = xsp; if (jj_scan_token(11)){ jj_scanpos = xsp; if (jj_scan_token(12)) return true; } } } return false; } private boolean jj_3_1(){ if (jj_3R_14()) return true; if (jj_scan_token(DOT)) return true; Token xsp; xsp = jj_scanpos; if (jj_3R_15()) jj_scanpos = xsp; if (jj_scan_token(ASTERISK)) return true; return false; } private boolean jj_3R_20(){ if (jj_3R_36()) return true; if (jj_scan_token(CONCAT)) return true; return false; } private boolean jj_3R_41(){ if (jj_scan_token(POLYGON)) return true; if (jj_scan_token(LEFT_PAR)) return true; if (jj_3R_138()) return true; if (jj_scan_token(COMMA)) return true; if (jj_3R_139()) return true; if (jj_scan_token(COMMA)) return true; if (jj_3R_139()) return true; if (jj_scan_token(COMMA)) return true; if (jj_3R_139()) return true; Token xsp; while(true){ xsp = jj_scanpos; if (jj_3R_140()){ jj_scanpos = xsp; break; } } if (jj_scan_token(RIGHT_PAR)) return true; return false; } private boolean jj_3R_72(){ if (jj_3R_22()) return true; return false; } private boolean jj_3R_40(){ if (jj_3R_59()) return true; return false; } private boolean jj_3_10(){ if (jj_3R_23()) return true; return false; } private boolean jj_3R_112(){ if (jj_3R_130()) return true; Token xsp; xsp = jj_scanpos; if (jj_3R_131()) jj_scanpos = xsp; return false; } private boolean jj_3_9(){ if (jj_3R_22()) return true; return false; } private boolean jj_3_7(){ if (jj_scan_token(REGULAR_IDENTIFIER)) return true; if (jj_scan_token(LEFT_PAR)) return true; return false; } private boolean jj_3_6(){ if (jj_scan_token(LEFT_PAR)) return true; return false; } private boolean jj_3R_39(){ if (jj_scan_token(CIRCLE)) return true; if (jj_scan_token(LEFT_PAR)) return true; if (jj_3R_138()) return true; if (jj_scan_token(COMMA)) return true; if (jj_3R_139()) return true; if (jj_scan_token(COMMA)) return true; if (jj_3R_112()) return true; if (jj_scan_token(RIGHT_PAR)) return true; return false; } private boolean jj_3_5(){ Token xsp; xsp = jj_scanpos; if (jj_scan_token(65)){ jj_scanpos = xsp; if (jj_3R_20()) return true; } return false; } private boolean jj_3_4(){ Token xsp; xsp = jj_scanpos; if (jj_3R_18()){ jj_scanpos = xsp; if (jj_3R_19()) return true; } return false; } private boolean jj_3R_18(){ Token xsp; xsp = jj_scanpos; if (jj_scan_token(9)){ jj_scanpos = xsp; if (jj_scan_token(10)) return true; } return false; } private boolean jj_3R_38(){ if (jj_scan_token(CENTROID)) return true; if (jj_scan_token(LEFT_PAR)) return true; if (jj_3R_119()) return true; if (jj_scan_token(RIGHT_PAR)) return true; return false; } private boolean jj_3_11(){ if (jj_3R_24()) return true; return false; } private boolean jj_3R_71(){ if (jj_3R_36()) return true; return false; } private boolean jj_3R_70(){ if (jj_3R_22()) return true; return false; } private boolean jj_3_8(){ if (jj_3R_21()) return true; return false; } private boolean jj_3R_69(){ if (jj_3R_26()) return true; return false; } private boolean jj_3R_68(){ if (jj_scan_token(LEFT_PAR)) return true; if (jj_3R_51()) return true; if (jj_scan_token(RIGHT_PAR)) return true; return false; } private boolean jj_3R_37(){ if (jj_scan_token(BOX)) return true; if (jj_scan_token(LEFT_PAR)) return true; if (jj_3R_138()) return true; if (jj_scan_token(COMMA)) return true; if (jj_3R_139()) return true; if (jj_scan_token(COMMA)) return true; if (jj_3R_112()) return true; if (jj_scan_token(COMMA)) return true; if (jj_3R_112()) return true; if (jj_scan_token(RIGHT_PAR)) return true; return false; } private boolean jj_3R_67(){ if (jj_3R_29()) return true; return false; } private boolean jj_3R_66(){ if (jj_3R_112()) return true; return false; } private boolean jj_3R_152(){ if (jj_3R_22()) return true; return false; } private boolean jj_3R_53(){ if (jj_scan_token(SELECT)) return true; return false; } private boolean jj_3R_125(){ if (jj_scan_token(LEFT_PAR)) return true; if (jj_3R_51()) return true; if (jj_scan_token(RIGHT_PAR)) return true; return false; } private boolean jj_3R_144(){ if (jj_3R_59()) return true; return false; } private boolean jj_3R_21(){ Token xsp; xsp = jj_scanpos; if (jj_3R_37()){ jj_scanpos = xsp; if (jj_3R_38()){ jj_scanpos = xsp; if (jj_3R_39()){ jj_scanpos = xsp; if (jj_3R_40()){ jj_scanpos = xsp; if (jj_3R_41()){ jj_scanpos = xsp; if (jj_3R_42()) return true; } } } } } return false; } private boolean jj_3R_148(){ if (jj_3R_51()) return true; return false; } private boolean jj_3R_142(){ if (jj_3R_59()) return true; return false; } private boolean jj_3R_124(){ if (jj_3R_22()) return true; return false; } private boolean jj_3R_51(){ Token xsp; xsp = jj_scanpos; if (jj_3R_66()){ jj_scanpos = xsp; if (jj_3R_67()){ jj_scanpos = xsp; if (jj_3R_68()){ jj_scanpos = xsp; if (jj_3R_69()){ jj_scanpos = xsp; if (jj_3_8()){ jj_scanpos = xsp; if (jj_3R_70()){ jj_scanpos = xsp; if (jj_3R_71()){ jj_scanpos = xsp; if (jj_3_11()){ jj_scanpos = xsp; if (jj_3R_72()) return true; } } } } } } } } return false; } private boolean jj_3R_147(){ if (jj_3R_22()) return true; return false; } private boolean jj_3R_123(){ if (jj_3R_129()) return true; return false; } private boolean jj_3R_151(){ if (jj_3R_59()) return true; return false; } private boolean jj_3R_16(){ if (jj_scan_token(LEFT_PAR)) return true; if (jj_3R_33()) return true; return false; } private boolean jj_3R_138(){ if (jj_3R_29()) return true; return false; } private boolean jj_3R_122(){ if (jj_3R_128()) return true; return false; } private boolean jj_3R_121(){ if (jj_3R_23()) return true; return false; } private boolean jj_3R_115(){ if (jj_scan_token(FULL)) return true; return false; } private boolean jj_3R_146(){ if (jj_3R_59()) return true; return false; } private boolean jj_3R_83(){ if (jj_scan_token(LEFT_PAR)) return true; if (jj_3R_112()) return true; if (jj_scan_token(RIGHT_PAR)) return true; return false; } private boolean jj_3R_82(){ if (jj_3R_129()) return true; return false; } private boolean jj_3R_78(){ Token xsp; xsp = jj_scanpos; if (jj_3R_121()){ jj_scanpos = xsp; if (jj_3R_122()){ jj_scanpos = xsp; if (jj_3R_123()){ jj_scanpos = xsp; if (jj_3R_124()){ jj_scanpos = xsp; if (jj_3R_125()) return true; } } } } return false; } private boolean jj_3R_81(){ if (jj_3R_22()) return true; return false; } private boolean jj_3R_80(){ if (jj_3R_128()) return true; return false; } private boolean jj_3R_111(){ if (jj_scan_token(DISTANCE)) return true; if (jj_scan_token(LEFT_PAR)) return true; Token xsp; xsp = jj_scanpos; if (jj_3R_146()){ jj_scanpos = xsp; if (jj_3R_147()) return true; } if (jj_scan_token(COMMA)) return true; xsp = jj_scanpos; if (jj_3R_151()){ jj_scanpos = xsp; if (jj_3R_152()) return true; } if (jj_scan_token(RIGHT_PAR)) return true; return false; } private boolean jj_3R_110(){ if (jj_scan_token(COORD2)) return true; if (jj_scan_token(LEFT_PAR)) return true; Token xsp; xsp = jj_scanpos; if (jj_3R_144()){ jj_scanpos = xsp; if (jj_3R_145()) return true; } if (jj_scan_token(RIGHT_PAR)) return true; return false; } private boolean jj_3R_109(){ if (jj_scan_token(COORD1)) return true; if (jj_scan_token(LEFT_PAR)) return true; Token xsp; xsp = jj_scanpos; if (jj_3R_142()){ jj_scanpos = xsp; if (jj_3R_143()) return true; } if (jj_scan_token(RIGHT_PAR)) return true; return false; } private boolean jj_3R_108(){ if (jj_scan_token(AREA)) return true; if (jj_scan_token(LEFT_PAR)) return true; if (jj_3R_119()) return true; if (jj_scan_token(RIGHT_PAR)) return true; return false; } private boolean jj_3R_118(){ if (jj_scan_token(FULL)) return true; return false; } private boolean jj_3R_62(){ Token xsp; xsp = jj_scanpos; if (jj_3R_80()){ jj_scanpos = xsp; if (jj_3R_81()){ jj_scanpos = xsp; if (jj_3R_82()){ jj_scanpos = xsp; if (jj_3R_83()) return true; } } } return false; } private boolean jj_3R_33(){ if (jj_3R_53()) return true; return false; } private boolean jj_3R_107(){ Token xsp; xsp = jj_scanpos; if (jj_scan_token(60)){ jj_scanpos = xsp; if (jj_scan_token(61)) return true; } if (jj_scan_token(LEFT_PAR)) return true; if (jj_3R_119()) return true; if (jj_scan_token(COMMA)) return true; if (jj_3R_119()) return true; if (jj_scan_token(RIGHT_PAR)) return true; return false; } private boolean jj_3R_114(){ if (jj_scan_token(RIGHT)) return true; return false; } private boolean jj_3R_154(){ Token xsp; xsp = jj_scanpos; if (jj_scan_token(9)){ jj_scanpos = xsp; if (jj_scan_token(10)) return true; } return false; } private boolean jj_3R_153(){ Token xsp; xsp = jj_scanpos; if (jj_3R_154()) jj_scanpos = xsp; if (jj_scan_token(UNSIGNED_INTEGER)) return true; return false; } private boolean jj_3R_65(){ Token xsp; xsp = jj_scanpos; if (jj_3R_107()){ jj_scanpos = xsp; if (jj_3R_108()){ jj_scanpos = xsp; if (jj_3R_109()){ jj_scanpos = xsp; if (jj_3R_110()){ jj_scanpos = xsp; if (jj_3R_111()) return true; } } } } return false; } private boolean jj_3R_139(){ if (jj_3R_112()) return true; if (jj_scan_token(COMMA)) return true; if (jj_3R_112()) return true; return false; } private boolean jj_3R_136(){ Token xsp; xsp = jj_scanpos; if (jj_scan_token(49)){ jj_scanpos = xsp; if (jj_scan_token(50)){ jj_scanpos = xsp; if (jj_scan_token(51)){ jj_scanpos = xsp; if (jj_scan_token(52)) return true; } } } if (jj_scan_token(LEFT_PAR)) return true; xsp = jj_scanpos; if (jj_scan_token(20)) jj_scanpos = xsp; if (jj_3R_51()) return true; if (jj_scan_token(RIGHT_PAR)) return true; return false; } private boolean jj_3R_135(){ if (jj_scan_token(COUNT)) return true; if (jj_scan_token(LEFT_PAR)) return true; Token xsp; xsp = jj_scanpos; if (jj_scan_token(20)) jj_scanpos = xsp; xsp = jj_scanpos; if (jj_scan_token(11)){ jj_scanpos = xsp; if (jj_3R_148()) return true; } if (jj_scan_token(RIGHT_PAR)) return true; return false; } private boolean jj_3R_117(){ if (jj_scan_token(RIGHT)) return true; return false; } /** Generated Token Manager. */ public ADQLParserTokenManager token_source; SimpleCharStream jj_input_stream; /** Current token. */ public Token token; /** Next token. */ public Token jj_nt; private int jj_ntk; private Token jj_scanpos, jj_lastpos; private int jj_la; private int jj_gen; final private int[] jj_la1 = new int[97]; static private int[] jj_la1_0; static private int[] jj_la1_1; static private int[] jj_la1_2; static private int[] jj_la1_3; static{ jj_la1_init_0(); jj_la1_init_1(); jj_la1_init_2(); jj_la1_init_3(); } private static void jj_la1_init_0(){ jj_la1_0 = new int[]{0x81,0x0,0x0,0x0,0x0,0x100000,0x200000,0x40,0x0,0x0,0x800000,0x800000,0x800,0x608,0x40,0x40,0x40,0x0,0x20,0x20,0x20,0x0,0x0,0x0,0x800000,0x800000,0x800000,0x0,0x8,0x7b000000,0x38000000,0x4000000,0x3a000000,0x3a000000,0x38000000,0x4000000,0x3a000000,0x3a000000,0x40,0x80000000,0x7b000000,0x0,0x0,0x0,0x600,0x600,0x8,0x8,0x0,0x600,0x600,0x1800,0x1800,0x600,0x600,0x8,0x100,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x7e000,0x0,0x0,0x608,0x7e000,0x0,0x0,0x40,0x8,0x100000,0xe08,0x0,0x100000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x40,0x0,0x0,0x608,0x40,0x40,0x0,0x0,0x40,0x608,}; } private static void jj_la1_init_1(){ jj_la1_1 = new int[]{0x0,0x2,0x1000,0x2000,0x4000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfffe0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18000,0x18000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x3e0000,0x3e0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e0000,0x0,0x0,0x3e0000,0xfc00000,0x10,0xc,0xc,0x10,0x0,0x10,0x10,0x0,0x210,0x400,0xfffe0000,0x0,0x10,0x10,0x0,0x0,0x0,0xfffe0000,0x1e0000,0x0,0x3e0000,0x30000000,0x2000000,0x2000000,0x2000000,0x2000000,0xf0000000,0x0,0xfc00000,0xf0000000,0xf03e0000,0x0,0x0,0x0,0x0,0x0,0xfffe0000,}; } private static void jj_la1_init_2(){ jj_la1_2 = new int[]{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x23ffffff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000000,0x0,0x0,0x0,0x0,0x0,0x20000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x20000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x23ffffff,0x0,0x0,0x0,0x0,0x0,0x0,0x23ffffff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5,0x0,0x0,0x3fffffd,0x3fffffd,0x0,0x0,0x3fff8,0x3fc0000,0x0,0x23ffffff,}; } private static void jj_la1_init_3(){ jj_la1_3 = new int[]{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x3,0x0,0x3,0x0,0x3b,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x23,0x0,0x0,0x0,0x3,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x38,0x30,0x0,0x0,0x3b,0x3b,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x3b,0x0,0x0,0x3b,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3b,0x0,0x0,0x0,0x0,0x0,0x0,0x3b,0x0,0x0,0x0,0x0,0x3,0x3,0x3,0x3,0x0,0x0,0x0,0x2,0x3b,0x0,0x0,0x0,0x0,0x0,0x3b,}; } final private JJCalls[] jj_2_rtns = new JJCalls[18]; private boolean jj_rescan = false; private int jj_gc = 0; /** Constructor with InputStream. */ public ADQLParser(java.io.InputStream stream){ this(stream, (String)null); } /** Constructor with InputStream and supplied encoding */ public ADQLParser(java.io.InputStream stream, String encoding){ try{ jj_input_stream = new SimpleCharStream(stream, encoding, 1, 1); }catch(java.io.UnsupportedEncodingException e){ throw new RuntimeException(e); } token_source = new ADQLParserTokenManager(jj_input_stream); token = new Token(); jj_ntk = -1; jj_gen = 0; for(int i = 0; i < 97; i++) jj_la1[i] = -1; for(int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls(); } /** Reinitialise. */ public void ReInit(java.io.InputStream stream){ ReInit(stream, null); } /** Reinitialise. */ public void ReInit(java.io.InputStream stream, String encoding){ try{ jj_input_stream.ReInit(stream, encoding, 1, 1); }catch(java.io.UnsupportedEncodingException e){ throw new RuntimeException(e); } token_source.ReInit(jj_input_stream); token = new Token(); jj_ntk = -1; jj_gen = 0; for(int i = 0; i < 97; i++) jj_la1[i] = -1; for(int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls(); } /** Constructor. */ public ADQLParser(java.io.Reader stream){ jj_input_stream = new SimpleCharStream(stream, 1, 1); token_source = new ADQLParserTokenManager(jj_input_stream); token = new Token(); jj_ntk = -1; jj_gen = 0; for(int i = 0; i < 97; i++) jj_la1[i] = -1; for(int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls(); } /** Reinitialise. */ public void ReInit(java.io.Reader stream){ jj_input_stream.ReInit(stream, 1, 1); token_source.ReInit(jj_input_stream); token = new Token(); jj_ntk = -1; jj_gen = 0; for(int i = 0; i < 97; i++) jj_la1[i] = -1; for(int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls(); } /** Constructor with generated Token Manager. */ public ADQLParser(ADQLParserTokenManager tm){ token_source = tm; token = new Token(); jj_ntk = -1; jj_gen = 0; for(int i = 0; i < 97; i++) jj_la1[i] = -1; for(int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls(); } /** Reinitialise. */ public void ReInit(ADQLParserTokenManager tm){ token_source = tm; token = new Token(); jj_ntk = -1; jj_gen = 0; for(int i = 0; i < 97; i++) jj_la1[i] = -1; for(int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls(); } private Token jj_consume_token(int kind) throws ParseException{ Token oldToken; if ((oldToken = token).next != null) token = token.next; else token = token.next = token_source.getNextToken(); jj_ntk = -1; if (token.kind == kind){ jj_gen++; if (++jj_gc > 100){ jj_gc = 0; for(int i = 0; i < jj_2_rtns.length; i++){ JJCalls c = jj_2_rtns[i]; while(c != null){ if (c.gen < jj_gen) c.first = null; c = c.next; } } } trace_token(token, ""); return token; } token = oldToken; jj_kind = kind; throw generateParseException(); } @SuppressWarnings("serial") static private final class LookaheadSuccess extends java.lang.Error {} final private LookaheadSuccess jj_ls = new LookaheadSuccess(); private boolean jj_scan_token(int kind){ if (jj_scanpos == jj_lastpos){ jj_la--; if (jj_scanpos.next == null){ jj_lastpos = jj_scanpos = jj_scanpos.next = token_source.getNextToken(); }else{ jj_lastpos = jj_scanpos = jj_scanpos.next; } }else{ jj_scanpos = jj_scanpos.next; } if (jj_rescan){ int i = 0; Token tok = token; while(tok != null && tok != jj_scanpos){ i++; tok = tok.next; } if (tok != null) jj_add_error_token(kind, i); } if (jj_scanpos.kind != kind) return true; if (jj_la == 0 && jj_scanpos == jj_lastpos) throw jj_ls; return false; } /** Get the next Token. */ final public Token getNextToken(){ if (token.next != null) token = token.next; else token = token.next = token_source.getNextToken(); jj_ntk = -1; jj_gen++; trace_token(token, " (in getNextToken)"); return token; } /** Get the specific Token. */ final public Token getToken(int index){ Token t = token; for(int i = 0; i < index; i++){ if (t.next != null) t = t.next; else t = t.next = token_source.getNextToken(); } return t; } private int jj_ntk_f(){ if ((jj_nt = token.next) == null) return (jj_ntk = (token.next = token_source.getNextToken()).kind); else return (jj_ntk = jj_nt.kind); } private java.util.List jj_expentries = new java.util.ArrayList(); private int[] jj_expentry; private int jj_kind = -1; private int[] jj_lasttokens = new int[100]; private int jj_endpos; private void jj_add_error_token(int kind, int pos){ if (pos >= 100) return; if (pos == jj_endpos + 1){ jj_lasttokens[jj_endpos++] = kind; }else if (jj_endpos != 0){ jj_expentry = new int[jj_endpos]; for(int i = 0; i < jj_endpos; i++){ jj_expentry[i] = jj_lasttokens[i]; } jj_entries_loop: for(java.util.Iterator it = jj_expentries.iterator(); it.hasNext();){ int[] oldentry = (int[])(it.next()); if (oldentry.length == jj_expentry.length){ for(int i = 0; i < jj_expentry.length; i++){ if (oldentry[i] != jj_expentry[i]){ continue jj_entries_loop; } } jj_expentries.add(jj_expentry); break jj_entries_loop; } } if (pos != 0) jj_lasttokens[(jj_endpos = pos) - 1] = kind; } } /** Generate ParseException. */ public ParseException generateParseException(){ jj_expentries.clear(); boolean[] la1tokens = new boolean[103]; if (jj_kind >= 0){ la1tokens[jj_kind] = true; jj_kind = -1; } for(int i = 0; i < 97; i++){ if (jj_la1[i] == jj_gen){ for(int j = 0; j < 32; j++){ if ((jj_la1_0[i] & (1 << j)) != 0){ la1tokens[j] = true; } if ((jj_la1_1[i] & (1 << j)) != 0){ la1tokens[32 + j] = true; } if ((jj_la1_2[i] & (1 << j)) != 0){ la1tokens[64 + j] = true; } if ((jj_la1_3[i] & (1 << j)) != 0){ la1tokens[96 + j] = true; } } } } for(int i = 0; i < 103; i++){ if (la1tokens[i]){ jj_expentry = new int[1]; jj_expentry[0] = i; jj_expentries.add(jj_expentry); } } jj_endpos = 0; jj_rescan_token(); jj_add_error_token(0, 0); int[][] exptokseq = new int[jj_expentries.size()][]; for(int i = 0; i < jj_expentries.size(); i++){ exptokseq[i] = jj_expentries.get(i); } return new ParseException(token, exptokseq, tokenImage); } private int trace_indent = 0; private boolean trace_enabled = true; /** Enable tracing. */ final public void enable_tracing(){ trace_enabled = true; } /** Disable tracing. */ final public void disable_tracing(){ trace_enabled = false; } private void trace_call(String s){ if (trace_enabled){ for(int i = 0; i < trace_indent; i++){ System.out.print(" "); } System.out.println("Call: " + s); } trace_indent = trace_indent + 2; } private void trace_return(String s){ trace_indent = trace_indent - 2; if (trace_enabled){ for(int i = 0; i < trace_indent; i++){ System.out.print(" "); } System.out.println("Return: " + s); } } private void trace_token(Token t, String where){ if (trace_enabled){ for(int i = 0; i < trace_indent; i++){ System.out.print(" "); } System.out.print("Consumed token: <" + tokenImage[t.kind]); if (t.kind != 0 && !tokenImage[t.kind].equals("\"" + t.image + "\"")){ System.out.print(": \"" + t.image + "\""); } System.out.println(" at line " + t.beginLine + " column " + t.beginColumn + ">" + where); } } private void trace_scan(Token t1, int t2){ if (trace_enabled){ for(int i = 0; i < trace_indent; i++){ System.out.print(" "); } System.out.print("Visited token: <" + tokenImage[t1.kind]); if (t1.kind != 0 && !tokenImage[t1.kind].equals("\"" + t1.image + "\"")){ System.out.print(": \"" + t1.image + "\""); } System.out.println(" at line " + t1.beginLine + " column " + t1.beginColumn + ">; Expected token: <" + tokenImage[t2] + ">"); } } private void jj_rescan_token(){ jj_rescan = true; for(int i = 0; i < 18; i++){ try{ JJCalls p = jj_2_rtns[i]; do{ if (p.gen > jj_gen){ jj_la = p.arg; jj_lastpos = jj_scanpos = p.first; switch(i){ case 0: jj_3_1(); break; case 1: jj_3_2(); break; case 2: jj_3_3(); break; case 3: jj_3_4(); break; case 4: jj_3_5(); break; case 5: jj_3_6(); break; case 6: jj_3_7(); break; case 7: jj_3_8(); break; case 8: jj_3_9(); break; case 9: jj_3_10(); break; case 10: jj_3_11(); break; case 11: jj_3_12(); break; case 12: jj_3_13(); break; case 13: jj_3_14(); break; case 14: jj_3_15(); break; case 15: jj_3_16(); break; case 16: jj_3_17(); break; case 17: jj_3_18(); break; } } p = p.next; }while(p != null); }catch(LookaheadSuccess ls){} } jj_rescan = false; } private void jj_save(int index, int xla){ JJCalls p = jj_2_rtns[index]; while(p.gen > jj_gen){ if (p.next == null){ p = p.next = new JJCalls(); break; } p = p.next; } p.gen = jj_gen + xla - jj_la; p.first = token; p.arg = xla; } static final class JJCalls { int gen; Token first; int arg; JJCalls next; } } src/adql/parser/QueryChecker.java0000644000175000017500000000352413177122310016006 0ustar olesolespackage adql.parser; /* * 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 . * * Copyright 2012-2013 - UDS/Centre de Données astronomiques de Strasbourg (CDS), * Astronomisches Rechen Institut (ARI) */ import adql.db.DBChecker; import adql.query.ADQLQuery; /** *

Used at the end of the parsing of each ADQL query by the {@link adql.parser.ADQLParser}, to check the generated {@link ADQLQuery} object.

* *

Usually, it consists to check the existence of referenced columns and tables. In this case, one default implementation of this interface can be used: {@link DBChecker}

* * @author Grégory Mantelet (CDS;ARI) * @version 1.2 (12/2013) */ public interface QueryChecker { /** *

Checks the given {@link ADQLQuery}.

* *

* Important note: * All subqueries must also be checked when calling this function! *

* *

If the query is correct, nothing happens. However at the first detected error, a {@link ParseException} is thrown.

* * @param query The query to check. * * @throws ParseException If the given query is not correct. */ public void check(ADQLQuery query) throws ParseException; } src/adql/parser/ParseException.java0000644000175000017500000002703713177122310016352 0ustar olesoles/* Generated By:JavaCC: Do not edit this line. ParseException.java Version 6.0 */ /* JavaCCOptions:KEEP_LINE_COL=null * * Modified by Grégory Mantelet (CDS), on March 2017 * Modifications: * - addition of a getPosition() function in order to get the exact location * of the error in the original ADQL query * - generate the error message at creation instead of doing that at each * call of getMessage() * - use of StringBuffer to build the error message * - small other alterations of the generated error message * * Modified by Grégory Mantelet (ARI), on Sept. 2017 * Modifications: * - addition of a HINT in the error message when an ADQL or SQL reserved * word is at the origin of the error (see initialise(...)) * * /!\ 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 * version of JavaCC). */ package adql.parser; import adql.query.TextPosition; /** * This exception is thrown when parse errors are encountered. * You can explicitly create objects of this exception type by * calling the method generateParseException in the generated * parser. * * You can modify this class to customize your error reporting * mechanisms so long as you retain the public fields. */ public class ParseException extends Exception { /** * The version identifier for this Serializable class. * Increment only if the serialized form of the * class changes. */ private static final long serialVersionUID = 1L; /** * This constructor is used by the method "generateParseException" * in the generated parser. Calling this constructor generates * a new object of this type with the fields "currentToken", * "expectedTokenSequences", and "tokenImage" set. */ public ParseException(Token currentTokenVal, int[][] expectedTokenSequencesVal, String[] tokenImageVal){ super(initialise(currentTokenVal, expectedTokenSequencesVal, tokenImageVal)); currentToken = currentTokenVal; expectedTokenSequences = expectedTokenSequencesVal; tokenImage = tokenImageVal; position = new TextPosition(currentToken.next); } /** * The following constructors are for use by you for whatever * purpose you can think of. Constructing the exception in this * manner makes the exception behave in the normal way - i.e., as * documented in the class "Throwable". The fields "errorToken", * "expectedTokenSequences", and "tokenImage" do not contain * relevant information. The JavaCC generated code does not use * these constructors. */ public ParseException(){ super(); } /** Constructor with message. */ public ParseException(String message){ super(message); } public ParseException(String message, TextPosition errorPosition){ this(message); position = errorPosition; } /** * This is the last token that has been consumed successfully. If * this object has been created due to a parse error, the token * followng this token will (therefore) be the first error token. */ public Token currentToken; /** * Each entry in this array is an array of integers. Each array * of integers represents a sequence of tokens (by their ordinal * values) that is expected at this point of the parse. */ public int[][] expectedTokenSequences; /** * This is a reference to the "tokenImage" array of the generated * parser within which the parse error occurred. This array is * defined in the generated ...Constants interface. */ public String[] tokenImage; /** Line in the ADQL query where the exception occurs. */ protected TextPosition position = null; /** Regular expression listing all ADQL reserved words. * *

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). *

* *

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). *

*/ 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. * *

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}). *

* *

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). *

*/ 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 null if unknown. */ public final TextPosition getPosition(){ return position; } /** * It uses "currentToken" and "expectedTokenSequences" to generate a parse * error message and returns it. If this object has been created * due to a parse error, and you do not catch it (it gets thrown * from the parser) the correct error message * gets displayed. */ 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){ maxSize = expectedTokenSequences[i].length; } for(int j = 0; j < expectedTokenSequences[i].length; j++){ expected.append(tokenImage[expectedTokenSequences[i][j]]); } expected.append(" "); } // Encountered token (s list): StringBuffer msg = new StringBuffer(); msg.append(" Encountered \""); Token tok = currentToken.next; StringBuffer tokenName = new StringBuffer(); for(int i = 0; i < maxSize; i++){ if (i != 0) tokenName.append(' '); if (tok.kind == 0){ tokenName.append(tokenImage[0]); break; } tokenName.append(add_escapes(tok.image)); tok = tok.next; } msg.append(tokenName.toString()).append("\"."); // Append the expected tokens list: if (expectedTokenSequences.length == 1){ msg.append(" Was expecting: "); }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.)"); return msg.toString(); /*String eol = System.getProperty("line.separator", "\n"); StringBuffer expected = new StringBuffer(); int maxSize = 0; 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++){ expected.append(tokenImage[expectedTokenSequences[i][j]]).append(' '); } if (expectedTokenSequences[i][expectedTokenSequences[i].length - 1] != 0){ expected.append("..."); } expected.append(eol).append(" "); } String retval = "Encountered \""; Token tok = currentToken.next; for(int i = 0; i < maxSize; i++){ if (i != 0) retval += " "; if (tok.kind == 0){ retval += tokenImage[0]; break; } retval += " " + tokenImage[tok.kind]; retval += " \""; retval += add_escapes(tok.image); retval += " \""; tok = tok.next; } retval += "\" at line " + currentToken.next.beginLine + ", column " + currentToken.next.beginColumn; retval += "." + eol; if (expectedTokenSequences.length == 1){ retval += "Was expecting:" + eol + " "; }else{ retval += "Was expecting one of:" + eol + " "; } retval += expected.toString(); return retval;*/ } /** * The end of line string for this machine. */ protected String eol = System.getProperty("line.separator", "\n"); /** * Used to convert raw characters to their escaped version * when these raw version cannot be used as part of an ASCII * string literal. */ static String add_escapes(String str){ StringBuffer retval = new StringBuffer(); char ch; for(int i = 0; i < str.length(); i++){ switch(str.charAt(i)){ case 0: continue; case '\b': retval.append("\\b"); continue; case '\t': retval.append("\\t"); continue; case '\n': retval.append("\\n"); continue; case '\f': retval.append("\\f"); continue; case '\r': retval.append("\\r"); continue; case '\"': retval.append("\\\""); continue; case '\'': retval.append("\\\'"); continue; case '\\': retval.append("\\\\"); continue; default: 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{ retval.append(ch); } continue; } } return retval.toString(); } } /* JavaCC - OriginalChecksum=ffea128e805df869244c5e22c2a37cbc (do not edit this line) */ src/adql/parser/adqlGrammar.jj0000644000175000017500000016333213226136430015335 0ustar olesoles/* * 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 . * * Copyright 2012-2018 - 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égory Mantelet (CDS;ARI) - gmantele@ari.uni-heidelberg.de * Version: 1.4 (01/2018) */ /* ########### */ /* # OPTIONS # */ /* ########### */ options { STATIC = false; IGNORE_CASE = true; DEBUG_PARSER = 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; /** * Parses an ADQL query thanks to the {@link ADQLParser#Query()} function. * *

* This parser is able, thanks to a {@link QueryChecker} object, to check each * {@link 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. *

* *

* 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(...)). *

* *

WARNING: * To modify this class it's strongly encouraged to modify the .jj file in the * section between PARSER_BEGIN and PARSER_END and to re-compile * it with JavaCC. *

* * @see QueryChecker * @see ADQLQueryFactory * * @author Grégory Mantelet (CDS;ARI) - gmantele@ari.uni-heidelberg.de * @version 1.4 (01/2018) */ 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 stackQuery = new Stack(); /** The object representation of the ADQL query to parse. * (ONLY USED DURING THE PARSING, else it is always null). */ private ADQLQuery query = null; /** Checks each {@link 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())); setDebug(false); } /** * Builds an ADQL parser without a query to parse but with a * {@link QueryChecker} and a {@link ADQLQueryFactory}. * * @param checker The object to use to check each {@link 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 * {@link QueryChecker}. * * @param checker The object to use to check each {@link ADQLQuery}. */ public ADQLParser(QueryChecker checker) { this(checker, null); } /** * Builds an ADQL parser without a query to parse but with a * {@link 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 {@link 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); setDebug(false); setDebug(false); 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 {@link 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 {@link 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); setDebug(false); 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 {@link 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 {@link 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); setDebug(false); setDebug(false); 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 {@link 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 {@link 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); setDebug(false); setDebug(false); 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 {@link 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 * ReInit 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; } /** * Gets the specified ADQL query and parses the given ADQL query. The SQL * translation is then printed if the syntax is correct. * *

* ONLY the syntax is checked: the query is NOT EXECUTED ! *

* *

Supplied parameters are: *

    *
  • [-debug] -url http://...
  • *
  • [-debug] -file ...
  • *
  • [-debug] -query SELECT...
  • *
*

* * @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] [|]\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 } /* ************************************************************************** */ /* Reserved SQL words */ /* */ /* NOTE: */ /* This list is the one provided by the ADQL-2.0 standard after removal of */ /* all ADQL used words (e.g. SELECT, AS, LIKE, AVG, ABS, COS, POINT). */ /* (see ParseException.initialise(Token, int[][], String[]) for more */ /* details) */ /* ************************************************************************** */ TOKEN : { < SQL_RESERVED_WORD: ("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") > } /* *********** */ /* 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 : { < BY: "BY" > | < GROUP: "GROUP" > | < HAVING: "HAVING" > | < ORDER: "ORDER" > | < 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 : { < (~["\n","\r"])* ("\n"|"\r"|"\r\n")? > } /* ****** */ /* String */ /* ****** */ MORE : { "'" : WithinString } MORE : { < ~["'"] | ("''") > } TOKEN : { < STRING_LITERAL: "'" >: DEFAULT } /* ************************************************* */ /* Identifier (column, tables, ...) */ /* ************************************************* */ MORE : { "\"" : WithinDelimitedId } MORE : { < ~["\""] | ("\"\"") > } TOKEN : { < DELIMITED_IDENTIFIER: "\"" >: DEFAULT } TOKEN : { < REGULAR_IDENTIFIER: ()+ ( | | "_")* > | < #Letter: ["a"-"z","A"-"Z"] > } /* *************** */ /* Primary numbers */ /* *************** */ TOKEN : { < SCIENTIFIC_NUMBER: (|) "E" (|)? > | < UNSIGNED_FLOAT: ( ()?) | ( ) > | < UNSIGNED_INTEGER: ()+ > | < #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 parseQuery functions. * * @return The object representation of the query. * @throws ParseException If the query syntax is incorrect. */ ADQLQuery Query(): {ADQLQuery q = null;}{ q=QueryExpression() ( | ) { // 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= q=QueryExpression() end= { q.setPosition(new TextPosition(start, end)); return q; } } void Select(): {ClauseSelect select = query.getSelect(); SelectItem item=null; Token start,t = null;} { start=