diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/UriInfoImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/UriInfoImpl.java index 2b4b266e3..9990da6e2 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/UriInfoImpl.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/UriInfoImpl.java @@ -53,6 +53,7 @@ import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; +import java.util.Map; public class UriInfoImpl implements UriInfo { @@ -62,9 +63,9 @@ public class UriInfoImpl implements UriInfo { private EdmEntityType entityTypeCast; // for $entity private List customQueryOptions = new ArrayList(); - private HashMap aliasToValue = new HashMap(); + private Map aliasToValue = new HashMap(); - HashMap systemQueryOptions = + Map systemQueryOptions = new HashMap(); private String fragment; @@ -239,36 +240,36 @@ public class UriInfoImpl implements UriInfo { } } - public UriInfoImpl setSystemQueryOption(final SystemQueryOptionImpl systemOption) { - - if (systemOption.getKind() == SystemQueryOptionKind.EXPAND) { - systemQueryOptions.put(SystemQueryOptionKind.EXPAND, systemOption); - } else if (systemOption.getKind() == SystemQueryOptionKind.FILTER) { - systemQueryOptions.put(SystemQueryOptionKind.FILTER, systemOption); - } else if (systemOption.getKind() == SystemQueryOptionKind.FORMAT) { - systemQueryOptions.put(SystemQueryOptionKind.FORMAT, systemOption); - } else if (systemOption.getKind() == SystemQueryOptionKind.ID) { - systemQueryOptions.put(SystemQueryOptionKind.ID, systemOption); - } else if (systemOption.getKind() == SystemQueryOptionKind.COUNT) { - systemQueryOptions.put(SystemQueryOptionKind.COUNT, systemOption); - } else if (systemOption.getKind() == SystemQueryOptionKind.ORDERBY) { - systemQueryOptions.put(SystemQueryOptionKind.ORDERBY, systemOption); - } else if (systemOption.getKind() == SystemQueryOptionKind.SEARCH) { - systemQueryOptions.put(SystemQueryOptionKind.SEARCH, systemOption); - } else if (systemOption.getKind() == SystemQueryOptionKind.SELECT) { - systemQueryOptions.put(SystemQueryOptionKind.SELECT, systemOption); - } else if (systemOption.getKind() == SystemQueryOptionKind.SKIP) { - systemQueryOptions.put(SystemQueryOptionKind.SKIP, systemOption); - } else if (systemOption.getKind() == SystemQueryOptionKind.SKIPTOKEN) { - systemQueryOptions.put(SystemQueryOptionKind.SKIPTOKEN, systemOption); - } else if (systemOption.getKind() == SystemQueryOptionKind.TOP) { - systemQueryOptions.put(SystemQueryOptionKind.TOP, systemOption); - } else if (systemOption.getKind() == SystemQueryOptionKind.LEVELS) { - systemQueryOptions.put(SystemQueryOptionKind.LEVELS, systemOption); - } else { + /** Adds system query option. + * @param systemOption the option to be added + * @return this object for method chaining + * @throws ODataRuntimeException if an unsupported option is provided + * or an option of this kind has been added before + */ + public UriInfoImpl setSystemQueryOption(final SystemQueryOption systemOption) { + final SystemQueryOptionKind kind = systemOption.getKind(); + switch (kind) { + case EXPAND: + case FILTER: + case FORMAT: + case ID: + case COUNT: + case ORDERBY: + case SEARCH: + case SELECT: + case SKIP: + case SKIPTOKEN: + case TOP: + case LEVELS: + if (systemQueryOptions.containsKey(kind)) { + throw new ODataRuntimeException("Double System Query Option: " + systemOption.getName()); + } else { + systemQueryOptions.put(kind, systemOption); + } + break; + default: throw new ODataRuntimeException("Unsupported System Query Option: " + systemOption.getName()); } - return this; } diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/Parser.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/Parser.java index c2be4388f..450ad2b2e 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/Parser.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/Parser.java @@ -27,12 +27,14 @@ import org.antlr.v4.runtime.RecognitionException; import org.antlr.v4.runtime.Token; import org.antlr.v4.runtime.atn.PredictionMode; import org.antlr.v4.runtime.misc.ParseCancellationException; +import org.apache.olingo.commons.api.ODataRuntimeException; import org.apache.olingo.commons.api.edm.Edm; import org.apache.olingo.commons.api.format.ODataFormat; import org.apache.olingo.server.api.uri.UriInfo; import org.apache.olingo.server.api.uri.UriInfoKind; import org.apache.olingo.server.api.uri.UriResource; import org.apache.olingo.server.api.uri.UriResourcePartTyped; +import org.apache.olingo.server.api.uri.queryoption.SystemQueryOption; import org.apache.olingo.server.api.uri.queryoption.SystemQueryOptionKind; import org.apache.olingo.server.core.uri.UriInfoImpl; import org.apache.olingo.server.core.uri.antlr.UriLexer; @@ -161,118 +163,127 @@ public class Parser { } if (readQueryParameter) { - // second, read the simple systemQueryOptions and the Custom QueryOptions + // second, read the system query options and the custom query options for (RawUri.QueryOption option : uri.queryOptionListDecoded) { - if (!option.name.startsWith("$")) { + if (option.name.startsWith("$")) { + SystemQueryOption systemOption = null; + if (option.name.equals(SystemQueryOptionKind.FILTER.toString())) { + FilterExpressionEOFContext ctxFilterExpression = + (FilterExpressionEOFContext) parseRule(option.value, ParserEntryRules.FilterExpression); + + FilterOptionImpl filterOption = + (FilterOptionImpl) uriParseTreeVisitor.visitFilterExpressionEOF(ctxFilterExpression); + + systemOption = filterOption; + + } else if (option.name.equals(SystemQueryOptionKind.FORMAT.toString())) { + FormatOptionImpl formatOption = new FormatOptionImpl(); + formatOption.setName(option.name); + formatOption.setText(option.value); + if (option.value.equalsIgnoreCase(ODataFormat.JSON.name()) + || option.value.equalsIgnoreCase(ODataFormat.XML.name()) + || option.value.equalsIgnoreCase(ODataFormat.ATOM.name()) + || isFormatSyntaxValid(option)) { + formatOption.setFormat(option.value); + } else { + throw new UriParserSyntaxException("Illegal value of $format option!", + UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION_FORMAT, option.value); + } + systemOption = formatOption; + + } else if (option.name.equals(SystemQueryOptionKind.EXPAND.toString())) { + ExpandItemsEOFContext ctxExpandItems = + (ExpandItemsEOFContext) parseRule(option.value, ParserEntryRules.ExpandItems); + + ExpandOptionImpl expandOption = + (ExpandOptionImpl) uriParseTreeVisitor.visitExpandItemsEOF(ctxExpandItems); + + systemOption = expandOption; + + } else if (option.name.equals(SystemQueryOptionKind.ID.toString())) { + IdOptionImpl idOption = new IdOptionImpl(); + idOption.setName(option.name); + idOption.setText(option.value); + idOption.setValue(option.value); + systemOption = idOption; + } else if (option.name.equals(SystemQueryOptionKind.LEVELS.toString())) { + throw new UriParserSyntaxException("System query option '$levels' is allowed only inside '$expand'!", + UriParserSyntaxException.MessageKeys.SYSTEM_QUERY_OPTION_LEVELS_NOT_ALLOWED_HERE); + } else if (option.name.equals(SystemQueryOptionKind.ORDERBY.toString())) { + OrderByEOFContext ctxOrderByExpression = + (OrderByEOFContext) parseRule(option.value, ParserEntryRules.Orderby); + + OrderByOptionImpl orderByOption = + (OrderByOptionImpl) uriParseTreeVisitor.visitOrderByEOF(ctxOrderByExpression); + + systemOption = orderByOption; + } else if (option.name.equals(SystemQueryOptionKind.SEARCH.toString())) { + throw new RuntimeException("System query option '$search' not implemented!"); + } else if (option.name.equals(SystemQueryOptionKind.SELECT.toString())) { + SelectEOFContext ctxSelectEOF = + (SelectEOFContext) parseRule(option.value, ParserEntryRules.Select); + + SelectOptionImpl selectOption = + (SelectOptionImpl) uriParseTreeVisitor.visitSelectEOF(ctxSelectEOF); + + systemOption = selectOption; + } else if (option.name.equals(SystemQueryOptionKind.SKIP.toString())) { + SkipOptionImpl skipOption = new SkipOptionImpl(); + skipOption.setName(option.name); + skipOption.setText(option.value); + try { + skipOption.setValue(Integer.parseInt(option.value)); + } catch (final NumberFormatException e) { + throw new UriParserSyntaxException("Illegal value of $skip option!", e, + UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION, + option.name, option.value); + } + systemOption = skipOption; + } else if (option.name.equals(SystemQueryOptionKind.SKIPTOKEN.toString())) { + SkipTokenOptionImpl skipTokenOption = new SkipTokenOptionImpl(); + skipTokenOption.setName(option.name); + skipTokenOption.setText(option.value); + skipTokenOption.setValue(option.value); + systemOption = skipTokenOption; + } else if (option.name.equals(SystemQueryOptionKind.TOP.toString())) { + TopOptionImpl topOption = new TopOptionImpl(); + topOption.setName(option.name); + topOption.setText(option.value); + try { + topOption.setValue(Integer.parseInt(option.value)); + } catch (final NumberFormatException e) { + throw new UriParserSyntaxException("Illegal value of $top option!", e, + UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION, + option.name, option.value); + } + systemOption = topOption; + } else if (option.name.equals(SystemQueryOptionKind.COUNT.toString())) { + CountOptionImpl inlineCountOption = new CountOptionImpl(); + inlineCountOption.setName(option.name); + inlineCountOption.setText(option.value); + if (option.value.equals("true") || option.value.equals("false")) { + inlineCountOption.setValue(Boolean.parseBoolean(option.value)); + } else { + throw new UriParserSyntaxException("Illegal value of $count option!", + UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION, + option.name, option.value); + } + systemOption = inlineCountOption; + } else { + throw new UriParserSyntaxException("Unknown system query option!", + UriParserSyntaxException.MessageKeys.UNKNOWN_SYSTEM_QUERY_OPTION, option.name); + } + try { + context.contextUriInfo.setSystemQueryOption(systemOption); + } catch (final ODataRuntimeException e) { + throw new UriParserSyntaxException("Double system query option!", e, + UriParserSyntaxException.MessageKeys.DOUBLE_SYSTEM_QUERY_OPTION, option.name); + } + } else { CustomQueryOptionImpl customOption = new CustomQueryOptionImpl(); customOption.setName(option.name); customOption.setText(option.value); context.contextUriInfo.addCustomQueryOption(customOption); - } else if (option.name.equals(SystemQueryOptionKind.FILTER.toString())) { - FilterExpressionEOFContext ctxFilterExpression = - (FilterExpressionEOFContext) parseRule(option.value, ParserEntryRules.FilterExpression); - - FilterOptionImpl filterOption = - (FilterOptionImpl) uriParseTreeVisitor.visitFilterExpressionEOF(ctxFilterExpression); - - context.contextUriInfo.setSystemQueryOption(filterOption); - - } else if (option.name.equals(SystemQueryOptionKind.FORMAT.toString())) { - FormatOptionImpl formatOption = new FormatOptionImpl(); - formatOption.setName(option.name); - formatOption.setText(option.value); - if (option.value.equalsIgnoreCase(ODataFormat.JSON.name()) - || option.value.equalsIgnoreCase(ODataFormat.XML.name()) - || option.value.equalsIgnoreCase(ODataFormat.ATOM.name()) - || isFormatSyntaxValid(option)) { - formatOption.setFormat(option.value); - } else { - throw new UriParserSyntaxException("Illegal value of $format option!", - UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION_FORMAT, option.value); - } - context.contextUriInfo.setSystemQueryOption(formatOption); - - } else if (option.name.equals(SystemQueryOptionKind.EXPAND.toString())) { - ExpandItemsEOFContext ctxExpandItems = - (ExpandItemsEOFContext) parseRule(option.value, ParserEntryRules.ExpandItems); - - ExpandOptionImpl expandOption = - (ExpandOptionImpl) uriParseTreeVisitor.visitExpandItemsEOF(ctxExpandItems); - - context.contextUriInfo.setSystemQueryOption(expandOption); - - } else if (option.name.equals(SystemQueryOptionKind.ID.toString())) { - IdOptionImpl idOption = new IdOptionImpl(); - idOption.setName(option.name); - idOption.setText(option.value); - idOption.setValue(option.value); - context.contextUriInfo.setSystemQueryOption(idOption); - } else if (option.name.equals(SystemQueryOptionKind.LEVELS.toString())) { - throw new UriParserSyntaxException("System query option '$levels' is allowed only inside '$expand'!", - UriParserSyntaxException.MessageKeys.SYSTEM_QUERY_OPTION_LEVELS_NOT_ALLOWED_HERE); - } else if (option.name.equals(SystemQueryOptionKind.ORDERBY.toString())) { - OrderByEOFContext ctxOrderByExpression = - (OrderByEOFContext) parseRule(option.value, ParserEntryRules.Orderby); - - OrderByOptionImpl orderByOption = - (OrderByOptionImpl) uriParseTreeVisitor.visitOrderByEOF(ctxOrderByExpression); - - context.contextUriInfo.setSystemQueryOption(orderByOption); - } else if (option.name.equals(SystemQueryOptionKind.SEARCH.toString())) { - throw new RuntimeException("System query option '$search' not implemented!"); - } else if (option.name.equals(SystemQueryOptionKind.SELECT.toString())) { - SelectEOFContext ctxSelectEOF = - (SelectEOFContext) parseRule(option.value, ParserEntryRules.Select); - - SelectOptionImpl selectOption = - (SelectOptionImpl) uriParseTreeVisitor.visitSelectEOF(ctxSelectEOF); - - context.contextUriInfo.setSystemQueryOption(selectOption); - } else if (option.name.equals(SystemQueryOptionKind.SKIP.toString())) { - SkipOptionImpl skipOption = new SkipOptionImpl(); - skipOption.setName(option.name); - skipOption.setText(option.value); - try { - skipOption.setValue(Integer.parseInt(option.value)); - } catch (final NumberFormatException e) { - throw new UriParserSyntaxException("Illegal value of $skip option!", e, - UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION, - option.name, option.value); - } - context.contextUriInfo.setSystemQueryOption(skipOption); - } else if (option.name.equals(SystemQueryOptionKind.SKIPTOKEN.toString())) { - SkipTokenOptionImpl skipTokenOption = new SkipTokenOptionImpl(); - skipTokenOption.setName(option.name); - skipTokenOption.setText(option.value); - skipTokenOption.setValue(option.value); - context.contextUriInfo.setSystemQueryOption(skipTokenOption); - } else if (option.name.equals(SystemQueryOptionKind.TOP.toString())) { - TopOptionImpl topOption = new TopOptionImpl(); - topOption.setName(option.name); - topOption.setText(option.value); - try { - topOption.setValue(Integer.parseInt(option.value)); - } catch (final NumberFormatException e) { - throw new UriParserSyntaxException("Illegal value of $top option!", e, - UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION, - option.name, option.value); - } - context.contextUriInfo.setSystemQueryOption(topOption); - } else if (option.name.equals(SystemQueryOptionKind.COUNT.toString())) { - CountOptionImpl inlineCountOption = new CountOptionImpl(); - inlineCountOption.setName(option.name); - inlineCountOption.setText(option.value); - if (option.value.equals("true") || option.value.equals("false")) { - inlineCountOption.setValue(Boolean.parseBoolean(option.value)); - } else { - throw new UriParserSyntaxException("Illegal value of $count option!", - UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION, - option.name, option.value); - } - context.contextUriInfo.setSystemQueryOption(inlineCountOption); - } else { - throw new UriParserSyntaxException("Unknown system query option!", - UriParserSyntaxException.MessageKeys.UNKNOWN_SYSTEM_QUERY_OPTION, option.name); } } } diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriParserSyntaxException.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriParserSyntaxException.java index 454ed3bfb..e5e195516 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriParserSyntaxException.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriParserSyntaxException.java @@ -25,6 +25,7 @@ public class UriParserSyntaxException extends UriParserException { public static enum MessageKeys implements MessageKey { /** parameter: query-option name */ UNKNOWN_SYSTEM_QUERY_OPTION, + /** parameter: query-option name */ DOUBLE_SYSTEM_QUERY_OPTION, /** parameters: query-option name, query-option value */ WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION, SYNTAX, SYSTEM_QUERY_OPTION_LEVELS_NOT_ALLOWED_HERE, diff --git a/lib/server-core/src/main/resources/server-core-exceptions-i18n.properties b/lib/server-core/src/main/resources/server-core-exceptions-i18n.properties index 57e805cc8..d9fa6f715 100644 --- a/lib/server-core/src/main/resources/server-core-exceptions-i18n.properties +++ b/lib/server-core/src/main/resources/server-core-exceptions-i18n.properties @@ -25,6 +25,7 @@ ODataHandlerException.FUNCTIONALITY_NOT_IMPLEMENTED=The requested functionality ODataHandlerException.ODATA_VERSION_NOT_SUPPORTED=OData version '%1$s' is not supported. UriParserSyntaxException.UNKNOWN_SYSTEM_QUERY_OPTION=The system query option '%1$s' is not defined. +UriParserSyntaxException.DOUBLE_SYSTEM_QUERY_OPTION=The system query option '%1$s' can be specified only once. UriParserSyntaxException.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION=The system query option '%1$s' has the not-allowed value '%2$s'. UriParserSyntaxException.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION_FORMAT=The system query option '$format' must be either 'json', 'xml', 'atom', or a valid content type; the value '%1$s' is neither. UriParserSyntaxException.SYNTAX=The URI is malformed. diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/UriInfoImplTest.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/UriInfoImplTest.java index 91b58c0c3..d4e8ebd32 100644 --- a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/UriInfoImplTest.java +++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/UriInfoImplTest.java @@ -18,6 +18,7 @@ */ package org.apache.olingo.server.core.uri; +import org.apache.olingo.commons.api.ODataRuntimeException; import org.apache.olingo.commons.api.edm.Edm; import org.apache.olingo.commons.api.edm.EdmEntityType; import org.apache.olingo.server.api.uri.UriInfoAll; @@ -122,6 +123,13 @@ public class UriInfoImplTest { assertEquals(entitySet1, uriInfo.getLastResourcePart()); } + @Test(expected = ODataRuntimeException.class) + public void doubleSystemQueryOptions() { + new UriInfoImpl() + .setSystemQueryOption(new FormatOptionImpl()) + .setSystemQueryOption(new FormatOptionImpl()); + } + @Test public void testCustomQueryOption() { UriInfoImpl uriInfo = new UriInfoImpl(); diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestUriParserImpl.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestUriParserImpl.java index c1eae9357..d1fa59ed9 100644 --- a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestUriParserImpl.java +++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestUriParserImpl.java @@ -30,6 +30,7 @@ import org.apache.olingo.server.api.uri.queryoption.expression.MethodKind; import org.apache.olingo.server.core.edm.provider.EdmProviderImpl; import org.apache.olingo.server.core.uri.parser.UriParserException; import org.apache.olingo.server.core.uri.parser.UriParserSemanticException; +import org.apache.olingo.server.core.uri.parser.UriParserSyntaxException; import org.apache.olingo.server.core.uri.testutil.EdmTechTestProvider; import org.apache.olingo.server.core.uri.testutil.FilterValidator; import org.apache.olingo.server.core.uri.testutil.ResourceValidator; @@ -240,7 +241,7 @@ public class TestUriParserImpl { .goFilter().is("< eq <123>>"); } - @Test(expected = UriValidationException.class) + @Test(expected = UriParserSyntaxException.class) public void testEntityFailOnValidation2() throws Exception { // simple entity set; with qualifiedentityTypeName; with 2xformat(before and after), expand, filter testUri.run("$entity/olingo.odata.test1.ETTwoPrim?" diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/validator/UriValidatorTest.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/validator/UriValidatorTest.java index e3e0fc44b..ee1378bc6 100644 --- a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/validator/UriValidatorTest.java +++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/validator/UriValidatorTest.java @@ -314,6 +314,11 @@ public class UriValidatorTest { parseAndValidate("ESAllPrim?$skip=foo", HttpMethod.GET); } + @Test(expected = UriParserSyntaxException.class) + public void validateDoubleSystemOptions() throws Exception { + parseAndValidate("ESAllPrim?$skip=1&$skip=2", HttpMethod.GET); + } + @Test(expected = UriValidationException.class) public void validateKeyPredicatesWrongKey() throws Exception { String uri = "ESTwoKeyNav(xxx=1, yyy='abc')";