http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8925274c/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriTokenizer.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriTokenizer.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriTokenizer.java index f505a21..3b673a6 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriTokenizer.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriTokenizer.java @@ -40,6 +40,15 @@ public class UriTokenizer { ROOT, IT, + EXPAND, + FILTER, + LEVELS, + ORDERBY, + SEARCH, + SELECT, + SKIP, + TOP, + ANY, ALL, @@ -53,8 +62,9 @@ public class UriTokenizer { EQ, STAR, PLUS, - MINUS, + NULL, + MAX, // variable-value tokens (convention: mixed case) ODataIdentifier, @@ -76,6 +86,13 @@ public class UriTokenizer { jsonArrayOrObject, + Word, + Phrase, + + OrOperatorSearch, + AndOperatorSearch, + NotOperatorSearch, + OrOperator, AndOperator, EqualsOperator, @@ -90,6 +107,7 @@ public class UriTokenizer { MulOperator, DivOperator, ModOperator, + MinusOperator, NotOperator, CastMethod, @@ -161,6 +179,10 @@ public class UriTokenizer { boolean found = false; final int previousIndex = index; switch (allowedTokenKind) { + case EOF: + found = index >= parseString.length(); + break; + // Constants case REF: found = nextConstant("$ref"); @@ -181,6 +203,31 @@ public class UriTokenizer { found = nextConstant("$it"); break; + case EXPAND: + found = nextConstant("$expand"); + break; + case FILTER: + found = nextConstant("$filter"); + break; + case LEVELS: + found = nextConstant("$levels"); + break; + case ORDERBY: + found = nextConstant("$orderby"); + break; + case SEARCH: + found = nextConstant("$search"); + break; + case SELECT: + found = nextConstant("$select"); + break; + case SKIP: + found = nextConstant("$skip"); + break; + case TOP: + found = nextConstant("$top"); + break; + case ANY: found = nextConstant("any"); break; @@ -218,14 +265,12 @@ public class UriTokenizer { case PLUS: found = nextCharacter('+'); break; - case MINUS: - found = nextMinus(); - break; + case NULL: found = nextConstant("null"); break; - case EOF: - found = index >= parseString.length(); + case MAX: + found = nextConstant("max"); break; // Identifiers @@ -282,6 +327,25 @@ public class UriTokenizer { found = nextJsonArrayOrObject(); break; + // Search + case Word: + found = nextWord(); + break; + case Phrase: + found = nextPhrase(); + break; + + // Operators in Search Expressions + case OrOperatorSearch: + found = nextBinaryOperator("OR"); + break; + case AndOperatorSearch: + found = nextAndOperatorSearch(); + break; + case NotOperatorSearch: + found = nextUnaryOperator("NOT"); + break; + // Operators case OrOperator: found = nextBinaryOperator("or"); @@ -325,8 +389,12 @@ public class UriTokenizer { case ModOperator: found = nextBinaryOperator("mod"); break; + case MinusOperator: + // To avoid unnecessary minus operators for negative numbers, we have to check what follows the minus sign. + found = nextCharacter('-') && !nextDigit() && !nextConstant("INF"); + break; case NotOperator: - found = nextConstant("not") && nextWhitespace(); + found = nextUnaryOperator("not"); break; // Methods @@ -444,27 +512,6 @@ public class UriTokenizer { return found; } - private boolean nextMinus() { - if(parseString.startsWith("-", index)) { - final int lastGoodIndex = index; - - if(nextDoubleValue()) { - index = lastGoodIndex; - return false; - } else if(nextDecimalValue()) { - index = lastGoodIndex; - return false; - } else if(nextIntegerValue(true)) { - index = lastGoodIndex; - return false; - } else { - index++; - return true; - } - } - return false; - } - /** * Moves past the given string constant if found; otherwise leaves the index unchanged. * @return whether the constant has been found at the current index @@ -569,34 +616,6 @@ public class UriTokenizer { } return count > 0; } - - /** - * Moves past the given whitespace-surrounded operator constant if found; - * otherwise leaves the index unchanged. - * @return whether the operator has been found at the current index - */ - private boolean nextBinaryOperator(final String operator) { - return nextWhitespace() && nextConstant(operator) && nextWhitespace(); - } - - /** - * Moves past the given method name and its immediately following opening parenthesis if found; - * otherwise leaves the index unchanged. - * @return whether the method has been found at the current index - */ - private boolean nextMethod(final String methodName) { - return nextConstant(methodName) && nextCharacter('('); - } - - /** - * Moves past (required) whitespace and the given suffix name if found; - * otherwise leaves the index unchanged. - * @return whether the suffix has been found at the current index - */ - private boolean nextSuffix(final String suffixName) { - return nextWhitespace() && nextConstant(suffixName); - } - /** * Moves past an OData identifier if found; otherwise leaves the index unchanged. * @return whether an OData identifier has been found at the current index @@ -650,6 +669,38 @@ public class UriTokenizer { } } + /** + * Moves past the given whitespace-surrounded operator constant if found. + * @return whether the operator has been found at the current index + */ + private boolean nextBinaryOperator(final String operator) { + return nextWhitespace() && nextConstant(operator) && nextWhitespace(); + } + + /** + * Moves past the given whitespace-suffixed operator constant if found. + * @return whether the operator has been found at the current index + */ + private boolean nextUnaryOperator(final String operator) { + return nextConstant(operator) && nextWhitespace(); + } + + /** + * Moves past the given method name and its immediately following opening parenthesis if found. + * @return whether the method has been found at the current index + */ + private boolean nextMethod(final String methodName) { + return nextConstant(methodName) && nextCharacter('('); + } + + /** + * Moves past (required) whitespace and the given suffix name if found. + * @return whether the suffix has been found at the current index + */ + private boolean nextSuffix(final String suffixName) { + return nextWhitespace() && nextConstant(suffixName); + } + private boolean nextParameterAliasName() { return nextCharacter('@') && nextODataIdentifier(); } @@ -978,4 +1029,52 @@ public class UriTokenizer { return false; } } + + private boolean nextAndOperatorSearch() { + if (nextWhitespace()) { + final int lastGoodIndex = index; + if (nextUnaryOperator("OR")) { + return false; + } else if (!(nextUnaryOperator("AND"))) { + index = lastGoodIndex; + } + return true; + } else { + return false; + } + } + + private boolean nextWord() { + int count = 0; + while (index < parseString.length()) { + final int code = parseString.codePointAt(index); + if (Character.isUnicodeIdentifierStart(code)) { + count++; + // Unicode characters outside of the Basic Multilingual Plane are represented as two Java characters. + index += Character.isSupplementaryCodePoint(code) ? 2 : 1; + } else { + break; + } + } + final String word = parseString.substring(index - count, index); + return count > 0 && !("OR".equals(word) || "AND".equals(word) || "NOT".equals(word)); + } + + private boolean nextPhrase() { + if (nextCharacter('"')) { + do { + if (nextCharacter('\\')) { + if (!(nextCharacter('\\') || nextCharacter('"'))) { + return false; + } + } else if (nextCharacter('"')) { + return true; + } else { + index++; + } + } while (index < parseString.length()); + return false; + } + return false; + } }
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8925274c/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/AliasImpl.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/AliasImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/AliasImpl.java index c7d7c20..8c8dab9 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/AliasImpl.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/AliasImpl.java @@ -41,4 +41,8 @@ public class AliasImpl implements Alias { return visitor.visitAlias(parameterName); } + @Override + public String toString() { + return parameterName; + } } http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8925274c/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/EnumerationImpl.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/EnumerationImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/EnumerationImpl.java index 256b8d1..a238104 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/EnumerationImpl.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/EnumerationImpl.java @@ -53,4 +53,10 @@ public class EnumerationImpl implements Enumeration { public <T> T accept(final ExpressionVisitor<T> visitor) throws ExpressionVisitException, ODataApplicationException { return visitor.visitEnum(type, values); } + + @Override + public String toString() { + return type == null ? null : + type.getFullQualifiedName().getFullQualifiedNameAsString() + getValues(); + } } http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8925274c/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/LambdaRefImpl.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/LambdaRefImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/LambdaRefImpl.java index 824943a..16232b8 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/LambdaRefImpl.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/LambdaRefImpl.java @@ -40,4 +40,9 @@ public class LambdaRefImpl implements LambdaRef { public <T> T accept(final ExpressionVisitor<T> visitor) throws ExpressionVisitException, ODataApplicationException { return visitor.visitLambdaReference(variableText); } + + @Override + public String toString() { + return variableText; + } } http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8925274c/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/TypeLiteralImpl.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/TypeLiteralImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/TypeLiteralImpl.java index 336c203..6a2a1c6 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/TypeLiteralImpl.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/TypeLiteralImpl.java @@ -41,4 +41,9 @@ public class TypeLiteralImpl implements TypeLiteral { public <T> T accept(final ExpressionVisitor<T> visitor) throws ExpressionVisitException, ODataApplicationException { return visitor.visitTypeLiteral(type); } + + @Override + public String toString() { + return type == null ? null : type.getFullQualifiedName().getFullQualifiedNameAsString(); + } } http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8925274c/lib/server-core/src/main/resources/server-core-exceptions-i18n.properties ---------------------------------------------------------------------- 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 0b43f70..e178fed 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 @@ -81,11 +81,11 @@ UriParserSemanticException.COMPLEX_PROPERTY_OF_ENTITY_TYPE_EXPECTED=A complex pr UriParserSemanticException.NOT_FOR_ENTITY_TYPE=Not allowed for entity type. UriParserSemanticException.PREVIOUS_PART_TYPED=The previous part is typed. UriParserSemanticException.RESOURCE_NOT_FOUND=Cannot find EntitySet, Singleton, ActionImport or FunctionImport with name '%1$s'. -UriParserSemanticException.NOT_IMPLEMENTED=%1$s is not implemented! -UriParserSemanticException.NAMESPACE_NOT_ALLOWED_AT_FIRST_ELEMENT=Namespace is not allowed for Entity Sets, Singeltons, Action Imports and Function Imports. Found '%1$s'. -UriParserSemanticException.COMPLEX_PARAMETER_IN_RESOURCE_PATH=Complex parameters must not appear in resource path segments. Found: '%1$s'. +UriParserSemanticException.NOT_IMPLEMENTED='%1$s' is not implemented! +UriParserSemanticException.NAMESPACE_NOT_ALLOWED_AT_FIRST_ELEMENT=Namespace is not allowed for Entity Sets, Singletons, Action Imports and Function Imports; found '%1$s'. +UriParserSemanticException.COMPLEX_PARAMETER_IN_RESOURCE_PATH=Complex parameters must not appear in resource path segments; found: '%1$s'. UriParserSemanticException.FUNCTION_IMPORT_NOT_ALLOWED=Function Imports are not allowed in $filter or $orderby. Found: '%1$s'. -UriParserSemanticException.TYPES_NOT_COMPATIBLE=Types are not compatible. Left type: '%1$s', right type: '%1$s'. +UriParserSemanticException.TYPES_NOT_COMPATIBLE=The types '%1$s' and '%2$s' are not compatible. UriValidationException.UNSUPPORTED_QUERY_OPTION=The query option '%1$s' is not supported. UriValidationException.UNSUPPORTED_URI_KIND=The URI kind '%1$s' is not supported. http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8925274c/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/ExpressionParserTest.java ---------------------------------------------------------------------- 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 4ab7fce..94d5373 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 @@ -22,9 +22,11 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.mockito.Mockito.mock; import java.util.Locale; +import org.apache.olingo.commons.api.edm.Edm; import org.apache.olingo.server.api.OData; import org.apache.olingo.server.api.uri.queryoption.expression.Expression; import org.apache.olingo.server.core.uri.parser.UriTokenizer.TokenKind; @@ -260,9 +262,17 @@ public class ExpressionParserTest { expression = parseMethod(TokenKind.SubstringMethod, "'abc'", "1"); assertEquals("{substring ['abc', 1]}", expression.toString()); + assertEquals("{cast [Edm.SByte]}", parseMethod(TokenKind.CastMethod, "Edm.SByte").toString()); + assertEquals("{cast [42, Edm.SByte]}", parseMethod(TokenKind.CastMethod, "42", "Edm.SByte").toString()); + + assertEquals("{isof [Edm.SByte]}", parseMethod(TokenKind.IsofMethod, "Edm.SByte").toString()); + assertEquals("{isof [42, Edm.SByte]}", parseMethod(TokenKind.IsofMethod, "42", "Edm.SByte").toString()); + wrongExpression("substring('abc')"); wrongExpression("substring('abc',1,2,3)"); wrongExpression("substring(1,2)"); + wrongExpression("cast(1,2)"); + wrongExpression("isof(Edm.Int16,2)"); } private Expression parseMethod(TokenKind kind, String... parameters) @@ -288,7 +298,7 @@ public class ExpressionParserTest { private Expression parseExpression(final String expressionString) throws UriParserException, UriValidationException { UriTokenizer tokenizer = new UriTokenizer(expressionString); - Expression expression = new ExpressionParser(null, odata).parse(tokenizer, null, null); + Expression expression = new ExpressionParser(mock(Edm.class), odata).parse(tokenizer, null, null); assertNotNull(expression); assertTrue(tokenizer.next(TokenKind.EOF)); return expression; @@ -296,7 +306,7 @@ public class ExpressionParserTest { private void wrongExpression(final String expressionString) { try { - new ExpressionParser(null, odata).parse(new UriTokenizer(expressionString), null, null); + new ExpressionParser(mock(Edm.class), odata).parse(new UriTokenizer(expressionString), null, null); fail("Expected exception not thrown."); } catch (final UriParserException e) { assertNotNull(e); http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8925274c/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/UriTokenizerTest.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/UriTokenizerTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/UriTokenizerTest.java index af45e80..e130457 100644 --- a/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/UriTokenizerTest.java +++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/UriTokenizerTest.java @@ -76,7 +76,7 @@ public class UriTokenizerTest { assertTrue(tokenizer.next(TokenKind.STAR)); assertTrue(tokenizer.next(TokenKind.SLASH)); assertTrue(tokenizer.next(TokenKind.PLUS)); - assertTrue(tokenizer.next(TokenKind.MINUS)); + assertTrue(tokenizer.next(TokenKind.MinusOperator)); assertTrue(tokenizer.next(TokenKind.EOF)); tokenizer = new UriTokenizer("any(a:true) or all(b:false)"); @@ -97,6 +97,45 @@ public class UriTokenizerTest { } @Test + public void systemQueryOptions() { + UriTokenizer tokenizer = new UriTokenizer("$expand=*;$filter=true;$levels=max;$orderby=false"); + assertTrue(tokenizer.next(TokenKind.EXPAND)); + assertTrue(tokenizer.next(TokenKind.EQ)); + assertTrue(tokenizer.next(TokenKind.STAR)); + assertTrue(tokenizer.next(TokenKind.SEMI)); + assertTrue(tokenizer.next(TokenKind.FILTER)); + assertTrue(tokenizer.next(TokenKind.EQ)); + assertTrue(tokenizer.next(TokenKind.BooleanValue)); + assertTrue(tokenizer.next(TokenKind.SEMI)); + assertTrue(tokenizer.next(TokenKind.LEVELS)); + assertTrue(tokenizer.next(TokenKind.EQ)); + assertTrue(tokenizer.next(TokenKind.MAX)); + assertTrue(tokenizer.next(TokenKind.SEMI)); + assertTrue(tokenizer.next(TokenKind.ORDERBY)); + assertTrue(tokenizer.next(TokenKind.EQ)); + assertTrue(tokenizer.next(TokenKind.BooleanValue)); + assertTrue(tokenizer.next(TokenKind.EOF)); + + tokenizer = new UriTokenizer("$search=A;$select=*;$skip=1;$top=2"); + assertTrue(tokenizer.next(TokenKind.SEARCH)); + assertTrue(tokenizer.next(TokenKind.EQ)); + assertTrue(tokenizer.next(TokenKind.ODataIdentifier)); + assertTrue(tokenizer.next(TokenKind.SEMI)); + assertTrue(tokenizer.next(TokenKind.SELECT)); + assertTrue(tokenizer.next(TokenKind.EQ)); + assertTrue(tokenizer.next(TokenKind.STAR)); + assertTrue(tokenizer.next(TokenKind.SEMI)); + assertTrue(tokenizer.next(TokenKind.SKIP)); + assertTrue(tokenizer.next(TokenKind.EQ)); + assertTrue(tokenizer.next(TokenKind.IntegerValue)); + assertTrue(tokenizer.next(TokenKind.SEMI)); + assertTrue(tokenizer.next(TokenKind.TOP)); + assertTrue(tokenizer.next(TokenKind.EQ)); + assertTrue(tokenizer.next(TokenKind.IntegerValue)); + assertTrue(tokenizer.next(TokenKind.EOF)); + } + + @Test public void identifier() { assertTrue(new UriTokenizer("name").next(TokenKind.ODataIdentifier)); assertTrue(new UriTokenizer("_name").next(TokenKind.ODataIdentifier)); @@ -390,11 +429,11 @@ public class UriTokenizerTest { assertTrue(tokenizer.next(TokenKind.IntegerValue)); assertTrue(tokenizer.next(TokenKind.EOF)); - tokenizer = new UriTokenizer("1ne 2"); + tokenizer = new UriTokenizer("-1ne 2"); assertTrue(tokenizer.next(TokenKind.IntegerValue)); assertFalse(tokenizer.next(TokenKind.NotEqualsOperator)); - tokenizer = new UriTokenizer("1 ne2"); + tokenizer = new UriTokenizer("1 ne-2"); assertTrue(tokenizer.next(TokenKind.IntegerValue)); assertFalse(tokenizer.next(TokenKind.NotEqualsOperator)); @@ -404,6 +443,11 @@ public class UriTokenizerTest { assertTrue(tokenizer.next(TokenKind.IntegerValue)); assertTrue(tokenizer.next(TokenKind.EOF)); + assertTrue(new UriTokenizer("-x").next(TokenKind.MinusOperator)); + assertFalse(new UriTokenizer("-1").next(TokenKind.MinusOperator)); + assertFalse(new UriTokenizer("-INF").next(TokenKind.MinusOperator)); + assertFalse(new UriTokenizer("+").next(TokenKind.MinusOperator)); + assertFalse(new UriTokenizer("nottrue").next(TokenKind.NotOperator)); assertFalse(new UriTokenizer("no true").next(TokenKind.NotOperator)); @@ -484,6 +528,38 @@ public class UriTokenizerTest { wrongToken(TokenKind.DescSuffix, " desc", 'D'); } + @Test + public void search() { + UriTokenizer tokenizer = new UriTokenizer("a AND b OR NOT \"c\" d"); + assertTrue(tokenizer.next(TokenKind.Word)); + assertTrue(tokenizer.next(TokenKind.AndOperatorSearch)); + assertTrue(tokenizer.next(TokenKind.Word)); + assertFalse(tokenizer.next(TokenKind.AndOperatorSearch)); + assertTrue(tokenizer.next(TokenKind.OrOperatorSearch)); + assertTrue(tokenizer.next(TokenKind.NotOperatorSearch)); + assertTrue(tokenizer.next(TokenKind.Phrase)); + assertTrue(tokenizer.next(TokenKind.AndOperatorSearch)); + assertTrue(tokenizer.next(TokenKind.Word)); + assertFalse(tokenizer.next(TokenKind.AndOperatorSearch)); + assertFalse(tokenizer.next(TokenKind.Word)); + assertFalse(tokenizer.next(TokenKind.Phrase)); + assertTrue(tokenizer.next(TokenKind.EOF)); + + assertTrue(new UriTokenizer("\"a\\\\x\\\"\"").next(TokenKind.Phrase)); + assertFalse(new UriTokenizer("\"a\\\"").next(TokenKind.Phrase)); + assertFalse(new UriTokenizer("\"a\\x\"").next(TokenKind.Phrase)); + wrongToken(TokenKind.Phrase, "\"a\"", '\\'); + + final String outsideBmpLetter = String.valueOf(Character.toChars(0x10330)); + assertTrue(new UriTokenizer("\"" + outsideBmpLetter + "\"").next(TokenKind.Phrase)); + + assertTrue(new UriTokenizer(outsideBmpLetter).next(TokenKind.Word)); + assertFalse(new UriTokenizer("1").next(TokenKind.Word)); + assertFalse(new UriTokenizer("AND").next(TokenKind.Word)); + assertFalse(new UriTokenizer("OR").next(TokenKind.Word)); + assertFalse(new UriTokenizer("NOT").next(TokenKind.Word)); + } + private void wrongToken(final TokenKind kind, final String value, final char disturbCharacter) { assertFalse(new UriTokenizer(disturbCharacter + value).next(kind)); http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8925274c/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchParserAndTokenizerTest.java ---------------------------------------------------------------------- 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 e028cfe..f3e50a2 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 @@ -35,7 +35,7 @@ public class SearchParserAndTokenizerTest { assertQuery("a AND b AND c").resultsIn("{{'a' AND 'b'} AND 'c'}"); assertQuery("a OR b").resultsIn("{'a' OR 'b'}"); assertQuery("a OR b OR c").resultsIn("{{'a' OR 'b'} OR 'c'}"); - + assertQuery("NOT a NOT b").resultsIn("{{NOT 'a'} AND {NOT 'b'}}"); assertQuery("NOT a AND NOT b").resultsIn("{{NOT 'a'} AND {NOT 'b'}}"); assertQuery("NOT a OR NOT b").resultsIn("{{NOT 'a'} OR {NOT 'b'}}"); @@ -59,16 +59,16 @@ public class SearchParserAndTokenizerTest { assertQuery("a AND (b OR c)").resultsIn("{'a' AND {'b' OR 'c'}}"); assertQuery("(a OR b) AND NOT c").resultsIn("{{'a' OR 'b'} AND {NOT 'c'}}"); assertQuery("(a OR B) AND (c OR d AND NOT e OR (f))") - .resultsIn("{{'a' OR 'B'} AND {{'c' OR {'d' AND {NOT 'e'}}} OR 'f'}}"); + .resultsIn("{{'a' OR 'B'} AND {{'c' OR {'d' AND {NOT 'e'}}} OR 'f'}}"); assertQuery("(a OR B) (c OR d NOT e OR (f))") - .resultsIn("{{'a' OR 'B'} AND {{'c' OR {'d' AND {NOT 'e'}}} OR 'f'}}"); + .resultsIn("{{'a' OR 'B'} AND {{'c' OR {'d' AND {NOT 'e'}}} OR 'f'}}"); assertQuery("((((a))))").resultsIn("'a'"); assertQuery("((((a)))) ((((a))))").resultsIn("{'a' AND 'a'}"); assertQuery("((((a)))) OR ((((a))))").resultsIn("{'a' OR 'a'}"); assertQuery("((((((a)))) ((((c))) OR (((C)))) ((((a))))))").resultsIn("{{'a' AND {'c' OR 'C'}} AND 'a'}"); assertQuery("((((\"a\")))) OR ((((\"a\"))))").resultsIn("{'a' OR 'a'}"); } - + @Test public void parseImplicitAnd() throws Exception { assertQuery("a b").resultsIn("{'a' AND 'b'}"); @@ -103,7 +103,7 @@ public class SearchParserAndTokenizerTest { assertQuery("((a AND b OR c)").resultsIn(SearchParserException.MessageKeys.MISSING_CLOSE); assertQuery("a AND (b OR c").resultsIn(SearchParserException.MessageKeys.MISSING_CLOSE); assertQuery("(a AND ((b OR c)").resultsIn(SearchParserException.MessageKeys.MISSING_CLOSE); - + assertQuery("NOT NOT a").resultsIn(SearchParserException.MessageKeys.INVALID_NOT_OPERAND); assertQuery("NOT (a)").resultsIn(SearchParserException.MessageKeys.TOKENIZER_EXCEPTION); } @@ -186,7 +186,6 @@ public class SearchParserAndTokenizerTest { // <Input>http://serviceRoot/Products?$search=blue</Input> assertQuery("blue").resultsIn("'blue'"); - // below cases can not be tested here // <TestCase Name="5.1.7 Search - on entity container" Rule="odataUri"> // <Input>http://serviceRoot/Model.Container/$all?$search=blue</Input> @@ -194,68 +193,47 @@ public class SearchParserAndTokenizerTest { // <Input>http://serviceRoot/$all?$search=blue</Input> } - private static Validator assertQuery(String searchQuery) { - return Validator.init(searchQuery); + return new Validator(searchQuery); } private static class Validator { - private boolean log; private final String searchQuery; private Validator(String searchQuery) { this.searchQuery = searchQuery; } - private static Validator init(String searchQuery) { - return new Validator(searchQuery); - } - - @SuppressWarnings("unused") - private Validator withLogging() { - log = true; - return this; - } - - private void resultsIn(SearchParserException.MessageKey key) - throws SearchTokenizerException { + private void resultsIn(SearchParserException.MessageKey key) throws SearchTokenizerException { try { resultsIn(searchQuery); } catch (SearchParserException e) { Assert.assertEquals("SearchParserException with unexpected message '" + e.getMessage() + "' was thrown.", key, e.getMessageKey()); - if(log) { - System.out.println("Caught SearchParserException with message key " + - e.getMessageKey() + " and message " + e.getMessage()); - } return; } Assert.fail("SearchParserException with message key " + key.getKey() + " was not thrown."); } - + public void resultsInExpectedTerm(final String actualToken) throws SearchTokenizerException { try { resultsIn(searchQuery); - } catch(SearchParserException e) { + } catch (SearchParserException e) { Assert.assertEquals(SearchParserException.MessageKeys.EXPECTED_DIFFERENT_TOKEN, e.getMessageKey()); Assert.assertEquals("Expected PHRASE||WORD found: " + actualToken, e.getMessage()); } } - + private void resultsIn(String expectedSearchExpression) throws SearchTokenizerException, SearchParserException { final SearchExpression searchExpression = getSearchExpression(); Assert.assertEquals(expectedSearchExpression, searchExpression.toString()); } private SearchExpression getSearchExpression() throws SearchParserException, SearchTokenizerException { - SearchParser tokenizer = new SearchParser(); - SearchOption result = tokenizer.parse(searchQuery); + SearchOption result = new SearchParser().parse(searchQuery); Assert.assertNotNull(result); final SearchExpression searchExpression = result.getSearchExpression(); Assert.assertNotNull(searchExpression); - if (log) { - System.out.println(searchExpression); - } return searchExpression; } }
