diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/search/SearchUnary.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/search/SearchUnary.java index 5d8e143e4..c26630878 100644 --- a/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/search/SearchUnary.java +++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/search/SearchUnary.java @@ -18,8 +18,9 @@ */ package org.apache.olingo.server.api.uri.queryoption.search; -public interface SearchUnary { +public interface SearchUnary extends SearchExpression { - SearchExpression getOperand(); + SearchUnaryOperatorKind getOperator(); + SearchTerm getOperand(); } diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchBinaryImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchBinaryImpl.java new file mode 100644 index 000000000..37b03c07d --- /dev/null +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchBinaryImpl.java @@ -0,0 +1,56 @@ +/* + * 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.search.SearchBinary; +import org.apache.olingo.server.api.uri.queryoption.search.SearchBinaryOperatorKind; +import org.apache.olingo.server.api.uri.queryoption.search.SearchExpression; + +public class SearchBinaryImpl implements SearchBinary { + + private final SearchBinaryOperatorKind operator; + private final SearchExpression left; + private final SearchExpression right; + + public SearchBinaryImpl(SearchExpression left, SearchBinaryOperatorKind operator, SearchExpression right) { + this.left = left; + this.operator = operator; + this.right = right; + } + + @Override + public SearchBinaryOperatorKind getOperator() { + return operator; + } + + @Override + public SearchExpression getLeftOperand() { + return left; + } + + @Override + public SearchExpression getRightOperand() { + return right; + } + + @Override + public String toString() { + return "{" + left + " " + operator.name() + " " + right + '}'; + } +} 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 ca45037c6..60fcd4b43 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 @@ -19,19 +19,106 @@ 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.api.uri.queryoption.search.SearchTerm; import org.apache.olingo.server.core.uri.queryoption.SearchOptionImpl; -import java.util.List; +import java.util.Iterator; public class SearchParser { + private Iterator tokens; + private SearchExpression root; +// private SearchQueryToken currentToken; + public SearchOption parse(String path, String value) { SearchTokenizer tokenizer = new SearchTokenizer(); try { - List tokens = tokenizer.tokenize(value); + tokens = tokenizer.tokenize(value).iterator(); +// currentToken = tokens.next(); + root = processSearchExpression(); } catch (SearchTokenizerException e) { return null; } - return new SearchOptionImpl(); + final SearchOptionImpl searchOption = new SearchOptionImpl(); + searchOption.setSearchExpression(root); + return searchOption; + } + + private SearchExpression processSearchExpression() { + SearchQueryToken token = nextToken(); + if(token.getToken() == SearchQueryToken.Token.OPEN) { + throw illegalState(); + } else if(token.getToken() == SearchQueryToken.Token.NOT) { + return processNot(); + } else if(token.getToken() == SearchQueryToken.Token.PHRASE || + token.getToken() == SearchQueryToken.Token.WORD) { + return processTerm(token); +// } else if(token.getToken() == SearchQueryToken.Token.AND) { +// return processAnd(); + } else { + throw illegalState(); + } + } + + private SearchExpression processAnd(SearchExpression se) { + SearchQueryToken token = nextToken(); + if(token.getToken() == SearchQueryToken.Token.PHRASE || + token.getToken() == SearchQueryToken.Token.WORD) { +// SearchExpression t = processTerm(token); + return new SearchBinaryImpl(se, SearchBinaryOperatorKind.AND, processTerm(token)); + } + throw illegalState(); + } + + private SearchExpression processOr(SearchExpression se) { + SearchQueryToken token = nextToken(); + if(token.getToken() == SearchQueryToken.Token.PHRASE || + token.getToken() == SearchQueryToken.Token.WORD) { + return new SearchBinaryImpl(se, SearchBinaryOperatorKind.OR, processTerm(token)); + } + throw illegalState(); + } + + private RuntimeException illegalState() { + return new RuntimeException(); + } + + private SearchUnaryImpl processNot() { + SearchQueryToken token = nextToken(); + if(token.getToken() == SearchQueryToken.Token.PHRASE || + token.getToken() == SearchQueryToken.Token.WORD) { + throw illegalState(); +// return new SearchUnaryImpl(processTerm(token)); + } + throw illegalState(); + } + + private SearchQueryToken nextToken() { +// if(tokens.hasNext()) { + return tokens.next(); +// } +// return null; + } + + private SearchExpression processTerm(SearchQueryToken token) { + SearchTerm searchTerm = new SearchTermImpl(token.getLiteral()); + if(isEof()) { + return searchTerm; + } + + SearchQueryToken next = nextToken(); + if(next.getToken() == SearchQueryToken.Token.AND) { + return processAnd(searchTerm); + } else if(next.getToken() == SearchQueryToken.Token.OR) { + return processOr(searchTerm); + } + + throw illegalState(); + } + + private boolean isEof() { + return !tokens.hasNext(); } } diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchTermImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchTermImpl.java new file mode 100644 index 000000000..24b12915b --- /dev/null +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchTermImpl.java @@ -0,0 +1,39 @@ +/* + * 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.search.SearchTerm; + +public class SearchTermImpl implements SearchTerm { + private final String term; + + public SearchTermImpl(String term) { + this.term = term; + } + + @Override + public String getSearchTerm() { + return term; + } + + @Override + public String toString() { + return "{'" + term + "'}"; + } +} diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchUnaryImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchUnaryImpl.java new file mode 100644 index 000000000..639540d7d --- /dev/null +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchUnaryImpl.java @@ -0,0 +1,46 @@ +/* + * 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.search.SearchTerm; +import org.apache.olingo.server.api.uri.queryoption.search.SearchUnary; +import org.apache.olingo.server.api.uri.queryoption.search.SearchUnaryOperatorKind; + +public class SearchUnaryImpl implements SearchUnary { + private final SearchTerm operand; + + public SearchUnaryImpl(SearchTerm operand) { + this.operand = operand; + } + + @Override + public SearchUnaryOperatorKind getOperator() { + return SearchUnaryOperatorKind.NOT; + } + + @Override + public SearchTerm getOperand() { + return operand; + } + + @Override + public String toString() { + return "{" + getOperator().name() + " " + operand + '}'; + } +} 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 0c51ba6f4..fa4229323 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 @@ -20,14 +20,149 @@ 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.SearchExpression; +import org.apache.olingo.server.api.uri.queryoption.search.SearchUnary; +import org.junit.Assert; +import org.junit.Ignore; import org.junit.Test; +import java.lang.reflect.Field; + +import static org.apache.olingo.server.api.uri.queryoption.search.SearchBinaryOperatorKind.*; + public class SearchParserTest { @Test - public void basicParsing() { - SearchParser parser = new SearchParser(); - SearchOption so = parser.parse("ESAllPrim", "abc"); - SearchExpression se = so.getSearchExpression(); + public void basicParsing() throws SearchTokenizerException { + SearchExpressionValidator.init("a") + .validate(with("a")); + SearchExpressionValidator.init("a AND b") + .validate(with("a", and("b"))); + SearchExpressionValidator.init("a AND b AND c") + .validate(with("a", and("b", and("c")))); + SearchExpressionValidator.init("a OR b") + .validate(with("a", or("b"))); + SearchExpressionValidator.init("a OR b OR c") + .validate(with("a", or("b", or("c")))); } + + @Test + @Ignore("Currently not working") + public void mixedParsing() throws Exception{ + SearchExpressionValidator.init("a AND b OR c") + .validate(with("c", or("a", and("b")))); + } + + @Test + @Ignore("Internal test, to be deleted") + public void sebuilder() { + System.out.println(with("c", or("a", and("b"))).toString()); + System.out.println(with("a", and("b", and("c"))).toString()); + System.out.println(with("a").toString()); + System.out.println(with(not("a")).toString()); + System.out.println(with("a", and("b")).toString()); + System.out.println(with("a", or("b")).toString()); + System.out.println(with("a", and(not("b"))).toString()); + } + + private static SearchExpression with(String term) { + return new SearchTermImpl(term); + } + + + private static SearchExpression with(String left, SearchExpression right) { + setLeftField(left, right); + return right; + } + + private static SearchUnary with(SearchUnary unary) { + return unary; + } + + private static SearchExpression or(String left, SearchExpression right) { + SearchExpression or = or(right); + setLeftField(left, right); + return or; + } + + private static SearchExpression and(String left, SearchExpression right) { + SearchExpression and = and(right); + setLeftField(left, right); + return and; + } + + private static SearchExpression or(SearchExpression right) { + return new SearchBinaryImpl(null, OR, right); + } + + private static SearchExpression and(SearchExpression right) { + return new SearchBinaryImpl(null, AND, right); + } + + private static SearchExpression and(String right) { + return and(new SearchTermImpl(right)); + } + + private static SearchExpression or(String right) { + return or(new SearchTermImpl(right)); + } + + private static SearchUnary not(String term) { + return new SearchUnaryImpl(new SearchTermImpl(term)); + } + + private static void setLeftField(String left, SearchExpression se) { + try { + Field field = null; + if(se instanceof SearchUnaryImpl) { + field = SearchBinaryImpl.class.getDeclaredField("operand"); + } else if(se instanceof SearchBinaryImpl) { + field = SearchBinaryImpl.class.getDeclaredField("left"); + } else { + Assert.fail("Unexpected exception: " + se.getClass()); + } + field.setAccessible(true); + field.set(se, new SearchTermImpl(left)); + } catch (Exception e) { + Assert.fail("Unexpected exception: " + e.getClass()); + } + } + + private static class SearchExpressionValidator { + private boolean log; + private final String searchQuery; + + private SearchExpressionValidator(String searchQuery) { + this.searchQuery = searchQuery; + } + + private static SearchExpressionValidator init(String searchQuery) { + return new SearchExpressionValidator(searchQuery); + } + private SearchExpressionValidator enableLogging() { + log = true; + return this; + } + private void validate(Class exception) throws SearchTokenizerException { + try { + new SearchTokenizer().tokenize(searchQuery); + } catch (Exception e) { + Assert.assertEquals(exception, e.getClass()); + return; + } + Assert.fail("Expected exception " + exception.getClass().getSimpleName() + " was not thrown."); + } + + private void validate(SearchExpression expectedSearchExpression) throws SearchTokenizerException { + SearchParser tokenizer = new SearchParser(); + SearchOption result = tokenizer.parse(null, searchQuery); + Assert.assertNotNull(result); + final SearchExpression searchExpression = result.getSearchExpression(); + Assert.assertNotNull(searchExpression); + if(log) { + System.out.println(expectedSearchExpression); + } + Assert.assertEquals(expectedSearchExpression.toString(), searchExpression.toString()); + } + } + } \ No newline at end of file