[OLINGO-834] URI parser shall not ignore empty path segments

Signed-off-by: Christian Amend <christian.amend@sap.com>
This commit is contained in:
Klaus Straubinger 2016-01-12 15:05:01 +01:00 committed by Christian Amend
parent b0866014df
commit 3295bcc062
15 changed files with 123 additions and 158 deletions

View File

@ -114,7 +114,7 @@ public class BasicHttpITCase extends AbstractBaseTestITCase {
@Test @Test
public void testIEEE754ParameterContentNegotiation() throws Exception { 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(); final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod(HttpMethod.GET.name()); connection.setRequestMethod(HttpMethod.GET.name());
connection.setRequestProperty(HttpHeader.ACCEPT, "application/json;IEEE754Compatible=false"); connection.setRequestProperty(HttpHeader.ACCEPT, "application/json;IEEE754Compatible=false");
@ -131,7 +131,7 @@ public class BasicHttpITCase extends AbstractBaseTestITCase {
@Test @Test
public void testIEEE754ParameterViaAcceptHeader() throws Exception { 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(); final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod(HttpMethod.GET.name()); connection.setRequestMethod(HttpMethod.GET.name());
connection.setRequestProperty(HttpHeader.ACCEPT, "application/json;IEEE754Compatible=true"); connection.setRequestProperty(HttpHeader.ACCEPT, "application/json;IEEE754Compatible=true");

View File

@ -19,6 +19,7 @@
package org.apache.olingo.server.core.uri.parser; package org.apache.olingo.server.core.uri.parser;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.Collections;
import java.util.Deque; import java.util.Deque;
import java.util.List; import java.util.List;
@ -60,6 +61,7 @@ public class Parser {
private static final String ATOM = "atom"; private static final String ATOM = "atom";
private static final String JSON = "json"; private static final String JSON = "json";
private static final String XML = "xml"; private static final String XML = "xml";
private static final String DOLLAR = "$";
private static final String AT = "@"; private static final String AT = "@";
private static final String NULL = "null"; private static final String NULL = "null";
@ -78,14 +80,19 @@ public class Parser {
Deque<EdmType> contextTypes = new ArrayDeque<EdmType>(); Deque<EdmType> contextTypes = new ArrayDeque<EdmType>();
boolean contextIsCollection = false; boolean contextIsCollection = false;
final List<String> pathSegmentsDecoded = UriDecoder.splitAndDecodePath(path); List<String> pathSegmentsDecoded = UriDecoder.splitAndDecodePath(path);
final int numberOfSegments = pathSegmentsDecoded.size(); 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 // first, read the decoded path segments
final String firstSegment = numberOfSegments == 0 ? "" : pathSegmentsDecoded.get(0); final String firstSegment = pathSegmentsDecoded.get(0);
if (firstSegment.isEmpty()) { if (firstSegment.isEmpty()) {
ensureLastSegment(firstSegment, 0, numberOfSegments); ensureLastSegment(firstSegment, 1, numberOfSegments);
contextUriInfo.setKind(UriInfoKind.service); contextUriInfo.setKind(UriInfoKind.service);
} else if (firstSegment.equals("$batch")) { } else if (firstSegment.equals("$batch")) {
@ -168,11 +175,12 @@ public class Parser {
} }
// second, read the system query options and the custom query options // second, read the system query options and the custom query options
final List<QueryOption> options = UriDecoder.splitAndDecodeOptions(query); final List<QueryOption> options =
query == null ? Collections.<QueryOption> emptyList() : UriDecoder.splitAndDecodeOptions(query);
for (final QueryOption option : options) { for (final QueryOption option : options) {
final String optionName = option.getName(); final String optionName = option.getName();
final String optionValue = option.getText(); final String optionValue = option.getText();
if (optionName.startsWith("$")) { if (optionName.startsWith(DOLLAR)) {
SystemQueryOption systemOption = null; SystemQueryOption systemOption = null;
if (optionName.equals(SystemQueryOptionKind.FILTER.toString())) { if (optionName.equals(SystemQueryOptionKind.FILTER.toString())) {
UriTokenizer filterTokenizer = new UriTokenizer(optionValue); UriTokenizer filterTokenizer = new UriTokenizer(optionValue);
@ -315,7 +323,7 @@ public class Parser {
UriParserSyntaxException.MessageKeys.DUPLICATED_ALIAS, optionName); UriParserSyntaxException.MessageKeys.DUPLICATED_ALIAS, optionName);
} }
} else { } else if (!optionName.isEmpty()) {
contextUriInfo.addCustomQueryOption((CustomQueryOption) option); contextUriInfo.addCustomQueryOption((CustomQueryOption) option);
} }
} }

View File

@ -157,7 +157,7 @@ public class ParserHelper {
final List<EdmKeyPropertyRef> keyPropertyRefs = edmEntityType.getKeyPropertyRefs(); final List<EdmKeyPropertyRef> keyPropertyRefs = edmEntityType.getKeyPropertyRefs();
if (tokenizer.next(TokenKind.CLOSE)) { if (tokenizer.next(TokenKind.CLOSE)) {
throw new UriParserSemanticException( 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, UriParserSemanticException.MessageKeys.WRONG_NUMBER_OF_KEY_PROPERTIES,
Integer.toString(keyPropertyRefs.size()), "0"); Integer.toString(keyPropertyRefs.size()), "0");
} }

View File

@ -19,8 +19,6 @@
package org.apache.olingo.server.core.uri.parser; package org.apache.olingo.server.core.uri.parser;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@ -33,7 +31,7 @@ public class UriDecoder {
/** Splits the path string at '/' characters and percent-decodes the resulting path segments. */ /** Splits the path string at '/' characters and percent-decodes the resulting path segments. */
protected static List<String> splitAndDecodePath(final String path) throws UriParserSyntaxException { protected static List<String> splitAndDecodePath(final String path) throws UriParserSyntaxException {
List<String> pathSegmentsDecoded = new ArrayList<String>(); List<String> pathSegmentsDecoded = new ArrayList<String>();
for (final String segment : splitSkipEmpty(path, '/')) { for (final String segment : split(path, '/')) {
pathSegmentsDecoded.add(decode(segment)); pathSegmentsDecoded.add(decode(segment));
} }
return pathSegmentsDecoded; return pathSegmentsDecoded;
@ -42,58 +40,39 @@ public class UriDecoder {
/** /**
* Splits the query-option string at '&' characters, the resulting parts at '=' characters, * 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. * 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<QueryOption> splitAndDecodeOptions(final String queryOptionString) protected static List<QueryOption> splitAndDecodeOptions(final String queryOptionString)
throws UriParserSyntaxException { throws UriParserSyntaxException {
if (queryOptionString == null || queryOptionString.isEmpty()) {
return Collections.emptyList();
}
List<QueryOption> queryOptions = new ArrayList<QueryOption>(); List<QueryOption> queryOptions = new ArrayList<QueryOption>();
for (final String option : splitSkipEmpty(queryOptionString, '&')) { for (final String option : split(queryOptionString, '&')) {
final List<String> pair = splitFirst(option, '='); 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() queryOptions.add(new CustomQueryOptionImpl()
.setName(decode(pair.get(0))) .setName(decode(name))
.setText(decode(pair.get(1)))); .setText(decode(text)));
} }
return queryOptions; return queryOptions;
} }
private static List<String> 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 input string to split
* @param c character at which to split * @param c character at which to split
* @return list of elements (can be empty) * @return list of elements (can be empty)
*/ */
private static List<String> splitSkipEmpty(final String input, final char c) { private static List<String> split(final String input, final char c) {
if (input.isEmpty() || input.length() == 1 && input.charAt(0) == c) {
return Collections.emptyList();
}
List<String> list = new LinkedList<String>(); List<String> list = new LinkedList<String>();
int start = 0; int start = 0;
int end; int end;
while ((end = input.indexOf(c, start)) >= 0) { 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; start = end + 1;
} }
if (input.charAt(input.length() - 1) != c) {
list.add(input.substring(start)); list.add(input.substring(start));
}
return list; return list;
} }

View File

@ -50,7 +50,7 @@ public class SearchParser {
searchExpression = parse(tokenizer.tokenize(searchQuery)); searchExpression = parse(tokenizer.tokenize(searchQuery));
} catch (SearchTokenizerException e) { } catch (SearchTokenizerException e) {
String message = e.getMessage(); 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); SearchParserException.MessageKeys.TOKENIZER_EXCEPTION, message);
} }
final SearchOptionImpl searchOption = new SearchOptionImpl(); final SearchOptionImpl searchOption = new SearchOptionImpl();

View File

@ -286,9 +286,9 @@ public class UriValidator {
RowIndexForUriType.mediaStream : RowIndexForUriType.propertyPrimitiveValue; RowIndexForUriType.mediaStream : RowIndexForUriType.propertyPrimitiveValue;
break; break;
default: default:
throw new UriValidationException("Unexpected kind in path segment before $value: " throw new UriValidationException(
+ secondLastPathSegment.getKind(), UriValidationException.MessageKeys.UNALLOWED_KIND_BEFORE_VALUE, "Unexpected kind in path segment before $value: " + secondLastPathSegment.getKind(),
secondLastPathSegment.toString()); UriValidationException.MessageKeys.UNALLOWED_KIND_BEFORE_VALUE, secondLastPathSegment.toString());
} }
return idx; return idx;
} }
@ -302,9 +302,9 @@ public class UriValidator {
return ((UriResourcePartTyped) secondLastPathSegment).isCollection() ? return ((UriResourcePartTyped) secondLastPathSegment).isCollection() ?
RowIndexForUriType.references : RowIndexForUriType.reference; RowIndexForUriType.references : RowIndexForUriType.reference;
} else { } else {
throw new UriValidationException("secondLastPathSegment not a class of UriResourcePartTyped: " throw new UriValidationException(
+ lastPathSegment.getClass(), UriValidationException.MessageKeys.LAST_SEGMENT_NOT_TYPED, lastPathSegment "secondLastPathSegment not a class of UriResourcePartTyped: " + lastPathSegment.getClass(),
.toString()); UriValidationException.MessageKeys.LAST_SEGMENT_NOT_TYPED, lastPathSegment.toString());
} }
} }
@ -314,40 +314,36 @@ public class UriValidator {
return ((UriResourcePartTyped) lastPathSegment).isCollection() ? return ((UriResourcePartTyped) lastPathSegment).isCollection() ?
RowIndexForUriType.propertyPrimitiveCollection : RowIndexForUriType.propertyPrimitive; RowIndexForUriType.propertyPrimitiveCollection : RowIndexForUriType.propertyPrimitive;
} else { } else {
throw new UriValidationException("lastPathSegment not a class of UriResourcePartTyped: " throw new UriValidationException(
+ lastPathSegment.getClass(), UriValidationException.MessageKeys.LAST_SEGMENT_NOT_TYPED, lastPathSegment "lastPathSegment not a class of UriResourcePartTyped: " + lastPathSegment.getClass(),
.toString()); UriValidationException.MessageKeys.LAST_SEGMENT_NOT_TYPED, lastPathSegment.toString());
} }
} }
private RowIndexForUriType rowIndexForFunction(final UriResource lastPathSegment) throws UriValidationException { private RowIndexForUriType rowIndexForFunction(final UriResource lastPathSegment) throws UriValidationException {
final UriResourceFunction uriFunction = (UriResourceFunction) lastPathSegment; final UriResourceFunction uriFunction = (UriResourceFunction) lastPathSegment;
final EdmReturnType returnType = uriFunction.getFunction().getReturnType();
if (!uriFunction.getFunction().isComposable()) { if (!uriFunction.getFunction().isComposable()) {
return RowIndexForUriType.none; return RowIndexForUriType.none;
} }
final boolean isCollection = uriFunction.isCollection();
final EdmTypeKind typeKind = uriFunction.getFunction().getReturnType().getType().getKind();
RowIndexForUriType idx; RowIndexForUriType idx;
switch (returnType.getType().getKind()) { switch (typeKind) {
case ENTITY: case ENTITY:
idx = returnType.isCollection() && uriFunction.getKeyPredicates().isEmpty() ? idx = isCollection ? RowIndexForUriType.entitySet : RowIndexForUriType.entity;
RowIndexForUriType.entitySet : RowIndexForUriType.entity;
break; break;
case PRIMITIVE: case PRIMITIVE:
case ENUM: case ENUM:
case DEFINITION: case DEFINITION:
idx = returnType.isCollection() ? RowIndexForUriType.propertyPrimitiveCollection : idx = isCollection ? RowIndexForUriType.propertyPrimitiveCollection : RowIndexForUriType.propertyPrimitive;
RowIndexForUriType.propertyPrimitive;
break; break;
case COMPLEX: case COMPLEX:
idx = returnType.isCollection() ? RowIndexForUriType.propertyComplexCollection : idx = isCollection ? RowIndexForUriType.propertyComplexCollection : RowIndexForUriType.propertyComplex;
RowIndexForUriType.propertyComplex;
break; break;
default: default:
throw new UriValidationException("Unsupported function return type: " + returnType.getType().getKind(), throw new UriValidationException("Unsupported function return type: " + typeKind,
UriValidationException.MessageKeys.UNSUPPORTED_FUNCTION_RETURN_TYPE, UriValidationException.MessageKeys.UNSUPPORTED_FUNCTION_RETURN_TYPE, typeKind.toString());
returnType.getType().getKind().toString());
} }
return idx; return idx;
@ -512,18 +508,17 @@ public class UriValidator {
for (SystemQueryOption option : uriInfo.getSystemQueryOptions()) { for (SystemQueryOption option : uriInfo.getSystemQueryOptions()) {
options.append(option.getName()).append(" "); options.append(option.getName()).append(" ");
} }
throw new UriValidationException("System query option " + options.toString() + " not allowed for method " throw new UriValidationException(
+ httpMethod, UriValidationException.MessageKeys.SYSTEM_QUERY_OPTION_NOT_ALLOWED_FOR_HTTP_METHOD, "System query option " + options.toString() + " not allowed for method " + httpMethod,
UriValidationException.MessageKeys.SYSTEM_QUERY_OPTION_NOT_ALLOWED_FOR_HTTP_METHOD,
options.toString(), httpMethod.toString()); options.toString(), httpMethod.toString());
} }
} }
private boolean isAction(final UriInfo uriInfo) { private boolean isAction(final UriInfo uriInfo) {
List<UriResource> uriResourceParts = uriInfo.getUriResourceParts(); List<UriResource> uriResourceParts = uriInfo.getUriResourceParts();
if (uriResourceParts.isEmpty()) { return !uriResourceParts.isEmpty()
return false; && UriResourceKind.action == uriResourceParts.get(uriResourceParts.size() - 1).getKind();
}
return UriResourceKind.action == uriResourceParts.get(uriResourceParts.size() - 1).getKind();
} }
private void validateParameters(final UriInfo uriInfo) throws UriValidationException { private void validateParameters(final UriInfo uriInfo) throws UriValidationException {
@ -531,18 +526,18 @@ public class UriValidator {
final boolean isFunction = pathSegment.getKind() == UriResourceKind.function; final boolean isFunction = pathSegment.getKind() == UriResourceKind.function;
if (isFunction) { if (isFunction) {
final UriResourceFunction functionPathSegement = (UriResourceFunction) pathSegment; final UriResourceFunction functionPathSegment = (UriResourceFunction) pathSegment;
final EdmFunction edmFuntion = functionPathSegement.getFunction(); final EdmFunction edmFunction = functionPathSegment.getFunction();
final Map<String, UriParameter> parameters = new HashMap<String, UriParameter>(); final Map<String, UriParameter> parameters = new HashMap<String, UriParameter>();
for (final UriParameter parameter : functionPathSegement.getParameters()) { for (final UriParameter parameter : functionPathSegment.getParameters()) {
parameters.put(parameter.getName(), parameter); parameters.put(parameter.getName(), parameter);
} }
boolean firstParameter = true; boolean firstParameter = true;
for (final String parameterName : edmFuntion.getParameterNames()) { for (final String parameterName : edmFunction.getParameterNames()) {
final UriParameter parameter = parameters.get(parameterName); final UriParameter parameter = parameters.get(parameterName);
final boolean isNullable = edmFuntion.getParameter(parameterName).isNullable(); final boolean isNullable = edmFunction.getParameter(parameterName).isNullable();
if (parameter != null) { if (parameter != null) {
/** No alias, value explicit null */ /** No alias, value explicit null */
@ -560,7 +555,7 @@ public class UriValidator {
} }
parameters.remove(parameterName); 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 // The first parameter of bound functions is implicit provided by the preceding path segment
throw new UriValidationException("Missing non nullable parameter " + parameterName, throw new UriValidationException("Missing non nullable parameter " + parameterName,
UriValidationException.MessageKeys.MISSING_PARAMETER, parameterName); UriValidationException.MessageKeys.MISSING_PARAMETER, parameterName);
@ -586,11 +581,11 @@ public class UriValidator {
if (isEntitySet || pathSegment.getKind() == UriResourceKind.navigationProperty || isEntityColFunction) { if (isEntitySet || pathSegment.getKind() == UriResourceKind.navigationProperty || isEntityColFunction) {
final List<UriParameter> keyPredicates = isEntitySet ? final List<UriParameter> keyPredicates = isEntitySet ?
((UriResourceEntitySet) pathSegment).getKeyPredicates() : ((UriResourceEntitySet) pathSegment).getKeyPredicates() :
isEntityColFunction ? ((UriResourceFunction) pathSegment).getKeyPredicates() isEntityColFunction ?
: ((UriResourceNavigation) pathSegment).getKeyPredicates(); ((UriResourceFunction) pathSegment).getKeyPredicates() :
((UriResourceNavigation) pathSegment).getKeyPredicates();
if (keyPredicates != null) { if (keyPredicates != null) {
final EdmEntityType entityType = isEntitySet ? final EdmEntityType entityType = isEntitySet ?
((UriResourceEntitySet) pathSegment).getEntityType() : ((UriResourceEntitySet) pathSegment).getEntityType() :
isEntityColFunction ? (EdmEntityType) ((UriResourceFunction) pathSegment).getType() isEntityColFunction ? (EdmEntityType) ((UriResourceFunction) pathSegment).getType()

View File

@ -290,15 +290,13 @@ public class ExpressionParserTest {
} }
expressionString += ')'; expressionString += ')';
Expression expression = parseExpression(expressionString); return parseExpression(expressionString);
assertNotNull(expression);
return expression;
} }
private Expression parseExpression(final String expressionString) private Expression parseExpression(final String expressionString)
throws UriParserException, UriValidationException { throws UriParserException, UriValidationException {
UriTokenizer tokenizer = new UriTokenizer(expressionString); 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); assertNotNull(expression);
assertTrue(tokenizer.next(TokenKind.EOF)); assertTrue(tokenizer.next(TokenKind.EOF));
return expression; return expression;
@ -306,7 +304,7 @@ public class ExpressionParserTest {
private void wrongExpression(final String expressionString) { private void wrongExpression(final String expressionString) {
try { try {
new ExpressionParser(mock(Edm.class), odata).parse(new UriTokenizer(expressionString), null, null); parseExpression(expressionString);
fail("Expected exception not thrown."); fail("Expected exception not thrown.");
} catch (final UriParserException e) { } catch (final UriParserException e) {
assertNotNull(e); assertNotNull(e);

View File

@ -16,7 +16,7 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * 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.assertEquals;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@ -28,21 +28,9 @@ import org.junit.Test;
/** /**
* Tests originally written for the ANTLR lexer. * Tests originally written for the ANTLR lexer.
*/ */
public class TestLexer { public class LexerTest {
private TokenValidator test = null; private TokenValidator test = new TokenValidator();
// 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();
}
@Test @Test
public void unary() { public void unary() {
@ -271,7 +259,14 @@ public class TestLexer {
@Test @Test
public void delims() { 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 lexer rule UNRESERVED
// test.run("$format=A/" + cUNRESERVED).has(TokenKind.FORMAT).isInput(); // test.run("$format=A/" + cUNRESERVED).has(TokenKind.FORMAT).isInput();
// test.run("$format=A/" + cUNRESERVED + reserved).has(TokenKind.FORMAT).isText(cUNRESERVED); // test.run("$format=A/" + cUNRESERVED + reserved).has(TokenKind.FORMAT).isText(cUNRESERVED);

View File

@ -19,7 +19,6 @@
package org.apache.olingo.server.core.uri.parser; package org.apache.olingo.server.core.uri.parser;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import java.util.Arrays; import java.util.Arrays;
@ -32,13 +31,14 @@ public class UriDecoderTest {
@Test @Test
public void split() throws Exception { public void split() throws Exception {
assertTrue(UriDecoder.splitAndDecodePath("").isEmpty()); assertEquals(Arrays.asList(""), UriDecoder.splitAndDecodePath(""));
assertTrue(UriDecoder.splitAndDecodePath("/").isEmpty()); 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", ""), 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", "b"), UriDecoder.splitAndDecodePath("a/b"));
assertEquals(Arrays.asList("a", "a"), UriDecoder.splitAndDecodePath("/a/a")); assertEquals(Arrays.asList("", "a", "b"), UriDecoder.splitAndDecodePath("/a/b"));
assertEquals(Arrays.asList("", "a", "", "", "b", ""), UriDecoder.splitAndDecodePath("/a///b/"));
} }
@Test @Test
@ -49,7 +49,7 @@ public class UriDecoderTest {
@Test @Test
public void options() throws Exception { public void options() throws Exception {
assertTrue(UriDecoder.splitAndDecodeOptions("").isEmpty()); checkOption("", "", "");
checkOption("a", "a", ""); checkOption("a", "a", "");
checkOption("a=b", "a", "b"); checkOption("a=b", "a", "b");
@ -67,6 +67,7 @@ public class UriDecoderTest {
checkOption("=&=", "", ""); checkOption("=&=", "", "");
assertEquals(2, UriDecoder.splitAndDecodeOptions("=&=").size()); assertEquals(2, UriDecoder.splitAndDecodeOptions("=&=").size());
assertEquals(13, UriDecoder.splitAndDecodeOptions("&&&&&&&&&&&&").size());
checkOption("=&c=d", "", ""); checkOption("=&c=d", "", "");
checkOption("=&c=d", "c", "d"); checkOption("=&c=d", "c", "d");

View File

@ -194,7 +194,7 @@ public class SearchParserAndTokenizerTest {
} }
private static Validator assertQuery(final String searchQuery) { private static Validator assertQuery(final String searchQuery) {
return Validator.init(searchQuery); return new Validator(searchQuery);
} }
private static class Validator { private static class Validator {
@ -204,18 +204,12 @@ public class SearchParserAndTokenizerTest {
this.searchQuery = searchQuery; this.searchQuery = searchQuery;
} }
private static Validator init(final String searchQuery) { private void resultsIn(final SearchParserException.MessageKey key) throws SearchTokenizerException {
return new Validator(searchQuery);
}
private void resultsIn(final SearchParserException.MessageKey key)
throws SearchTokenizerException {
try { try {
resultsIn(searchQuery); resultsIn(searchQuery);
} catch (SearchParserException e) { } catch (final SearchParserException e) {
Assert.assertEquals("SearchParserException with unexpected message '" + e.getMessage() + Assert.assertEquals("SearchParserException with unexpected message '" + e.getMessage() + "' was thrown.",
"' was thrown.", key, e.getMessageKey()); key, e.getMessageKey());
return; return;
} }
Assert.fail("SearchParserException with message key " + key.getKey() + " was not thrown."); 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 { public void resultsInExpectedTerm(final String actualToken) throws SearchTokenizerException {
try { try {
resultsIn(searchQuery); resultsIn(searchQuery);
} catch (SearchParserException e) { } catch (final SearchParserException e) {
Assert.assertEquals(SearchParserException.MessageKeys.EXPECTED_DIFFERENT_TOKEN, e.getMessageKey()); Assert.assertEquals(SearchParserException.MessageKeys.EXPECTED_DIFFERENT_TOKEN, e.getMessageKey());
Assert.assertEquals("Expected PHRASE||WORD found: " + actualToken, e.getMessage()); 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 { private void resultsIn(final String expectedSearchExpression)
final SearchExpression searchExpression = getSearchExpression(); throws SearchTokenizerException, SearchParserException {
Assert.assertEquals(expectedSearchExpression, searchExpression.toString()); final SearchOption result = new SearchParser().parse(searchQuery);
}
private SearchExpression getSearchExpression() throws SearchParserException, SearchTokenizerException {
SearchOption result = new SearchParser().parse(searchQuery);
Assert.assertNotNull(result); Assert.assertNotNull(result);
final SearchExpression searchExpression = result.getSearchExpression(); final SearchExpression searchExpression = result.getSearchExpression();
Assert.assertNotNull(searchExpression); Assert.assertNotNull(searchExpression);
return searchExpression; Assert.assertEquals(expectedSearchExpression, searchExpression.toString());
} }
} }
} }

View File

@ -224,34 +224,34 @@ public class SearchParserTest extends SearchParser {
private void runEx(final MessageKeys key, final Token... tokenArray) { private void runEx(final MessageKeys key, final Token... tokenArray) {
try { try {
run(tokenArray); run(tokenArray);
fail("Expected UriParserSyntaxException with key " + key); fail("Expected SearchParserException with key " + key);
} catch (SearchParserException e) { } catch (SearchParserException e) {
assertEquals(key, e.getMessageKey()); assertEquals(key, e.getMessageKey());
} }
} }
private SearchExpression run(final SearchQueryToken.Token... tokenArray) throws SearchParserException { private SearchExpression run(final Token... tokenArray) throws SearchParserException {
List<SearchQueryToken> tokenList = prepareTokens(tokenArray); List<SearchQueryToken> tokenList = prepareTokens(tokenArray);
SearchExpression se = parse(tokenList); SearchExpression se = parse(tokenList);
assertNotNull(se); assertNotNull(se);
return se; return se;
} }
public List<SearchQueryToken> prepareTokens(final SearchQueryToken.Token... tokenArray) { public List<SearchQueryToken> prepareTokens(final Token... tokenArray) {
ArrayList<SearchQueryToken> tokenList = new ArrayList<SearchQueryToken>(); ArrayList<SearchQueryToken> tokenList = new ArrayList<SearchQueryToken>();
int wordNumber = 1; int wordNumber = 1;
int phraseNumber = 1; int phraseNumber = 1;
for (Token aTokenArray : tokenArray) { for (Token aToken : tokenArray) {
SearchQueryToken token = mock(SearchQueryToken.class); SearchQueryToken token = mock(SearchQueryToken.class);
when(token.getToken()).thenReturn(aTokenArray); when(token.getToken()).thenReturn(aToken);
if (aTokenArray == Token.WORD) { if (aToken == Token.WORD) {
when(token.getLiteral()).thenReturn("word" + wordNumber); when(token.getLiteral()).thenReturn("word" + wordNumber);
wordNumber++; wordNumber++;
} else if (aTokenArray == Token.PHRASE) { } else if (aToken == Token.PHRASE) {
when(token.getLiteral()).thenReturn("\"phrase" + phraseNumber + "\""); when(token.getLiteral()).thenReturn("\"phrase" + phraseNumber + "\"");
phraseNumber++; phraseNumber++;
} }
when(token.toString()).thenReturn("" + aTokenArray); when(token.toString()).thenReturn("" + aToken);
tokenList.add(token); tokenList.add(token);
} }
return tokenList; return tokenList;

View File

@ -269,7 +269,7 @@ public class SearchTokenizerTest {
assertQuery("\"\"").resultsIn(SearchTokenizerException.MessageKeys.INVALID_TOKEN_STATE); assertQuery("\"\"").resultsIn(SearchTokenizerException.MessageKeys.INVALID_TOKEN_STATE);
assertQuery("some AND)").resultsIn(SearchTokenizerException.MessageKeys.FORBIDDEN_CHARACTER); assertQuery("some AND)").resultsIn(SearchTokenizerException.MessageKeys.FORBIDDEN_CHARACTER);
assertQuery("some OR)").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 @Test
@ -308,7 +308,6 @@ public class SearchTokenizerTest {
private static class Validator { private static class Validator {
private List<Tuple> validations = new ArrayList<Tuple>(); private List<Tuple> validations = new ArrayList<Tuple>();
private boolean log;
private final String searchQuery; private final String searchQuery;
public void resultsIn(final SearchQueryToken.Token... tokens) throws SearchTokenizerException { public void resultsIn(final SearchQueryToken.Token... tokens) throws SearchTokenizerException {
@ -345,11 +344,6 @@ public class SearchTokenizerTest {
this.searchQuery = searchQuery; this.searchQuery = searchQuery;
} }
private Validator enableLogging() {
log = true;
return this;
}
private Validator addExpected(final SearchQueryToken.Token token, final String literal) { private Validator addExpected(final SearchQueryToken.Token token, final String literal) {
validations.add(new Tuple(token, literal)); validations.add(new Tuple(token, literal));
return this; return this;
@ -368,10 +362,6 @@ public class SearchTokenizerTest {
validate(); validate();
} catch (SearchTokenizerException e) { } catch (SearchTokenizerException e) {
Assert.assertEquals("SearchTokenizerException with unexpected message was thrown.", key, e.getMessageKey()); 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; return;
} }
Assert.fail("No SearchTokenizerException was not thrown."); Assert.fail("No SearchTokenizerException was not thrown.");
@ -381,9 +371,6 @@ public class SearchTokenizerTest {
SearchTokenizer tokenizer = new SearchTokenizer(); SearchTokenizer tokenizer = new SearchTokenizer();
List<SearchQueryToken> result = tokenizer.tokenize(searchQuery); List<SearchQueryToken> result = tokenizer.tokenize(searchQuery);
Assert.assertNotNull(result); Assert.assertNotNull(result);
if (log) {
System.out.println(result);
}
if (validations.size() != 0) { if (validations.size() != 0) {
Assert.assertEquals(validations.size(), result.size()); Assert.assertEquals(validations.size(), result.size());

View File

@ -16,7 +16,7 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * 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.Arrays;
import java.util.Collections; import java.util.Collections;
@ -3011,6 +3011,11 @@ public class TestFullResourcePath {
testUri.run("$batch") testUri.run("$batch")
.isKind(UriInfoKind.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("$metadata/$ref").isExSyntax(UriParserSyntaxException.MessageKeys.MUST_BE_LAST_SEGMENT);
testUri.runEx("$batch/$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); testUri.runEx("$all/$ref").isExSyntax(UriParserSyntaxException.MessageKeys.MUST_BE_LAST_SEGMENT);

View File

@ -16,7 +16,7 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * 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.Arrays;
import java.util.Collections; import java.util.Collections;

View File

@ -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.SystemQueryOptionKind;
import org.apache.olingo.server.api.uri.queryoption.expression.Expression; 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.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.AliasImpl;
import org.apache.olingo.server.core.uri.queryoption.expression.LiteralImpl; import org.apache.olingo.server.core.uri.queryoption.expression.LiteralImpl;
import org.junit.Test; import org.junit.Test;
//TOOD add getKind check to all
public class QueryOptionTest { public class QueryOptionTest {
@Test @Test
@ -196,6 +196,7 @@ public class QueryOptionTest {
@Test @Test
public void testOrderByOptionImpl() { public void testOrderByOptionImpl() {
OrderByOptionImpl option = new OrderByOptionImpl(); OrderByOptionImpl option = new OrderByOptionImpl();
assertEquals(SystemQueryOptionKind.ORDERBY, option.getKind());
OrderByItemImpl order0 = new OrderByItemImpl(); OrderByItemImpl order0 = new OrderByItemImpl();
OrderByItemImpl order1 = new OrderByItemImpl(); OrderByItemImpl order1 = new OrderByItemImpl();
@ -217,14 +218,17 @@ public class QueryOptionTest {
} }
@Test @Test
public void testSearchOptionImpl() { public void searchOptionImpl() {
SearchOptionImpl option = new SearchOptionImpl(); SearchOptionImpl option = new SearchOptionImpl();
assertEquals(SystemQueryOptionKind.SEARCH, option.getKind()); assertEquals(SystemQueryOptionKind.SEARCH, option.getKind());
// $search is not supported yet
final SearchTermImpl searchExpression = new SearchTermImpl("A");
option.setSearchExpression(searchExpression);
assertEquals(searchExpression, option.getSearchExpression());
} }
@Test @Test
public void testSelectItemImpl() { public void selectItemImpl() {
SelectItemImpl option = new SelectItemImpl(); SelectItemImpl option = new SelectItemImpl();
// no typed collection else case ( e.g. if not path is added) // no typed collection else case ( e.g. if not path is added)
@ -244,7 +248,7 @@ public class QueryOptionTest {
} }
@Test @Test
public void testSelectOptionImpl() { public void selectOptionImpl() {
SelectOptionImpl option = new SelectOptionImpl(); SelectOptionImpl option = new SelectOptionImpl();
assertEquals(SystemQueryOptionKind.SELECT, option.getKind()); assertEquals(SystemQueryOptionKind.SELECT, option.getKind());