You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@olingo.apache.org by mi...@apache.org on 2015/11/28 06:30:27 UTC
[19/47] olingo-odata4 git commit: [OLINGO-568] SearchParser negative
tests
[OLINGO-568] SearchParser negative tests
Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo
Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/8457c0f6
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/8457c0f6
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/8457c0f6
Branch: refs/heads/OLINGO-811_CountForExpand
Commit: 8457c0f60a084f92f7a98c61039c9a5540980a3c
Parents: cef72e4
Author: Christian Amend <ch...@sap.com>
Authored: Fri Nov 13 14:56:29 2015 +0100
Committer: Christian Amend <ch...@sap.com>
Committed: Fri Nov 13 14:56:29 2015 +0100
----------------------------------------------------------------------
.../core/uri/parser/search/SearchParser.java | 89 ++++++++------
.../parser/search/SearchParserException.java | 54 +++++++++
.../core/uri/parser/search/SearchTokenizer.java | 110 ++++++++++-------
.../parser/search/SearchTokenizerException.java | 25 +++-
.../search/SearchParserAndTokenizerTest.java | 10 +-
.../uri/parser/search/SearchParserTest.java | 118 ++++++++++++++-----
6 files changed, 290 insertions(+), 116 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8457c0f6/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchParser.java
----------------------------------------------------------------------
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 d5109a5..2cd03c6 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
@@ -6,9 +6,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -21,6 +21,7 @@ package org.apache.olingo.server.core.uri.parser.search;
import org.apache.olingo.server.api.uri.queryoption.SearchOption;
import org.apache.olingo.server.api.uri.queryoption.search.SearchBinaryOperatorKind;
import org.apache.olingo.server.api.uri.queryoption.search.SearchExpression;
+import org.apache.olingo.server.core.uri.parser.search.SearchQueryToken.Token;
import org.apache.olingo.server.core.uri.queryoption.SearchOptionImpl;
import java.util.Iterator;
@@ -31,13 +32,11 @@ public class SearchParser {
private Iterator<SearchQueryToken> tokens;
private SearchQueryToken token;
- public SearchOption parse(String path, String value) {
+ public SearchOption parse(String path, String value) throws SearchParserException, SearchTokenizerException {
SearchTokenizer tokenizer = new SearchTokenizer();
SearchExpression searchExpression;
try {
- tokens = tokenizer.tokenize(value).iterator();
- nextToken();
- searchExpression = processSearchExpression(null);
+ searchExpression = parseInternal(tokenizer.tokenize(value));
} catch (SearchTokenizerException e) {
return null;
}
@@ -46,32 +45,45 @@ public class SearchParser {
return searchOption;
}
- protected SearchExpression parseInternal(List<SearchQueryToken> tokens) {
+ protected SearchExpression parseInternal(List<SearchQueryToken> tokens) throws SearchParserException {
this.tokens = tokens.iterator();
nextToken();
+ if (token == null) {
+ throw new SearchParserException("No search String", SearchParserException.MessageKeys.NO_EXPRESSION_FOUND);
+ }
return processSearchExpression(null);
}
- private SearchExpression processSearchExpression(SearchExpression left) {
- if(token == null) {
+ private SearchExpression processSearchExpression(SearchExpression left) throws SearchParserException {
+ if (token == null) {
return left;
}
+ if (left == null && (isToken(SearchQueryToken.Token.AND) || isToken(SearchQueryToken.Token.OR))) {
+ throw new SearchParserException(token.getToken() + " needs a left operand.",
+ SearchParserException.MessageKeys.INVALID_BINARY_OPERATOR_POSITION, token.getToken().toString());
+ }
+
SearchExpression expression = left;
- if(isToken(SearchQueryToken.Token.OPEN)) {
+ if (isToken(SearchQueryToken.Token.OPEN)) {
processOpen();
expression = processSearchExpression(left);
validateToken(SearchQueryToken.Token.CLOSE);
processClose();
- } else if(isTerm()) {
+ } else if (isTerm()) {
expression = processTerm();
}
- if(isToken(SearchQueryToken.Token.AND) || isTerm()) {
- expression = processAnd(expression);
- } else if(isToken(SearchQueryToken.Token.OR)) {
- expression = processOr(expression);
- } else if(isEof()) {
+ if (expression == null) {
+ throw new SearchParserException("Brackets must contain an expression.",
+ SearchParserException.MessageKeys.NO_EXPRESSION_FOUND);
+ }
+
+ if (isToken(SearchQueryToken.Token.AND) || isToken(SearchQueryToken.Token.OPEN) || isTerm()) {
+ expression = processAnd(expression);
+ } else if (isToken(SearchQueryToken.Token.OR)) {
+ expression = processOr(expression);
+ } else if (isEof()) {
return expression;
}
return expression;
@@ -88,15 +100,17 @@ public class SearchParser {
}
private boolean isToken(SearchQueryToken.Token toCheckToken) {
- if(token == null) {
+ if (token == null) {
return false;
}
return token.getToken() == toCheckToken;
}
- private void validateToken(SearchQueryToken.Token toValidateToken) {
- if(!isToken(toValidateToken)) {
- throw illegalState();
+ private void validateToken(SearchQueryToken.Token toValidateToken) throws SearchParserException {
+ if (!isToken(toValidateToken)) {
+ String actualToken = token == null ? "null" : token.getToken().toString();
+ throw new SearchParserException("Expected " + toValidateToken + " but was " + actualToken,
+ SearchParserException.MessageKeys.EXPECTED_DIFFERENT_TOKEN, toValidateToken.toString(), actualToken);
}
}
@@ -108,23 +122,27 @@ public class SearchParser {
nextToken();
}
- private SearchExpression processAnd(SearchExpression left) {
- if(isToken(SearchQueryToken.Token.AND)) {
+ private SearchExpression processAnd(SearchExpression left) throws SearchParserException {
+ if (isToken(SearchQueryToken.Token.AND)) {
nextToken();
}
SearchExpression se = left;
- if(isTerm()) {
+ if (isTerm()) {
se = processTerm();
se = new SearchBinaryImpl(left, SearchBinaryOperatorKind.AND, se);
return processSearchExpression(se);
} else {
+ if (isToken(SearchQueryToken.Token.AND) || isToken(SearchQueryToken.Token.OR)) {
+ throw new SearchParserException("Operators must not be followed by an AND or an OR",
+ SearchParserException.MessageKeys.INVALID_OPERATOR_AFTER_AND, token.getToken().toString());
+ }
se = processSearchExpression(se);
return new SearchBinaryImpl(left, SearchBinaryOperatorKind.AND, se);
}
}
- public SearchExpression processOr(SearchExpression left) {
- if(isToken(SearchQueryToken.Token.OR)) {
+ public SearchExpression processOr(SearchExpression left) throws SearchParserException {
+ if (isToken(SearchQueryToken.Token.OR)) {
nextToken();
}
SearchExpression se = processSearchExpression(left);
@@ -135,30 +153,31 @@ public class SearchParser {
return new RuntimeException();
}
- private SearchExpression processNot() {
+ private SearchExpression processNot() throws SearchParserException {
nextToken();
- SearchExpression searchExpression = processTerm();
- if(searchExpression.isSearchTerm()) {
+ if (isToken(Token.WORD) || isToken(Token.PHRASE)) {
+ SearchExpression searchExpression = processTerm();
return new SearchUnaryImpl(searchExpression.asSearchTerm());
}
- throw illegalState();
+ throw new SearchParserException("NOT must be followed by a term not a " + token.getToken(),
+ SearchParserException.MessageKeys.INVALID_NOT_OPERAND, token.getToken().toString());
}
private void nextToken() {
- if(tokens.hasNext()) {
- token = tokens.next();
+ if (tokens.hasNext()) {
+ token = tokens.next();
} else {
token = null;
}
}
- private SearchExpression processTerm() {
- if(isToken(SearchQueryToken.Token.NOT)) {
+ private SearchExpression processTerm() throws SearchParserException {
+ if (isToken(SearchQueryToken.Token.NOT)) {
return processNot();
}
- if(isToken(SearchQueryToken.Token.PHRASE)) {
+ if (isToken(SearchQueryToken.Token.PHRASE)) {
return processPhrase();
- } else if(isToken(SearchQueryToken.Token.WORD)) {
+ } else if (isToken(SearchQueryToken.Token.WORD)) {
return processWord();
}
throw illegalState();
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8457c0f6/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchParserException.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchParserException.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchParserException.java
new file mode 100644
index 0000000..78a12be
--- /dev/null
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchParserException.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.olingo.server.core.uri.parser.search;
+
+import org.apache.olingo.server.core.uri.parser.UriParserSyntaxException;
+
+public class SearchParserException extends UriParserSyntaxException {
+
+ private static final long serialVersionUID = 5781553037561337795L;
+
+ public static enum MessageKeys implements MessageKey {
+ /** parameter: operatorkind */
+ INVALID_BINARY_OPERATOR_POSITION,
+ /** parameter: operatorkind */
+ INVALID_NOT_OPERAND,
+ /** parameters: expectedToken actualToken */
+ EXPECTED_DIFFERENT_TOKEN,
+ NO_EXPRESSION_FOUND,
+ /** parameter: operatorkind */
+ INVALID_OPERATOR_AFTER_AND;
+
+ @Override
+ public String getKey() {
+ return name();
+ }
+ }
+
+ public SearchParserException(final String developmentMessage, final MessageKey messageKey,
+ final String... parameters) {
+ super(developmentMessage, messageKey, parameters);
+ }
+
+ public SearchParserException(final String developmentMessage, final Throwable cause, final MessageKey messageKey,
+ final String... parameters) {
+ super(developmentMessage, cause, messageKey, parameters);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8457c0f6/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizer.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizer.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizer.java
index a9a5895..1e3b2ef 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizer.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizer.java
@@ -6,9 +6,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -24,18 +24,18 @@ import java.util.List;
/**
* <code>
* searchExpr = ( OPEN BWS searchExpr BWS CLOSE
- * / searchTerm
- * ) [ searchOrExpr
- * / searchAndExpr
- * ]
+ * / searchTerm
+ * ) [ searchOrExpr
+ * / searchAndExpr
+ * ]
*
- * searchOrExpr = RWS 'OR' RWS searchExpr
- * searchAndExpr = RWS [ 'AND' RWS ] searchExpr
+ * searchOrExpr = RWS 'OR' RWS searchExpr
+ * searchAndExpr = RWS [ 'AND' RWS ] searchExpr
*
- * searchTerm = [ 'NOT' RWS ] ( searchPhrase / searchWord )
- * searchPhrase = quotation-mark 1*qchar-no-AMP-DQUOTE quotation-mark
- * searchWord = 1*ALPHA ; Actually: any character from the Unicode categories L or Nl,
- * ; but not the words AND, OR, and NOT
+ * searchTerm = [ 'NOT' RWS ] ( searchPhrase / searchWord )
+ * searchPhrase = quotation-mark 1*qchar-no-AMP-DQUOTE quotation-mark
+ * searchWord = 1*ALPHA ; Actually: any character from the Unicode categories L or Nl,
+ * ; but not the words AND, OR, and NOT
* </code>
*/
public class SearchTokenizer {
@@ -65,7 +65,8 @@ public class SearchTokenizer {
}
public State forbidden(char c) throws SearchTokenizerException {
- throw new SearchTokenizerException("Forbidden character for " + this.getClass().getName() + "->" + c);
+ throw new SearchTokenizerException("Forbidden character for " + this.getClass().getName() + "->" + c,
+ SearchTokenizerException.MessageKeys.FORBIDDEN_CHARACTER, "" + c);
}
public State finish() {
@@ -97,20 +98,20 @@ public class SearchTokenizer {
}
/**
- * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
- * other-delims = "!" / "(" / ")" / "*" / "+" / "," / ";"
+ * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
+ * other-delims = "!" / "(" / ")" / "*" / "+" / "," / ";"
* qchar-unescaped = unreserved / pct-encoded-unescaped / other-delims / ":" / "@" / "/" / "?" / "$" / "'" / "="
- * pct-encoded-unescaped = "%" ( "0" / "1" / "3" / "4" / "6" / "7" / "8" / "9" / A-to-F ) HEXDIG
- * / "%" "2" ( "0" / "1" / "3" / "4" / "5" / "6" / "7" / "8" / "9" / A-to-F )
- * / "%" "5" ( DIGIT / "A" / "B" / "D" / "E" / "F" )
+ * pct-encoded-unescaped = "%" ( "0" / "1" / "3" / "4" / "6" / "7" / "8" / "9" / A-to-F ) HEXDIG
+ * / "%" "2" ( "0" / "1" / "3" / "4" / "5" / "6" / "7" / "8" / "9" / A-to-F )
+ * / "%" "5" ( DIGIT / "A" / "B" / "D" / "E" / "F" )
*
- * qchar-no-AMP-DQUOTE = qchar-unescaped / escape ( escape / quotation-mark )
+ * qchar-no-AMP-DQUOTE = qchar-unescaped / escape ( escape / quotation-mark )
*
- * escape = "\" / "%5C" ; reverse solidus U+005C
- * quotation-mark = DQUOTE / "%22"
+ * escape = "\" / "%5C" ; reverse solidus U+005C
+ * quotation-mark = DQUOTE / "%22"
*
- * ALPHA = %x41-5A / %x61-7A
- * DIGIT = %x30-39
+ * ALPHA = %x41-5A / %x61-7A
+ * DIGIT = %x30-39
* DQUOTE = %x22
*
* @param character which is checked
@@ -137,10 +138,10 @@ public class SearchTokenizer {
|| '0' <= character && character <= '9'; // case 0..9
}
- //BWS = *( SP / HTAB / "%20" / "%09" ) ; "bad" whitespace
- //RWS = 1*( SP / HTAB / "%20" / "%09" ) ; "required" whitespace
+ // BWS = *( SP / HTAB / "%20" / "%09" ) ; "bad" whitespace
+ // RWS = 1*( SP / HTAB / "%20" / "%09" ) ; "required" whitespace
static boolean isWhitespace(final char character) {
- //( SP / HTAB / "%20" / "%09" )
+ // ( SP / HTAB / "%20" / "%09" )
// TODO mibo: add missing whitespaces
return character == ' ' || character == '\t';
}
@@ -158,29 +159,35 @@ public class SearchTokenizer {
private static abstract class LiteralState extends State {
protected final StringBuilder literal = new StringBuilder();
+
public LiteralState(Token t) {
super(t);
}
+
public LiteralState(Token t, char c) throws SearchTokenizerException {
super(t);
init(c);
}
+
public LiteralState(Token t, String initLiteral) {
super(t);
literal.append(initLiteral);
}
+
public State allowed(char c) {
literal.append(c);
return this;
}
+
@Override
public String getLiteral() {
return literal.toString();
}
public State init(char c) throws SearchTokenizerException {
- if(isFinished()) {
- throw new SearchTokenizerException(toString() + " is already finished.");
+ if (isFinished()) {
+ throw new SearchTokenizerException(toString() + " is already finished.",
+ SearchTokenizerException.MessageKeys.ALREADY_FINISHED);
}
literal.append(c);
return this;
@@ -191,13 +198,14 @@ public class SearchTokenizer {
public SearchExpressionState() {
super(null);
}
+
@Override
public State nextChar(char c) throws SearchTokenizerException {
if (c == CHAR_OPEN) {
return new OpenState();
} else if (isWhitespace(c)) {
return new RwsState();
- } else if(c == CHAR_CLOSE) {
+ } else if (c == CHAR_CLOSE) {
return new CloseState();
} else {
return new SearchTermState().init(c);
@@ -214,9 +222,10 @@ public class SearchTokenizer {
public SearchTermState() {
super(Token.TERM);
}
+
@Override
public State nextChar(char c) throws SearchTokenizerException {
- if(c == CHAR_N) {
+ if (c == CHAR_N) {
return new NotState(c);
} else if (c == QUOTATION_MARK) {
return new SearchPhraseState(c);
@@ -225,6 +234,7 @@ public class SearchTokenizer {
}
return forbidden(c);
}
+
@Override
public State init(char c) throws SearchTokenizerException {
return nextChar(c);
@@ -234,15 +244,16 @@ public class SearchTokenizer {
private class SearchWordState extends LiteralState {
public SearchWordState(char c) throws SearchTokenizerException {
super(Token.WORD, c);
- if(!isAllowedWord(c)) {
+ if (!isAllowedWord(c)) {
forbidden(c);
}
}
+
public SearchWordState(State toConsume) throws SearchTokenizerException {
super(Token.WORD, toConsume.getLiteral());
char[] chars = literal.toString().toCharArray();
for (char aChar : chars) {
- if(!isAllowedWord(aChar)) {
+ if (!isAllowedWord(aChar)) {
forbidden(aChar);
}
}
@@ -271,7 +282,7 @@ public class SearchTokenizer {
private class SearchPhraseState extends LiteralState {
public SearchPhraseState(char c) throws SearchTokenizerException {
super(Token.PHRASE, c);
- if(c != QUOTATION_MARK) {
+ if (c != QUOTATION_MARK) {
forbidden(c);
}
}
@@ -286,7 +297,7 @@ public class SearchTokenizer {
finish();
allowed(c);
return new SearchExpressionState();
- } else if(isFinished()) {
+ } else if (isFinished()) {
return new SearchExpressionState().init(c);
}
return forbidden(c);
@@ -298,6 +309,7 @@ public class SearchTokenizer {
super(Token.OPEN);
finish();
}
+
@Override
public State nextChar(char c) throws SearchTokenizerException {
finish();
@@ -323,37 +335,40 @@ public class SearchTokenizer {
private class NotState extends LiteralState {
public NotState(char c) throws SearchTokenizerException {
super(Token.NOT, c);
- if(c != CHAR_N) {
+ if (c != CHAR_N) {
forbidden(c);
}
}
+
@Override
public State nextChar(char c) throws SearchTokenizerException {
if (literal.length() == 1 && c == CHAR_O) {
return allowed(c);
} else if (literal.length() == 2 && c == CHAR_T) {
return allowed(c);
- } else if(literal.length() == 3 && isWhitespace(c)) {
+ } else if (literal.length() == 3 && isWhitespace(c)) {
finish();
return new BeforePhraseOrWordRwsState();
}
return forbidden(c);
}
}
+
private class AndState extends LiteralState {
public AndState(char c) throws SearchTokenizerException {
super(Token.AND, c);
- if(c != CHAR_A) {
+ if (c != CHAR_A) {
forbidden(c);
}
}
+
@Override
public State nextChar(char c) throws SearchTokenizerException {
if (literal.length() == 1 && c == CHAR_N) {
return allowed(c);
} else if (literal.length() == 2 && c == CHAR_D) {
return allowed(c);
- } else if(literal.length() == 3 && isWhitespace(c)) {
+ } else if (literal.length() == 3 && isWhitespace(c)) {
finish();
return new BeforeSearchExpressionRwsState();
} else {
@@ -361,18 +376,20 @@ public class SearchTokenizer {
}
}
}
+
private class OrState extends LiteralState {
public OrState(char c) throws SearchTokenizerException {
super(Token.OR, c);
- if(c != CHAR_O) {
+ if (c != CHAR_O) {
forbidden(c);
}
}
+
@Override
public State nextChar(char c) throws SearchTokenizerException {
if (literal.length() == 1 && (c == CHAR_R)) {
return allowed(c);
- } else if(literal.length() == 2 && isWhitespace(c)) {
+ } else if (literal.length() == 2 && isWhitespace(c)) {
finish();
return new BeforeSearchExpressionRwsState();
} else {
@@ -381,12 +398,13 @@ public class SearchTokenizer {
}
}
- // RWS 'OR' RWS searchExpr
+ // RWS 'OR' RWS searchExpr
// RWS [ 'AND' RWS ] searchExpr
private class BeforeSearchExpressionRwsState extends State {
public BeforeSearchExpressionRwsState() {
super(null);
}
+
@Override
public State nextChar(char c) throws SearchTokenizerException {
if (isWhitespace(c)) {
@@ -401,11 +419,12 @@ public class SearchTokenizer {
public BeforePhraseOrWordRwsState() {
super(null);
}
+
@Override
public State nextChar(char c) throws SearchTokenizerException {
if (isWhitespace(c)) {
return allowed(c);
- } else if(c == '"') {
+ } else if (c == '"') {
return new SearchPhraseState(c);
} else {
return new SearchWordState(c);
@@ -417,6 +436,7 @@ public class SearchTokenizer {
public RwsState() {
super(null);
}
+
@Override
public State nextChar(char c) throws SearchTokenizerException {
if (isWhitespace(c)) {
@@ -438,10 +458,10 @@ public class SearchTokenizer {
* @param searchQuery search query to be tokenized
* @return list of tokens
* @throws SearchTokenizerException if something in query is not valid
- * (based on OData search query ABNF)
+ * (based on OData search query ABNF)
*/
public List<SearchQueryToken> tokenize(final String searchQuery)
- throws SearchTokenizerException {
+ throws SearchTokenizerException {
char[] chars = searchQuery.trim().toCharArray();
@@ -455,7 +475,7 @@ public class SearchTokenizer {
state = next;
}
- if(state.close().isFinished()) {
+ if (state.close().isFinished()) {
states.add(state);
}
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8457c0f6/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerException.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerException.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerException.java
index 451632b..fb20efe 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerException.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerException.java
@@ -18,11 +18,30 @@
*/
package org.apache.olingo.server.core.uri.parser.search;
-public class SearchTokenizerException extends Exception {
+import org.apache.olingo.server.core.uri.parser.UriParserSyntaxException;
+
+public class SearchTokenizerException extends UriParserSyntaxException {
private static final long serialVersionUID = -8295456415309640166L;
- public SearchTokenizerException(String message) {
- super(message);
+ public static enum MessageKeys implements MessageKey {
+ /** parameter: character */
+ FORBIDDEN_CHARACTER,
+ ALREADY_FINISHED;
+
+ @Override
+ public String getKey() {
+ return name();
+ }
+ }
+
+ public SearchTokenizerException(final String developmentMessage, final MessageKey messageKey,
+ final String... parameters) {
+ super(developmentMessage, messageKey, parameters);
+ }
+
+ public SearchTokenizerException(final String developmentMessage, final Throwable cause, final MessageKey messageKey,
+ final String... parameters) {
+ super(developmentMessage, cause, messageKey, parameters);
}
}
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8457c0f6/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 271b617..dd5ab70 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
@@ -33,7 +33,7 @@ import org.junit.Test;
public class SearchParserAndTokenizerTest {
@Test
- public void basicParsing() throws SearchTokenizerException {
+ public void basicParsing() throws Exception {
SearchExpressionValidator.init("a")
.validate(with("a"));
SearchExpressionValidator.init("a AND b")
@@ -172,17 +172,18 @@ public class SearchParserAndTokenizerTest {
Assert.fail("Expected exception " + exception.getClass().getSimpleName() + " was not thrown.");
}
- private void validate(SearchExpression expectedSearchExpression) throws SearchTokenizerException {
+ private void validate(SearchExpression expectedSearchExpression) throws SearchTokenizerException,
+ SearchParserException {
final SearchExpression searchExpression = getSearchExpression();
Assert.assertEquals(expectedSearchExpression.toString(), searchExpression.toString());
}
- private void validate(String expectedSearchExpression) throws SearchTokenizerException {
+ private void validate(String expectedSearchExpression) throws SearchTokenizerException, SearchParserException {
final SearchExpression searchExpression = getSearchExpression();
Assert.assertEquals(expectedSearchExpression, searchExpression.toString());
}
- private SearchExpression getSearchExpression() {
+ private SearchExpression getSearchExpression() throws SearchParserException, SearchTokenizerException {
SearchParser tokenizer = new SearchParser();
SearchOption result = tokenizer.parse(null, searchQuery);
Assert.assertNotNull(result);
@@ -195,5 +196,4 @@ public class SearchParserAndTokenizerTest {
}
}
-
}
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8457c0f6/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchParserTest.java
----------------------------------------------------------------------
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 89b59b4..0902e8a 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
@@ -21,6 +21,7 @@ package org.apache.olingo.server.core.uri.parser.search;
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 static org.mockito.Mockito.when;
@@ -29,35 +30,35 @@ import java.util.List;
import org.apache.olingo.server.api.uri.queryoption.search.SearchBinaryOperatorKind;
import org.apache.olingo.server.api.uri.queryoption.search.SearchExpression;
+import org.apache.olingo.server.core.uri.parser.search.SearchParserException.MessageKeys;
import org.apache.olingo.server.core.uri.parser.search.SearchQueryToken.Token;
-import org.junit.Ignore;
import org.junit.Test;
public class SearchParserTest extends SearchParser {
@Test
- public void simple() {
+ public void simple() throws Exception {
SearchExpression se = run(Token.WORD);
assertEquals("'word1'", se.toString());
assertTrue(se.isSearchTerm());
assertEquals("word1", se.asSearchTerm().getSearchTerm());
-
+
se = run(Token.PHRASE);
assertEquals("'phrase1'", se.toString());
assertTrue(se.isSearchTerm());
- //TODO: Check if quotation marks should be part of the string we deliver
+ // TODO: Check if quotation marks should be part of the string we deliver
assertEquals("phrase1", se.asSearchTerm().getSearchTerm());
}
@Test
- public void simpleAnd() {
+ public void simpleAnd() throws Exception {
SearchExpression se = run(Token.WORD, Token.AND, Token.WORD);
assertEquals("{'word1' AND 'word2'}", se.toString());
assertTrue(se.isSearchBinary());
assertEquals(SearchBinaryOperatorKind.AND, se.asSearchBinary().getOperator());
assertEquals("word1", se.asSearchBinary().getLeftOperand().asSearchTerm().getSearchTerm());
assertEquals("word2", se.asSearchBinary().getRightOperand().asSearchTerm().getSearchTerm());
-
+
se = run(Token.PHRASE, Token.AND, Token.PHRASE);
assertEquals("{'phrase1' AND 'phrase2'}", se.toString());
assertTrue(se.isSearchBinary());
@@ -65,16 +66,16 @@ public class SearchParserTest extends SearchParser {
assertEquals("phrase1", se.asSearchBinary().getLeftOperand().asSearchTerm().getSearchTerm());
assertEquals("phrase2", se.asSearchBinary().getRightOperand().asSearchTerm().getSearchTerm());
}
-
+
@Test
- public void simpleOr() {
+ public void simpleOr() throws Exception {
SearchExpression se = run(Token.WORD, Token.OR, Token.WORD);
assertEquals("{'word1' OR 'word2'}", se.toString());
assertTrue(se.isSearchBinary());
assertEquals(SearchBinaryOperatorKind.OR, se.asSearchBinary().getOperator());
assertEquals("word1", se.asSearchBinary().getLeftOperand().asSearchTerm().getSearchTerm());
assertEquals("word2", se.asSearchBinary().getRightOperand().asSearchTerm().getSearchTerm());
-
+
se = run(Token.PHRASE, Token.OR, Token.PHRASE);
assertEquals("{'phrase1' OR 'phrase2'}", se.toString());
assertTrue(se.isSearchBinary());
@@ -82,16 +83,16 @@ public class SearchParserTest extends SearchParser {
assertEquals("phrase1", se.asSearchBinary().getLeftOperand().asSearchTerm().getSearchTerm());
assertEquals("phrase2", se.asSearchBinary().getRightOperand().asSearchTerm().getSearchTerm());
}
-
+
@Test
- public void simpleImplicitAnd() {
+ public void simpleImplicitAnd() throws Exception {
SearchExpression se = run(Token.WORD, Token.WORD);
assertEquals("{'word1' AND 'word2'}", se.toString());
assertTrue(se.isSearchBinary());
assertEquals(SearchBinaryOperatorKind.AND, se.asSearchBinary().getOperator());
assertEquals("word1", se.asSearchBinary().getLeftOperand().asSearchTerm().getSearchTerm());
assertEquals("word2", se.asSearchBinary().getRightOperand().asSearchTerm().getSearchTerm());
-
+
se = run(Token.PHRASE, Token.PHRASE);
assertEquals("{'phrase1' AND 'phrase2'}", se.toString());
assertTrue(se.isSearchBinary());
@@ -99,59 +100,119 @@ public class SearchParserTest extends SearchParser {
assertEquals("phrase1", se.asSearchBinary().getLeftOperand().asSearchTerm().getSearchTerm());
assertEquals("phrase2", se.asSearchBinary().getRightOperand().asSearchTerm().getSearchTerm());
}
-
+
@Test
- public void simpleBrackets() {
+ public void simpleBrackets() throws Exception {
SearchExpression se = run(Token.OPEN, Token.WORD, Token.CLOSE);
assertEquals("'word1'", se.toString());
assertTrue(se.isSearchTerm());
assertEquals("word1", se.asSearchTerm().getSearchTerm());
-
+
se = run(Token.OPEN, Token.PHRASE, Token.CLOSE);
assertEquals("'phrase1'", se.toString());
assertTrue(se.isSearchTerm());
assertEquals("phrase1", se.asSearchTerm().getSearchTerm());
}
-
+
@Test
- public void simpleNot() {
+ public void simpleNot() throws Exception {
SearchExpression se = run(Token.NOT, Token.WORD);
assertEquals("{NOT 'word1'}", se.toString());
assertTrue(se.isSearchUnary());
assertEquals("word1", se.asSearchUnary().getOperand().asSearchTerm().getSearchTerm());
-
+
se = run(Token.NOT, Token.PHRASE);
assertEquals("{NOT 'phrase1'}", se.toString());
assertTrue(se.isSearchUnary());
assertEquals("phrase1", se.asSearchUnary().getOperand().asSearchTerm().getSearchTerm());
}
-
+
@Test
- public void precedenceLast() {
- //word1 AND (word2 AND word3)
+ public void precedenceLast() throws Exception {
+ // word1 AND (word2 AND word3)
SearchExpression se = run(Token.WORD, Token.AND, Token.OPEN, Token.WORD, Token.AND, Token.WORD, Token.CLOSE);
assertEquals("{'word1' AND {'word2' AND 'word3'}}", se.toString());
}
-
+
@Test
- public void precedenceFirst() {
- //(word1 AND word2) AND word3
+ public void precedenceFirst() throws Exception {
+ // (word1 AND word2) AND word3
SearchExpression se = run(Token.OPEN, Token.WORD, Token.AND, Token.WORD, Token.CLOSE, Token.AND, Token.WORD);
assertEquals("{{'word1' AND 'word2'} AND 'word3'}", se.toString());
}
@Test
- public void combinationAndOr() {
- //word1 AND word2 OR word3
+ public void combinationAndOr() throws Exception {
+ // word1 AND word2 OR word3
SearchExpression se = run(Token.WORD, Token.AND, Token.WORD, Token.OR, Token.WORD);
assertEquals("{{'word1' AND 'word2'} OR 'word3'}", se.toString());
- //word1 OR word2 AND word3
+ // word1 OR word2 AND word3
se = run(Token.WORD, Token.OR, Token.WORD, Token.AND, Token.WORD);
assertEquals("{'word1' OR {'word2' AND 'word3'}}", se.toString());
}
+ @Test
+ public void unnecessaryBrackets() throws Exception {
+ // (word1) (word2)
+ SearchExpression se = run(Token.OPEN, Token.WORD, Token.CLOSE, Token.OPEN, Token.WORD, Token.CLOSE);
+ assertEquals("{'word1' AND 'word2'}", se.toString());
+ }
+
+ @Test
+ public void complex() throws Exception {
+ // ((word1 word2) word3) OR word4
+ SearchExpression se =
+ run(Token.OPEN, Token.OPEN, Token.WORD, Token.WORD, Token.CLOSE, Token.WORD, Token.CLOSE, Token.OR, Token.WORD);
+ assertEquals("{{{'word1' AND 'word2'} AND 'word3'} OR 'word4'}", se.toString());
+ }
+
+ @Test
+ public void doubleNot() throws Exception {
+ SearchExpression se = run(Token.NOT, Token.WORD, Token.AND, Token.NOT, Token.PHRASE);
+ assertEquals("{{NOT 'word1'} AND {NOT 'phrase1'}}", se.toString());
+ }
+
+ @Test
+ public void notAnd() throws Exception {
+ runEx(SearchParserException.MessageKeys.INVALID_NOT_OPERAND, Token.NOT, Token.AND);
+ }
+
+ @Test
+ public void doubleAnd() throws Exception {
+ runEx(SearchParserException.MessageKeys.INVALID_OPERATOR_AFTER_AND, Token.WORD, Token.AND, Token.AND, Token.WORD);
+ }
+
+ @Test
+ public void singleAnd() {
+ runEx(SearchParserException.MessageKeys.INVALID_BINARY_OPERATOR_POSITION, Token.AND);
+ }
+
+ @Test
+ public void singleOpenBracket() {
+ runEx(SearchParserException.MessageKeys.EXPECTED_DIFFERENT_TOKEN, Token.OPEN);
+ }
+
+ @Test
+ public void emptyBrackets() {
+ runEx(SearchParserException.MessageKeys.NO_EXPRESSION_FOUND, Token.OPEN, Token.CLOSE);
+ }
+
+ @Test
+ public void empty() {
+ Token[] emptyArray = new Token[0];
+ runEx(SearchParserException.MessageKeys.NO_EXPRESSION_FOUND, emptyArray);
+ }
+
+ private void runEx(MessageKeys key, Token... tokenArray) {
+ try {
+ run(tokenArray);
+ fail("Expected UriParserSyntaxException with key " + key);
+ } catch (SearchParserException e) {
+ assertEquals(key, e.getMessageKey());
+ }
+ }
- private SearchExpression run(SearchQueryToken.Token... tokenArray) {
+ private SearchExpression run(SearchQueryToken.Token... tokenArray) throws SearchParserException {
List<SearchQueryToken> tokenList = prepareTokens(tokenArray);
SearchExpression se = parseInternal(tokenList);
assertNotNull(se);
@@ -172,6 +233,7 @@ public class SearchParserTest extends SearchParser {
when(token.getLiteral()).thenReturn("phrase" + phraseNumber);
phraseNumber++;
}
+ when(token.toString()).thenReturn("" + tokenArray[i]);
tokenList.add(token);
}
return tokenList;