diff --git a/src/adql/parser/ADQLParser200.java b/src/adql/parser/ADQLParser200.java index 42126da635909d306e010f7bdacbcd74c6a3304d..a4b1e1d4843b957b4f85e0c80a2a09b02c6b1f08 100644 --- a/src/adql/parser/ADQLParser200.java +++ b/src/adql/parser/ADQLParser200.java @@ -126,7 +126,7 @@ public class ADQLParser200 implements ADQLParser, ADQLParser200Constants { * <p><i><b>Note:</b> * By default, all optional features are supported. * </i></p> */ - private FeatureSet supportedFeatures = new FeatureSet(false); + private FeatureSet supportedFeatures = new FeatureSet(false, true); /** The stack of queries (because there may be some sub-queries). */ private Stack<ADQLQuery> stackQuery = new Stack<ADQLQuery>(); diff --git a/src/adql/parser/ADQLParser201.java b/src/adql/parser/ADQLParser201.java index 189eefb4c3dd005b768b11aaed1c2447ca885af4..29691f2f4c47d0a2de4f3b755b9b0fedd9371783 100644 --- a/src/adql/parser/ADQLParser201.java +++ b/src/adql/parser/ADQLParser201.java @@ -130,7 +130,7 @@ public class ADQLParser201 implements ADQLParser, ADQLParser201Constants { * <p><i><b>Note:</b> * By default, all optional features are supported. * </i></p> */ - private FeatureSet supportedFeatures = new FeatureSet(true); + private FeatureSet supportedFeatures = new FeatureSet(true, true); /** The stack of queries (because there may be some sub-queries). */ private Stack<ADQLQuery> stackQuery = new Stack<ADQLQuery>(); @@ -199,7 +199,6 @@ public class ADQLParser201 implements ADQLParser, ADQLParser201Constants { */ public ADQLParser201(java.io.InputStream stream, QueryChecker checker, ADQLQueryFactory factory) { this(stream); - setDebug(false); setDebug(false); @@ -241,6 +240,7 @@ public class ADQLParser201 implements ADQLParser, ADQLParser201Constants { */ public ADQLParser201(java.io.InputStream stream, String encoding, QueryChecker checker, ADQLQueryFactory factory) { this(stream, encoding); + setDebug(false); queryChecker = checker; @@ -282,7 +282,6 @@ public class ADQLParser201 implements ADQLParser, ADQLParser201Constants { */ public ADQLParser201(java.io.Reader reader, QueryChecker checker, ADQLQueryFactory factory) { this(reader); - setDebug(false); setDebug(false); @@ -323,7 +322,6 @@ public class ADQLParser201 implements ADQLParser, ADQLParser201Constants { */ public ADQLParser201(ADQLParser201TokenManager tm, QueryChecker checker, ADQLQueryFactory factory) { this(tm); - setDebug(false); setDebug(false); @@ -4478,6 +4476,17 @@ public class ADQLParser201 implements ADQLParser, ADQLParser201Constants { } } + 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; @@ -6203,17 +6212,6 @@ public class ADQLParser201 implements ADQLParser, ADQLParser201Constants { 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; - } - /** Generated Token Manager. */ public ADQLParser201TokenManager token_source; SimpleCharStream jj_input_stream; diff --git a/src/adql/parser/adqlGrammar200.jj b/src/adql/parser/adqlGrammar200.jj index 36cffe7ed9bee43888462074da1bc4b2bbe6da81..a0ac86cdded0645a81ac6381e89239fe7717b6ef 100644 --- a/src/adql/parser/adqlGrammar200.jj +++ b/src/adql/parser/adqlGrammar200.jj @@ -159,7 +159,7 @@ public class ADQLParser200 implements ADQLParser { * <p><i><b>Note:</b> * By default, all optional features are supported. * </i></p> */ - private FeatureSet supportedFeatures = new FeatureSet(false); + private FeatureSet supportedFeatures = new FeatureSet(false, true); /** The stack of queries (because there may be some sub-queries). */ private Stack<ADQLQuery> stackQuery = new Stack<ADQLQuery>(); diff --git a/src/adql/parser/adqlGrammar201.jj b/src/adql/parser/adqlGrammar201.jj index 2ca25408bb39e8f16ead1837971f4c8c4bd2467c..a5db23ba57f97af40297ea223ffc6fbcd64343a9 100644 --- a/src/adql/parser/adqlGrammar201.jj +++ b/src/adql/parser/adqlGrammar201.jj @@ -166,7 +166,7 @@ public class ADQLParser201 implements ADQLParser { * <p><i><b>Note:</b> * By default, all optional features are supported. * </i></p> */ - private FeatureSet supportedFeatures = new FeatureSet(true); + private FeatureSet supportedFeatures = new FeatureSet(true, true); /** The stack of queries (because there may be some sub-queries). */ private Stack<ADQLQuery> stackQuery = new Stack<ADQLQuery>(); @@ -235,7 +235,6 @@ public class ADQLParser201 implements ADQLParser { */ public ADQLParser201(java.io.InputStream stream, QueryChecker checker, ADQLQueryFactory factory) { this(stream); - setDebug(false); setDebug(false); @@ -277,6 +276,7 @@ public class ADQLParser201 implements ADQLParser { */ public ADQLParser201(java.io.InputStream stream, String encoding, QueryChecker checker, ADQLQueryFactory factory) { this(stream, encoding); + setDebug(false); queryChecker = checker; @@ -318,7 +318,6 @@ public class ADQLParser201 implements ADQLParser { */ public ADQLParser201(java.io.Reader reader, QueryChecker checker, ADQLQueryFactory factory) { this(reader); - setDebug(false); setDebug(false); @@ -359,7 +358,6 @@ public class ADQLParser201 implements ADQLParser { */ public ADQLParser201(ADQLParser201TokenManager tm, QueryChecker checker, ADQLQueryFactory factory) { this(tm); - setDebug(false); setDebug(false); diff --git a/src/adql/parser/feature/FeatureSet.java b/src/adql/parser/feature/FeatureSet.java index 022e389b9820782e760afccf7908cec42ce12967..e857e2dc569794361aeb1fdb26e21d3a9de49433 100644 --- a/src/adql/parser/feature/FeatureSet.java +++ b/src/adql/parser/feature/FeatureSet.java @@ -110,7 +110,37 @@ import adql.query.operand.function.string.LowerFunction; * <p><i><b>Warning:</b> * Both functions will not work for User Defined Functions that has to be * added individually in the {@link FeatureSet}. - * <i></p> + * </i></p> + * + * <h3>Special case of User Defined Functions (UDFs)</h3> + * + * <p> + * UDFs are also optional features. However, it is not possible to have a list + * of available UDFs....indeed, by definition they are <i>user defined</i>. + * Consequently, supported UDFs must be explicitly declared. + * </p> + * + * <p> + * However, it is often useful (e.g. when just checking the ADQL syntax + * of a query) to not raise errors when non-declared UDFs are encountered. + * For that reason, there is a special option inside this {@link FeatureSet} to + * allow/forbid non-declared UDFs. This option/flag has just an impact on the + * result of the function {@link #isSupporting(LanguageFeature)} ; it is not + * visible in any of the {@link #getSupportedFeatures()} functions. + * </p> + * + * <p> + * This flag can be checked with {@link #isAnyUdfAllowed()} and can be changed + * with any of the following functions: + * </p> + * <ul> + * <li>{@link #allowAnyUdf(boolean)},</li> + * <li>{@link #supportAll()},</li> + * <li>{@link #supportAll(String)} with {@link LanguageFeature#TYPE_UDF},</li> + * <li>{@link #unsupportAll()},</li> + * <li>{@link #unsupportAll(String)} with {@link LanguageFeature#TYPE_UDF},</li> + * <li>and any of the constructors.</li> + * </ul> * * @author Grégory Mantelet (CDS) * @version 2.0 (07/2019) @@ -121,8 +151,16 @@ public class FeatureSet implements Iterable<LanguageFeature> { /** Set of all supported features. */ protected final Map<String, Set<LanguageFeature>> supportedFeatures; + /** Indicate whether any UDF (even if not declared) should be considered as + * supported. */ + protected boolean anyUdfAllowed = false; + /** * Create a feature set with all available features supported by default. + * + * <p><i><b>Note:</b> + * With this constructor, non-declared UDFs will be considered as supported. + * </i></p> */ public FeatureSet() { this(true); @@ -132,16 +170,70 @@ public class FeatureSet implements Iterable<LanguageFeature> { * Create a feature set will all available features supported or not, * depending of the given boolean parameter. * + * <i> + * <p><b>Note:</b> + * With this constructor, non-declared UDFs will be considered as supported + * or not depending on the given parameter: + * </p> + * <ul> + * <li><code>true</code> will support all available features and will allow + * non-declared UDFs,</li> + * <li><code>false</code> will un-support all available features and will + * forbid non-declared UDFs.</li> + * </ul> + * </i> + * * @param allSupported <code>true</code> to support all available features, * <code>false</code> to not support any. */ public FeatureSet(final boolean allSupported) { + this(allSupported, allSupported); + } + + /** + * Create a feature set will all available features supported or not, + * depending of the given boolean parameter. + * + * @param allSupported <code>true</code> to support all available features, + * <code>false</code> to not support any. + * @param allowAnyUdf <code>true</code> to support any UDF (even if not + * declared), + * <code>false</code> to force declaration of supported + * UDFs. + */ + public FeatureSet(final boolean allSupported, final boolean allowAnyUdf) { // Init. the list of supported features: supportedFeatures = new HashMap<>(); // If asked, support all available features: if (allSupported) supportAll(); + + // If asked, allow any UDF: + this.anyUdfAllowed = allowAnyUdf; + } + + /** + * Let specify whether any UDF (even if not declared) should be considered + * as supported or not. If not, UDFs must be explicitly declared to be + * considered as supported (as any other optional language feature). + * + * @param allowed <code>true</code> to support any UDF, + * <code>false</code> to force the declaration of supported + * UDFs. + */ + public void allowAnyUdf(final boolean allowed) { + this.anyUdfAllowed = allowed; + } + + /** + * Tell whether UDFs are considered as supported even if undeclared. + * + * @return <code>true</code> if any UDF is considered as supported, + * <code>false</code> if supported UDFs must be explicitly declared. + */ + public boolean isAnyUdfAllowed() { + return anyUdfAllowed; } /** @@ -191,6 +283,11 @@ public class FeatureSet implements Iterable<LanguageFeature> { /** * Support all the features of the given type. * + * <p><i><b>Note:</b> + * If the given type is {@link LanguageFeature#TYPE_UDF}, then any + * UDF (event if not declared) is considered as supported. + * </i></p> + * * @param type The type of language features to support. * * @return <code>true</code> if all the available features of the given @@ -203,9 +300,17 @@ public class FeatureSet implements Iterable<LanguageFeature> { public final boolean supportAll(final String type) { boolean done = false; if (type != null) { - for(LanguageFeature feature : availableFeatures) { - if (feature.type == type) - done = support(feature) || done; + + // CASE: UDF + if (LanguageFeature.TYPE_UDF == type) + done = anyUdfAllowed = true; + + // OTHERWISE + else { + for(LanguageFeature feature : availableFeatures) { + if (feature.type == type) + done = support(feature) || done; + } } } return done; @@ -214,11 +319,19 @@ public class FeatureSet implements Iterable<LanguageFeature> { /** * Support all available features. * + * <p><i><b>Note:</b> + * This function also allows non-declared UDFs. + * </i></p> + * * @see #getAvailableFeatures() */ public final void supportAll() { + // support all available features: for(LanguageFeature feature : availableFeatures) support(feature); + + // also allow non-declared UDFs: + anyUdfAllowed = true; } /** @@ -271,6 +384,11 @@ public class FeatureSet implements Iterable<LanguageFeature> { /** * Un-support all the features of the given type. * + * <p><i><b>Note:</b> + * If the given type is {@link LanguageFeature#TYPE_UDF}, then supported + * UDFs must be explicitly declared. + * </i></p> + * * @param type The type of language features to un-support. * * @return <code>true</code> if all the available features of the given @@ -283,9 +401,17 @@ public class FeatureSet implements Iterable<LanguageFeature> { public final boolean unsupportAll(final String type) { boolean done = false; if (type != null) { - for(LanguageFeature feature : availableFeatures) { - if (feature.type == type) - done = unsupport(feature) || done; + + // CASE: UDF + if (LanguageFeature.TYPE_UDF == type) + done = !(anyUdfAllowed = false); + + // OTHERWISE + else { + for(LanguageFeature feature : availableFeatures) { + if (feature.type == type) + done = unsupport(feature) || done; + } } } return done; @@ -294,11 +420,19 @@ public class FeatureSet implements Iterable<LanguageFeature> { /** * Un-support all available features. * + * <p><i><b>Note:</b> + * This function also forbids non-declared UDFs. + * </i></p> + * * @see #getAvailableFeatures() */ public final void unsupportAll() { + // unsupport all available features: for(LanguageFeature feature : availableFeatures) unsupport(feature); + + // also unsupport any UDF: + anyUdfAllowed = false; } /** @@ -329,10 +463,17 @@ public class FeatureSet implements Iterable<LanguageFeature> { if (feature == null || feature.type == null || !feature.optional) return false; - // Get the corresponding Set of features: - Set<LanguageFeature> features = supportedFeatures.get(feature.type); + // CASE: ANY UDF + if (anyUdfAllowed && LanguageFeature.TYPE_UDF == feature.type) + return true; + + // OTHERWISE + else { + // Get the corresponding Set of features: + Set<LanguageFeature> features = supportedFeatures.get(feature.type); - return (features != null && features.contains(feature)); + return (features != null && features.contains(feature)); + } } /** diff --git a/test/adql/parser/feature/TestFeatureSet.java b/test/adql/parser/feature/TestFeatureSet.java index 0e57a9d43993b7581b229481b4781c92d85de5ec..1c273f017109f3d42f004446708fe1437cb8bbcc 100644 --- a/test/adql/parser/feature/TestFeatureSet.java +++ b/test/adql/parser/feature/TestFeatureSet.java @@ -40,6 +40,7 @@ public class TestFeatureSet { assertTrue(set.supportedFeatures.containsKey(feat.type)); assertTrue(set.supportedFeatures.get(feat.type).contains(feat)); } + assertTrue(set.anyUdfAllowed); // With this constructor, none of the available features are supported: set = new FeatureSet(false); @@ -47,6 +48,48 @@ public class TestFeatureSet { assertNotNull(feat); assertFalse(set.supportedFeatures.containsKey(feat.type)); } + assertFalse(set.anyUdfAllowed); + } + + @Test + public void testFeatureSetBooleanBoolean() { + /* With this constructor, all available features and non-declared UDFs + * must be already supported: */ + FeatureSet set = new FeatureSet(true, true); + for(LanguageFeature feat : FeatureSet.availableFeatures) { + assertNotNull(feat); + assertTrue(set.supportedFeatures.containsKey(feat.type)); + assertTrue(set.supportedFeatures.get(feat.type).contains(feat)); + } + assertTrue(set.anyUdfAllowed); + + /* With this constructor, all available features must be already + * supported BUT NOT non-declared UDFs: */ + set = new FeatureSet(true, false); + for(LanguageFeature feat : FeatureSet.availableFeatures) { + assertNotNull(feat); + assertTrue(set.supportedFeatures.containsKey(feat.type)); + assertTrue(set.supportedFeatures.get(feat.type).contains(feat)); + } + assertFalse(set.anyUdfAllowed); + + /* With this constructor, none of the available features are supported, + * as well as non-declared UDFs: */ + set = new FeatureSet(false, false); + for(LanguageFeature feat : FeatureSet.availableFeatures) { + assertNotNull(feat); + assertFalse(set.supportedFeatures.containsKey(feat.type)); + } + assertFalse(set.anyUdfAllowed); + + /* With this constructor, none of the available features are supported, + * BUT non-declared UDFs are allowed: */ + set = new FeatureSet(false, true); + for(LanguageFeature feat : FeatureSet.availableFeatures) { + assertNotNull(feat); + assertFalse(set.supportedFeatures.containsKey(feat.type)); + } + assertTrue(set.anyUdfAllowed); } @Test @@ -93,6 +136,11 @@ public class TestFeatureSet { assertTrue(geoSet.contains(feat)); } } + + // CASE: UDF => non-declared UDFs are allowed + assertFalse(set.isAnyUdfAllowed()); + assertTrue(set.supportAll(LanguageFeature.TYPE_UDF)); + assertTrue(set.isAnyUdfAllowed()); } @Test @@ -105,6 +153,7 @@ public class TestFeatureSet { assertTrue(set.supportedFeatures.containsKey(feat.type)); assertTrue(set.supportedFeatures.get(feat.type).contains(feat)); } + assertTrue(set.isAnyUdfAllowed()); } @Test @@ -155,6 +204,11 @@ public class TestFeatureSet { assertTrue(set.supportedFeatures.containsKey(LanguageFeature.TYPE_ADQL_GEO)); assertTrue(set.unsupportAll(LanguageFeature.TYPE_ADQL_GEO)); assertFalse(set.supportedFeatures.containsKey(LanguageFeature.TYPE_ADQL_GEO)); + + // CASE: UDF => non-declared UDFs are forbidden + assertTrue(set.isAnyUdfAllowed()); + assertTrue(set.unsupportAll(LanguageFeature.TYPE_UDF)); + assertFalse(set.isAnyUdfAllowed()); } @Test @@ -166,6 +220,7 @@ public class TestFeatureSet { assertNotNull(feat); assertFalse(set.supportedFeatures.containsKey(feat.type)); } + assertFalse(set.isAnyUdfAllowed()); } @Test