From 3295bcc062206b2764fe33692004541d0b602bed Mon Sep 17 00:00:00 2001 From: Klaus Straubinger Date: Tue, 12 Jan 2016 15:05:01 +0100 Subject: [PATCH] [OLINGO-834] URI parser shall not ignore empty path segments Signed-off-by: Christian Amend --- .../fit/tecsvc/http/BasicHttpITCase.java | 4 +- .../olingo/server/core/uri/parser/Parser.java | 22 ++++-- .../server/core/uri/parser/ParserHelper.java | 2 +- .../server/core/uri/parser/UriDecoder.java | 45 ++++--------- .../core/uri/parser/search/SearchParser.java | 2 +- .../core/uri/validator/UriValidator.java | 67 +++++++++---------- .../core/uri/parser/ExpressionParserTest.java | 8 +-- .../server/core/uri/parser/LexerTest.java} | 27 +++----- .../core/uri/parser/UriDecoderTest.java | 17 ++--- .../search/SearchParserAndTokenizerTest.java | 33 ++++----- .../uri/parser/search/SearchParserTest.java | 16 ++--- .../parser/search/SearchTokenizerTest.java | 15 +---- .../TestFullResourcePath.java | 7 +- .../{antlr => parser}/TestUriParserImpl.java | 2 +- .../core/uri/queryoption/QueryOptionTest.java | 14 ++-- 15 files changed, 123 insertions(+), 158 deletions(-) rename lib/{server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestLexer.java => server-core/src/test/java/org/apache/olingo/server/core/uri/parser/LexerTest.java} (96%) rename lib/server-test/src/test/java/org/apache/olingo/server/core/uri/{antlr => parser}/TestFullResourcePath.java (99%) rename lib/server-test/src/test/java/org/apache/olingo/server/core/uri/{antlr => parser}/TestUriParserImpl.java (99%) diff --git a/fit/src/test/java/org/apache/olingo/fit/tecsvc/http/BasicHttpITCase.java b/fit/src/test/java/org/apache/olingo/fit/tecsvc/http/BasicHttpITCase.java index e0975819a..bb1398952 100644 --- a/fit/src/test/java/org/apache/olingo/fit/tecsvc/http/BasicHttpITCase.java +++ b/fit/src/test/java/org/apache/olingo/fit/tecsvc/http/BasicHttpITCase.java @@ -114,7 +114,7 @@ public class BasicHttpITCase extends AbstractBaseTestITCase { @Test public void testIEEE754ParameterContentNegotiation() throws Exception { - final URL url = new URL(SERVICE_URI + "/ESAllPrim(32767)?$format=application/json;IEEE754Compatible=true"); + final URL url = new URL(SERVICE_URI + "ESAllPrim(32767)?$format=application/json;IEEE754Compatible=true"); final HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod(HttpMethod.GET.name()); connection.setRequestProperty(HttpHeader.ACCEPT, "application/json;IEEE754Compatible=false"); @@ -131,7 +131,7 @@ public class BasicHttpITCase extends AbstractBaseTestITCase { @Test public void testIEEE754ParameterViaAcceptHeader() throws Exception { - final URL url = new URL(SERVICE_URI + "/ESAllPrim(32767)"); + final URL url = new URL(SERVICE_URI + "ESAllPrim(32767)"); final HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod(HttpMethod.GET.name()); connection.setRequestProperty(HttpHeader.ACCEPT, "application/json;IEEE754Compatible=true"); 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 86f574952..34944c8d8 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 @@ -19,6 +19,7 @@ package org.apache.olingo.server.core.uri.parser; import java.util.ArrayDeque; +import java.util.Collections; import java.util.Deque; import java.util.List; @@ -60,6 +61,7 @@ public class Parser { private static final String ATOM = "atom"; private static final String JSON = "json"; private static final String XML = "xml"; + private static final String DOLLAR = "$"; private static final String AT = "@"; private static final String NULL = "null"; @@ -78,14 +80,19 @@ public class Parser { Deque contextTypes = new ArrayDeque(); boolean contextIsCollection = false; - final List pathSegmentsDecoded = UriDecoder.splitAndDecodePath(path); - final int numberOfSegments = pathSegmentsDecoded.size(); + List pathSegmentsDecoded = UriDecoder.splitAndDecodePath(path); + int numberOfSegments = pathSegmentsDecoded.size(); + // Remove an initial empty segment resulting from the expected '/' at the beginning of the path. + if (numberOfSegments > 1 && pathSegmentsDecoded.get(0).isEmpty()) { + pathSegmentsDecoded.remove(0); + numberOfSegments--; + } // first, read the decoded path segments - final String firstSegment = numberOfSegments == 0 ? "" : pathSegmentsDecoded.get(0); + final String firstSegment = pathSegmentsDecoded.get(0); if (firstSegment.isEmpty()) { - ensureLastSegment(firstSegment, 0, numberOfSegments); + ensureLastSegment(firstSegment, 1, numberOfSegments); contextUriInfo.setKind(UriInfoKind.service); } else if (firstSegment.equals("$batch")) { @@ -168,11 +175,12 @@ public class Parser { } // second, read the system query options and the custom query options - final List options = UriDecoder.splitAndDecodeOptions(query); + final List options = + query == null ? Collections. emptyList() : UriDecoder.splitAndDecodeOptions(query); for (final QueryOption option : options) { final String optionName = option.getName(); final String optionValue = option.getText(); - if (optionName.startsWith("$")) { + if (optionName.startsWith(DOLLAR)) { SystemQueryOption systemOption = null; if (optionName.equals(SystemQueryOptionKind.FILTER.toString())) { UriTokenizer filterTokenizer = new UriTokenizer(optionValue); @@ -315,7 +323,7 @@ public class Parser { UriParserSyntaxException.MessageKeys.DUPLICATED_ALIAS, optionName); } - } else { + } else if (!optionName.isEmpty()) { contextUriInfo.addCustomQueryOption((CustomQueryOption) option); } } diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ParserHelper.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ParserHelper.java index 7f4abf756..852c43a69 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ParserHelper.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ParserHelper.java @@ -157,7 +157,7 @@ public class ParserHelper { final List keyPropertyRefs = edmEntityType.getKeyPropertyRefs(); if (tokenizer.next(TokenKind.CLOSE)) { throw new UriParserSemanticException( - "Expected " + keyPropertyRefs.size() + " key predicates but none.", + "Expected " + keyPropertyRefs.size() + " key predicates but got none.", UriParserSemanticException.MessageKeys.WRONG_NUMBER_OF_KEY_PROPERTIES, Integer.toString(keyPropertyRefs.size()), "0"); } diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriDecoder.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriDecoder.java index 6450bf609..1cc5ec5f3 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriDecoder.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriDecoder.java @@ -19,8 +19,6 @@ package org.apache.olingo.server.core.uri.parser; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; import java.util.LinkedList; import java.util.List; @@ -33,7 +31,7 @@ public class UriDecoder { /** Splits the path string at '/' characters and percent-decodes the resulting path segments. */ protected static List splitAndDecodePath(final String path) throws UriParserSyntaxException { List pathSegmentsDecoded = new ArrayList(); - for (final String segment : splitSkipEmpty(path, '/')) { + for (final String segment : split(path, '/')) { pathSegmentsDecoded.add(decode(segment)); } return pathSegmentsDecoded; @@ -42,58 +40,39 @@ public class UriDecoder { /** * Splits the query-option string at '&' characters, the resulting parts at '=' characters, * and separately percent-decodes names and values of the resulting name-value pairs. + * If there is no '=' character in an option, the whole option is considered as name. */ protected static List splitAndDecodeOptions(final String queryOptionString) throws UriParserSyntaxException { - if (queryOptionString == null || queryOptionString.isEmpty()) { - return Collections.emptyList(); - } - List queryOptions = new ArrayList(); - for (final String option : splitSkipEmpty(queryOptionString, '&')) { - final List pair = splitFirst(option, '='); + for (final String option : split(queryOptionString, '&')) { + final int pos = option.indexOf('='); + final String name = pos >= 0 ? option.substring(0, pos) : option; + final String text = pos >= 0 ? option.substring(pos + 1) : ""; queryOptions.add(new CustomQueryOptionImpl() - .setName(decode(pair.get(0))) - .setText(decode(pair.get(1)))); + .setName(decode(name)) + .setText(decode(text))); } return queryOptions; } - private static List splitFirst(final String input, final char c) { - int pos = input.indexOf(c); - if (pos >= 0) { - return Arrays.asList(input.substring(0, pos), input.substring(pos + 1)); - } else { - return Arrays.asList(input, ""); - } - } - /** - * Splits the input string at the given character and drops all empty elements. + * Splits the input string at the given character. * @param input string to split * @param c character at which to split * @return list of elements (can be empty) */ - private static List splitSkipEmpty(final String input, final char c) { - if (input.isEmpty() || input.length() == 1 && input.charAt(0) == c) { - return Collections.emptyList(); - } - + private static List split(final String input, final char c) { List list = new LinkedList(); int start = 0; int end; - while ((end = input.indexOf(c, start)) >= 0) { - if (start != end) { - list.add(input.substring(start, end)); - } + list.add(input.substring(start, end)); start = end + 1; } - if (input.charAt(input.length() - 1) != c) { - list.add(input.substring(start)); - } + list.add(input.substring(start)); return list; } diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchParser.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchParser.java index 4fccb81a5..6c3db4ff0 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchParser.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchParser.java @@ -50,7 +50,7 @@ public class SearchParser { searchExpression = parse(tokenizer.tokenize(searchQuery)); } catch (SearchTokenizerException e) { String message = e.getMessage(); - throw new SearchParserException("Tokenizer exception with message: " + message, + throw new SearchParserException("Tokenizer exception with message: " + message, e, SearchParserException.MessageKeys.TOKENIZER_EXCEPTION, message); } final SearchOptionImpl searchOption = new SearchOptionImpl(); diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/validator/UriValidator.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/validator/UriValidator.java index 149d1fc02..5d842fbfe 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/validator/UriValidator.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/validator/UriValidator.java @@ -286,9 +286,9 @@ public class UriValidator { RowIndexForUriType.mediaStream : RowIndexForUriType.propertyPrimitiveValue; break; default: - throw new UriValidationException("Unexpected kind in path segment before $value: " - + secondLastPathSegment.getKind(), UriValidationException.MessageKeys.UNALLOWED_KIND_BEFORE_VALUE, - secondLastPathSegment.toString()); + throw new UriValidationException( + "Unexpected kind in path segment before $value: " + secondLastPathSegment.getKind(), + UriValidationException.MessageKeys.UNALLOWED_KIND_BEFORE_VALUE, secondLastPathSegment.toString()); } return idx; } @@ -302,9 +302,9 @@ public class UriValidator { return ((UriResourcePartTyped) secondLastPathSegment).isCollection() ? RowIndexForUriType.references : RowIndexForUriType.reference; } else { - throw new UriValidationException("secondLastPathSegment not a class of UriResourcePartTyped: " - + lastPathSegment.getClass(), UriValidationException.MessageKeys.LAST_SEGMENT_NOT_TYPED, lastPathSegment - .toString()); + throw new UriValidationException( + "secondLastPathSegment not a class of UriResourcePartTyped: " + lastPathSegment.getClass(), + UriValidationException.MessageKeys.LAST_SEGMENT_NOT_TYPED, lastPathSegment.toString()); } } @@ -314,40 +314,36 @@ public class UriValidator { return ((UriResourcePartTyped) lastPathSegment).isCollection() ? RowIndexForUriType.propertyPrimitiveCollection : RowIndexForUriType.propertyPrimitive; } else { - throw new UriValidationException("lastPathSegment not a class of UriResourcePartTyped: " - + lastPathSegment.getClass(), UriValidationException.MessageKeys.LAST_SEGMENT_NOT_TYPED, lastPathSegment - .toString()); + throw new UriValidationException( + "lastPathSegment not a class of UriResourcePartTyped: " + lastPathSegment.getClass(), + UriValidationException.MessageKeys.LAST_SEGMENT_NOT_TYPED, lastPathSegment.toString()); } } private RowIndexForUriType rowIndexForFunction(final UriResource lastPathSegment) throws UriValidationException { final UriResourceFunction uriFunction = (UriResourceFunction) lastPathSegment; - final EdmReturnType returnType = uriFunction.getFunction().getReturnType(); - if (!uriFunction.getFunction().isComposable()) { return RowIndexForUriType.none; } + final boolean isCollection = uriFunction.isCollection(); + final EdmTypeKind typeKind = uriFunction.getFunction().getReturnType().getType().getKind(); RowIndexForUriType idx; - switch (returnType.getType().getKind()) { + switch (typeKind) { case ENTITY: - idx = returnType.isCollection() && uriFunction.getKeyPredicates().isEmpty() ? - RowIndexForUriType.entitySet : RowIndexForUriType.entity; + idx = isCollection ? RowIndexForUriType.entitySet : RowIndexForUriType.entity; break; case PRIMITIVE: case ENUM: case DEFINITION: - idx = returnType.isCollection() ? RowIndexForUriType.propertyPrimitiveCollection : - RowIndexForUriType.propertyPrimitive; + idx = isCollection ? RowIndexForUriType.propertyPrimitiveCollection : RowIndexForUriType.propertyPrimitive; break; case COMPLEX: - idx = returnType.isCollection() ? RowIndexForUriType.propertyComplexCollection : - RowIndexForUriType.propertyComplex; + idx = isCollection ? RowIndexForUriType.propertyComplexCollection : RowIndexForUriType.propertyComplex; break; default: - throw new UriValidationException("Unsupported function return type: " + returnType.getType().getKind(), - UriValidationException.MessageKeys.UNSUPPORTED_FUNCTION_RETURN_TYPE, - returnType.getType().getKind().toString()); + throw new UriValidationException("Unsupported function return type: " + typeKind, + UriValidationException.MessageKeys.UNSUPPORTED_FUNCTION_RETURN_TYPE, typeKind.toString()); } return idx; @@ -512,18 +508,17 @@ public class UriValidator { for (SystemQueryOption option : uriInfo.getSystemQueryOptions()) { options.append(option.getName()).append(" "); } - throw new UriValidationException("System query option " + options.toString() + " not allowed for method " - + httpMethod, UriValidationException.MessageKeys.SYSTEM_QUERY_OPTION_NOT_ALLOWED_FOR_HTTP_METHOD, + throw new UriValidationException( + "System query option " + options.toString() + " not allowed for method " + httpMethod, + UriValidationException.MessageKeys.SYSTEM_QUERY_OPTION_NOT_ALLOWED_FOR_HTTP_METHOD, options.toString(), httpMethod.toString()); } } private boolean isAction(final UriInfo uriInfo) { List uriResourceParts = uriInfo.getUriResourceParts(); - if (uriResourceParts.isEmpty()) { - return false; - } - return UriResourceKind.action == uriResourceParts.get(uriResourceParts.size() - 1).getKind(); + return !uriResourceParts.isEmpty() + && UriResourceKind.action == uriResourceParts.get(uriResourceParts.size() - 1).getKind(); } private void validateParameters(final UriInfo uriInfo) throws UriValidationException { @@ -531,18 +526,18 @@ public class UriValidator { final boolean isFunction = pathSegment.getKind() == UriResourceKind.function; if (isFunction) { - final UriResourceFunction functionPathSegement = (UriResourceFunction) pathSegment; - final EdmFunction edmFuntion = functionPathSegement.getFunction(); + final UriResourceFunction functionPathSegment = (UriResourceFunction) pathSegment; + final EdmFunction edmFunction = functionPathSegment.getFunction(); final Map parameters = new HashMap(); - for (final UriParameter parameter : functionPathSegement.getParameters()) { + for (final UriParameter parameter : functionPathSegment.getParameters()) { parameters.put(parameter.getName(), parameter); } boolean firstParameter = true; - for (final String parameterName : edmFuntion.getParameterNames()) { + for (final String parameterName : edmFunction.getParameterNames()) { final UriParameter parameter = parameters.get(parameterName); - final boolean isNullable = edmFuntion.getParameter(parameterName).isNullable(); + final boolean isNullable = edmFunction.getParameter(parameterName).isNullable(); if (parameter != null) { /** No alias, value explicit null */ @@ -560,7 +555,7 @@ public class UriValidator { } parameters.remove(parameterName); - } else if (!isNullable && !(firstParameter && edmFuntion.isBound())) { + } else if (!isNullable && !(firstParameter && edmFunction.isBound())) { // The first parameter of bound functions is implicit provided by the preceding path segment throw new UriValidationException("Missing non nullable parameter " + parameterName, UriValidationException.MessageKeys.MISSING_PARAMETER, parameterName); @@ -586,11 +581,11 @@ public class UriValidator { if (isEntitySet || pathSegment.getKind() == UriResourceKind.navigationProperty || isEntityColFunction) { final List keyPredicates = isEntitySet ? ((UriResourceEntitySet) pathSegment).getKeyPredicates() : - isEntityColFunction ? ((UriResourceFunction) pathSegment).getKeyPredicates() - : ((UriResourceNavigation) pathSegment).getKeyPredicates(); + isEntityColFunction ? + ((UriResourceFunction) pathSegment).getKeyPredicates() : + ((UriResourceNavigation) pathSegment).getKeyPredicates(); if (keyPredicates != null) { - final EdmEntityType entityType = isEntitySet ? ((UriResourceEntitySet) pathSegment).getEntityType() : isEntityColFunction ? (EdmEntityType) ((UriResourceFunction) pathSegment).getType() diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/ExpressionParserTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/ExpressionParserTest.java index 94d5373c6..f495c5b6f 100644 --- a/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/ExpressionParserTest.java +++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/ExpressionParserTest.java @@ -290,15 +290,13 @@ public class ExpressionParserTest { } expressionString += ')'; - Expression expression = parseExpression(expressionString); - assertNotNull(expression); - return expression; + return parseExpression(expressionString); } private Expression parseExpression(final String expressionString) throws UriParserException, UriValidationException { UriTokenizer tokenizer = new UriTokenizer(expressionString); - Expression expression = new ExpressionParser(mock(Edm.class), odata).parse(tokenizer, null, null); + final Expression expression = new ExpressionParser(mock(Edm.class), odata).parse(tokenizer, null, null); assertNotNull(expression); assertTrue(tokenizer.next(TokenKind.EOF)); return expression; @@ -306,7 +304,7 @@ public class ExpressionParserTest { private void wrongExpression(final String expressionString) { try { - new ExpressionParser(mock(Edm.class), odata).parse(new UriTokenizer(expressionString), null, null); + parseExpression(expressionString); fail("Expected exception not thrown."); } catch (final UriParserException e) { assertNotNull(e); diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestLexer.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/LexerTest.java similarity index 96% rename from lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestLexer.java rename to lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/LexerTest.java index 6102fd836..df3b50652 100644 --- a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestLexer.java +++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/LexerTest.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.olingo.server.core.uri.antlr; +package org.apache.olingo.server.core.uri.parser; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -28,21 +28,9 @@ import org.junit.Test; /** * Tests originally written for the ANTLR lexer. */ -public class TestLexer { +public class LexerTest { - private TokenValidator test = null; - - // The last two chars are not in cPCT_ENCODED_UNESCAPED. - private static final String cPCT_ENCODED = "%45%46%47" + "%22" + "%5C"; - private static final String cUNRESERVED = "ABCabc123-._~"; - private static final String cOTHER_DELIMS = "!()*+,;"; - private static final String cSUB_DELIMS = "$&'=" + cOTHER_DELIMS; - - private static final String cPCHAR = cUNRESERVED + cPCT_ENCODED + cSUB_DELIMS + ":@"; - - public TestLexer() { - test = new TokenValidator(); - } + private TokenValidator test = new TokenValidator(); @Test public void unary() { @@ -271,7 +259,14 @@ public class TestLexer { @Test public void delims() { - final String reserved = "/"; + // The last two chars are not in cPCT_ENCODED_UNESCAPED. +// final String cPCT_ENCODED = "%45%46%47" + "%22" + "%5C"; +// final String cUNRESERVED = "ABCabc123-._~"; +// final String cOTHER_DELIMS = "!()*+,;"; +// final String cSUB_DELIMS = "$&'=" + cOTHER_DELIMS; + +// private static final String cPCHAR = cUNRESERVED + cPCT_ENCODED + cSUB_DELIMS + ":@"; +// final String reserved = "/"; // Test lexer rule UNRESERVED // test.run("$format=A/" + cUNRESERVED).has(TokenKind.FORMAT).isInput(); // test.run("$format=A/" + cUNRESERVED + reserved).has(TokenKind.FORMAT).isText(cUNRESERVED); diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/UriDecoderTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/UriDecoderTest.java index 20ab94f5f..243cb455c 100644 --- a/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/UriDecoderTest.java +++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/UriDecoderTest.java @@ -19,7 +19,6 @@ package org.apache.olingo.server.core.uri.parser; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.util.Arrays; @@ -32,13 +31,14 @@ public class UriDecoderTest { @Test public void split() throws Exception { - assertTrue(UriDecoder.splitAndDecodePath("").isEmpty()); - assertTrue(UriDecoder.splitAndDecodePath("/").isEmpty()); + assertEquals(Arrays.asList(""), UriDecoder.splitAndDecodePath("")); + assertEquals(Arrays.asList("", ""), UriDecoder.splitAndDecodePath("/")); assertEquals(Arrays.asList("a"), UriDecoder.splitAndDecodePath("a")); - assertEquals(Arrays.asList("a"), UriDecoder.splitAndDecodePath("a/")); - assertEquals(Arrays.asList("a"), UriDecoder.splitAndDecodePath("/a")); - assertEquals(Arrays.asList("a", "a"), UriDecoder.splitAndDecodePath("a/a")); - assertEquals(Arrays.asList("a", "a"), UriDecoder.splitAndDecodePath("/a/a")); + assertEquals(Arrays.asList("a", ""), UriDecoder.splitAndDecodePath("a/")); + assertEquals(Arrays.asList("", "a"), UriDecoder.splitAndDecodePath("/a")); + assertEquals(Arrays.asList("a", "b"), UriDecoder.splitAndDecodePath("a/b")); + assertEquals(Arrays.asList("", "a", "b"), UriDecoder.splitAndDecodePath("/a/b")); + assertEquals(Arrays.asList("", "a", "", "", "b", ""), UriDecoder.splitAndDecodePath("/a///b/")); } @Test @@ -49,7 +49,7 @@ public class UriDecoderTest { @Test public void options() throws Exception { - assertTrue(UriDecoder.splitAndDecodeOptions("").isEmpty()); + checkOption("", "", ""); checkOption("a", "a", ""); checkOption("a=b", "a", "b"); @@ -67,6 +67,7 @@ public class UriDecoderTest { checkOption("=&=", "", ""); assertEquals(2, UriDecoder.splitAndDecodeOptions("=&=").size()); + assertEquals(13, UriDecoder.splitAndDecodeOptions("&&&&&&&&&&&&").size()); checkOption("=&c=d", "", ""); checkOption("=&c=d", "c", "d"); diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchParserAndTokenizerTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchParserAndTokenizerTest.java index 9bb7d6fd4..f19fc826b 100644 --- a/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchParserAndTokenizerTest.java +++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchParserAndTokenizerTest.java @@ -194,7 +194,7 @@ public class SearchParserAndTokenizerTest { } private static Validator assertQuery(final String searchQuery) { - return Validator.init(searchQuery); + return new Validator(searchQuery); } private static class Validator { @@ -204,18 +204,12 @@ public class SearchParserAndTokenizerTest { this.searchQuery = searchQuery; } - private static Validator init(final String searchQuery) { - return new Validator(searchQuery); - } - - private void resultsIn(final SearchParserException.MessageKey key) - throws SearchTokenizerException { - + private void resultsIn(final SearchParserException.MessageKey key) throws SearchTokenizerException { try { resultsIn(searchQuery); - } catch (SearchParserException e) { - Assert.assertEquals("SearchParserException with unexpected message '" + e.getMessage() + - "' was thrown.", key, e.getMessageKey()); + } catch (final SearchParserException e) { + Assert.assertEquals("SearchParserException with unexpected message '" + e.getMessage() + "' was thrown.", + key, e.getMessageKey()); return; } Assert.fail("SearchParserException with message key " + key.getKey() + " was not thrown."); @@ -224,23 +218,22 @@ public class SearchParserAndTokenizerTest { public void resultsInExpectedTerm(final String actualToken) throws SearchTokenizerException { try { resultsIn(searchQuery); - } catch (SearchParserException e) { + } catch (final SearchParserException e) { Assert.assertEquals(SearchParserException.MessageKeys.EXPECTED_DIFFERENT_TOKEN, e.getMessageKey()); Assert.assertEquals("Expected PHRASE||WORD found: " + actualToken, e.getMessage()); + return; } + Assert.fail("SearchParserException with message key " + + SearchParserException.MessageKeys.EXPECTED_DIFFERENT_TOKEN + " was not thrown."); } - private void resultsIn(String expectedSearchExpression) throws SearchTokenizerException, SearchParserException { - final SearchExpression searchExpression = getSearchExpression(); - Assert.assertEquals(expectedSearchExpression, searchExpression.toString()); - } - - private SearchExpression getSearchExpression() throws SearchParserException, SearchTokenizerException { - SearchOption result = new SearchParser().parse(searchQuery); + private void resultsIn(final String expectedSearchExpression) + throws SearchTokenizerException, SearchParserException { + final SearchOption result = new SearchParser().parse(searchQuery); Assert.assertNotNull(result); final SearchExpression searchExpression = result.getSearchExpression(); Assert.assertNotNull(searchExpression); - return searchExpression; + Assert.assertEquals(expectedSearchExpression, searchExpression.toString()); } } } diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchParserTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchParserTest.java index 780c209aa..40be4ac39 100644 --- a/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchParserTest.java +++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchParserTest.java @@ -224,34 +224,34 @@ public class SearchParserTest extends SearchParser { private void runEx(final MessageKeys key, final Token... tokenArray) { try { run(tokenArray); - fail("Expected UriParserSyntaxException with key " + key); + fail("Expected SearchParserException with key " + key); } catch (SearchParserException e) { assertEquals(key, e.getMessageKey()); } } - private SearchExpression run(final SearchQueryToken.Token... tokenArray) throws SearchParserException { + private SearchExpression run(final Token... tokenArray) throws SearchParserException { List tokenList = prepareTokens(tokenArray); SearchExpression se = parse(tokenList); assertNotNull(se); return se; } - public List prepareTokens(final SearchQueryToken.Token... tokenArray) { + public List prepareTokens(final Token... tokenArray) { ArrayList tokenList = new ArrayList(); int wordNumber = 1; int phraseNumber = 1; - for (Token aTokenArray : tokenArray) { + for (Token aToken : tokenArray) { SearchQueryToken token = mock(SearchQueryToken.class); - when(token.getToken()).thenReturn(aTokenArray); - if (aTokenArray == Token.WORD) { + when(token.getToken()).thenReturn(aToken); + if (aToken == Token.WORD) { when(token.getLiteral()).thenReturn("word" + wordNumber); wordNumber++; - } else if (aTokenArray == Token.PHRASE) { + } else if (aToken == Token.PHRASE) { when(token.getLiteral()).thenReturn("\"phrase" + phraseNumber + "\""); phraseNumber++; } - when(token.toString()).thenReturn("" + aTokenArray); + when(token.toString()).thenReturn("" + aToken); tokenList.add(token); } return tokenList; diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java index a6c13758c..81790272a 100644 --- a/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java +++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java @@ -269,7 +269,7 @@ public class SearchTokenizerTest { assertQuery("\"\"").resultsIn(SearchTokenizerException.MessageKeys.INVALID_TOKEN_STATE); assertQuery("some AND)").resultsIn(SearchTokenizerException.MessageKeys.FORBIDDEN_CHARACTER); assertQuery("some OR)").resultsIn(SearchTokenizerException.MessageKeys.FORBIDDEN_CHARACTER); - assertQuery("some NOT)").enableLogging().resultsIn(SearchTokenizerException.MessageKeys.FORBIDDEN_CHARACTER); + assertQuery("some NOT)").resultsIn(SearchTokenizerException.MessageKeys.FORBIDDEN_CHARACTER); } @Test @@ -308,7 +308,6 @@ public class SearchTokenizerTest { private static class Validator { private List validations = new ArrayList(); - private boolean log; private final String searchQuery; public void resultsIn(final SearchQueryToken.Token... tokens) throws SearchTokenizerException { @@ -345,11 +344,6 @@ public class SearchTokenizerTest { this.searchQuery = searchQuery; } - private Validator enableLogging() { - log = true; - return this; - } - private Validator addExpected(final SearchQueryToken.Token token, final String literal) { validations.add(new Tuple(token, literal)); return this; @@ -368,10 +362,6 @@ public class SearchTokenizerTest { validate(); } catch (SearchTokenizerException e) { Assert.assertEquals("SearchTokenizerException with unexpected message was thrown.", key, e.getMessageKey()); - if (log) { - System.out.println("Caught SearchTokenizerException with message key " + - e.getMessageKey() + " and message " + e.getMessage()); - } return; } Assert.fail("No SearchTokenizerException was not thrown."); @@ -381,9 +371,6 @@ public class SearchTokenizerTest { SearchTokenizer tokenizer = new SearchTokenizer(); List result = tokenizer.tokenize(searchQuery); Assert.assertNotNull(result); - if (log) { - System.out.println(result); - } if (validations.size() != 0) { Assert.assertEquals(validations.size(), result.size()); diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestFullResourcePath.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/parser/TestFullResourcePath.java similarity index 99% rename from lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestFullResourcePath.java rename to lib/server-test/src/test/java/org/apache/olingo/server/core/uri/parser/TestFullResourcePath.java index a94026fa9..828be80ea 100644 --- a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestFullResourcePath.java +++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/parser/TestFullResourcePath.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.olingo.server.core.uri.antlr; +package org.apache.olingo.server.core.uri.parser; import java.util.Arrays; import java.util.Collections; @@ -3011,6 +3011,11 @@ public class TestFullResourcePath { testUri.run("$batch") .isKind(UriInfoKind.batch); + testUri.runEx("//").isExSyntax(UriParserSyntaxException.MessageKeys.MUST_BE_LAST_SEGMENT); + testUri.runEx("$metadata/").isExSyntax(UriParserSyntaxException.MessageKeys.MUST_BE_LAST_SEGMENT); + testUri.runEx("//$metadata").isExSyntax(UriParserSyntaxException.MessageKeys.MUST_BE_LAST_SEGMENT); + testUri.runEx("ESKeyNav//$count").isExSyntax(UriParserSyntaxException.MessageKeys.SYNTAX); + testUri.runEx("$metadata/$ref").isExSyntax(UriParserSyntaxException.MessageKeys.MUST_BE_LAST_SEGMENT); testUri.runEx("$batch/$ref").isExSyntax(UriParserSyntaxException.MessageKeys.MUST_BE_LAST_SEGMENT); testUri.runEx("$all/$ref").isExSyntax(UriParserSyntaxException.MessageKeys.MUST_BE_LAST_SEGMENT); 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/parser/TestUriParserImpl.java similarity index 99% rename from lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestUriParserImpl.java rename to lib/server-test/src/test/java/org/apache/olingo/server/core/uri/parser/TestUriParserImpl.java index dd517f9ef..fa7a0df48 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/parser/TestUriParserImpl.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.olingo.server.core.uri.antlr; +package org.apache.olingo.server.core.uri.parser; import java.util.Arrays; import java.util.Collections; diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/queryoption/QueryOptionTest.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/queryoption/QueryOptionTest.java index db9f5be5d..c3d90c9ab 100644 --- a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/queryoption/QueryOptionTest.java +++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/queryoption/QueryOptionTest.java @@ -33,11 +33,11 @@ import org.apache.olingo.server.api.uri.queryoption.SystemQueryOption; import org.apache.olingo.server.api.uri.queryoption.SystemQueryOptionKind; import org.apache.olingo.server.api.uri.queryoption.expression.Expression; import org.apache.olingo.server.core.uri.UriInfoImpl; +import org.apache.olingo.server.core.uri.parser.search.SearchTermImpl; import org.apache.olingo.server.core.uri.queryoption.expression.AliasImpl; import org.apache.olingo.server.core.uri.queryoption.expression.LiteralImpl; import org.junit.Test; -//TOOD add getKind check to all public class QueryOptionTest { @Test @@ -196,6 +196,7 @@ public class QueryOptionTest { @Test public void testOrderByOptionImpl() { OrderByOptionImpl option = new OrderByOptionImpl(); + assertEquals(SystemQueryOptionKind.ORDERBY, option.getKind()); OrderByItemImpl order0 = new OrderByItemImpl(); OrderByItemImpl order1 = new OrderByItemImpl(); @@ -217,14 +218,17 @@ public class QueryOptionTest { } @Test - public void testSearchOptionImpl() { + public void searchOptionImpl() { SearchOptionImpl option = new SearchOptionImpl(); assertEquals(SystemQueryOptionKind.SEARCH, option.getKind()); - // $search is not supported yet + + final SearchTermImpl searchExpression = new SearchTermImpl("A"); + option.setSearchExpression(searchExpression); + assertEquals(searchExpression, option.getSearchExpression()); } @Test - public void testSelectItemImpl() { + public void selectItemImpl() { SelectItemImpl option = new SelectItemImpl(); // no typed collection else case ( e.g. if not path is added) @@ -244,7 +248,7 @@ public class QueryOptionTest { } @Test - public void testSelectOptionImpl() { + public void selectOptionImpl() { SelectOptionImpl option = new SelectOptionImpl(); assertEquals(SystemQueryOptionKind.SELECT, option.getKind());