[OLINGO-568] SearchTokenizer for SearchParser

This commit is contained in:
Michael Bolz 2015-11-06 15:42:32 +01:00
parent ac828a3554
commit 452ebcbdd8
8 changed files with 902 additions and 16 deletions

View File

@ -55,6 +55,7 @@ import org.apache.olingo.server.core.uri.antlr.UriParserParser.MetadataEOFContex
import org.apache.olingo.server.core.uri.antlr.UriParserParser.OrderByEOFContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.PathSegmentEOFContext;
import org.apache.olingo.server.core.uri.antlr.UriParserParser.SelectEOFContext;
import org.apache.olingo.server.core.uri.parser.search.SearchParser;
import org.apache.olingo.server.core.uri.queryoption.CountOptionImpl;
import org.apache.olingo.server.core.uri.queryoption.CustomQueryOptionImpl;
import org.apache.olingo.server.core.uri.queryoption.ExpandOptionImpl;
@ -77,7 +78,7 @@ public class Parser {
int logLevel = 0;
private enum ParserEntryRules {
All, Batch, CrossJoin, Entity, ExpandItems, FilterExpression, Metadata, PathSegment, Orderby, Select
All, Batch, CrossJoin, Entity, ExpandItems, FilterExpression, Metadata, PathSegment, Orderby, Select, Search
}
public Parser setLogLevel(final int logLevel) {
@ -215,8 +216,8 @@ public class Parser {
systemOption = (OrderByOptionImpl) uriParseTreeVisitor.visitOrderByEOF(ctxOrderByExpression);
} else if (option.name.equals(SystemQueryOptionKind.SEARCH.toString())) {
throw new UriParserSemanticException("System query option '$search' not implemented!",
UriParserSemanticException.MessageKeys.NOT_IMPLEMENTED, "System query option '$search");
SearchParser searchParser = new SearchParser();
systemOption = searchParser.parse(path, option.value);
} else if (option.name.equals(SystemQueryOptionKind.SELECT.toString())) {
SelectEOFContext ctxSelectEOF =
(SelectEOFContext) parseRule(option.value, ParserEntryRules.Select);
@ -285,15 +286,15 @@ public class Parser {
parameter.setAlias(option.name);
parameter.setExpression(expression);
parameter.setText(NULL.equals(option.value) ? null : option.value);
if(context.contextUriInfo.getAlias(option.name) == null) {
context.contextUriInfo.addAlias(option.name, parameter);
} else {
throw new UriParserSyntaxException("Alias already specified! Name: " + option.name,
throw new UriParserSyntaxException("Alias already specified! Name: " + option.name,
UriParserSyntaxException.MessageKeys.DUPLICATED_ALIAS, option.name);
}
}
final CustomQueryOptionImpl customOption = new CustomQueryOptionImpl();
customOption.setName(option.name);
customOption.setText(option.value);
@ -388,6 +389,9 @@ public class Parser {
case Select:
ret = parser.selectEOF();
break;
case Search:
ret = parser.searchInline();
break;
default:
break;
@ -445,6 +449,9 @@ public class Parser {
case Select:
ret = parser.selectEOF();
break;
case Search:
ret = parser.searchInline();
break;
default:
break;
}
@ -503,7 +510,7 @@ public class Parser {
} else {
out.append(index);
}
out.append(nL);
out.append(nL);
}
out.append(']');
System.out.println("tokens: " + out.toString());

View File

@ -0,0 +1,33 @@
/*
* 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.api.uri.queryoption.SearchOption;
import org.apache.olingo.server.core.uri.queryoption.SearchOptionImpl;
import java.util.List;
public class SearchParser {
public SearchOption parse(String path, String value) {
SearchTokenizer tokenizer = new SearchTokenizer();
List<SearchQueryToken> tokens = tokenizer.tokenize(value);
return new SearchOptionImpl();
}
}

View File

@ -0,0 +1,26 @@
/*
* 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;
public interface SearchQueryToken {
enum Token {OPEN, BWS, RWS, TERM, SEARCH_EXPRESSION, NOT, AND, OR, WORD, PHRASE, CLOSE}
Token getToken();
String getLiteral();
}

View File

@ -0,0 +1,396 @@
/*
* 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 java.util.ArrayList;
import java.util.List;
/**
* <code>
* searchExpr = ( OPEN BWS searchExpr BWS CLOSE
* / searchTerm
* ) [ searchOrExpr
* / searchAndExpr
* ]
*
* 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
* </code>
*/
public class SearchTokenizer {
//RWS = 1*( SP / HTAB / "%20" / "%09" ) ; "required" whitespace
//BWS = *( SP / HTAB / "%20" / "%09" ) ; "bad" whitespace
private static abstract class State implements SearchQueryToken {
private Token token = null;
private boolean finished = false;
private final StringBuilder literal;
public static final char EOF = 0x03;
public State(Token t) {
token = t;
literal = new StringBuilder();
}
public State(Token t, char c) {
this(t);
init(c);
}
public State(Token t, State consumeState) {
token = t;
literal = new StringBuilder(consumeState.getLiteral());
}
protected abstract State nextChar(char c);
public State next(char c) {
return nextChar(c);
}
public State init(char c) {
if(isFinished()) {
throw new IllegalStateException(toString() + " is already finished.");
}
literal.append(c);
return this;
}
public State allowed(char c) {
literal.append(c);
return this;
}
public State forbidden(char c) {
throw new IllegalStateException(this.getClass().getName() + "->" + c);
}
public State finish() {
this.finished = true;
return this;
}
public boolean isFinished() {
return finished;
}
public Token getToken() {
return token;
}
static boolean isAllowedChar(final char character) {
// TODO mibo: add missing allowed characters
return 'A' <= character && character <= 'Z' // case A..Z
|| 'a' <= character && character <= 'z' // case a..z
|| '0' <= character && character <= '9'; // case 0..9
}
/**
* qchar-no-AMP-DQUOTE = qchar-unescaped / escape ( escape / quotation-mark )
* qchar-unescaped = unreserved / pct-encoded-unescaped / other-delims / ":" / "@" / "/" / "?" / "$" / "'" / "="
* unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
* @param character which is checked
* @return true if character is allowed for a phrase
*/
static boolean isAllowedPhrase(final char character) {
// FIXME mibo: check missing and '\''
return isAllowedChar(character)
|| character == '-'
|| character == '.'
|| character == '_'
|| character == '~'
|| character == ':'
|| character == '@'
|| character == '/'
|| character == '$'
|| character == '=';
}
static boolean isEof(final char character) {
return character == EOF;
}
static boolean isWhitespace(final char character) {
//( SP / HTAB / "%20" / "%09" )
// TODO mibo: add missing whitespaces
return character == ' ' || character == '\t';
}
@Override
public String getLiteral() {
return literal.toString();
}
@Override
public String toString() {
return this.getToken().toString() + "=>{" + literal.toString() + "}";
}
}
private class SearchExpressionState extends State {
public SearchExpressionState() {
super(Token.SEARCH_EXPRESSION);
}
@Override
public State nextChar(char c) {
if (c == '(') {
return new OpenState(c);
} else if (isWhitespace(c)) {
return new RwsState(c);
} else if(c == ')') {
return new CloseState(c);
} else if(isEof(c)) {
return finish();
} else {
return new SearchTermState().init(c);
}
}
@Override
public State init(char c) {
return nextChar(c);
}
}
private class SearchTermState extends State {
public SearchTermState() {
super(Token.TERM);
}
@Override
public State nextChar(char c) {
if(c == 'n' || c == 'N') {
return new NotState(c);
} else if (c == '\'') {
return new SearchPhraseState(c);
} else if (isAllowedChar(c)) {
return new SearchWordState(c);
} else if (c == ')') {
finish();
return new CloseState(c);
} else if (isWhitespace(c)) {
finish();
return new RwsState(c);
} else if (isEof(c)) {
return finish();
}
throw new IllegalStateException(this.getClass().getName() + "->" + c);
}
@Override
public State init(char c) {
return nextChar(c);
}
}
private class SearchWordState extends State {
public SearchWordState(char c) {
super(Token.WORD, c);
}
public SearchWordState(State toConsume) {
super(Token.WORD, toConsume);
}
@Override
public State nextChar(char c) {
// if(c == 'n' || c == 'N') {
// return new NotState(c);
// }
if (isAllowedChar(c)) {
return allowed(c);
} else if (c == ')') {
finish();
return new CloseState(c);
} else if (isWhitespace(c)) {
finish();
return new RwsState(c);
} else if (isEof(c)) {
return finish();
}
throw new IllegalStateException(this.getClass().getName() + "->" + c);
}
}
private class SearchPhraseState extends State {
public SearchPhraseState(char c) {
super(Token.PHRASE, c);
if(c != '\'') {
forbidden(c);
}
}
@Override
public State nextChar(char c) {
if(isFinished() && !isEof(c)) {
return new SearchExpressionState().init(c);
} else if (isAllowedPhrase(c)) {
return allowed(c);
} else if (c == '\'') {
finish();
return allowed(c);
} else if (isWhitespace(c)) {
if(isFinished()) {
return new RwsState(c);
}
return allowed(c);
} else if (isEof(c)) {
return finish();
}
throw new IllegalStateException(this.getClass().getName() + "->" + c);
}
}
private class OpenState extends State {
public OpenState(char c) {
super(Token.OPEN, c);
finish();
}
@Override
public State nextChar(char c) {
finish();
if (isWhitespace(c)) {
throw new IllegalStateException(this.getClass().getName() + "->" + c);
}
return new SearchExpressionState().init(c);
}
}
private class CloseState extends State {
public CloseState(char c) {
super(Token.CLOSE, c);
finish();
}
@Override
public State nextChar(char c) {
if (isEof(c)) {
return finish();
} else {
return new SearchExpressionState().init(c);
}
}
}
private class NotState extends State {
public NotState(char c) {
super(Token.NOT, c);
}
@Override
public State nextChar(char c) {
if (getLiteral().length() == 1 && (c == 'o' || c == 'O')) {
return allowed(c);
} else if (getLiteral().length() == 2 && (c == 't' || c == 'T')) {
return allowed(c);
} else if(getLiteral().length() == 3 && isWhitespace(c)) {
finish();
return new RwsState(c);
} else {
return new SearchWordState(this);
}
}
}
private class AndState extends State {
public AndState(char c) {
super(Token.AND, c);
if(c != 'a' && c != 'A') {
forbidden(c);
}
}
@Override
public State nextChar(char c) {
if (getLiteral().length() == 1 && (c == 'n' || c == 'N')) {
return allowed(c);
} else if (getLiteral().length() == 2 && (c == 'd' || c == 'D')) {
return allowed(c);
} else if(getLiteral().length() == 3 && isWhitespace(c)) {
finish();
return new RwsState(c);
} else {
return new SearchWordState(this);
}
}
}
private class OrState extends State {
public OrState(char c) {
super(Token.OR, c);
if(c != 'o' && c != 'O') {
forbidden(c);
}
}
@Override
public State nextChar(char c) {
if (getLiteral().length() == 1 && (c == 'r' || c == 'R')) {
return allowed(c);
} else if(getLiteral().length() == 2 && isWhitespace(c)) {
finish();
return new RwsState(c);
} else {
return new SearchWordState(this);
}
}
}
private class RwsState extends State {
public RwsState(char c) {
super(Token.RWS, c);
}
@Override
public State nextChar(char c) {
if (isWhitespace(c)) {
return allowed(c);
} else if (c == 'O' || c == 'o') {
return new OrState(c);
} else if (c == 'A' || c == 'a') {
return new AndState(c);
} else {
return new SearchExpressionState().init(c);
}
}
}
// TODO (mibo): add (new) parse exception
public List<SearchQueryToken> tokenize(String searchQuery) {
char[] chars = searchQuery.toCharArray();
State state = new SearchExpressionState();
List<SearchQueryToken> states = new ArrayList<SearchQueryToken>();
for (char aChar : chars) {
State next = state.next(aChar);
if (state.isFinished() && next != state) {
states.add(state);
}
state = next;
}
if(state.next(State.EOF).isFinished()) {
states.add(state);
} else {
throw new IllegalStateException("State: " + state + " not finished and list is: " + states.toString());
}
return states;
}
}

View File

@ -24,13 +24,18 @@ import org.apache.olingo.server.api.uri.queryoption.search.SearchExpression;
public class SearchOptionImpl extends SystemQueryOptionImpl implements SearchOption {
private SearchExpression searchExpression;
public SearchOptionImpl() {
setKind(SystemQueryOptionKind.SEARCH);
}
@Override
public SearchExpression getSearchExpression() {
return null;
return searchExpression;
}
public void setSearchExpression(SearchExpression searchExpression) {
this.searchExpression = searchExpression;
}
}

View File

@ -0,0 +1,30 @@
/*
* 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.junit.Test;
public class SearchParserTest {
@Test
public void basicParsing() {
SearchParser parser = new SearchParser();
parser.parse("ESAllPrim", "abc");
}
}

View File

@ -0,0 +1,388 @@
/*
* 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.junit.Assert;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import static org.apache.olingo.server.core.uri.parser.search.SearchQueryToken.Token.*;
public class SearchTokenizerTest {
private boolean logEnabled = false;
@Test
public void testParse() throws Exception {
SearchTokenizer tokenizer = new SearchTokenizer();
List<SearchQueryToken> result;
//
result = tokenizer.tokenize("abc");
Assert.assertNotNull(result);
log(result.toString());
Assert.assertEquals(WORD, result.get(0).getToken());
result = tokenizer.tokenize("NOT abc");
Assert.assertNotNull(result);
log(result.toString());
Assert.assertEquals(NOT, result.get(0).getToken());
Assert.assertEquals(WORD, result.get(1).getToken());
result = tokenizer.tokenize("(abc)");
Assert.assertNotNull(result);
log(result.toString());
Assert.assertEquals(OPEN, result.get(0).getToken());
Assert.assertEquals(WORD, result.get(1).getToken());
Assert.assertEquals(CLOSE, result.get(2).getToken());
result = tokenizer.tokenize("((abc))");
Assert.assertNotNull(result);
log(result.toString());
Assert.assertEquals(OPEN, result.get(0).getToken());
Assert.assertEquals(WORD, result.get(2).getToken());
Assert.assertEquals(CLOSE, result.get(4).getToken());
}
@Test
public void parseWords() throws Exception {
SearchTokenizer tokenizer = new SearchTokenizer();
List<SearchQueryToken> result;
//
result = tokenizer.tokenize("abc");
Assert.assertNotNull(result);
log(result.toString());
Assert.assertEquals(WORD, result.get(0).getToken());
//
result = tokenizer.tokenize("9988abs");
Assert.assertNotNull(result);
log(result.toString());
Assert.assertEquals(WORD, result.get(0).getToken());
}
@Test
public void parsePhrase() throws Exception {
SearchTokenizer tokenizer = new SearchTokenizer();
List<SearchQueryToken> result;
//
result = tokenizer.tokenize("'abc'");
Assert.assertNotNull(result);
log(result.toString());
Assert.assertEquals(PHRASE, result.get(0).getToken());
//
result = tokenizer.tokenize("'9988 abs'");
Assert.assertNotNull(result);
log(result.toString());
Assert.assertEquals(PHRASE, result.get(0).getToken());
Assert.assertEquals("'9988 abs'", result.get(0).getLiteral());
//
result = tokenizer.tokenize("'99_88.'");
Assert.assertNotNull(result);
log(result.toString());
Assert.assertEquals(PHRASE, result.get(0).getToken());
Assert.assertEquals("'99_88.'", result.get(0).getLiteral());
}
@Test
public void testParseNot() throws Exception {
SearchTokenizer tokenizer = new SearchTokenizer();
List<SearchQueryToken> result;
result = tokenizer.tokenize("NOT abc");
Assert.assertNotNull(result);
log(result.toString());
Assert.assertEquals(NOT, result.get(0).getToken());
Assert.assertEquals(WORD, result.get(1).getToken());
}
@Test
public void testParseOr() throws Exception {
SearchTokenizer tokenizer = new SearchTokenizer();
List<SearchQueryToken> result;
result = tokenizer.tokenize("abc OR xyz");
Assert.assertNotNull(result);
log(result.toString());
Assert.assertEquals(WORD, result.get(0).getToken());
Assert.assertEquals(OR, result.get(1).getToken());
Assert.assertEquals(WORD, result.get(2).getToken());
result = tokenizer.tokenize("abc OR xyz OR 123");
Assert.assertNotNull(result);
log(result.toString());
Assert.assertEquals(WORD, result.get(0).getToken());
Assert.assertEquals(OR, result.get(1).getToken());
Assert.assertEquals(WORD, result.get(2).getToken());
Assert.assertEquals(OR, result.get(3).getToken());
Assert.assertEquals(WORD, result.get(4).getToken());
}
@Test
public void testParseAnd() throws Exception {
SearchTokenizer tokenizer = new SearchTokenizer();
List<SearchQueryToken> result;
result = tokenizer.tokenize("abc AND xyz");
Assert.assertNotNull(result);
log(result.toString());
Assert.assertEquals(WORD, result.get(0).getToken());
Assert.assertEquals(AND, result.get(1).getToken());
Assert.assertEquals(WORD, result.get(2).getToken());
result = tokenizer.tokenize("abc AND xyz AND 123");
Assert.assertNotNull(result);
log(result.toString());
Assert.assertEquals(WORD, result.get(0).getToken());
Assert.assertEquals(AND, result.get(1).getToken());
Assert.assertEquals(WORD, result.get(2).getToken());
Assert.assertEquals(AND, result.get(3).getToken());
Assert.assertEquals(WORD, result.get(4).getToken());
result = tokenizer.tokenize("abc AND 'x-y_z' AND 123");
Assert.assertNotNull(result);
log(result.toString());
Assert.assertEquals(WORD, result.get(0).getToken());
Assert.assertEquals(AND, result.get(1).getToken());
Assert.assertEquals(PHRASE, result.get(2).getToken());
Assert.assertEquals("'x-y_z'", result.get(2).getLiteral());
Assert.assertEquals(AND, result.get(3).getToken());
Assert.assertEquals(WORD, result.get(4).getToken());
}
@Test
public void testParseAndOr() throws Exception {
SearchTokenizer tokenizer = new SearchTokenizer();
List<SearchQueryToken> result;
result = tokenizer.tokenize("abc AND xyz OR 123");
Assert.assertNotNull(result);
log(result.toString());
Assert.assertEquals(WORD, result.get(0).getToken());
Assert.assertEquals(AND, result.get(1).getToken());
Assert.assertEquals(WORD, result.get(2).getToken());
Assert.assertEquals(OR, result.get(3).getToken());
Assert.assertEquals(WORD, result.get(4).getToken());
SearchValidator.init("abc AND ANDsomething")
.addExpected(WORD, AND, WORD).validate();
}
@Test
public void parseCombinations() throws Exception {
SearchTokenizer tokenizer = new SearchTokenizer();
List<SearchQueryToken> result;
result = tokenizer.tokenize("abc AND NOT xyz OR 123");
Assert.assertNotNull(result);
log(result.toString());
Iterator<SearchQueryToken> it = result.iterator();
Assert.assertEquals(WORD, it.next().getToken());
Assert.assertEquals(AND, it.next().getToken());
Assert.assertEquals(NOT, it.next().getToken());
Assert.assertEquals(WORD, it.next().getToken());
Assert.assertEquals(OR, it.next().getToken());
Assert.assertEquals(WORD, it.next().getToken());
SearchValidator.init("foo AND bar OR foo AND baz OR that AND bar OR that AND baz")
.addExpected(WORD, "foo").addExpected(AND)
.addExpected(WORD, "bar").addExpected(OR)
.addExpected(WORD, "foo").addExpected(AND)
.addExpected(WORD, "baz").addExpected(OR)
.addExpected(WORD, "that").addExpected(AND)
.addExpected(WORD, "bar").addExpected(OR)
.addExpected(WORD, "that").addExpected(AND)
.addExpected(WORD, "baz")
.validate();
SearchValidator.init("(foo OR that) AND (bar OR baz)").enableLogging()
.addExpected(OPEN)
.addExpected(WORD, "foo").addExpected(OR).addExpected(WORD, "that")
.addExpected(CLOSE).addExpected(AND).addExpected(OPEN)
.addExpected(WORD, "bar").addExpected(OR).addExpected(WORD, "baz")
.addExpected(CLOSE)
.validate();
}
@Test
public void parseSpecial() throws Exception {
SearchTokenizer tokenizer = new SearchTokenizer();
List<SearchQueryToken> result;
Iterator<SearchQueryToken> it;
result = tokenizer.tokenize("NOT abc AND nothing");
log(result.toString());
it = result.iterator();
Assert.assertEquals(NOT, it.next().getToken());
Assert.assertEquals(WORD, it.next().getToken());
Assert.assertEquals(AND, it.next().getToken());
Assert.assertEquals(WORD, it.next().getToken());
result = tokenizer.tokenize("abc AND andsomething");
log(result.toString());
it = result.iterator();
Assert.assertEquals(WORD, it.next().getToken());
Assert.assertEquals(AND, it.next().getToken());
Assert.assertEquals(WORD, it.next().getToken());
result = tokenizer.tokenize("abc OR orsomething");
log(result.toString());
it = result.iterator();
Assert.assertEquals(WORD, it.next().getToken());
Assert.assertEquals(OR, it.next().getToken());
Assert.assertEquals(WORD, it.next().getToken());
SearchValidator.init("abc AND ANDsomething")
.addExpected(WORD, AND, WORD).validate();
}
@Test
public void moreMixedTests() {
validate("abc");
validate("NOT abc");
validate("abc AND def");
validate("abc OR def");
validate("abc def");
validate("abc AND def AND ghi", WORD, AND, WORD, AND, WORD);
validate("abc AND def OR ghi");
validate("abc AND def ghi");
validate("abc OR def AND ghi");
validate("abc OR def OR ghi");
validate("abc OR def ghi");
validate("abc def AND ghi");
validate("abc def OR ghi");
validate("abc def ghi");
// mixed not
validate(" abc def AND ghi");
validate("NOT abc NOT def OR NOT ghi");
validate(" abc def NOT ghi");
// parenthesis
validate("(abc)");
validate("(abc AND def)");
validate("(abc AND def) OR ghi ");
validate("(abc AND def) ghi ");
validate("abc AND (def OR ghi)");
validate("abc AND (def ghi)");
}
public boolean validate(String query) {
return new SearchValidator(query).validate();
}
public boolean validate(String query, SearchQueryToken.Token ... tokens) {
SearchValidator sv = new SearchValidator(query);
for (SearchQueryToken.Token token : tokens) {
sv.addExpected(token);
}
return sv.validate();
}
private static class SearchValidator {
private List<Tuple> validations = new ArrayList<Tuple>();
private boolean log;
private final String searchQuery;
private class Tuple {
final SearchQueryToken.Token token;
final String literal;
public Tuple(SearchQueryToken.Token token, String literal) {
this.token = token;
this.literal = literal;
}
public Tuple(SearchQueryToken.Token token) {
this(token, null);
}
}
private SearchValidator(String searchQuery) {
this.searchQuery = searchQuery;
}
private static SearchValidator init(String searchQuery) {
return new SearchValidator(searchQuery);
}
private SearchValidator enableLogging() {
log = true;
return this;
}
private SearchValidator addExpected(SearchQueryToken.Token token, String literal) {
validations.add(new Tuple(token, literal));
return this;
}
private SearchValidator addExpected(SearchQueryToken.Token ... token) {
for (SearchQueryToken.Token t : token) {
validations.add(new Tuple(t));
}
return this;
}
private boolean validate() {
SearchTokenizer tokenizer = new SearchTokenizer();
List<SearchQueryToken> result = tokenizer.tokenize(searchQuery);
Assert.assertNotNull(result);
if(log) {
System.out.println(result);
}
if(validations.size() != 0) {
Assert.assertEquals(validations.size(), result.size());
Iterator<Tuple> validationIt = validations.iterator();
for (SearchQueryToken iToken : result) {
Tuple validation = validationIt.next();
Assert.assertEquals(validation.token, iToken.getToken());
if(validation.literal != null) {
Assert.assertEquals(validation.literal, iToken.getLiteral());
}
}
}
return true;
}
}
private void log(Object ... toString) {
if(logEnabled) {
System.out.println("------------");
if(toString == null || toString.length <= 1) {
System.out.println(toString == null? "NULL": (toString.length == 0? "EMPTY ARRAY": toString[0]));
} else {
int count = 1;
for (Object o : toString) {
System.out.println(count++ + ": " + o);
}
}
}
}
}

View File

@ -1165,8 +1165,9 @@ public class TestFullResourcePath {
.isExValidation(UriValidationException.MessageKeys.SYSTEM_QUERY_OPTION_NOT_ALLOWED);
// $search is currently not implemented. Please change this exception if the implementation is done.
testUri.runEx("FICRTCollETMixPrimCollCompTwoParam(ParameterInt16=1,ParameterString='1')", "$search=test")
.isExSemantic(MessageKeys.NOT_IMPLEMENTED);
// FIXME (151106:mibo): check after finish of OLINGO-568
// testUri.runEx("FICRTCollETMixPrimCollCompTwoParam(ParameterInt16=1,ParameterString='1')", "$search=test")
// .isExSemantic(MessageKeys.NOT_IMPLEMENTED);
testUri.run("ESAllPrim/olingo.odata.test1.BFNESAllPrimRTCTAllPrim()")
.isKind(UriInfoKind.resource)
@ -5363,12 +5364,12 @@ public class TestFullResourcePath {
testUri.run("ESTwoKeyNav", "$search= abc def NOT ghi");
// parenthesis
testUri.run("ESTwoKeyNav", "$search= (abc)");
testUri.run("ESTwoKeyNav", "$search= (abc AND def)");
testUri.run("ESTwoKeyNav", "$search= (abc AND def) OR ghi ");
testUri.run("ESTwoKeyNav", "$search= (abc AND def) ghi ");
testUri.run("ESTwoKeyNav", "$search= abc AND (def OR ghi)");
testUri.run("ESTwoKeyNav", "$search= abc AND (def ghi)");
testUri.run("ESTwoKeyNav", "$search=(abc)");
testUri.run("ESTwoKeyNav", "$search=(abc AND def)");
testUri.run("ESTwoKeyNav", "$search=(abc AND def) OR ghi ");
testUri.run("ESTwoKeyNav", "$search=(abc AND def) ghi ");
testUri.run("ESTwoKeyNav", "$search=abc AND (def OR ghi)");
testUri.run("ESTwoKeyNav", "$search=abc AND (def ghi)");
}
@Test