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/17 16:04:11 UTC

[01/23] olingo-odata4 git commit: [OLINGO-568] SearchTokenizer for SearchParser

Repository: olingo-odata4
Updated Branches:
  refs/heads/master be3b10a24 -> ae1b2754b


[OLINGO-568] SearchTokenizer for SearchParser


Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo
Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/452ebcbd
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/452ebcbd
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/452ebcbd

Branch: refs/heads/master
Commit: 452ebcbdd80d358b71e1e1eb2f8a9830e7aaa59f
Parents: ac828a3
Author: Michael Bolz <mi...@sap.com>
Authored: Fri Nov 6 15:42:32 2015 +0100
Committer: Michael Bolz <mi...@sap.com>
Committed: Fri Nov 6 15:43:36 2015 +0100

----------------------------------------------------------------------
 .../olingo/server/core/uri/parser/Parser.java   |  21 +-
 .../core/uri/parser/search/SearchParser.java    |  33 ++
 .../uri/parser/search/SearchQueryToken.java     |  26 ++
 .../core/uri/parser/search/SearchTokenizer.java | 396 +++++++++++++++++++
 .../core/uri/queryoption/SearchOptionImpl.java  |   7 +-
 .../uri/parser/search/SearchParserTest.java     |  30 ++
 .../uri/parser/search/SearchTokenizerTest.java  | 388 ++++++++++++++++++
 .../core/uri/antlr/TestFullResourcePath.java    |  17 +-
 8 files changed, 902 insertions(+), 16 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/452ebcbd/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/Parser.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/Parser.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/Parser.java
index 8732341..82094cf 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/Parser.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/Parser.java
@@ -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());

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/452ebcbd/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
new file mode 100644
index 0000000..d508932
--- /dev/null
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchParser.java
@@ -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();
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/452ebcbd/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchQueryToken.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchQueryToken.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchQueryToken.java
new file mode 100644
index 0000000..a08c1a2
--- /dev/null
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchQueryToken.java
@@ -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();
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/452ebcbd/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
new file mode 100644
index 0000000..7d2b559
--- /dev/null
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizer.java
@@ -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;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/452ebcbd/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/SearchOptionImpl.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/SearchOptionImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/SearchOptionImpl.java
index ec42147..51323a8 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/SearchOptionImpl.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/SearchOptionImpl.java
@@ -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;
+  }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/452ebcbd/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
new file mode 100644
index 0000000..81147ba
--- /dev/null
+++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchParserTest.java
@@ -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");
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/452ebcbd/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java
new file mode 100644
index 0000000..15d35df
--- /dev/null
+++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java
@@ -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);
+        }
+      }
+    }
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/452ebcbd/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestFullResourcePath.java
----------------------------------------------------------------------
diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestFullResourcePath.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestFullResourcePath.java
index 6f1c4ad..259b61f 100644
--- a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestFullResourcePath.java
+++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestFullResourcePath.java
@@ -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


[22/23] olingo-odata4 git commit: [OLINGO-568] Merge branch 'OLINGO-568_SearchParser_Draft'

Posted by mi...@apache.org.
[OLINGO-568] Merge branch 'OLINGO-568_SearchParser_Draft'


Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo
Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/1a59a580
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/1a59a580
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/1a59a580

Branch: refs/heads/master
Commit: 1a59a5804b16c714f5c9354ddd5b2c4a93987c1e
Parents: be3b10a 6235f3a
Author: Michael Bolz <mi...@sap.com>
Authored: Tue Nov 17 15:31:44 2015 +0100
Committer: Michael Bolz <mi...@sap.com>
Committed: Tue Nov 17 15:31:44 2015 +0100

----------------------------------------------------------------------
 .../tecsvc/client/SystemQueryOptionITCase.java  |  42 +-
 .../queryoption/search/SearchExpression.java    |  14 +-
 .../api/uri/queryoption/search/SearchUnary.java |   5 +-
 .../olingo/server/core/uri/parser/Parser.java   |  15 +-
 .../uri/parser/search/SearchBinaryImpl.java     |  56 ++
 .../uri/parser/search/SearchExpressionImpl.java |  58 ++
 .../core/uri/parser/search/SearchParser.java    | 196 +++++++
 .../parser/search/SearchParserException.java    |  54 ++
 .../uri/parser/search/SearchQueryToken.java     |  26 +
 .../core/uri/parser/search/SearchTermImpl.java  |  39 ++
 .../core/uri/parser/search/SearchTokenizer.java | 571 +++++++++++++++++++
 .../parser/search/SearchTokenizerException.java |  47 ++
 .../core/uri/parser/search/SearchUnaryImpl.java |  46 ++
 .../core/uri/queryoption/SearchOptionImpl.java  |   7 +-
 .../search/SearchParserAndTokenizerTest.java    | 198 +++++++
 .../uri/parser/search/SearchParserTest.java     | 248 ++++++++
 .../uri/parser/search/SearchTokenizerTest.java  | 500 ++++++++++++++++
 .../processor/TechnicalEntityProcessor.java     |   2 +
 .../tecsvc/processor/TechnicalProcessor.java    |   3 +-
 .../queryoptions/options/SearchHandler.java     |  93 +++
 .../core/uri/antlr/TestFullResourcePath.java    |  19 +-
 21 files changed, 2211 insertions(+), 28 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/1a59a580/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/Parser.java
----------------------------------------------------------------------
diff --cc lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/Parser.java
index c5857c7,d6cb557..d12b853
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/Parser.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/Parser.java
@@@ -58,7 -55,7 +58,8 @@@ import org.apache.olingo.server.core.ur
  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.AliasQueryOptionImpl;
  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;

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/1a59a580/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestFullResourcePath.java
----------------------------------------------------------------------


[11/23] olingo-odata4 git commit: [OLINGO-568] Removed implicit and and added unicode for words

Posted by mi...@apache.org.
[OLINGO-568] Removed implicit and and added unicode for words


Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo
Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/ba5220ab
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/ba5220ab
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/ba5220ab

Branch: refs/heads/master
Commit: ba5220ab4a74d0c5aaff9fe1b72632e2a8bc8778
Parents: 37c5827
Author: mibo <mi...@apache.org>
Authored: Wed Nov 11 20:36:51 2015 +0100
Committer: mibo <mi...@apache.org>
Committed: Wed Nov 11 20:58:11 2015 +0100

----------------------------------------------------------------------
 .../core/uri/parser/search/SearchTokenizer.java | 169 +++++++++++++------
 .../uri/parser/search/SearchTokenizerTest.java  | 168 ++++++++++--------
 2 files changed, 212 insertions(+), 125 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/ba5220ab/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 1ec4df1..9288981 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
@@ -73,11 +73,6 @@ public class SearchTokenizer {
       return this;
     }
 
-    public State finish(Token token) {
-      this.token = token;
-      return finish();
-    }
-
     public boolean isFinished() {
       return finished;
     }
@@ -90,23 +85,40 @@ public class SearchTokenizer {
       return this;
     }
 
-    static boolean isAllowedChar(final char character) {
+    static boolean isAllowedWord(final char character) {
       // TODO mibo: add missing allowed characters
-      return CHAR_A <= character && character <= 'Z' // case A..Z
-          || 'a' <= character && character <= 'z' // case a..z
-          || '0' <= character && character <= '9'; // case 0..9
+      int type = Character.getType(character);
+      return (type == Character.LETTER_NUMBER
+          || type == Character.LOWERCASE_LETTER
+          || type == Character.MODIFIER_LETTER
+          || type == Character.OTHER_LETTER
+          || type == Character.TITLECASE_LETTER
+          || type == Character.UPPERCASE_LETTER);
     }
 
     /**
-     * qchar-no-AMP-DQUOTE   = qchar-unescaped / escape ( escape / quotation-mark )
-     * qchar-unescaped  = unreserved / pct-encoded-unescaped / 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" )
+     *
+     * qchar-no-AMP-DQUOTE   = qchar-unescaped  / escape ( escape / quotation-mark )
+     *
+     * escape = "\" / "%5C"     ; reverse solidus U+005C
+     * quotation-mark  = DQUOTE / "%22"
+     *
+     * ALPHA  = %x41-5A / %x61-7A
+     * DIGIT  = %x30-39
+     * DQUOTE = %x22
+     *
      * @param character which is checked
      * @return true if character is allowed for a phrase
      */
     static boolean isAllowedPhrase(final char character) {
       // FIXME mibo: check missing
-      return isAllowedChar(character)
+      return isAlphaOrDigit(character)
           || character == '-'
           || character == '.'
           || character == '_'
@@ -119,6 +131,12 @@ public class SearchTokenizer {
           || character == '=';
     }
 
+    private static boolean isAlphaOrDigit(char character) {
+      return 'A' <= character && character <= 'Z' // case A..Z
+          || 'a' <= character && character <= 'z' // case a..z
+          || '0' <= character && character <= '9'; // case 0..9
+    }
+
     //BWS =  *( SP / HTAB / "%20" / "%09" )  ; "bad" whitespace
     //RWS = 1*( SP / HTAB / "%20" / "%09" )  ; "required" whitespace
     static boolean isWhitespace(final char character) {
@@ -173,15 +191,12 @@ public class SearchTokenizer {
     public SearchExpressionState() {
       super(null);
     }
-    public SearchExpressionState(String initLiteral) {
-      super(null, initLiteral);
-    }
     @Override
     public State nextChar(char c) throws SearchTokenizerException {
       if (c == CHAR_OPEN) {
         return new OpenState();
       } else if (isWhitespace(c)) {
-        return new RwsImplicitAndOrState();
+        return new RwsState();
       } else if(c == CHAR_CLOSE) {
         return new CloseState();
       } else {
@@ -205,7 +220,7 @@ public class SearchTokenizer {
         return new NotState(c);
       } else if (c == QUOTATION_MARK) {
         return new SearchPhraseState(c);
-      } else if (isAllowedChar(c)) {
+      } else if (isAllowedWord(c)) {
         return new SearchWordState(c);
       }
       return forbidden(c);
@@ -219,21 +234,30 @@ public class SearchTokenizer {
   private class SearchWordState extends LiteralState {
     public SearchWordState(char c) throws SearchTokenizerException {
       super(Token.WORD, c);
+      if(!isAllowedWord(c)) {
+        forbidden(c);
+      }
     }
-    public SearchWordState(State toConsume) {
+    public SearchWordState(State toConsume) throws SearchTokenizerException {
       super(Token.WORD, toConsume.getLiteral());
+      char[] chars = literal.toString().toCharArray();
+      for (char aChar : chars) {
+        if(!isAllowedWord(aChar)) {
+          forbidden(aChar);
+        }
+      }
     }
 
     @Override
     public State nextChar(char c) throws SearchTokenizerException {
-      if (isAllowedChar(c)) {
+      if (isAllowedWord(c)) {
         return allowed(c);
       } else if (c == CHAR_CLOSE) {
         finish();
         return new CloseState();
       } else if (isWhitespace(c)) {
         finish();
-        return new RwsImplicitAndOrState();
+        return new RwsState();
       }
       return forbidden(c);
     }
@@ -304,13 +328,52 @@ public class SearchTokenizer {
       }
     }
     @Override
-    public State nextChar(char c) {
+    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)) {
         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) {
+        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)) {
+        finish();
+        return new BeforeSearchExpressionRwsState();
+      } else {
+        return new SearchWordState(this);
+      }
+    }
+  }
+  private class OrState extends LiteralState {
+    public OrState(char c) throws SearchTokenizerException {
+      super(Token.OR, c);
+      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)) {
+        finish();
         return new BeforeSearchExpressionRwsState();
       } else {
         return new SearchWordState(this);
@@ -334,47 +397,53 @@ public class SearchTokenizer {
     }
   }
 
-  // implicit and
-  private class RwsImplicitAndOrState extends LiteralState {
-    private boolean noneRws = false;
-    public RwsImplicitAndOrState() {
+  private class BeforePhraseOrWordRwsState extends State {
+    public BeforePhraseOrWordRwsState() {
       super(null);
     }
     @Override
     public State nextChar(char c) throws SearchTokenizerException {
-      if (!noneRws && isWhitespace(c)) {
-        return allowed(c);
-      } else if (c == CHAR_O) {
-        noneRws = true;
+      if (isWhitespace(c)) {
         return allowed(c);
-      } else if (literal.length() == 1 && c == CHAR_R) {
+      } else if(c == '"') {
+        return new SearchPhraseState(c);
+      } else {
+        return new SearchWordState(c);
+      }
+    }
+  }
+
+  private class RwsState extends State {
+    public RwsState() {
+      super(null);
+    }
+    @Override
+    public State nextChar(char c) throws SearchTokenizerException {
+      if (isWhitespace(c)) {
         return allowed(c);
-      } else if (literal.length() == 2 && isWhitespace(c)) {
-        finish(Token.OR);
-        return new BeforeSearchExpressionRwsState();
+      } else if (c == CHAR_O) {
+        return new OrState(c);
       } else if (c == CHAR_A) {
-        noneRws = true;
-        return allowed(c);
-      } else 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)) {
-        finish(Token.AND);
-        return new BeforeSearchExpressionRwsState();
-      } else if(noneRws) {
-        finish(Token.AND);
-        return new SearchWordState(this);
+        return new AndState(c);
       } else {
-        finish(Token.AND);
-        return new SearchExpressionState(literal.toString()).init(c);
+        return new SearchExpressionState().init(c);
       }
     }
   }
 
-  // TODO (mibo): add (new) parse exception
-  public List<SearchQueryToken> tokenize(String searchQuery) throws SearchTokenizerException {
-    char[] chars = searchQuery.toCharArray();
+  /**
+   * Take the search query and split into according SearchQueryToken.
+   * Before split into tokens the given search query is 'trimmed'.
+   *
+   * @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)
+   */
+  public List<SearchQueryToken> tokenize(final String searchQuery)
+        throws SearchTokenizerException {
+    
+    char[] chars = searchQuery.trim().toCharArray();
 
     State state = new SearchExpressionState();
     List<SearchQueryToken> states = new ArrayList<SearchQueryToken>();

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/ba5220ab/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java
index ea3cab9..828b4c4 100644
--- a/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java
+++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java
@@ -29,8 +29,6 @@ import static org.apache.olingo.server.core.uri.parser.search.SearchQueryToken.T
 
 public class SearchTokenizerTest {
 
-  private boolean logEnabled = false;
-
   @Test
   public void parseBasics() throws Exception {
     SearchTokenizer tokenizer = new SearchTokenizer();
@@ -39,25 +37,25 @@ public class SearchTokenizerTest {
     //
     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());
@@ -71,13 +69,13 @@ public class SearchTokenizerTest {
     //
     result = tokenizer.tokenize("abc");
     Assert.assertNotNull(result);
-    log(result.toString());
+    
     Assert.assertEquals(WORD, result.get(0).getToken());
 
     //
-    result = tokenizer.tokenize("9988abs");
+    result = tokenizer.tokenize("anotherWord\u1234");
     Assert.assertNotNull(result);
-    log(result.toString());
+    
     Assert.assertEquals(WORD, result.get(0).getToken());
   }
 
@@ -86,29 +84,29 @@ public class SearchTokenizerTest {
     SearchTokenizer tokenizer = new SearchTokenizer();
     List<SearchQueryToken> result;
 
-    SearchValidator.init("abc AND \"x-y_z\" AND 123").validate();
+    SearchValidator.init("abc AND \"x-y_z\" AND olingo").validate();
 
     //
     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());
 
-    SearchValidator.init("abc or \"xyz\"").addExpected(WORD, AND, WORD, AND, PHRASE).validate();
+    SearchValidator.init("abc or \"xyz\"").addExpected(WORD, WORD, PHRASE).validate();
   }
 
   @Test
@@ -118,11 +116,14 @@ public class SearchTokenizerTest {
 
     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());
 
-    SearchValidator.init("not abc").addExpected(WORD, AND, WORD).validate();
+    SearchValidator.init("not abc").addExpected(WORD, WORD).validate();
+    SearchValidator.init("NOT    abc").addExpected(NOT, WORD).validate();
+    SearchValidator.init("NOT    \"abc\"").addExpected(NOT, PHRASE).validate();
+    SearchValidator.init("NOT (sdf)").validate(SearchTokenizerException.class);
   }
 
   @Test
@@ -132,30 +133,30 @@ public class SearchTokenizerTest {
 
     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");
+    result = tokenizer.tokenize("abc OR xyz OR olingo");
     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());
 
-    SearchValidator.init("abc or xyz").addExpected(WORD, AND, WORD, AND, WORD).validate();
+    SearchValidator.init("abc or xyz").addExpected(WORD, WORD, WORD).validate();
   }
 
   @Test
   public void parseImplicitAnd() throws SearchTokenizerException {
-    SearchValidator.init("a b").addExpected(WORD, AND, WORD).validate();
-    SearchValidator.init("a b OR c").addExpected(WORD, AND, WORD, OR, WORD).validate();
-    SearchValidator.init("a bc OR c").addExpected(WORD, AND, WORD, OR, WORD).validate();
-    SearchValidator.init("a bc c").addExpected(WORD, AND, WORD, AND, WORD).validate();
-    SearchValidator.init("(a OR x) bc c").addExpected(OPEN, WORD, OR, WORD, CLOSE, AND, WORD, AND, WORD).validate();
+    SearchValidator.init("a b").addExpected(WORD, WORD).validate();
+    SearchValidator.init("a b OR c").addExpected(WORD, WORD, OR, WORD).validate();
+    SearchValidator.init("a bc OR c").addExpected(WORD, WORD, OR, WORD).validate();
+    SearchValidator.init("a bc c").addExpected(WORD, WORD, WORD).validate();
+    SearchValidator.init("(a OR x) bc c").addExpected(OPEN, WORD, OR, WORD, CLOSE, WORD, WORD).validate();
   }
 
   @Test
@@ -165,7 +166,7 @@ public class SearchTokenizerTest {
 
     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());
@@ -173,34 +174,31 @@ public class SearchTokenizerTest {
     // no lower case allowed for AND
     result = tokenizer.tokenize("abc and xyz");
     Assert.assertNotNull(result);
-    Assert.assertEquals(5, result.size());
-    log(result.toString());
+    Assert.assertEquals(3, result.size());
+    
     Assert.assertEquals(WORD, result.get(0).getToken());
-    Assert.assertEquals(AND, result.get(1).getToken());
+    Assert.assertEquals(WORD, 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());
 
     // implicit AND
     result = tokenizer.tokenize("abc 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());
+    Assert.assertEquals(WORD, result.get(1).getToken());
 
-    result = tokenizer.tokenize("abc AND xyz AND 123");
+    result = tokenizer.tokenize("abc AND xyz AND olingo");
     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");
+    result = tokenizer.tokenize("abc AND \"x-y_z\" AND olingo");
     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());
@@ -214,9 +212,9 @@ public class SearchTokenizerTest {
     SearchTokenizer tokenizer = new SearchTokenizer();
     List<SearchQueryToken> result;
 
-    result = tokenizer.tokenize("abc AND xyz OR 123");
+    result = tokenizer.tokenize("abc AND xyz OR olingo");
     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());
@@ -233,9 +231,9 @@ public class SearchTokenizerTest {
     SearchTokenizer tokenizer = new SearchTokenizer();
     List<SearchQueryToken> result;
 
-    result = tokenizer.tokenize("abc AND NOT xyz OR 123");
+    result = tokenizer.tokenize("abc AND NOT xyz OR olingo");
     Assert.assertNotNull(result);
-    log(result.toString());
+    
     Iterator<SearchQueryToken> it = result.iterator();
     Assert.assertEquals(WORD, it.next().getToken());
     Assert.assertEquals(AND, it.next().getToken());
@@ -273,7 +271,7 @@ public class SearchTokenizerTest {
     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());
@@ -281,7 +279,7 @@ public class SearchTokenizerTest {
     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());
@@ -291,17 +289,47 @@ public class SearchTokenizerTest {
         .addExpected(WORD, AND, WORD).validate();
 
     SearchValidator.init("abc ANDsomething")
-        .addExpected(WORD, AND, WORD).validate();
+        .addExpected(WORD, WORD).validate();
 
     SearchValidator.init("abc ORsomething")
-        .addExpected(WORD, AND, WORD).validate();
+        .addExpected(WORD, WORD).validate();
 
     SearchValidator.init("abc OR orsomething")
         .addExpected(WORD, OR, WORD).validate();
 
     SearchValidator.init("abc OR ORsomething")
         .addExpected(WORD, OR, WORD).validate();
+  }
+
+  @Test
+  public void unicodeInWords() throws Exception {
+    // Ll, Lm, Lo, Lt, Lu, Nl
+    SearchValidator.init("abc OR Ll\u01E3Lm\u02B5Lo\u1BE4Lt\u01F2Lu\u03D3Nl\u216F")
+        .addExpected(WORD, OR, WORD).validate();
+  }
 
+  /**
+   * 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" )
+   *
+   * qchar-no-AMP-DQUOTE   = qchar-unescaped  / escape ( escape / quotation-mark )
+   *
+   * escape = "\" / "%5C"     ; reverse solidus U+005C
+   * quotation-mark  = DQUOTE / "%22"
+   * ALPHA  = %x41-5A / %x61-7A
+   * DIGIT  = %x30-39
+   * DQUOTE = %x22
+   *
+   * @throws Exception
+   */
+  @Test
+  public void characterInPhrase() throws Exception {
+    SearchValidator.init("\"123\" OR \"ALPHA-._~\"")
+        .addExpected(PHRASE, OR, PHRASE).validate();
   }
 
   @Test
@@ -311,32 +339,32 @@ public class SearchTokenizerTest {
 
     validate("abc AND def");
     validate("abc  OR def");
-    validate("abc     def");
+    validate("abc     def", WORD, WORD);
 
     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  OR def AND ghi", WORD, OR, WORD, AND, WORD);
+    validate("abc  OR def  OR ghi", WORD, OR, WORD, OR, WORD);
+    validate("abc  OR def     ghi", WORD, OR, WORD, WORD);
 
     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");
+    SearchValidator.init("    abc         def AND     ghi").validate(WORD, WORD, AND, WORD);
+    validate("NOT abc  NOT    def  OR NOT ghi", NOT, WORD, NOT, WORD, OR, NOT, WORD);
+    validate("    abc         def     NOT ghi", WORD, WORD, NOT, WORD);
 
     // 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)");
+    validate("(abc)", OPEN, WORD, CLOSE);
+    validate("(abc AND  def)", OPEN, WORD, AND, WORD, CLOSE);
+    validate("(abc AND  def)   OR  ghi", OPEN, WORD, AND, WORD, CLOSE, OR, WORD);
+    validate("(abc AND  def)       ghi", OPEN, WORD, AND, WORD, CLOSE, WORD);
+    validate("abc AND (def    OR  ghi)", WORD, AND, OPEN, WORD, OR, WORD, CLOSE);
+    validate("abc AND (def        ghi)", WORD, AND, OPEN, WORD, WORD, CLOSE);
   }
 
   @Test
@@ -363,6 +391,12 @@ public class SearchTokenizerTest {
     private List<Tuple> validations = new ArrayList<Tuple>();
     private boolean log;
     private final String searchQuery;
+
+    public void validate(SearchQueryToken.Token... tokens) throws SearchTokenizerException {
+      addExpected(tokens);
+      validate();
+    }
+
     private class Tuple {
       final SearchQueryToken.Token token;
       final String literal;
@@ -427,20 +461,4 @@ public class SearchTokenizerTest {
       }
     }
   }
-
-
-
-  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);
-        }
-      }
-    }
-  }
 }
\ No newline at end of file


[08/23] olingo-odata4 git commit: [OLINGO-568] Fixed issue with implicit and

Posted by mi...@apache.org.
[OLINGO-568] Fixed issue with implicit and


Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo
Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/f64abe13
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/f64abe13
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/f64abe13

Branch: refs/heads/master
Commit: f64abe136b0d8b6b8ec195319f2a5e3db98c27d5
Parents: 96483ae
Author: Michael Bolz <mi...@sap.com>
Authored: Tue Nov 10 06:25:54 2015 +0100
Committer: Michael Bolz <mi...@sap.com>
Committed: Tue Nov 10 06:25:54 2015 +0100

----------------------------------------------------------------------
 .../core/uri/parser/search/SearchTokenizer.java | 94 ++++++++------------
 .../uri/parser/search/SearchTokenizerTest.java  |  9 +-
 2 files changed, 40 insertions(+), 63 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/f64abe13/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 12af609..f393d22 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
@@ -73,6 +73,11 @@ public class SearchTokenizer {
       return this;
     }
 
+    public State finish(Token token) {
+      this.token = token;
+      return finish();
+    }
+
     public boolean isFinished() {
       return finished;
     }
@@ -167,12 +172,15 @@ public class SearchTokenizer {
     public SearchExpressionState() {
       super(null);
     }
+    public SearchExpressionState(String initLiteral) {
+      super(null, initLiteral);
+    }
     @Override
     public State nextChar(char c) {
       if (c == CHAR_OPEN) {
         return new OpenState();
       } else if (isWhitespace(c)) {
-        return new RwsImplicitAndState();
+        return new RwsImplicitAndOrState();
       } else if(c == CHAR_CLOSE) {
         return new CloseState();
       } else {
@@ -224,7 +232,7 @@ public class SearchTokenizer {
         return new CloseState();
       } else if (isWhitespace(c)) {
         finish();
-        return new RwsImplicitAndState();
+        return new RwsImplicitAndOrState();
       }
       return forbidden(c);
     }
@@ -254,14 +262,8 @@ public class SearchTokenizer {
         allowed(c);
         return new SearchExpressionState();
       } else if (isWhitespace(c)) {
-        if(isFinished()) {
-          return new RwsImplicitAndState();
-        }
         return allowed(c);
       } else if (c == CHAR_CLOSE) {
-        if(isFinished()) {
-          return new CloseState();
-        }
         return allowed(c);
       }
       return forbidden(c);
@@ -317,48 +319,6 @@ public class SearchTokenizer {
     }
   }
 
-  private class AndState extends LiteralState {
-    public AndState(char c) {
-      super(Token.AND, c);
-      if(c != CHAR_A) {
-        forbidden(c);
-      }
-    }
-    @Override
-    public State nextChar(char c) {
-      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)) {
-        finish();
-        return new BeforeSearchExpressionRwsState();
-      } else {
-        return new SearchWordState(this);
-      }
-    }
-  }
-
-  private class OrState extends LiteralState {
-    public OrState(char c) {
-      super(Token.OR, c);
-      if(c != CHAR_O) {
-        forbidden(c);
-      }
-    }
-    @Override
-    public State nextChar(char c) {
-      if (literal.length() == 1 && (c == CHAR_R)) {
-        return allowed(c);
-      } else if(literal.length() == 2 && isWhitespace(c)) {
-        finish();
-        return new BeforeSearchExpressionRwsState();
-      } else {
-        return new SearchWordState(this);
-      }
-    }
-  }
-
   // RWS 'OR'  RWS searchExpr
   // RWS [ 'AND' RWS ] searchExpr
   private class BeforeSearchExpressionRwsState extends State {
@@ -376,21 +336,39 @@ public class SearchTokenizer {
   }
 
   // implicit and
-  private class RwsImplicitAndState extends State {
-    public RwsImplicitAndState() {
-      super(Token.AND);
+  private class RwsImplicitAndOrState extends LiteralState {
+    private boolean noneRws = false;
+    public RwsImplicitAndOrState() {
+      super(null);
     }
     @Override
     public State nextChar(char c) {
-      if (isWhitespace(c)) {
+      if (!noneRws && isWhitespace(c)) {
         return allowed(c);
       } else if (c == CHAR_O) {
-        return new OrState(c);
+        noneRws = true;
+        return allowed(c);
+      } else if (literal.length() == 1 && c == CHAR_R) {
+        return allowed(c);
+      } else if (literal.length() == 2 && isWhitespace(c)) {
+        finish(Token.OR);
+        return new BeforeSearchExpressionRwsState();
       } else if (c == CHAR_A) {
-        return new AndState(c);
+        noneRws = true;
+        return allowed(c);
+      } else 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)) {
+        finish(Token.AND);
+        return new BeforeSearchExpressionRwsState();
+      } else if(noneRws) {
+        finish(Token.AND);
+        return new SearchWordState(this);
       } else {
-        finish();
-        return new SearchExpressionState().init(c);
+        finish(Token.AND);
+        return new SearchExpressionState(literal.toString()).init(c);
       }
     }
   }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/f64abe13/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java
index 29287cd..548d3fd 100644
--- a/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java
+++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java
@@ -290,12 +290,11 @@ public class SearchTokenizerTest {
     SearchValidator.init("abc AND ANDsomething")
         .addExpected(WORD, AND, WORD).validate();
 
-    // FIXME (mibo): issue with implicit and
-//    SearchValidator.init("abc ANDsomething").enableLogging()
-//        .addExpected(WORD, AND, WORD).validate();
+    SearchValidator.init("abc ANDsomething")
+        .addExpected(WORD, AND, WORD).validate();
 
-//    SearchValidator.init("abc ORsomething")
-//        .addExpected(WORD, AND, WORD).validate();
+    SearchValidator.init("abc ORsomething")
+        .addExpected(WORD, AND, WORD).validate();
 
     SearchValidator.init("abc OR orsomething")
         .addExpected(WORD, OR, WORD).validate();


[15/23] olingo-odata4 git commit: [OLINGO-568] Added missing SearchParser parse paths

Posted by mi...@apache.org.
[OLINGO-568] Added missing SearchParser parse paths


Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo
Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/cef72e45
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/cef72e45
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/cef72e45

Branch: refs/heads/master
Commit: cef72e45ab0509310ed46c26d0c3df4a30885d5f
Parents: 40962a9
Author: mibo <mi...@apache.org>
Authored: Fri Nov 13 08:16:15 2015 +0100
Committer: mibo <mi...@apache.org>
Committed: Fri Nov 13 08:16:15 2015 +0100

----------------------------------------------------------------------
 .../core/uri/parser/search/SearchParser.java    | 99 +++++++++++++-------
 .../search/SearchParserAndTokenizerTest.java    | 19 +++-
 .../uri/parser/search/SearchParserTest.java     |  9 +-
 3 files changed, 85 insertions(+), 42 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/cef72e45/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 25c52f2..d5109a5 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
@@ -29,20 +29,20 @@ import java.util.List;
 public class SearchParser {
 
   private Iterator<SearchQueryToken> tokens;
-  private SearchExpression root;
   private SearchQueryToken token;
 
   public SearchOption parse(String path, String value) {
     SearchTokenizer tokenizer = new SearchTokenizer();
+    SearchExpression searchExpression;
     try {
       tokens = tokenizer.tokenize(value).iterator();
       nextToken();
-      root = processSearchExpression(null);
+      searchExpression = processSearchExpression(null);
     } catch (SearchTokenizerException e) {
       return null;
     }
     final SearchOptionImpl searchOption = new SearchOptionImpl();
-    searchOption.setSearchExpression(root);
+    searchOption.setSearchExpression(searchExpression);
     return searchOption;
   }
 
@@ -57,26 +57,47 @@ public class SearchParser {
       return left;
     }
 
-    if(token.getToken() == SearchQueryToken.Token.OPEN) {
+    SearchExpression expression = left;
+    if(isToken(SearchQueryToken.Token.OPEN)) {
       processOpen();
-      throw illegalState();
-    } else if(token.getToken() == SearchQueryToken.Token.CLOSE) {
-        processClose();
-        throw illegalState();
-    } else if(token.getToken() == SearchQueryToken.Token.NOT) {
-      processNot();
-    } else if(token.getToken() == SearchQueryToken.Token.PHRASE ||
-        token.getToken() == SearchQueryToken.Token.WORD) {
-      return processSearchExpression(processTerm());
-    } else if(token.getToken() == SearchQueryToken.Token.AND) {
-        SearchExpression se = processAnd(left);
-        return processSearchExpression(se);
-    } else if(token.getToken() == SearchQueryToken.Token.OR) {
-        return processOr(left);
-    } else {
+      expression = processSearchExpression(left);
+      validateToken(SearchQueryToken.Token.CLOSE);
+      processClose();
+    } 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()) {
+      return expression;
+    }
+    return expression;
+  }
+
+  private boolean isTerm() {
+    return isToken(SearchQueryToken.Token.NOT)
+        || isToken(SearchQueryToken.Token.PHRASE)
+        || isToken(SearchQueryToken.Token.WORD);
+  }
+
+  private boolean isEof() {
+    return token == null;
+  }
+
+  private boolean isToken(SearchQueryToken.Token toCheckToken) {
+    if(token == null) {
+      return false;
+    }
+    return token.getToken() == toCheckToken;
+  }
+
+  private void validateToken(SearchQueryToken.Token toValidateToken) {
+    if(!isToken(toValidateToken)) {
       throw illegalState();
     }
-    throw illegalState();
   }
 
   private void processClose() {
@@ -88,13 +109,24 @@ public class SearchParser {
   }
 
   private SearchExpression processAnd(SearchExpression left) {
-    nextToken();
-    SearchExpression se = processTerm();
-    return new SearchBinaryImpl(left, SearchBinaryOperatorKind.AND, se);
+    if(isToken(SearchQueryToken.Token.AND)) {
+      nextToken();
+    }
+    SearchExpression se = left;
+    if(isTerm()) {
+      se = processTerm();
+      se = new SearchBinaryImpl(left, SearchBinaryOperatorKind.AND, se);
+      return processSearchExpression(se);
+    } else {
+      se = processSearchExpression(se);
+      return new SearchBinaryImpl(left, SearchBinaryOperatorKind.AND, se);
+    }
   }
 
   public SearchExpression processOr(SearchExpression left) {
-    nextToken();
+    if(isToken(SearchQueryToken.Token.OR)) {
+      nextToken();
+    }
     SearchExpression se = processSearchExpression(left);
     return new SearchBinaryImpl(left, SearchBinaryOperatorKind.OR, se);
   }
@@ -103,8 +135,13 @@ public class SearchParser {
     return new RuntimeException();
   }
 
-  private void processNot() {
+  private SearchExpression processNot() {
     nextToken();
+    SearchExpression searchExpression = processTerm();
+    if(searchExpression.isSearchTerm()) {
+      return new SearchUnaryImpl(searchExpression.asSearchTerm());
+    }
+    throw illegalState();
   }
 
   private void nextToken() {
@@ -113,20 +150,18 @@ public class SearchParser {
     } else {
       token = null;
     }
-//    return null;
   }
 
   private SearchExpression processTerm() {
-    if(token.getToken() == SearchQueryToken.Token.NOT) {
-      return new SearchUnaryImpl(processPhrase());
+    if(isToken(SearchQueryToken.Token.NOT)) {
+      return processNot();
     }
-    if(token.getToken() == SearchQueryToken.Token.PHRASE) {
+    if(isToken(SearchQueryToken.Token.PHRASE)) {
       return processPhrase();
-    }
-    if(token.getToken() == SearchQueryToken.Token.WORD) {
+    } else if(isToken(SearchQueryToken.Token.WORD)) {
       return processWord();
     }
-    return null;
+    throw illegalState();
   }
 
   private SearchTermImpl processWord() {

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/cef72e45/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 3ecd517..271b617 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
@@ -34,9 +34,6 @@ public class SearchParserAndTokenizerTest {
 
   @Test
   public void basicParsing() throws SearchTokenizerException {
-//    SearchExpressionValidator.init("a AND b OR c").enableLogging()
-//        .validate(with("a"));
-
     SearchExpressionValidator.init("a")
         .validate(with("a"));
     SearchExpressionValidator.init("a AND b")
@@ -57,6 +54,22 @@ public class SearchParserAndTokenizerTest {
         .validate("{'a' OR {'b' AND 'c'}}");
   }
 
+  @Test
+  public void notParsing() throws Exception {
+    SearchExpressionValidator.init("NOT a AND b OR c")
+        .validate("{{{NOT 'a'} AND 'b'} OR 'c'}");
+    SearchExpressionValidator.init("a OR b AND NOT c")
+        .validate("{'a' OR {'b' AND {NOT 'c'}}}");
+  }
+
+  @Test
+  public void parenthesesParsing() throws Exception {
+    SearchExpressionValidator.init("a AND (b OR c)")
+        .validate("{'a' AND {'b' OR 'c'}}");
+    SearchExpressionValidator.init("(a OR b) AND NOT c")
+        .validate("{{'a' OR 'b'} AND {NOT 'c'}}");
+  }
+
   @Ignore
   @Test
   public void sebuilder() {

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/cef72e45/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 ce37f3d..89b59b4 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
@@ -83,7 +83,6 @@ public class SearchParserTest extends SearchParser {
     assertEquals("phrase2", se.asSearchBinary().getRightOperand().asSearchTerm().getSearchTerm());
   }
   
-  @Ignore
   @Test
   public void simpleImplicitAnd() {
     SearchExpression se = run(Token.WORD, Token.WORD);
@@ -101,7 +100,6 @@ public class SearchParserTest extends SearchParser {
     assertEquals("phrase2", se.asSearchBinary().getRightOperand().asSearchTerm().getSearchTerm());
   }
   
-  @Ignore
   @Test
   public void simpleBrackets() {
     SearchExpression se = run(Token.OPEN, Token.WORD, Token.CLOSE);
@@ -115,7 +113,6 @@ public class SearchParserTest extends SearchParser {
     assertEquals("phrase1", se.asSearchTerm().getSearchTerm());
   }
   
-  @Ignore
   @Test
   public void simpleNot() {
     SearchExpression se = run(Token.NOT, Token.WORD);
@@ -123,13 +120,12 @@ public class SearchParserTest extends SearchParser {
     assertTrue(se.isSearchUnary());
     assertEquals("word1", se.asSearchUnary().getOperand().asSearchTerm().getSearchTerm());
     
-    se = run(Token.NOT, Token.WORD);
-    assertEquals("'phrase1'", se.toString());
+    se = run(Token.NOT, Token.PHRASE);
+    assertEquals("{NOT 'phrase1'}", se.toString());
     assertTrue(se.isSearchUnary());
     assertEquals("phrase1", se.asSearchUnary().getOperand().asSearchTerm().getSearchTerm());
   }
   
-  @Ignore
   @Test
   public void precedenceLast() {
     //word1 AND (word2 AND word3) 
@@ -137,7 +133,6 @@ public class SearchParserTest extends SearchParser {
     assertEquals("{'word1' AND {'word2' AND 'word3'}}", se.toString());
   }
   
-  @Ignore
   @Test
   public void precedenceFirst() {
     //(word1 AND word2) AND word3 


[12/23] olingo-odata4 git commit: [OLINGO-568] search parser tests refactoring

Posted by mi...@apache.org.
[OLINGO-568] search parser tests refactoring


Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo
Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/22d152fc
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/22d152fc
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/22d152fc

Branch: refs/heads/master
Commit: 22d152fc4c1681f91b2b4e545c005873e655290c
Parents: ba5220a
Author: Christian Amend <ch...@sap.com>
Authored: Thu Nov 12 13:58:11 2015 +0100
Committer: Christian Amend <ch...@sap.com>
Committed: Thu Nov 12 13:58:38 2015 +0100

----------------------------------------------------------------------
 .../queryoption/search/SearchExpression.java    |  14 +-
 .../uri/parser/search/SearchBinaryImpl.java     |   2 +-
 .../uri/parser/search/SearchExpressionImpl.java |  58 +++++
 .../core/uri/parser/search/SearchParser.java    |  10 +-
 .../core/uri/parser/search/SearchTermImpl.java  |   4 +-
 .../core/uri/parser/search/SearchUnaryImpl.java |   2 +-
 .../search/SearchParserAndTokenizerTest.java    | 171 ++++++++++++
 .../uri/parser/search/SearchParserTest.java     | 260 ++++++++++---------
 .../uri/parser/search/SearchTokenizerTest.java  |   2 +-
 9 files changed, 387 insertions(+), 136 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/22d152fc/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/search/SearchExpression.java
----------------------------------------------------------------------
diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/search/SearchExpression.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/search/SearchExpression.java
index ed66f5f..983919c 100644
--- a/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/search/SearchExpression.java
+++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/search/SearchExpression.java
@@ -19,5 +19,17 @@
 package org.apache.olingo.server.api.uri.queryoption.search;
 
 public interface SearchExpression {
-  //No additional methods needed for now.
+  
+  boolean isSearchTerm();
+  
+  SearchTerm asSearchTerm();
+  
+  boolean isSearchBinary();
+  
+  SearchBinary asSearchBinary();
+  
+  boolean isSearchUnary();
+  
+  SearchUnary asSearchUnary();
+  
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/22d152fc/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchBinaryImpl.java
----------------------------------------------------------------------
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
index 37b03c0..418d9e7 100644
--- 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
@@ -22,7 +22,7 @@ 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 {
+public class SearchBinaryImpl extends SearchExpressionImpl implements SearchBinary {
 
   private final SearchBinaryOperatorKind operator;
   private final SearchExpression left;

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/22d152fc/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchExpressionImpl.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchExpressionImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchExpressionImpl.java
new file mode 100644
index 0000000..ee5a197
--- /dev/null
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchExpressionImpl.java
@@ -0,0 +1,58 @@
+/*
+ * 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.SearchExpression;
+import org.apache.olingo.server.api.uri.queryoption.search.SearchTerm;
+import org.apache.olingo.server.api.uri.queryoption.search.SearchUnary;
+
+public abstract class SearchExpressionImpl implements SearchExpression {
+
+  @Override
+  public boolean isSearchTerm() {
+    return this instanceof SearchTerm;
+  }
+
+  @Override
+  public SearchTerm asSearchTerm() {
+    return isSearchTerm() ? (SearchTerm) this : null;
+  }
+
+  @Override
+  public boolean isSearchBinary() {
+    return this instanceof SearchBinary;
+  }
+
+  @Override
+  public SearchBinary asSearchBinary() {
+    return isSearchBinary() ? (SearchBinary) this : null;
+  }
+
+  @Override
+  public boolean isSearchUnary() {
+    return this instanceof SearchUnary;
+  }
+
+  @Override
+  public SearchUnary asSearchUnary() {
+    return isSearchUnary() ? (SearchUnary) this : null;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/22d152fc/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 60fcd4b..5e26c35 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
@@ -28,8 +28,8 @@ import java.util.Iterator;
 
 public class SearchParser {
 
-  private Iterator<SearchQueryToken> tokens;
-  private SearchExpression root;
+  protected Iterator<SearchQueryToken> tokens;
+  protected SearchExpression root;
 //  private SearchQueryToken currentToken;
 
   public SearchOption parse(String path, String value) {
@@ -37,7 +37,7 @@ public class SearchParser {
     try {
       tokens = tokenizer.tokenize(value).iterator();
 //      currentToken = tokens.next();
-      root = processSearchExpression();
+      root = processTokens();
     } catch (SearchTokenizerException e) {
       return null;
     }
@@ -46,8 +46,10 @@ public class SearchParser {
     return searchOption;
   }
 
-  private SearchExpression processSearchExpression() {
+  protected SearchExpression processTokens() {
     SearchQueryToken token = nextToken();
+    
+    
     if(token.getToken() == SearchQueryToken.Token.OPEN) {
       throw illegalState();
     } else if(token.getToken() == SearchQueryToken.Token.NOT) {

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/22d152fc/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchTermImpl.java
----------------------------------------------------------------------
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
index 24b1291..efd7280 100644
--- 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
@@ -20,7 +20,7 @@ package org.apache.olingo.server.core.uri.parser.search;
 
 import org.apache.olingo.server.api.uri.queryoption.search.SearchTerm;
 
-public class SearchTermImpl implements SearchTerm {
+public class SearchTermImpl extends SearchExpressionImpl implements SearchTerm {
   private final String term;
 
   public SearchTermImpl(String term) {
@@ -34,6 +34,6 @@ public class SearchTermImpl implements SearchTerm {
 
   @Override
   public String toString() {
-    return "{'" + term + "'}";
+    return "'" + term + "'";
   }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/22d152fc/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchUnaryImpl.java
----------------------------------------------------------------------
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
index 639540d..51e3a24 100644
--- 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
@@ -22,7 +22,7 @@ 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 {
+public class SearchUnaryImpl extends SearchExpressionImpl implements SearchUnary {
   private final SearchTerm operand;
 
   public SearchUnaryImpl(SearchTerm operand) {

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/22d152fc/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
new file mode 100644
index 0000000..ca9d80b
--- /dev/null
+++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchParserAndTokenizerTest.java
@@ -0,0 +1,171 @@
+/*
+ * 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 static org.apache.olingo.server.api.uri.queryoption.search.SearchBinaryOperatorKind.AND;
+import static org.apache.olingo.server.api.uri.queryoption.search.SearchBinaryOperatorKind.OR;
+
+import java.lang.reflect.Field;
+
+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;
+
+public class SearchParserAndTokenizerTest {
+
+  @Test
+  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"))));
+  }
+
+  @Ignore
+  @Test
+  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<? extends Exception> 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());
+    }
+  }
+
+  
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/22d152fc/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 fa42293..961663c 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
@@ -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
@@ -18,151 +18,159 @@
  */
 package org.apache.olingo.server.core.uri.parser.search;
 
-import org.apache.olingo.server.api.uri.queryoption.SearchOption;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+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.api.uri.queryoption.search.SearchUnary;
-import org.junit.Assert;
+import org.apache.olingo.server.core.uri.parser.search.SearchQueryToken.Token;
 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 {
+public class SearchParserTest extends SearchParser {
 
   @Test
-  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"))));
+  public void simple() {
+    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
+    assertEquals("phrase1", se.asSearchTerm().getSearchTerm());
   }
 
   @Test
-  @Ignore("Currently not working")
-  public void mixedParsing() throws Exception{
-    SearchExpressionValidator.init("a AND b OR c")
-            .validate(with("c", or("a", and("b"))));
+  public void simpleAnd() {
+    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());
+    assertEquals(SearchBinaryOperatorKind.AND, se.asSearchBinary().getOperator());
+    assertEquals("phrase1", se.asSearchBinary().getLeftOperand().asSearchTerm().getSearchTerm());
+    assertEquals("phrase2", se.asSearchBinary().getRightOperand().asSearchTerm().getSearchTerm());
   }
-
+  
   @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;
+  public void simpleOr() {
+    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());
+    assertEquals(SearchBinaryOperatorKind.OR, se.asSearchBinary().getOperator());
+    assertEquals("phrase1", se.asSearchBinary().getLeftOperand().asSearchTerm().getSearchTerm());
+    assertEquals("phrase2", se.asSearchBinary().getRightOperand().asSearchTerm().getSearchTerm());
   }
-
-  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);
+  
+  @Ignore
+  @Test
+  public void simpleImplicitAnd() {
+    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());
+    assertEquals(SearchBinaryOperatorKind.AND, se.asSearchBinary().getOperator());
+    assertEquals("phrase1", se.asSearchBinary().getLeftOperand().asSearchTerm().getSearchTerm());
+    assertEquals("phrase2", se.asSearchBinary().getRightOperand().asSearchTerm().getSearchTerm());
   }
-
-  private static SearchExpression and(SearchExpression right) {
-    return new SearchBinaryImpl(null, AND, right);
+  
+  @Ignore
+  @Test
+  public void simpleBrackets() {
+    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());
   }
-
-  private static SearchExpression and(String right) {
-    return and(new SearchTermImpl(right));
+  
+  @Ignore
+  @Test
+  public void simpleNot() {
+    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.WORD);
+    assertEquals("'phrase1'", se.toString());
+    assertTrue(se.isSearchUnary());
+    assertEquals("phrase1", se.asSearchUnary().getOperand().asSearchTerm().getSearchTerm());
   }
-
-  private static SearchExpression or(String right) {
-    return or(new SearchTermImpl(right));
+  
+  @Ignore
+  @Test
+  public void precedenceLast() {
+    //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());
   }
-
-  private static SearchUnary not(String term) {
-    return new SearchUnaryImpl(new SearchTermImpl(term));
+  
+  @Ignore
+  @Test
+  public void precedenceFirst() {
+    //(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());
   }
-
-  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 SearchExpression run(SearchQueryToken.Token... tokenArray) {
+    List<SearchQueryToken> tokenList = prepareTokens(tokenArray);
+    tokens = tokenList.iterator();
+    SearchExpression se = processTokens();
+    assertNotNull(se);
+    return se;
   }
 
-  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<? extends Exception> 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);
+  public List<SearchQueryToken> prepareTokens(SearchQueryToken.Token... tokenArray) {
+    ArrayList<SearchQueryToken> tokenList = new ArrayList<SearchQueryToken>();
+    int wordNumber = 1;
+    int phraseNumber = 1;
+    for (int i = 0; i < tokenArray.length; i++) {
+      SearchQueryToken token = mock(SearchQueryToken.class);
+      when(token.getToken()).thenReturn(tokenArray[i]);
+      if (tokenArray[i] == Token.WORD) {
+        when(token.getLiteral()).thenReturn("word" + wordNumber);
+        wordNumber++;
+      } else if (tokenArray[i] == Token.PHRASE) {
+        when(token.getLiteral()).thenReturn("phrase" + phraseNumber);
+        phraseNumber++;
       }
-      Assert.assertEquals(expectedSearchExpression.toString(), searchExpression.toString());
+      tokenList.add(token);
     }
+    return tokenList;
   }
 
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/22d152fc/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java
index 828b4c4..904bd3f 100644
--- a/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java
+++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java
@@ -33,7 +33,7 @@ public class SearchTokenizerTest {
   public void parseBasics() throws Exception {
     SearchTokenizer tokenizer = new SearchTokenizer();
     List<SearchQueryToken> result;
-
+    
     //
     result = tokenizer.tokenize("abc");
     Assert.assertNotNull(result);


[20/23] olingo-odata4 git commit: [OLINGO-568] Merge branch 'master' into OLINGO-568_SearchParser_Draft

Posted by mi...@apache.org.
[OLINGO-568] Merge branch 'master' into OLINGO-568_SearchParser_Draft

arser_Draft


Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo
Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/c0adc020
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/c0adc020
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/c0adc020

Branch: refs/heads/master
Commit: c0adc020b22ed728aaed1659fc8485e4c214141f
Parents: 9ff30e7 6614aea
Author: Michael Bolz <mi...@sap.com>
Authored: Tue Nov 17 10:50:06 2015 +0100
Committer: Michael Bolz <mi...@sap.com>
Committed: Tue Nov 17 10:50:06 2015 +0100

----------------------------------------------------------------------
 .../commons/core/edm/EdmEnumTypeImpl.java       |  47 ++++++--
 .../server/core/edm/provider/EdmEnumTest.java   |  16 ++-
 .../core/uri/parser/UriParseTreeVisitor.java    |  18 ++-
 .../TechnicalPrimitiveComplexProcessor.java     |   8 +-
 .../expression/ExpressionVisitorImpl.java       |   9 +-
 .../expression/operand/UntypedOperand.java      |  19 +---
 .../queryoptions/options/FilterHandler.java     |   2 +-
 .../queryoptions/options/OrderByHandler.java    |   4 +-
 .../core/uri/antlr/TestFullResourcePath.java    | 113 +++++++++++++++----
 9 files changed, 170 insertions(+), 66 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/c0adc020/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestFullResourcePath.java
----------------------------------------------------------------------


[14/23] olingo-odata4 git commit: [OLINGO-568] Refactored SearchParser for further development

Posted by mi...@apache.org.
[OLINGO-568] Refactored SearchParser for further development


Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo
Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/40962a9a
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/40962a9a
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/40962a9a

Branch: refs/heads/master
Commit: 40962a9a18259a19410e22ee2573c1816b7a5cbc
Parents: a8d63fb
Author: mibo <mi...@apache.org>
Authored: Thu Nov 12 23:08:35 2015 +0100
Committer: mibo <mi...@apache.org>
Committed: Thu Nov 12 23:08:35 2015 +0100

----------------------------------------------------------------------
 .../core/uri/parser/search/SearchParser.java    | 125 +++++++++++--------
 .../core/uri/parser/search/SearchTokenizer.java |   2 +-
 .../search/SearchParserAndTokenizerTest.java    |  24 +++-
 .../uri/parser/search/SearchParserTest.java     |  15 ++-
 4 files changed, 103 insertions(+), 63 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/40962a9a/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 5e26c35..25c52f2 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
@@ -21,23 +21,23 @@ 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.Iterator;
+import java.util.List;
 
 public class SearchParser {
 
-  protected Iterator<SearchQueryToken> tokens;
-  protected SearchExpression root;
-//  private SearchQueryToken currentToken;
+  private Iterator<SearchQueryToken> tokens;
+  private SearchExpression root;
+  private SearchQueryToken token;
 
   public SearchOption parse(String path, String value) {
     SearchTokenizer tokenizer = new SearchTokenizer();
     try {
       tokens = tokenizer.tokenize(value).iterator();
-//      currentToken = tokens.next();
-      root = processTokens();
+      nextToken();
+      root = processSearchExpression(null);
     } catch (SearchTokenizerException e) {
       return null;
     }
@@ -46,81 +46,98 @@ public class SearchParser {
     return searchOption;
   }
 
-  protected SearchExpression processTokens() {
-    SearchQueryToken token = nextToken();
-    
-    
+  protected SearchExpression parseInternal(List<SearchQueryToken> tokens) {
+    this.tokens = tokens.iterator();
+    nextToken();
+    return processSearchExpression(null);
+  }
+
+  private SearchExpression processSearchExpression(SearchExpression left) {
+    if(token == null) {
+      return left;
+    }
+
     if(token.getToken() == SearchQueryToken.Token.OPEN) {
+      processOpen();
       throw illegalState();
+    } else if(token.getToken() == SearchQueryToken.Token.CLOSE) {
+        processClose();
+        throw illegalState();
     } else if(token.getToken() == SearchQueryToken.Token.NOT) {
-      return processNot();
+      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();
+      return processSearchExpression(processTerm());
+    } else if(token.getToken() == SearchQueryToken.Token.AND) {
+        SearchExpression se = processAnd(left);
+        return processSearchExpression(se);
+    } else if(token.getToken() == SearchQueryToken.Token.OR) {
+        return processOr(left);
     } else {
       throw illegalState();
     }
+    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 void processClose() {
+    nextToken();
   }
 
-  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 void processOpen() {
+    nextToken();
+  }
+
+  private SearchExpression processAnd(SearchExpression left) {
+    nextToken();
+    SearchExpression se = processTerm();
+    return new SearchBinaryImpl(left, SearchBinaryOperatorKind.AND, se);
+  }
+
+  public SearchExpression processOr(SearchExpression left) {
+    nextToken();
+    SearchExpression se = processSearchExpression(left);
+    return new SearchBinaryImpl(left, SearchBinaryOperatorKind.OR, se);
   }
 
   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 void processNot() {
+    nextToken();
   }
 
-  private SearchQueryToken nextToken() {
-//    if(tokens.hasNext()) {
-    return tokens.next();
-//    }
+  private void nextToken() {
+    if(tokens.hasNext()) {
+     token = tokens.next();
+    } else {
+      token = null;
+    }
 //    return null;
   }
 
-  private SearchExpression processTerm(SearchQueryToken token) {
-    SearchTerm searchTerm = new SearchTermImpl(token.getLiteral());
-    if(isEof()) {
-      return searchTerm;
+  private SearchExpression processTerm() {
+    if(token.getToken() == SearchQueryToken.Token.NOT) {
+      return new SearchUnaryImpl(processPhrase());
     }
-
-    SearchQueryToken next = nextToken();
-    if(next.getToken() == SearchQueryToken.Token.AND) {
-      return processAnd(searchTerm);
-    } else if(next.getToken() == SearchQueryToken.Token.OR) {
-      return processOr(searchTerm);
+    if(token.getToken() == SearchQueryToken.Token.PHRASE) {
+      return processPhrase();
     }
+    if(token.getToken() == SearchQueryToken.Token.WORD) {
+      return processWord();
+    }
+    return null;
+  }
 
-    throw illegalState();
+  private SearchTermImpl processWord() {
+    String literal = token.getLiteral();
+    nextToken();
+    return new SearchTermImpl(literal);
   }
 
-  private boolean isEof() {
-    return !tokens.hasNext();
+  private SearchTermImpl processPhrase() {
+    String literal = token.getLiteral();
+    nextToken();
+    return new SearchTermImpl(literal);
   }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/40962a9a/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 9288981..a9a5895 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
@@ -442,7 +442,7 @@ public class SearchTokenizer {
    */
   public List<SearchQueryToken> tokenize(final String searchQuery)
         throws SearchTokenizerException {
-    
+
     char[] chars = searchQuery.trim().toCharArray();
 
     State state = new SearchExpressionState();

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/40962a9a/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 43b63c3..3ecd517 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
@@ -34,12 +34,15 @@ public class SearchParserAndTokenizerTest {
 
   @Test
   public void basicParsing() throws SearchTokenizerException {
+//    SearchExpressionValidator.init("a AND b OR c").enableLogging()
+//        .validate(with("a"));
+
     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"))));
+        .validate("{{'a' AND 'b'} AND 'c'}");
     SearchExpressionValidator.init("a OR b")
         .validate(with("a", or("b")));
     SearchExpressionValidator.init("a OR b OR c")
@@ -47,10 +50,11 @@ public class SearchParserAndTokenizerTest {
   }
 
   @Test
-  @Ignore("Currently not working")
   public void mixedParsing() throws Exception {
     SearchExpressionValidator.init("a AND b OR c")
-        .validate(with("c", or("a", and("b"))));
+        .validate("{{'a' AND 'b'} OR 'c'}");
+    SearchExpressionValidator.init("a OR b AND c")
+        .validate("{'a' OR {'b' AND 'c'}}");
   }
 
   @Ignore
@@ -156,15 +160,25 @@ public class SearchParserAndTokenizerTest {
     }
 
     private void validate(SearchExpression expectedSearchExpression) throws SearchTokenizerException {
+      final SearchExpression searchExpression = getSearchExpression();
+      Assert.assertEquals(expectedSearchExpression.toString(), searchExpression.toString());
+    }
+
+    private void validate(String expectedSearchExpression) throws SearchTokenizerException {
+      final SearchExpression searchExpression = getSearchExpression();
+      Assert.assertEquals(expectedSearchExpression, searchExpression.toString());
+    }
+
+    private SearchExpression getSearchExpression() {
       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);
+        System.out.println(searchExpression);
       }
-      Assert.assertEquals(expectedSearchExpression.toString(), searchExpression.toString());
+      return searchExpression;
     }
   }
 

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/40962a9a/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 961663c..ce37f3d 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
@@ -144,12 +144,21 @@ public class SearchParserTest extends SearchParser {
     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
+    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
+    se = run(Token.WORD, Token.OR, Token.WORD, Token.AND, Token.WORD);
+    assertEquals("{'word1' OR {'word2' AND 'word3'}}", se.toString());
+  }
+
 
   private SearchExpression run(SearchQueryToken.Token... tokenArray) {
     List<SearchQueryToken> tokenList = prepareTokens(tokenArray);
-    tokens = tokenList.iterator();
-    SearchExpression se = processTokens();
+    SearchExpression se = parseInternal(tokenList);
     assertNotNull(se);
     return se;
   }


[05/23] olingo-odata4 git commit: [OLINGO-568] Code clean up

Posted by mi...@apache.org.
[OLINGO-568] Code clean up


Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo
Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/b3bbfa6f
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/b3bbfa6f
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/b3bbfa6f

Branch: refs/heads/master
Commit: b3bbfa6fe154d91a5f58328acc8816bd81af798a
Parents: bbdd0d7
Author: Michael Bolz <mi...@sap.com>
Authored: Mon Nov 9 13:29:32 2015 +0100
Committer: Michael Bolz <mi...@sap.com>
Committed: Mon Nov 9 13:29:32 2015 +0100

----------------------------------------------------------------------
 .../core/uri/parser/search/SearchTokenizer.java | 69 +++++++++++---------
 .../uri/parser/search/SearchTokenizerTest.java  | 21 ++++--
 2 files changed, 54 insertions(+), 36 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/b3bbfa6f/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 e058a00..67d4655 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
@@ -39,16 +39,21 @@ import java.util.List;
  * </code>
  */
 public class SearchTokenizer {
-  public static final char QUOTATION_MARK = '\"';
-  //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;
 
-    public static final char EOF = 0x03;
+    protected static final char EOF = 0x03;
+    protected static final char QUOTATION_MARK = '\"';
+    protected static final char CHAR_N = 'N';
+    protected static final char CHAR_O = 'O';
+    protected static final char CHAR_T = 'T';
+    protected static final char CHAR_A = 'A';
+    protected static final char CHAR_D = 'D';
+    protected static final char CHAR_R = 'R';
+    protected static final char CHAR_CLOSE = ')';
+    protected static final char CHAR_OPEN = '(';
 
     public State(Token t) {
       token = t;
@@ -79,7 +84,7 @@ public class SearchTokenizer {
 
     static boolean isAllowedChar(final char character) {
       // TODO mibo: add missing allowed characters
-      return 'A' <= character && character <= 'Z' // case A..Z
+      return CHAR_A <= character && character <= 'Z' // case A..Z
           || 'a' <= character && character <= 'z' // case a..z
           || '0' <= character && character <= '9'; // case 0..9
     }
@@ -109,6 +114,8 @@ public class SearchTokenizer {
       return character == EOF;
     }
 
+    //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" )
       // TODO mibo: add missing whitespaces
@@ -163,11 +170,11 @@ public class SearchTokenizer {
     }
     @Override
     public State nextChar(char c) {
-      if (c == '(') {
+      if (c == CHAR_OPEN) {
         return new OpenState();
       } else if (isWhitespace(c)) {
         return new RwsState();
-      } else if(c == ')') {
+      } else if(c == CHAR_CLOSE) {
         return new CloseState();
       } else if(isEof(c)) {
         return finish();
@@ -190,7 +197,7 @@ public class SearchTokenizer {
     }
     @Override
     public State nextChar(char c) {
-      if(c == 'n' || c == 'N') {
+      if(c == CHAR_N) {
         return new NotState(c);
       } else if (c == QUOTATION_MARK) {
         return new SearchPhraseState(c);
@@ -217,7 +224,7 @@ public class SearchTokenizer {
     public State nextChar(char c) {
       if (isAllowedChar(c)) {
         return allowed(c);
-      } else if (c == ')') {
+      } else if (c == CHAR_CLOSE) {
         finish();
         return new CloseState();
       } else if (isWhitespace(c)) {
@@ -226,7 +233,7 @@ public class SearchTokenizer {
       } else if (isEof(c)) {
         return finish();
       }
-      throw new IllegalStateException(this.getClass().getName() + "->" + c);
+      return forbidden(c);
     }
   }
 
@@ -255,7 +262,7 @@ public class SearchTokenizer {
       } else if (isEof(c)) {
         return finish();
       }
-      throw new IllegalStateException(this.getClass().getName() + "->" + c);
+      return forbidden(c);
     }
   }
 
@@ -268,7 +275,7 @@ public class SearchTokenizer {
     public State nextChar(char c) {
       finish();
       if (isWhitespace(c)) {
-        throw new IllegalStateException(this.getClass().getName() + "->" + c);
+        return forbidden(c);
       }
       return new SearchExpressionState().init(c);
     }
@@ -293,12 +300,15 @@ public class SearchTokenizer {
   private class NotState extends LiteralState {
     public NotState(char c) {
       super(Token.NOT, c);
+      if(c != CHAR_N) {
+        forbidden(c);
+      }
     }
     @Override
     public State nextChar(char c) {
-      if (getLiteral().length() == 1 && (c == 'o' || c == 'O')) {
+      if (getLiteral().length() == 1 && (c == CHAR_O)) {
         return allowed(c);
-      } else if (getLiteral().length() == 2 && (c == 't' || c == 'T')) {
+      } else if (getLiteral().length() == 2 && (c == CHAR_T)) {
         return allowed(c);
       } else if(getLiteral().length() == 3 && isWhitespace(c)) {
         finish();
@@ -328,15 +338,15 @@ public class SearchTokenizer {
   private class AndState extends LiteralState {
     public AndState(char c) {
       super(Token.AND, c);
-      if(c != 'a' && c != 'A') {
+      if(c != CHAR_A) {
         forbidden(c);
       }
     }
     @Override
     public State nextChar(char c) {
-      if (getLiteral().length() == 1 && (c == 'n' || c == 'N')) {
+      if (getLiteral().length() == 1 && (c == CHAR_N)) {
         return allowed(c);
-      } else if (getLiteral().length() == 2 && (c == 'd' || c == 'D')) {
+      } else if (getLiteral().length() == 2 && (c == CHAR_D)) {
         return allowed(c);
       } else if(getLiteral().length() == 3 && isWhitespace(c)) {
         finish();
@@ -350,13 +360,13 @@ public class SearchTokenizer {
   private class OrState extends LiteralState {
     public OrState(char c) {
       super(Token.OR, c);
-      if(c != 'o' && c != 'O') {
+      if(c != CHAR_O) {
         forbidden(c);
       }
     }
     @Override
     public State nextChar(char c) {
-      if (getLiteral().length() == 1 && (c == 'r' || c == 'R')) {
+      if (getLiteral().length() == 1 && (c == CHAR_R)) {
         return allowed(c);
       } else if(getLiteral().length() == 2 && isWhitespace(c)) {
         finish();
@@ -367,6 +377,7 @@ public class SearchTokenizer {
     }
   }
 
+  // RWS 'OR'  RWS searchExpr
   // RWS [ 'AND' RWS ] searchExpr
   private class BeforeSearchExpressionRwsState extends State {
     public BeforeSearchExpressionRwsState() {
@@ -382,7 +393,6 @@ public class SearchTokenizer {
     }
   }
 
-
   private class RwsState extends State {
     public RwsState() {
       super(Token.RWS);
@@ -391,9 +401,9 @@ public class SearchTokenizer {
     public State nextChar(char c) {
       if (isWhitespace(c)) {
         return allowed(c);
-      } else if (c == 'O' || c == 'o') {
+      } else if (c == CHAR_O) {
         return new OrState(c);
-      } else if (c == 'A' || c == 'a') {
+      } else if (c == CHAR_A) {
         return new AndState(c);
       } else {
         return new ImplicitAndState(c);
@@ -407,15 +417,15 @@ public class SearchTokenizer {
 
     State state = new SearchExpressionState();
     List<SearchQueryToken> states = new ArrayList<SearchQueryToken>();
-    State lastAddedToken = null;
+    State lastAdded = null;
     for (char aChar : chars) {
       State next = state.nextChar(aChar);
       if (next instanceof ImplicitAndState) {
-        lastAddedToken = next;
+        lastAdded = next;
         states.add(next);
         next = ((ImplicitAndState)next).nextState();
-      } else if (state.isFinished() && state != lastAddedToken) {
-        lastAddedToken = state;
+      } else if (state.isFinished() && state != lastAdded) {
+        lastAdded = state;
         states.add(state);
       }
       state = next;
@@ -423,9 +433,8 @@ public class SearchTokenizer {
 
     final State lastState = state.nextChar(State.EOF);
     if(lastState.isFinished()) {
-      states.add(state);
-      if(state.getToken() != lastState.getToken()) {
-        states.add(lastState);
+      if(state != lastAdded) {
+        states.add(state);
       }
     } else {
       throw new IllegalStateException("State: " + state + " not finished and list is: " + states.toString());

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/b3bbfa6f/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java
index ceae8d8..9044282 100644
--- a/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java
+++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java
@@ -32,7 +32,7 @@ public class SearchTokenizerTest {
   private boolean logEnabled = false;
 
   @Test
-  public void testParse() throws Exception {
+  public void parseBasics() throws Exception {
     SearchTokenizer tokenizer = new SearchTokenizer();
     List<SearchQueryToken> result;
 
@@ -105,10 +105,12 @@ public class SearchTokenizerTest {
     log(result.toString());
     Assert.assertEquals(PHRASE, result.get(0).getToken());
     Assert.assertEquals("\"99_88.\"", result.get(0).getLiteral());
+
+    SearchValidator.init("abc or \"xyz\"").addExpected(WORD, AND, WORD, AND, PHRASE).validate();
   }
 
   @Test
-  public void testParseNot() throws Exception {
+  public void parseNot() throws Exception {
     SearchTokenizer tokenizer = new SearchTokenizer();
     List<SearchQueryToken> result;
 
@@ -117,10 +119,12 @@ public class SearchTokenizerTest {
     log(result.toString());
     Assert.assertEquals(NOT, result.get(0).getToken());
     Assert.assertEquals(WORD, result.get(1).getToken());
+
+    SearchValidator.init("not abc").addExpected(WORD, AND, WORD).validate();
   }
 
   @Test
-  public void testParseOr() throws Exception {
+  public void parseOr() throws Exception {
     SearchTokenizer tokenizer = new SearchTokenizer();
     List<SearchQueryToken> result;
 
@@ -139,6 +143,8 @@ public class SearchTokenizerTest {
     Assert.assertEquals(WORD, result.get(2).getToken());
     Assert.assertEquals(OR, result.get(3).getToken());
     Assert.assertEquals(WORD, result.get(4).getToken());
+
+    SearchValidator.init("abc or xyz").addExpected(WORD, AND, WORD, AND, WORD).validate();
   }
 
   @Test
@@ -151,7 +157,7 @@ public class SearchTokenizerTest {
   }
 
   @Test
-  public void testParseAnd() throws Exception {
+  public void parseAnd() throws Exception {
     SearchTokenizer tokenizer = new SearchTokenizer();
     List<SearchQueryToken> result;
 
@@ -165,10 +171,13 @@ public class SearchTokenizerTest {
     // no lower case allowed for AND
     result = tokenizer.tokenize("abc and xyz");
     Assert.assertNotNull(result);
+    Assert.assertEquals(5, result.size());
     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());
 
     // implicit AND
     result = tokenizer.tokenize("abc xyz");
@@ -199,7 +208,7 @@ public class SearchTokenizerTest {
   }
 
   @Test
-  public void testParseAndOr() throws Exception {
+  public void parseAndOr() throws Exception {
     SearchTokenizer tokenizer = new SearchTokenizer();
     List<SearchQueryToken> result;
 
@@ -321,7 +330,7 @@ public class SearchTokenizerTest {
     validate("abc AND (def    OR  ghi)");
     validate("abc AND (def        ghi)");
   }
-
+  
   public boolean validate(String query) {
     return new SearchValidator(query).validate();
   }


[18/23] olingo-odata4 git commit: [OLINGO-568] Enabled serch option in TevSvc

Posted by mi...@apache.org.
[OLINGO-568] Enabled serch option in TevSvc


Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo
Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/21e115b6
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/21e115b6
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/21e115b6

Branch: refs/heads/master
Commit: 21e115b6bf91c608c31b1584da83e353664ebdca
Parents: 7d4944d
Author: mibo <mi...@apache.org>
Authored: Mon Nov 16 23:00:18 2015 +0100
Committer: mibo <mi...@apache.org>
Committed: Mon Nov 16 23:06:29 2015 +0100

----------------------------------------------------------------------
 .../processor/TechnicalEntityProcessor.java     |  2 +
 .../tecsvc/processor/TechnicalProcessor.java    |  3 +-
 .../queryoptions/options/SearchHandler.java     | 92 ++++++++++++++++++++
 3 files changed, 95 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/21e115b6/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java
----------------------------------------------------------------------
diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java
index 74cdd51..923bf29 100644
--- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java
+++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java
@@ -67,6 +67,7 @@ import org.apache.olingo.server.tecsvc.processor.queryoptions.ExpandSystemQueryO
 import org.apache.olingo.server.tecsvc.processor.queryoptions.options.CountHandler;
 import org.apache.olingo.server.tecsvc.processor.queryoptions.options.FilterHandler;
 import org.apache.olingo.server.tecsvc.processor.queryoptions.options.OrderByHandler;
+import org.apache.olingo.server.tecsvc.processor.queryoptions.options.SearchHandler;
 import org.apache.olingo.server.tecsvc.processor.queryoptions.options.ServerSidePagingHandler;
 import org.apache.olingo.server.tecsvc.processor.queryoptions.options.SkipHandler;
 import org.apache.olingo.server.tecsvc.processor.queryoptions.options.TopHandler;
@@ -486,6 +487,7 @@ public class TechnicalEntityProcessor extends TechnicalProcessor
     OrderByHandler.applyOrderByOption(uriInfo.getOrderByOption(), entitySet, uriInfo, serviceMetadata.getEdm());
     SkipHandler.applySkipSystemQueryHandler(uriInfo.getSkipOption(), entitySet);
     TopHandler.applyTopSystemQueryOption(uriInfo.getTopOption(), entitySet);
+    SearchHandler.applySearchSystemQueryOption(uriInfo.getSearchOption(), entitySet);
 
     final Integer pageSize = odata.createPreferences(request.getHeaders(HttpHeader.PREFER)).getMaxPageSize();
     final Integer serverPageSize = ServerSidePagingHandler.applyServerSidePaging(uriInfo.getSkipTokenOption(),

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/21e115b6/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalProcessor.java
----------------------------------------------------------------------
diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalProcessor.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalProcessor.java
index b63bdd8..28cf63d 100644
--- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalProcessor.java
+++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalProcessor.java
@@ -233,8 +233,7 @@ public abstract class TechnicalProcessor implements Processor {
   }
 
   protected void validateOptions(final UriInfoResource uriInfo) throws ODataApplicationException {
-    if (uriInfo.getIdOption() != null
-        || uriInfo.getSearchOption() != null) {
+    if (uriInfo.getIdOption() != null) {
       throw new ODataApplicationException("Not all of the specified options are supported.",
           HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT);
     }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/21e115b6/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/options/SearchHandler.java
----------------------------------------------------------------------
diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/options/SearchHandler.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/options/SearchHandler.java
new file mode 100644
index 0000000..5ce4530
--- /dev/null
+++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/options/SearchHandler.java
@@ -0,0 +1,92 @@
+/*
+ * 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.tecsvc.processor.queryoptions.options;
+
+import org.apache.olingo.commons.api.data.Entity;
+import org.apache.olingo.commons.api.data.EntityCollection;
+import org.apache.olingo.commons.api.data.Property;
+import org.apache.olingo.commons.api.http.HttpStatusCode;
+import org.apache.olingo.server.api.ODataApplicationException;
+import org.apache.olingo.server.api.uri.queryoption.SearchOption;
+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;
+import org.apache.olingo.server.api.uri.queryoption.search.SearchTerm;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+
+public class SearchHandler {
+  public static void applySearchSystemQueryOption(final SearchOption searchOption, final EntityCollection entitySet)
+      throws ODataApplicationException {
+
+    if (searchOption != null) {
+      Iterator<Entity> it = entitySet.getEntities().iterator();
+      while(it.hasNext()) {
+        boolean keep = false;
+        Entity next = it.next();
+        List<Property> propertyList = next.getProperties();
+        for (Property property : propertyList) {
+          SearchExpression se = searchOption.getSearchExpression();
+          if(isTrue(se, property)) {
+            keep = true;
+            break;
+          }
+        }
+        if(!keep) {
+          it.remove();
+        }
+      }
+    }
+  }
+
+  private static boolean isTrue(SearchTerm term, Property property) {
+    if(property.isPrimitive()) {
+      String propertyString = property.asPrimitive().toString();
+      return propertyString != null && propertyString.contains(term.getSearchTerm());
+    }
+    return false;
+  }
+
+  private static boolean isTrue(SearchBinary binary, Property property) throws ODataApplicationException {
+    SearchExpression left = binary.getLeftOperand();
+    SearchExpression right = binary.getRightOperand();
+    if(binary.getOperator() == SearchBinaryOperatorKind.AND) {
+      return isTrue(left, property) && isTrue(right, property);
+    } else if(binary.getOperator() == SearchBinaryOperatorKind.OR) {
+      return isTrue(left, property) || isTrue(right, property);
+    } else {
+      throw new ODataApplicationException("Found unknown SearchBinaryOperatorKind: " + binary.getOperator(),
+          HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(), Locale.ROOT);
+    }
+  }
+
+  private static boolean isTrue(SearchExpression searchExpression, Property property) throws ODataApplicationException {
+    if(searchExpression.isSearchBinary()) {
+      return isTrue(searchExpression.asSearchBinary(), property);
+    } else if(searchExpression.isSearchTerm()) {
+      return isTrue(searchExpression.asSearchTerm(), property);
+    } else if(searchExpression.isSearchUnary()) {
+      return !isTrue(searchExpression.asSearchUnary().getOperand(), property);
+    }
+    throw new ODataApplicationException("Found unknown SearchExpression: " + searchExpression,
+        HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(), Locale.ROOT);
+  }
+}


[21/23] olingo-odata4 git commit: [OLINGO-568] Minor code and character validation improvements

Posted by mi...@apache.org.
[OLINGO-568] Minor code and character validation improvements


Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo
Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/6235f3a4
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/6235f3a4
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/6235f3a4

Branch: refs/heads/master
Commit: 6235f3a4434545d6db1026f2b40698b5296f1944
Parents: c0adc02
Author: Michael Bolz <mi...@sap.com>
Authored: Tue Nov 17 15:29:01 2015 +0100
Committer: Michael Bolz <mi...@sap.com>
Committed: Tue Nov 17 15:29:01 2015 +0100

----------------------------------------------------------------------
 .../olingo/server/core/uri/parser/Parser.java   |   2 +-
 .../core/uri/parser/search/SearchParser.java    |  13 +--
 .../uri/parser/search/SearchQueryToken.java     |   2 +-
 .../core/uri/parser/search/SearchTokenizer.java | 117 ++++++++++++++++---
 .../search/SearchParserAndTokenizerTest.java    |   3 +-
 .../uri/parser/search/SearchParserTest.java     |   4 +-
 .../uri/parser/search/SearchTokenizerTest.java  |  34 ++++++
 7 files changed, 146 insertions(+), 29 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/6235f3a4/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/Parser.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/Parser.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/Parser.java
index 82094cf..d6cb557 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/Parser.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/Parser.java
@@ -217,7 +217,7 @@ public class Parser {
             systemOption = (OrderByOptionImpl) uriParseTreeVisitor.visitOrderByEOF(ctxOrderByExpression);
           } else if (option.name.equals(SystemQueryOptionKind.SEARCH.toString())) {
             SearchParser searchParser = new SearchParser();
-            systemOption = searchParser.parse(path, option.value);
+            systemOption = searchParser.parse(option.value);
           } else if (option.name.equals(SystemQueryOptionKind.SELECT.toString())) {
             SelectEOFContext ctxSelectEOF =
                 (SelectEOFContext) parseRule(option.value, ParserEntryRules.Select);

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/6235f3a4/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 804ca67..a9fe332 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
@@ -33,11 +33,11 @@ public class SearchParser {
   private Iterator<SearchQueryToken> tokens;
   private SearchQueryToken token;
 
-  public SearchOption parse(String path, String value) throws SearchParserException, SearchTokenizerException {
+  public SearchOption parse(String searchQuery) throws SearchParserException, SearchTokenizerException {
     SearchTokenizer tokenizer = new SearchTokenizer();
     SearchExpression searchExpression;
     try {
-      searchExpression = parseInternal(tokenizer.tokenize(value));
+      searchExpression = parse(tokenizer.tokenize(searchQuery));
     } catch (SearchTokenizerException e) {
       return null;
     }
@@ -46,7 +46,7 @@ public class SearchParser {
     return searchOption;
   }
 
-  protected SearchExpression parseInternal(List<SearchQueryToken> tokens) throws SearchParserException {
+  protected SearchExpression parse(List<SearchQueryToken> tokens) throws SearchParserException {
     this.tokens = tokens.iterator();
     nextToken();
     if (token == null) {
@@ -101,10 +101,7 @@ public class SearchParser {
   }
 
   private boolean isToken(SearchQueryToken.Token toCheckToken) {
-    if (token == null) {
-      return false;
-    }
-    return token.getToken() == toCheckToken;
+    return token != null && token.getToken() == toCheckToken;
   }
 
   private void validateToken(SearchQueryToken.Token toValidateToken) throws SearchParserException {
@@ -194,6 +191,6 @@ public class SearchParser {
   private SearchTerm processPhrase() {
     String literal = token.getLiteral();
     nextToken();
-    return new SearchTermImpl(literal);
+    return new SearchTermImpl(literal.substring(1,literal.length()-1));
   }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/6235f3a4/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchQueryToken.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchQueryToken.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchQueryToken.java
index eb1a009..3fb66f1 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchQueryToken.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchQueryToken.java
@@ -19,7 +19,7 @@
 package org.apache.olingo.server.core.uri.parser.search;
 
 public interface SearchQueryToken {
-  enum Token {OPEN, TERM, NOT, AND, OR, WORD, PHRASE, CLOSE}
+  enum Token {OPEN, NOT, AND, OR, WORD, PHRASE, CLOSE}
 
   Token getToken();
   String getLiteral();

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/6235f3a4/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 1e3b2ef..fb0ad94 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
@@ -98,16 +98,22 @@ public class SearchTokenizer {
     }
 
     /**
-     * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
-     * other-delims = "!" / "(" / ")" / "*" / "+" / "," / ";"
+     * searchPhrase = quotation-mark 1*qchar-no-AMP-DQUOTE quotation-mark
+     *
+     * qchar-no-AMP-DQUOTE = qchar-unescaped / escape ( escape / quotation-mark )
+     *
      * qchar-unescaped = unreserved / pct-encoded-unescaped / other-delims / ":" / "@" / "/" / "?" / "$" / "'" / "="
+     *
+     * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
+     *
+     * escape = "\" / "%5C" ; reverse solidus U+005C
+     *
      * 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 )
+     * other-delims = "!" / "(" / ")" / "*" / "+" / "," / ";"
      *
-     * escape = "\" / "%5C" ; reverse solidus U+005C
      * quotation-mark = DQUOTE / "%22"
      *
      * ALPHA = %x41-5A / %x61-7A
@@ -119,19 +125,100 @@ public class SearchTokenizer {
      */
     static boolean isAllowedPhrase(final char character) {
       // FIXME mibo: check missing
+      return isQCharUnescaped(character) || isEscaped(character);
+    }
+
+    /**
+     * escape = "\" / "%5C" ; reverse solidus U+005C
+     * @param character which is checked
+     * @return true if character is allowed
+     */
+    private static boolean isEscaped(char character) {
+      // TODO: mibo(151117): check how to implement
+      return false;
+    }
+
+    /**
+     * qchar-unescaped = unreserved / pct-encoded-unescaped / other-delims / ":" / "@" / "/" / "?" / "$" / "'" / "="
+     * @param character which is checked
+     * @return true if character is allowed
+     */
+    private static boolean isQCharUnescaped(char character) {
+      return isUnreserved(character)
+              || isPctEncodedUnescaped(character)
+              || isOtherDelims(character)
+              || character == ':'
+              || character == '@'
+              || character == '/'
+              || character == '$'
+              || character == '\''
+              || character == '=';
+    }
+
+    /**
+     * other-delims = "!" / "(" / ")" / "*" / "+" / "," / ";"
+     * @param character which is checked
+     * @return true if character is allowed
+     */
+    private static boolean isOtherDelims(char character) {
+      return character == '!'
+              || character == '('
+              || character == ')'
+              || character == '*'
+              || character == '+'
+              || character == ','
+              || character == ';';
+    }
+
+    /**
+     * 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" )
+     *
+     * HEXDIG = DIGIT / A-to-F
+     *
+     * @param character which is checked
+     * @return true if character is allowed
+     */
+    private static boolean isPctEncodedUnescaped(char character) {
+      String hex = Integer.toHexString((int) character);
+      char aschar[] = hex.toCharArray();
+      if(aschar[0] == '%') {
+        if(aschar[1] == '2') {
+          return aschar[2] != '2' && isHexDigit(aschar[2]);
+        } else if(aschar[1] == '5') {
+          return aschar[2] != 'C' && isHexDigit(aschar[2]);
+        } else if(isHexDigit(aschar[1])) {
+          return isHexDigit(aschar[2]);
+        }
+      }
+      return false;
+    }
+
+    private static boolean isHexDigit(char character) {
+      return 'A' <= character && character <= 'F' // case A..F
+              || '0' <= character && character <= '9'; // case 0..9
+    }
+
+    /**
+     * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
+     * @param character which is checked
+     * @return true if character is allowed
+     */
+    private static boolean isUnreserved(char character) {
       return isAlphaOrDigit(character)
-          || character == '-'
-          || character == '.'
-          || character == '_'
-          || character == '~'
-          || character == ':'
-          || character == '@'
-          || character == '/'
-          || character == '$'
-          || character == '\''
-          || character == '=';
+              || character == '-'
+              || character == '.'
+              || character == '_'
+              || character == '~';
     }
 
+    /**
+     * ALPHA = %x41-5A / %x61-7A
+     * DIGIT = %x30-39
+     * @param character which is checked
+     * @return true if character is allowed
+     */
     private static boolean isAlphaOrDigit(char character) {
       return 'A' <= character && character <= 'Z' // case A..Z
           || 'a' <= character && character <= 'z' // case a..z
@@ -220,7 +307,7 @@ public class SearchTokenizer {
 
   private class SearchTermState extends LiteralState {
     public SearchTermState() {
-      super(Token.TERM);
+      super(null);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/6235f3a4/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 dd5ab70..23cac8e 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
@@ -185,7 +185,7 @@ public class SearchParserAndTokenizerTest {
 
     private SearchExpression getSearchExpression() throws SearchParserException, SearchTokenizerException {
       SearchParser tokenizer = new SearchParser();
-      SearchOption result = tokenizer.parse(null, searchQuery);
+      SearchOption result = tokenizer.parse(searchQuery);
       Assert.assertNotNull(result);
       final SearchExpression searchExpression = result.getSearchExpression();
       Assert.assertNotNull(searchExpression);
@@ -195,5 +195,4 @@ public class SearchParserAndTokenizerTest {
       return searchExpression;
     }
   }
-
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/6235f3a4/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 97e941c..ee10e1a 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
@@ -220,7 +220,7 @@ public class SearchParserTest extends SearchParser {
 
   private SearchExpression run(SearchQueryToken.Token... tokenArray) throws SearchParserException {
     List<SearchQueryToken> tokenList = prepareTokens(tokenArray);
-    SearchExpression se = parseInternal(tokenList);
+    SearchExpression se = parse(tokenList);
     assertNotNull(se);
     return se;
   }
@@ -236,7 +236,7 @@ public class SearchParserTest extends SearchParser {
         when(token.getLiteral()).thenReturn("word" + wordNumber);
         wordNumber++;
       } else if (aTokenArray == Token.PHRASE) {
-        when(token.getLiteral()).thenReturn("phrase" + phraseNumber);
+        when(token.getLiteral()).thenReturn("\"phrase" + phraseNumber + "\"");
         phraseNumber++;
       }
       when(token.toString()).thenReturn("" + aTokenArray);

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/6235f3a4/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java
index ce4c3ca..8408e93 100644
--- a/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java
+++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java
@@ -19,6 +19,7 @@
 package org.apache.olingo.server.core.uri.parser.search;
 
 import org.junit.Assert;
+import org.junit.Ignore;
 import org.junit.Test;
 
 import java.util.ArrayList;
@@ -109,7 +110,40 @@ public class SearchTokenizerTest {
     SearchValidator.init("abc or \"xyz\"").addExpected(WORD, WORD, PHRASE).validate();
   }
 
+  /**
+   * https://tools.oasis-open.org/version-control/browse/wsvn/odata/trunk/spec/ABNF/odata-abnf-testcases.xml
+   * @throws Exception
+   */
   @Test
+  @Ignore("Test must be moved to SearchParserTest and SearchParserAndTokenizerTest")
+  public void parsePhraseAbnfTestcases() throws Exception {
+    //    <TestCase Name="5.1.7 Search - simple phrase" Rule="queryOptions">
+    SearchValidator.init("\"blue%20green\"").validate();
+    //    <TestCase Name="5.1.7 Search - simple phrase" Rule="queryOptions">
+    SearchValidator.init("\"blue%20green%22").validate();
+    //    <TestCase Name="5.1.7 Search - phrase with escaped double-quote" Rule="queryOptions">
+    //    <Input>$search="blue\"green"</Input>
+    SearchValidator.init("\"blue\\\"green\"").validate();
+
+    //    <TestCase Name="5.1.7 Search - phrase with escaped backslash" Rule="queryOptions">
+    //    <Input>$search="blue\\green"</Input>
+    SearchValidator.init("\"blue\\\\green\"").validate();
+
+    //    <TestCase Name="5.1.7 Search - phrase with unescaped double-quote" Rule="queryOptions" FailAt="14">
+    SearchValidator.init("\"blue\"green\"").validate();
+
+    //    <TestCase Name="5.1.7 Search - phrase with unescaped double-quote" Rule="queryOptions" FailAt="16">
+    SearchValidator.init("\"blue%22green\"").validate();
+
+//    <TestCase Name="5.1.7 Search - implicit AND" Rule="queryOptions">
+//    <Input>$search=blue green</Input>
+//    SearchValidator.init("\"blue%20green\"").validate();
+    //    <TestCase Name="5.1.7 Search - implicit AND, encoced" Rule="queryOptions">
+//    SearchValidator.init("blue%20green").validate();
+  }
+
+
+    @Test
   public void parseNot() throws Exception {
     SearchTokenizer tokenizer = new SearchTokenizer();
     List<SearchQueryToken> result;


[17/23] olingo-odata4 git commit: [OLINGO-568] Minor code clean up

Posted by mi...@apache.org.
[OLINGO-568] Minor code clean up


Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo
Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/7d4944d1
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/7d4944d1
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/7d4944d1

Branch: refs/heads/master
Commit: 7d4944d10e3da762ea5d089b911aafcd3f6d73c5
Parents: 8457c0f
Author: mibo <mi...@apache.org>
Authored: Fri Nov 13 17:51:24 2015 +0100
Committer: mibo <mi...@apache.org>
Committed: Fri Nov 13 17:51:24 2015 +0100

----------------------------------------------------------------------
 .../core/uri/parser/search/SearchParser.java    | 26 +++++++++++---------
 .../uri/parser/search/SearchParserTest.java     | 16 ++++++++----
 2 files changed, 25 insertions(+), 17 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/7d4944d1/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 2cd03c6..804ca67 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
@@ -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.api.uri.queryoption.search.SearchTerm;
 import org.apache.olingo.server.core.uri.parser.search.SearchQueryToken.Token;
 import org.apache.olingo.server.core.uri.queryoption.SearchOptionImpl;
 
@@ -55,7 +56,7 @@ public class SearchParser {
   }
 
   private SearchExpression processSearchExpression(SearchExpression left) throws SearchParserException {
-    if (token == null) {
+    if (isEof()) {
       return left;
     }
 
@@ -149,15 +150,10 @@ public class SearchParser {
     return new SearchBinaryImpl(left, SearchBinaryOperatorKind.OR, se);
   }
 
-  private RuntimeException illegalState() {
-    return new RuntimeException();
-  }
-
   private SearchExpression processNot() throws SearchParserException {
     nextToken();
     if (isToken(Token.WORD) || isToken(Token.PHRASE)) {
-      SearchExpression searchExpression = processTerm();
-      return new SearchUnaryImpl(searchExpression.asSearchTerm());
+      return new SearchUnaryImpl(processWordOrPhrase());
     }
     throw new SearchParserException("NOT must be followed by a term not a " + token.getToken(),
         SearchParserException.MessageKeys.INVALID_NOT_OPERAND, token.getToken().toString());
@@ -175,21 +171,27 @@ public class SearchParser {
     if (isToken(SearchQueryToken.Token.NOT)) {
       return processNot();
     }
-    if (isToken(SearchQueryToken.Token.PHRASE)) {
+    return processWordOrPhrase();
+  }
+
+  private SearchTerm processWordOrPhrase() throws SearchParserException {
+    if (isToken(Token.PHRASE)) {
       return processPhrase();
-    } else if (isToken(SearchQueryToken.Token.WORD)) {
+    } else if (isToken(Token.WORD)) {
       return processWord();
     }
-    throw illegalState();
+    throw new SearchParserException("Expected PHRASE||WORD found: " + token.getToken(),
+        SearchParserException.MessageKeys.EXPECTED_DIFFERENT_TOKEN,
+        Token.PHRASE.name() + "" + Token.WORD.name(), token.getToken().toString());
   }
 
-  private SearchTermImpl processWord() {
+  private SearchTerm processWord() {
     String literal = token.getLiteral();
     nextToken();
     return new SearchTermImpl(literal);
   }
 
-  private SearchTermImpl processPhrase() {
+  private SearchTerm processPhrase() {
     String literal = token.getLiteral();
     nextToken();
     return new SearchTermImpl(literal);

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/7d4944d1/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 0902e8a..97e941c 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
@@ -177,6 +177,12 @@ public class SearchParserTest extends SearchParser {
     runEx(SearchParserException.MessageKeys.INVALID_NOT_OPERAND, Token.NOT, Token.AND);
   }
 
+
+  @Test
+  public void notNotWord() throws Exception {
+    runEx(SearchParserException.MessageKeys.INVALID_NOT_OPERAND, Token.NOT, Token.NOT, Token.WORD);
+  }
+
   @Test
   public void doubleAnd() throws Exception {
     runEx(SearchParserException.MessageKeys.INVALID_OPERATOR_AFTER_AND, Token.WORD, Token.AND, Token.AND, Token.WORD);
@@ -223,17 +229,17 @@ public class SearchParserTest extends SearchParser {
     ArrayList<SearchQueryToken> tokenList = new ArrayList<SearchQueryToken>();
     int wordNumber = 1;
     int phraseNumber = 1;
-    for (int i = 0; i < tokenArray.length; i++) {
+    for (Token aTokenArray : tokenArray) {
       SearchQueryToken token = mock(SearchQueryToken.class);
-      when(token.getToken()).thenReturn(tokenArray[i]);
-      if (tokenArray[i] == Token.WORD) {
+      when(token.getToken()).thenReturn(aTokenArray);
+      if (aTokenArray == Token.WORD) {
         when(token.getLiteral()).thenReturn("word" + wordNumber);
         wordNumber++;
-      } else if (tokenArray[i] == Token.PHRASE) {
+      } else if (aTokenArray == Token.PHRASE) {
         when(token.getLiteral()).thenReturn("phrase" + phraseNumber);
         phraseNumber++;
       }
-      when(token.toString()).thenReturn("" + tokenArray[i]);
+      when(token.toString()).thenReturn("" + aTokenArray);
       tokenList.add(token);
     }
     return tokenList;


[23/23] olingo-odata4 git commit: [OLINGO-568] Added exception messages and some tests

Posted by mi...@apache.org.
[OLINGO-568] Added exception messages and some 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/ae1b2754
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/ae1b2754
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/ae1b2754

Branch: refs/heads/master
Commit: ae1b2754b765a2519e7d9c1fa30e1a5af5de3862
Parents: 1a59a58
Author: Michael Bolz <mi...@sap.com>
Authored: Tue Nov 17 15:58:28 2015 +0100
Committer: Michael Bolz <mi...@sap.com>
Committed: Tue Nov 17 15:58:28 2015 +0100

----------------------------------------------------------------------
 .../core/uri/parser/search/SearchParser.java    |  4 +++-
 .../parser/search/SearchParserException.java    |  6 ++++-
 .../server-core-exceptions-i18n.properties      |  9 ++++++++
 .../search/SearchParserAndTokenizerTest.java    | 24 +++++++++-----------
 4 files changed, 28 insertions(+), 15 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/ae1b2754/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 a9fe332..2723024 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
@@ -39,7 +39,9 @@ public class SearchParser {
     try {
       searchExpression = parse(tokenizer.tokenize(searchQuery));
     } catch (SearchTokenizerException e) {
-      return null;
+      String message = e.getMessage();
+      throw new SearchParserException("Tokenizer exception with message: " + message,
+              SearchParserException.MessageKeys.TOKENIZER_EXCEPTION, message);
     }
     final SearchOptionImpl searchOption = new SearchOptionImpl();
     searchOption.setSearchExpression(searchExpression);

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/ae1b2754/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
index 78a12be..9e612a0 100644
--- 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
@@ -24,7 +24,11 @@ public class SearchParserException extends UriParserSyntaxException {
 
   private static final long serialVersionUID = 5781553037561337795L;
 
-  public static enum MessageKeys implements MessageKey {
+  public enum MessageKeys implements MessageKey {
+    /** parameter: message */
+    TOKENIZER_EXCEPTION,
+    /** parameter: tokenCharacter */
+    INVALID_TOKEN_CHARACTER_FOUND,
     /** parameter: operatorkind */
     INVALID_BINARY_OPERATOR_POSITION, 
     /** parameter: operatorkind */

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/ae1b2754/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 28c6dd7..bfb9440 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
@@ -37,6 +37,15 @@ UriParserSyntaxException.SYSTEM_QUERY_OPTION_LEVELS_NOT_ALLOWED_HERE=The system
 UriParserSyntaxException.SYNTAX=The URI is malformed.
 UriParserSyntaxException.DUPLICATED_ALIAS=Duplicated alias. An alias '%1$s' was already specified!.
 
+SearchParserException.TOKENIZER_EXCEPTION=Exception during tokenizer creation with message '%1$s'.
+SearchParserException.INVALID_TOKEN_CHARACTER_FOUND=Invalid token character with value '%1$s' found.
+SearchParserException.INVALID_BINARY_OPERATOR_POSITION=Invalid binary operator position for kind '%1$s' found.
+SearchParserException.INVALID_NOT_OPERAND=Invalid not operand for kind '%1$s' found.
+SearchParserException.EXPECTED_DIFFERENT_TOKEN=Expected token '%1$s' but found '%2$s'.
+SearchParserException.NO_EXPRESSION_FOUND=No expression found.
+SearchParserException.INVALID_OPERATOR_AFTER_AND=Invalid operator after AND found of kind '%1$s'.
+
+
 UriParserSemanticException.FUNCTION_NOT_FOUND=The function import '%1$s' has no function with parameters '%2$s'.
 UriParserSemanticException.RESOURCE_PART_ONLY_FOR_TYPED_PARTS='%1$s' is only allowed for typed parts.
 UriParserSemanticException.RESOURCE_PART_MUST_BE_PRECEDED_BY_STRUCTURAL_TYPE=The resource part '%1$s' must be preceded by a structural type.

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/ae1b2754/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 23cac8e..fa419a9 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
@@ -23,17 +23,19 @@ import static org.apache.olingo.server.api.uri.queryoption.search.SearchBinaryOp
 
 import java.lang.reflect.Field;
 
+import org.apache.olingo.server.api.ODataLibraryException;
 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;
 
 public class SearchParserAndTokenizerTest {
 
   @Test
   public void basicParsing() throws Exception {
+    SearchExpressionValidator.init("\"99\"")
+            .validate(with("99"));
     SearchExpressionValidator.init("a")
         .validate(with("a"));
     SearchExpressionValidator.init("a AND b")
@@ -70,16 +72,10 @@ public class SearchParserAndTokenizerTest {
         .validate("{{'a' OR 'b'} AND {NOT 'c'}}");
   }
 
-  @Ignore
   @Test
-  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());
+  public void invalidSearchQuery() throws Exception {
+    SearchExpressionValidator.init("99").validate(SearchParserException.class,
+            SearchParserException.MessageKeys.TOKENIZER_EXCEPTION);
   }
 
   private static SearchExpression with(String term) {
@@ -162,11 +158,13 @@ public class SearchParserAndTokenizerTest {
       return this;
     }
 
-    private void validate(Class<? extends Exception> exception) throws SearchTokenizerException {
+    private void validate(Class<? extends ODataLibraryException> exception, ODataLibraryException.MessageKey key)
+            throws SearchTokenizerException {
       try {
-        new SearchTokenizer().tokenize(searchQuery);
-      } catch (Exception e) {
+        validate(searchQuery);
+      } catch (ODataLibraryException e) {
         Assert.assertEquals(exception, e.getClass());
+        Assert.assertEquals(key, e.getMessageKey());
         return;
       }
       Assert.fail("Expected exception " + exception.getClass().getSimpleName() + " was not thrown.");


[04/23] olingo-odata4 git commit: [OLINGO-568] Added implicit AND support

Posted by mi...@apache.org.
[OLINGO-568] Added implicit AND support


Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo
Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/bbdd0d75
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/bbdd0d75
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/bbdd0d75

Branch: refs/heads/master
Commit: bbdd0d755ed43d61b61d5eba87aed6d2cab410c4
Parents: 762c924
Author: Michael Bolz <mi...@sap.com>
Authored: Mon Nov 9 12:02:02 2015 +0100
Committer: Michael Bolz <mi...@sap.com>
Committed: Mon Nov 9 12:02:02 2015 +0100

----------------------------------------------------------------------
 .../core/uri/parser/search/SearchTokenizer.java | 62 +++++++++++++++++---
 .../uri/parser/search/SearchParserTest.java     |  5 +-
 .../uri/parser/search/SearchTokenizerTest.java  | 39 +++++++++---
 3 files changed, 89 insertions(+), 17 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/bbdd0d75/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 951d641..e058a00 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
@@ -39,6 +39,7 @@ import java.util.List;
  * </code>
  */
 public class SearchTokenizer {
+  public static final char QUOTATION_MARK = '\"';
   //RWS = 1*( SP / HTAB / "%20" / "%09" )  ; "required" whitespace
   //BWS =  *( SP / HTAB / "%20" / "%09" )  ; "bad" whitespace
 
@@ -170,6 +171,8 @@ public class SearchTokenizer {
         return new CloseState();
       } else if(isEof(c)) {
         return finish();
+      } else if(isWhitespace(c)) {
+        return new AndState(c);
       } else {
         return new SearchTermState().init(c);
       }
@@ -189,7 +192,7 @@ public class SearchTokenizer {
     public State nextChar(char c) {
       if(c == 'n' || c == 'N') {
         return new NotState(c);
-      } else if (c == '\'') {
+      } else if (c == QUOTATION_MARK) {
         return new SearchPhraseState(c);
       } else if (isAllowedChar(c)) {
         return new SearchWordState(c);
@@ -230,7 +233,7 @@ public class SearchTokenizer {
   private class SearchPhraseState extends LiteralState {
     public SearchPhraseState(char c) {
       super(Token.PHRASE, c);
-      if(c != '\'') {
+      if(c != QUOTATION_MARK) {
         forbidden(c);
       }
     }
@@ -241,7 +244,7 @@ public class SearchTokenizer {
         return new SearchExpressionState().init(c);
       } else if (isAllowedPhrase(c)) {
         return allowed(c);
-      } else if (c == '\'') {
+      } else if (c == QUOTATION_MARK) {
         finish();
         return allowed(c);
       } else if (isWhitespace(c)) {
@@ -299,13 +302,29 @@ public class SearchTokenizer {
         return allowed(c);
       } else if(getLiteral().length() == 3 && isWhitespace(c)) {
         finish();
-        return new RwsState();
+        return new BeforeSearchExpressionRwsState();
       } else {
         return new SearchWordState(this);
       }
     }
   }
 
+  private class ImplicitAndState extends LiteralState {
+    private State followingState;
+    public ImplicitAndState(char c) {
+      super(Token.AND);
+      finish();
+      followingState = new SearchExpressionState().init(c);
+    }
+    public State nextState() {
+      return followingState;
+    }
+    @Override
+    public State nextChar(char c) {
+      return followingState.nextChar(c);
+    }
+  }
+
   private class AndState extends LiteralState {
     public AndState(char c) {
       super(Token.AND, c);
@@ -321,7 +340,7 @@ public class SearchTokenizer {
         return allowed(c);
       } else if(getLiteral().length() == 3 && isWhitespace(c)) {
         finish();
-        return new RwsState();
+        return new BeforeSearchExpressionRwsState();
       } else {
         return new SearchWordState(this);
       }
@@ -341,13 +360,28 @@ public class SearchTokenizer {
         return allowed(c);
       } else if(getLiteral().length() == 2 && isWhitespace(c)) {
         finish();
-        return new RwsState();
+        return new BeforeSearchExpressionRwsState();
       } else {
         return new SearchWordState(this);
       }
     }
   }
 
+  // RWS [ 'AND' RWS ] searchExpr
+  private class BeforeSearchExpressionRwsState extends State {
+    public BeforeSearchExpressionRwsState() {
+      super(Token.RWS);
+    }
+    @Override
+    public State nextChar(char c) {
+      if (isWhitespace(c)) {
+        return allowed(c);
+      } else {
+        return new SearchExpressionState().init(c);
+      }
+    }
+  }
+
 
   private class RwsState extends State {
     public RwsState() {
@@ -362,7 +396,7 @@ public class SearchTokenizer {
       } else if (c == 'A' || c == 'a') {
         return new AndState(c);
       } else {
-        return new SearchExpressionState().init(c);
+        return new ImplicitAndState(c);
       }
     }
   }
@@ -373,16 +407,26 @@ public class SearchTokenizer {
 
     State state = new SearchExpressionState();
     List<SearchQueryToken> states = new ArrayList<SearchQueryToken>();
+    State lastAddedToken = null;
     for (char aChar : chars) {
       State next = state.nextChar(aChar);
-      if (state.isFinished() && next != state) {
+      if (next instanceof ImplicitAndState) {
+        lastAddedToken = next;
+        states.add(next);
+        next = ((ImplicitAndState)next).nextState();
+      } else if (state.isFinished() && state != lastAddedToken) {
+        lastAddedToken = state;
         states.add(state);
       }
       state = next;
     }
 
-    if(state.nextChar(State.EOF).isFinished()) {
+    final State lastState = state.nextChar(State.EOF);
+    if(lastState.isFinished()) {
       states.add(state);
+      if(state.getToken() != lastState.getToken()) {
+        states.add(lastState);
+      }
     } else {
       throw new IllegalStateException("State: " + state + " not finished and list is: " + states.toString());
     }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/bbdd0d75/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 81147ba..0c51ba6 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
@@ -18,6 +18,8 @@
  */
 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.junit.Test;
 
 public class SearchParserTest {
@@ -25,6 +27,7 @@ public class SearchParserTest {
   @Test
   public void basicParsing() {
     SearchParser parser = new SearchParser();
-    parser.parse("ESAllPrim", "abc");
+    SearchOption so = parser.parse("ESAllPrim", "abc");
+    SearchExpression se = so.getSearchExpression();
   }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/bbdd0d75/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java
index 24d782f..ceae8d8 100644
--- a/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java
+++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java
@@ -87,24 +87,24 @@ public class SearchTokenizerTest {
     List<SearchQueryToken> result;
 
     //
-    result = tokenizer.tokenize("'abc'");
+    result = tokenizer.tokenize("\"abc\"");
     Assert.assertNotNull(result);
     log(result.toString());
     Assert.assertEquals(PHRASE, result.get(0).getToken());
 
     //
-    result = tokenizer.tokenize("'9988  abs'");
+    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());
+    Assert.assertEquals("\"9988  abs\"", result.get(0).getLiteral());
 
     //
-    result = tokenizer.tokenize("'99_88.'");
+    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());
+    Assert.assertEquals("\"99_88.\"", result.get(0).getLiteral());
   }
 
   @Test
@@ -142,6 +142,15 @@ public class SearchTokenizerTest {
   }
 
   @Test
+  public void parseImplicitAnd() {
+    SearchValidator.init("a b").addExpected(WORD, AND, WORD).validate();
+    SearchValidator.init("a b OR c").addExpected(WORD, AND, WORD, OR, WORD).validate();
+    SearchValidator.init("a bc OR c").addExpected(WORD, AND, WORD, OR, WORD).validate();
+    SearchValidator.init("a bc c").addExpected(WORD, AND, WORD, AND, WORD).validate();
+    SearchValidator.init("(a OR x) bc c").addExpected(OPEN, WORD, OR, WORD, CLOSE, AND, WORD, AND, WORD).validate();
+  }
+
+  @Test
   public void testParseAnd() throws Exception {
     SearchTokenizer tokenizer = new SearchTokenizer();
     List<SearchQueryToken> result;
@@ -153,6 +162,22 @@ public class SearchTokenizerTest {
     Assert.assertEquals(AND, result.get(1).getToken());
     Assert.assertEquals(WORD, result.get(2).getToken());
 
+    // no lower case allowed for AND
+    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());
+
+    // implicit AND
+    result = tokenizer.tokenize("abc 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());
@@ -162,13 +187,13 @@ public class SearchTokenizerTest {
     Assert.assertEquals(AND, result.get(3).getToken());
     Assert.assertEquals(WORD, result.get(4).getToken());
 
-    result = tokenizer.tokenize("abc AND 'x-y_z' AND 123");
+    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("\"x-y_z\"", result.get(2).getLiteral());
     Assert.assertEquals(AND, result.get(3).getToken());
     Assert.assertEquals(WORD, result.get(4).getToken());
   }


[13/23] olingo-odata4 git commit: [OLINGO-568] Fix unused stuff

Posted by mi...@apache.org.
[OLINGO-568] Fix unused stuff


Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo
Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/a8d63fbe
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/a8d63fbe
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/a8d63fbe

Branch: refs/heads/master
Commit: a8d63fbee2c145c029fb5a2f8e927b9d7ac4e665
Parents: 22d152f
Author: Christian Amend <ch...@sap.com>
Authored: Thu Nov 12 14:05:55 2015 +0100
Committer: Christian Amend <ch...@sap.com>
Committed: Thu Nov 12 14:05:55 2015 +0100

----------------------------------------------------------------------
 .../server/core/uri/parser/search/SearchTokenizerException.java   | 3 +++
 .../core/uri/parser/search/SearchParserAndTokenizerTest.java      | 1 +
 .../olingo/server/core/uri/parser/search/SearchTokenizerTest.java | 2 ++
 3 files changed, 6 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/a8d63fbe/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 dd6f71e..451632b 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
@@ -19,6 +19,9 @@
 package org.apache.olingo.server.core.uri.parser.search;
 
 public class SearchTokenizerException extends Exception {
+
+  private static final long serialVersionUID = -8295456415309640166L;
+
   public SearchTokenizerException(String message) {
     super(message);
   }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/a8d63fbe/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 ca9d80b..43b63c3 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
@@ -139,6 +139,7 @@ public class SearchParserAndTokenizerTest {
       return new SearchExpressionValidator(searchQuery);
     }
 
+    @SuppressWarnings("unused")
     private SearchExpressionValidator enableLogging() {
       log = true;
       return this;

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/a8d63fbe/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java
index 904bd3f..ce4c3ca 100644
--- a/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java
+++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java
@@ -416,6 +416,8 @@ public class SearchTokenizerTest {
     private static SearchValidator init(String searchQuery) {
       return new SearchValidator(searchQuery);
     }
+    
+    @SuppressWarnings("unused")
     private SearchValidator enableLogging() {
       log = true;
       return this;


[03/23] olingo-odata4 git commit: [OLINGO-568] Merge branch 'master' into OLINGO-568_SearchParserPoC

Posted by mi...@apache.org.
[OLINGO-568] Merge branch 'master' into OLINGO-568_SearchParserPoC


Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo
Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/762c924c
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/762c924c
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/762c924c

Branch: refs/heads/master
Commit: 762c924cf6a187f513bafe97d4702d4377bab655
Parents: 81fcbea 639362c
Author: mibo <mi...@apache.org>
Authored: Fri Nov 6 21:33:03 2015 +0100
Committer: mibo <mi...@apache.org>
Committed: Fri Nov 6 21:33:03 2015 +0100

----------------------------------------------------------------------
 .../olingo/fit/tecsvc/client/BasicITCase.java   |    8 +-
 .../tecsvc/client/FilterSystemQueryITCase.java  |    2 +-
 .../deserializer/xml/ODataXmlDeserializer.java  |  188 +-
 .../serializer/json/ODataJsonSerializer.java    |    7 +-
 .../olingo/server/tecsvc/data/DataCreator.java  | 1052 +--
 .../olingo/server/tecsvc/data/DataProvider.java |   51 +-
 .../tecsvc/provider/ComplexTypeProvider.java    |    2 +-
 .../tecsvc/provider/EntityTypeProvider.java     |    8 +-
 .../tecsvc/provider/PropertyProvider.java       |    9 +-
 .../json/ODataJsonDeserializerEntityTest.java   |   25 +
 .../xml/ODataXmlDeserializerTest.java           |    4 +-
 .../json/ODataJsonSerializerTest.java           |   60 +-
 .../serializer/xml/ODataXmlSerializerTest.java  |    2 -
 .../core/uri/antlr/TestFullResourcePath.java    | 6130 +++++++++---------
 ...tityETMixEnumDefCollCompWithEnumStrings.json |   24 +
 15 files changed, 3867 insertions(+), 3705 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/762c924c/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestFullResourcePath.java
----------------------------------------------------------------------
diff --cc lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestFullResourcePath.java
index 259b61f,69458ab..ffc91e8
--- a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestFullResourcePath.java
+++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestFullResourcePath.java
@@@ -1127,60 -1146,59 +1146,60 @@@ public class TestFullResourcePath 
    @Test
    public void runNonComposableFunctions() throws Exception {
      testUri.run("FICRTCollETMixPrimCollCompTwoParam(ParameterInt16=1,ParameterString='1')")
-       .isKind(UriInfoKind.resource)
-       .goPath().first()
-       .isFunctionImport("FICRTCollETMixPrimCollCompTwoParam")
-       .isFunction("UFCRTCollETMixPrimCollCompTwoParam")
-       .isParameter(0, "ParameterInt16", "1")
-       .isParameter(1, "ParameterString", "'1'");
-     
+         .isKind(UriInfoKind.resource)
+         .goPath().first()
+         .isFunctionImport("FICRTCollETMixPrimCollCompTwoParam")
+         .isFunction("UFCRTCollETMixPrimCollCompTwoParam")
+         .isParameter(0, "ParameterInt16", "1")
+         .isParameter(1, "ParameterString", "'1'");
+ 
      testUri.run("FICRTCollETMixPrimCollCompTwoParam(ParameterInt16=1,ParameterString='1')(0)")
-       .isKind(UriInfoKind.resource)
-       .goPath().first()
-       .isFunctionImport("FICRTCollETMixPrimCollCompTwoParam")
-       .isFunction("UFCRTCollETMixPrimCollCompTwoParam")
-       .isParameter(0, "ParameterInt16", "1")
-       .isParameter(1, "ParameterString", "'1'");
-     
+         .isKind(UriInfoKind.resource)
+         .goPath().first()
+         .isFunctionImport("FICRTCollETMixPrimCollCompTwoParam")
+         .isFunction("UFCRTCollETMixPrimCollCompTwoParam")
+         .isParameter(0, "ParameterInt16", "1")
+         .isParameter(1, "ParameterString", "'1'");
+ 
      testUri.runEx("FICRTCollETMixPrimCollCompTwoParam(ParameterInt16=1,ParameterString='1')(0)/PropertyInt16")
-       .isExValidation(UriValidationException.MessageKeys.UNALLOWED_RESOURCE_PATH);
-     
+         .isExValidation(UriValidationException.MessageKeys.UNALLOWED_RESOURCE_PATH);
+ 
      testUri.runEx("FICRTCollETMixPrimCollCompTwoParam(ParameterInt16=1,ParameterString='1')", "$skip=1")
-       .isExValidation(UriValidationException.MessageKeys.SYSTEM_QUERY_OPTION_NOT_ALLOWED);
-     
+         .isExValidation(UriValidationException.MessageKeys.SYSTEM_QUERY_OPTION_NOT_ALLOWED);
+ 
      testUri.runEx("FICRTCollETMixPrimCollCompTwoParam(ParameterInt16=1,ParameterString='1')", "$top=1")
-     .isExValidation(UriValidationException.MessageKeys.SYSTEM_QUERY_OPTION_NOT_ALLOWED);
-     
-     testUri.runEx("FICRTCollETMixPrimCollCompTwoParam(ParameterInt16=1,ParameterString='1')", 
+         .isExValidation(UriValidationException.MessageKeys.SYSTEM_QUERY_OPTION_NOT_ALLOWED);
+ 
+     testUri.runEx("FICRTCollETMixPrimCollCompTwoParam(ParameterInt16=1,ParameterString='1')",
          "$filter=PropertyInt16 eq 1")
-     .isExValidation(UriValidationException.MessageKeys.SYSTEM_QUERY_OPTION_NOT_ALLOWED);
-     
+         .isExValidation(UriValidationException.MessageKeys.SYSTEM_QUERY_OPTION_NOT_ALLOWED);
+ 
      testUri.runEx("FICRTCollETMixPrimCollCompTwoParam(ParameterInt16=1,ParameterString='1')", "$skip=1")
-     .isExValidation(UriValidationException.MessageKeys.SYSTEM_QUERY_OPTION_NOT_ALLOWED);
-     
+         .isExValidation(UriValidationException.MessageKeys.SYSTEM_QUERY_OPTION_NOT_ALLOWED);
+ 
      testUri.runEx("FICRTCollETMixPrimCollCompTwoParam(ParameterInt16=1,ParameterString='1')", "$count=true")
-     .isExValidation(UriValidationException.MessageKeys.SYSTEM_QUERY_OPTION_NOT_ALLOWED);
-     
+         .isExValidation(UriValidationException.MessageKeys.SYSTEM_QUERY_OPTION_NOT_ALLOWED);
+ 
      testUri.runEx("FICRTCollETMixPrimCollCompTwoParam(ParameterInt16=1,ParameterString='1')", "$skiptoken=5")
-     .isExValidation(UriValidationException.MessageKeys.SYSTEM_QUERY_OPTION_NOT_ALLOWED);
-     
+         .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)
-       .goPath().first()
-       .isEntitySet("ESAllPrim")
-       .at(1)
-       .isFunction("BFNESAllPrimRTCTAllPrim");
-     
-     testUri.runEx("ESAllPrim/olingo.odata.test1.BFNESAllPrimRTCTAllPrim()" 
-           + "/PropertyString")
-       .isExValidation(UriValidationException.MessageKeys.UNALLOWED_RESOURCE_PATH);
+         .isKind(UriInfoKind.resource)
+         .goPath().first()
+         .isEntitySet("ESAllPrim")
+         .at(1)
+         .isFunction("BFNESAllPrimRTCTAllPrim");
+ 
+     testUri.runEx("ESAllPrim/olingo.odata.test1.BFNESAllPrimRTCTAllPrim()"
+         + "/PropertyString")
+         .isExValidation(UriValidationException.MessageKeys.UNALLOWED_RESOURCE_PATH);
    }
-   
+ 
    @Test
    public void runEsNameCast() throws Exception {
      testUri.run("ESTwoPrim/olingo.odata.test1.ETBase")


[19/23] olingo-odata4 git commit: [OLINGO-568] Added search integration test

Posted by mi...@apache.org.
[OLINGO-568] Added search integration test


Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo
Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/9ff30e72
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/9ff30e72
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/9ff30e72

Branch: refs/heads/master
Commit: 9ff30e729dffe392dca1dba70356580266e2aef6
Parents: 21e115b
Author: mibo <mi...@apache.org>
Authored: Tue Nov 17 06:21:35 2015 +0100
Committer: mibo <mi...@apache.org>
Committed: Tue Nov 17 06:30:23 2015 +0100

----------------------------------------------------------------------
 .../tecsvc/client/SystemQueryOptionITCase.java  | 42 +++++++++++++++-----
 .../queryoptions/options/SearchHandler.java     |  3 +-
 2 files changed, 35 insertions(+), 10 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/9ff30e72/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/SystemQueryOptionITCase.java
----------------------------------------------------------------------
diff --git a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/SystemQueryOptionITCase.java b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/SystemQueryOptionITCase.java
index 4341a53..e605836 100644
--- a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/SystemQueryOptionITCase.java
+++ b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/SystemQueryOptionITCase.java
@@ -21,10 +21,11 @@ package org.apache.olingo.fit.tecsvc.client;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
 
+import java.io.IOException;
 import java.net.URI;
+import java.util.List;
 
 import org.apache.olingo.client.api.communication.ODataClientErrorException;
-import org.apache.olingo.client.api.communication.ODataServerErrorException;
 import org.apache.olingo.client.api.communication.request.retrieve.ODataEntitySetRequest;
 import org.apache.olingo.client.api.communication.response.ODataRetrieveResponse;
 import org.apache.olingo.client.api.domain.ClientEntity;
@@ -301,18 +302,41 @@ public class SystemQueryOptionITCase extends AbstractParamTecSvcITCase {
   }
   
   @Test
-  public void negativeSearch() {
+  public void basicSearch() {
     ODataEntitySetRequest<ClientEntitySet> request = getClient().getRetrieveRequestFactory()
         .getEntitySetRequest(getClient().newURIBuilder(SERVICE_URI)
             .appendEntitySetSegment(ES_ALL_PRIM)
-            .search("ABC")
+            .search("Second")
             .build());
     setCookieHeader(request);
-    try {
-      request.execute();
-      fail();
-    } catch (ODataServerErrorException e) {
-      assertEquals("HTTP/1.1 501 Not Implemented", e.getMessage());
-    }
+    ODataRetrieveResponse<ClientEntitySet> response = request.execute();
+    List<ClientEntity> entities = response.getBody().getEntities();
+    assertEquals(1, entities.size());
+  }
+
+  @Test
+  public void andSearch() {
+    ODataEntitySetRequest<ClientEntitySet> request = getClient().getRetrieveRequestFactory()
+        .getEntitySetRequest(getClient().newURIBuilder(SERVICE_URI)
+            .appendEntitySetSegment(ES_ALL_PRIM)
+            .search("Second AND positive")
+            .build());
+    setCookieHeader(request);
+    ODataRetrieveResponse<ClientEntitySet> response = request.execute();
+    List<ClientEntity> entities = response.getBody().getEntities();
+    assertEquals(0, entities.size());
+  }
+
+  @Test
+  public void orSearch() {
+    ODataEntitySetRequest<ClientEntitySet> request = getClient().getRetrieveRequestFactory()
+        .getEntitySetRequest(getClient().newURIBuilder(SERVICE_URI)
+            .appendEntitySetSegment(ES_ALL_PRIM)
+            .search("Second OR positive")
+            .build());
+    setCookieHeader(request);
+    ODataRetrieveResponse<ClientEntitySet> response = request.execute();
+    List<ClientEntity> entities = response.getBody().getEntities();
+    assertEquals(2, entities.size());
   }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/9ff30e72/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/options/SearchHandler.java
----------------------------------------------------------------------
diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/options/SearchHandler.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/options/SearchHandler.java
index 5ce4530..e56e083 100644
--- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/options/SearchHandler.java
+++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/options/SearchHandler.java
@@ -58,7 +58,8 @@ public class SearchHandler {
   }
 
   private static boolean isTrue(SearchTerm term, Property property) {
-    if(property.isPrimitive()) {
+    if(property.isPrimitive() && !property.isNull()) {
+      // TODO: mibo(151117): pass EDM information to do correct 'string' convertation
       String propertyString = property.asPrimitive().toString();
       return propertyString != null && propertyString.contains(term.getSearchTerm());
     }


[02/23] olingo-odata4 git commit: [OLINGO-568] Minor tokenizer optimization

Posted by mi...@apache.org.
[OLINGO-568] Minor tokenizer optimization


Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo
Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/81fcbeaf
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/81fcbeaf
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/81fcbeaf

Branch: refs/heads/master
Commit: 81fcbeafcf281ce07fa90b0ac58876c59e569ff1
Parents: 452ebcb
Author: mibo <mi...@apache.org>
Authored: Fri Nov 6 21:31:35 2015 +0100
Committer: mibo <mi...@apache.org>
Committed: Fri Nov 6 21:31:35 2015 +0100

----------------------------------------------------------------------
 .../core/uri/parser/search/SearchTokenizer.java | 120 +++++++++----------
 .../uri/parser/search/SearchTokenizerTest.java  |   2 +-
 2 files changed, 59 insertions(+), 63 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/81fcbeaf/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 7d2b559..951d641 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
@@ -46,39 +46,16 @@ public class SearchTokenizer {
   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;
     }
 
@@ -139,27 +116,58 @@ public class SearchTokenizer {
 
     @Override
     public String getLiteral() {
-      return literal.toString();
+      return token.toString();
     }
 
     @Override
     public String toString() {
-      return this.getToken().toString() + "=>{" + literal.toString() + "}";
+      return this.getToken().toString() + "=>{" + getLiteral() + "}";
+    }
+  }
+
+  private static abstract class LiteralState extends State {
+    private final StringBuilder literal = new StringBuilder();
+    public LiteralState(Token t) {
+      super(t);
+    }
+    public LiteralState(Token t, char c) {
+      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) {
+      if(isFinished()) {
+        throw new IllegalStateException(toString() + " is already finished.");
+      }
+      literal.append(c);
+      return this;
     }
   }
 
-  private class SearchExpressionState extends State {
+  private class SearchExpressionState extends LiteralState {
     public SearchExpressionState() {
       super(Token.SEARCH_EXPRESSION);
     }
     @Override
     public State nextChar(char c) {
       if (c == '(') {
-        return new OpenState(c);
+        return new OpenState();
       } else if (isWhitespace(c)) {
-        return new RwsState(c);
+        return new RwsState();
       } else if(c == ')') {
-        return new CloseState(c);
+        return new CloseState();
       } else if(isEof(c)) {
         return finish();
       } else {
@@ -173,7 +181,7 @@ public class SearchTokenizer {
     }
   }
 
-  private class SearchTermState extends State {
+  private class SearchTermState extends LiteralState {
     public SearchTermState() {
       super(Token.TERM);
     }
@@ -185,14 +193,6 @@ public class SearchTokenizer {
         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);
     }
@@ -202,27 +202,24 @@ public class SearchTokenizer {
     }
   }
 
-  private class SearchWordState extends State {
+  private class SearchWordState extends LiteralState {
     public SearchWordState(char c) {
       super(Token.WORD, c);
     }
     public SearchWordState(State toConsume) {
-      super(Token.WORD, toConsume);
+      super(Token.WORD, toConsume.getLiteral());
     }
 
     @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);
+        return new CloseState();
       } else if (isWhitespace(c)) {
         finish();
-        return new RwsState(c);
+        return new RwsState();
       } else if (isEof(c)) {
         return finish();
       }
@@ -230,7 +227,7 @@ public class SearchTokenizer {
     }
   }
 
-  private class SearchPhraseState extends State {
+  private class SearchPhraseState extends LiteralState {
     public SearchPhraseState(char c) {
       super(Token.PHRASE, c);
       if(c != '\'') {
@@ -249,7 +246,7 @@ public class SearchTokenizer {
         return allowed(c);
       } else if (isWhitespace(c)) {
         if(isFinished()) {
-          return new RwsState(c);
+          return new RwsState();
         }
         return allowed(c);
       } else if (isEof(c)) {
@@ -260,8 +257,8 @@ public class SearchTokenizer {
   }
 
   private class OpenState extends State {
-    public OpenState(char c) {
-      super(Token.OPEN, c);
+    public OpenState() {
+      super(Token.OPEN);
       finish();
     }
     @Override
@@ -275,8 +272,8 @@ public class SearchTokenizer {
   }
 
   private class CloseState extends State {
-    public CloseState(char c) {
-      super(Token.CLOSE, c);
+    public CloseState() {
+      super(Token.CLOSE);
       finish();
     }
 
@@ -290,7 +287,7 @@ public class SearchTokenizer {
     }
   }
 
-  private class NotState extends State {
+  private class NotState extends LiteralState {
     public NotState(char c) {
       super(Token.NOT, c);
     }
@@ -302,14 +299,14 @@ public class SearchTokenizer {
         return allowed(c);
       } else if(getLiteral().length() == 3 && isWhitespace(c)) {
         finish();
-        return new RwsState(c);
+        return new RwsState();
       } else {
         return new SearchWordState(this);
       }
     }
   }
 
-  private class AndState extends State {
+  private class AndState extends LiteralState {
     public AndState(char c) {
       super(Token.AND, c);
       if(c != 'a' && c != 'A') {
@@ -324,14 +321,14 @@ public class SearchTokenizer {
         return allowed(c);
       } else if(getLiteral().length() == 3 && isWhitespace(c)) {
         finish();
-        return new RwsState(c);
+        return new RwsState();
       } else {
         return new SearchWordState(this);
       }
     }
   }
 
-  private class OrState extends State {
+  private class OrState extends LiteralState {
     public OrState(char c) {
       super(Token.OR, c);
       if(c != 'o' && c != 'O') {
@@ -344,7 +341,7 @@ public class SearchTokenizer {
         return allowed(c);
       } else if(getLiteral().length() == 2 && isWhitespace(c)) {
         finish();
-        return new RwsState(c);
+        return new RwsState();
       } else {
         return new SearchWordState(this);
       }
@@ -353,8 +350,8 @@ public class SearchTokenizer {
 
 
   private class RwsState extends State {
-    public RwsState(char c) {
-      super(Token.RWS, c);
+    public RwsState() {
+      super(Token.RWS);
     }
     @Override
     public State nextChar(char c) {
@@ -377,14 +374,14 @@ public class SearchTokenizer {
     State state = new SearchExpressionState();
     List<SearchQueryToken> states = new ArrayList<SearchQueryToken>();
     for (char aChar : chars) {
-      State next = state.next(aChar);
+      State next = state.nextChar(aChar);
       if (state.isFinished() && next != state) {
         states.add(state);
       }
       state = next;
     }
 
-    if(state.next(State.EOF).isFinished()) {
+    if(state.nextChar(State.EOF).isFinished()) {
       states.add(state);
     } else {
       throw new IllegalStateException("State: " + state + " not finished and list is: " + states.toString());
@@ -392,5 +389,4 @@ public class SearchTokenizer {
 
     return states;
   }
-
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/81fcbeaf/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java
index 15d35df..24d782f 100644
--- a/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java
+++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java
@@ -220,7 +220,7 @@ public class SearchTokenizerTest {
         .validate();
 
 
-    SearchValidator.init("(foo OR that) AND (bar OR baz)").enableLogging()
+    SearchValidator.init("(foo OR that) AND (bar OR baz)")
         .addExpected(OPEN)
         .addExpected(WORD, "foo").addExpected(OR).addExpected(WORD, "that")
         .addExpected(CLOSE).addExpected(AND).addExpected(OPEN)


[09/23] olingo-odata4 git commit: [OLINGO-568] Added SearchTokenizerException

Posted by mi...@apache.org.
[OLINGO-568] Added SearchTokenizerException


Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo
Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/3eef0bf6
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/3eef0bf6
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/3eef0bf6

Branch: refs/heads/master
Commit: 3eef0bf605b4eff6c724f15e661d9c9c44964ddc
Parents: f64abe1
Author: mibo <mi...@apache.org>
Authored: Tue Nov 10 20:10:02 2015 +0100
Committer: mibo <mi...@apache.org>
Committed: Tue Nov 10 20:10:02 2015 +0100

----------------------------------------------------------------------
 .../core/uri/parser/search/SearchParser.java    |  6 +-
 .../core/uri/parser/search/SearchTokenizer.java | 63 ++++++++++----------
 .../parser/search/SearchTokenizerException.java | 25 ++++++++
 .../uri/parser/search/SearchTokenizerTest.java  | 31 ++++++----
 4 files changed, 82 insertions(+), 43 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/3eef0bf6/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 d508932..ca45037 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
@@ -27,7 +27,11 @@ public class SearchParser {
 
   public SearchOption parse(String path, String value) {
     SearchTokenizer tokenizer = new SearchTokenizer();
-    List<SearchQueryToken> tokens = tokenizer.tokenize(value);
+    try {
+      List<SearchQueryToken> tokens = tokenizer.tokenize(value);
+    } catch (SearchTokenizerException e) {
+      return null;
+    }
     return new SearchOptionImpl();
   }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/3eef0bf6/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 f393d22..1ec4df1 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
@@ -58,14 +58,14 @@ public class SearchTokenizer {
       token = t;
     }
 
-    protected abstract State nextChar(char c);
+    protected abstract State nextChar(char c) throws SearchTokenizerException;
 
     public State allowed(char c) {
       return this;
     }
 
-    public State forbidden(char c) {
-      throw new IllegalStateException(this.getClass().getName() + "->" + c);
+    public State forbidden(char c) throws SearchTokenizerException {
+      throw new SearchTokenizerException("Forbidden character for " + this.getClass().getName() + "->" + c);
     }
 
     public State finish() {
@@ -105,7 +105,7 @@ public class SearchTokenizer {
      * @return true if character is allowed for a phrase
      */
     static boolean isAllowedPhrase(final char character) {
-      // FIXME mibo: check missing and '\''
+      // FIXME mibo: check missing
       return isAllowedChar(character)
           || character == '-'
           || character == '.'
@@ -115,6 +115,7 @@ public class SearchTokenizer {
           || character == '@'
           || character == '/'
           || character == '$'
+          || character == '\''
           || character == '=';
     }
 
@@ -142,7 +143,7 @@ public class SearchTokenizer {
     public LiteralState(Token t) {
       super(t);
     }
-    public LiteralState(Token t, char c) {
+    public LiteralState(Token t, char c) throws SearchTokenizerException {
       super(t);
       init(c);
     }
@@ -159,9 +160,9 @@ public class SearchTokenizer {
       return literal.toString();
     }
 
-    public State init(char c) {
+    public State init(char c) throws SearchTokenizerException {
       if(isFinished()) {
-        throw new IllegalStateException(toString() + " is already finished.");
+        throw new SearchTokenizerException(toString() + " is already finished.");
       }
       literal.append(c);
       return this;
@@ -176,7 +177,7 @@ public class SearchTokenizer {
       super(null, initLiteral);
     }
     @Override
-    public State nextChar(char c) {
+    public State nextChar(char c) throws SearchTokenizerException {
       if (c == CHAR_OPEN) {
         return new OpenState();
       } else if (isWhitespace(c)) {
@@ -189,7 +190,7 @@ public class SearchTokenizer {
     }
 
     @Override
-    public State init(char c) {
+    public State init(char c) throws SearchTokenizerException {
       return nextChar(c);
     }
   }
@@ -199,7 +200,7 @@ public class SearchTokenizer {
       super(Token.TERM);
     }
     @Override
-    public State nextChar(char c) {
+    public State nextChar(char c) throws SearchTokenizerException {
       if(c == CHAR_N) {
         return new NotState(c);
       } else if (c == QUOTATION_MARK) {
@@ -207,16 +208,16 @@ public class SearchTokenizer {
       } else if (isAllowedChar(c)) {
         return new SearchWordState(c);
       }
-      throw new IllegalStateException(this.getClass().getName() + "->" + c);
+      return forbidden(c);
     }
     @Override
-    public State init(char c) {
+    public State init(char c) throws SearchTokenizerException {
       return nextChar(c);
     }
   }
 
   private class SearchWordState extends LiteralState {
-    public SearchWordState(char c) {
+    public SearchWordState(char c) throws SearchTokenizerException {
       super(Token.WORD, c);
     }
     public SearchWordState(State toConsume) {
@@ -224,7 +225,7 @@ public class SearchTokenizer {
     }
 
     @Override
-    public State nextChar(char c) {
+    public State nextChar(char c) throws SearchTokenizerException {
       if (isAllowedChar(c)) {
         return allowed(c);
       } else if (c == CHAR_CLOSE) {
@@ -244,7 +245,7 @@ public class SearchTokenizer {
   }
 
   private class SearchPhraseState extends LiteralState {
-    public SearchPhraseState(char c) {
+    public SearchPhraseState(char c) throws SearchTokenizerException {
       super(Token.PHRASE, c);
       if(c != QUOTATION_MARK) {
         forbidden(c);
@@ -252,19 +253,17 @@ public class SearchTokenizer {
     }
 
     @Override
-    public State nextChar(char c) {
-      if(isFinished()) {
-        return new SearchExpressionState().init(c);
-      } else if (isAllowedPhrase(c)) {
+    public State nextChar(char c) throws SearchTokenizerException {
+      if (isAllowedPhrase(c)) {
+        return allowed(c);
+      } else if (isWhitespace(c)) {
         return allowed(c);
       } else if (c == QUOTATION_MARK) {
         finish();
         allowed(c);
         return new SearchExpressionState();
-      } else if (isWhitespace(c)) {
-        return allowed(c);
-      } else if (c == CHAR_CLOSE) {
-        return allowed(c);
+      } else if(isFinished()) {
+        return new SearchExpressionState().init(c);
       }
       return forbidden(c);
     }
@@ -276,7 +275,7 @@ public class SearchTokenizer {
       finish();
     }
     @Override
-    public State nextChar(char c) {
+    public State nextChar(char c) throws SearchTokenizerException {
       finish();
       if (isWhitespace(c)) {
         return forbidden(c);
@@ -292,13 +291,13 @@ public class SearchTokenizer {
     }
 
     @Override
-    public State nextChar(char c) {
+    public State nextChar(char c) throws SearchTokenizerException {
       return new SearchExpressionState().init(c);
     }
   }
 
   private class NotState extends LiteralState {
-    public NotState(char c) {
+    public NotState(char c) throws SearchTokenizerException {
       super(Token.NOT, c);
       if(c != CHAR_N) {
         forbidden(c);
@@ -306,11 +305,11 @@ public class SearchTokenizer {
     }
     @Override
     public State nextChar(char c) {
-      if (getLiteral().length() == 1 && c == CHAR_O) {
+      if (literal.length() == 1 && c == CHAR_O) {
         return allowed(c);
-      } else if (getLiteral().length() == 2 && c == CHAR_T) {
+      } else if (literal.length() == 2 && c == CHAR_T) {
         return allowed(c);
-      } else if(getLiteral().length() == 3 && isWhitespace(c)) {
+      } else if(literal.length() == 3 && isWhitespace(c)) {
         finish();
         return new BeforeSearchExpressionRwsState();
       } else {
@@ -326,7 +325,7 @@ public class SearchTokenizer {
       super(null);
     }
     @Override
-    public State nextChar(char c) {
+    public State nextChar(char c) throws SearchTokenizerException {
       if (isWhitespace(c)) {
         return allowed(c);
       } else {
@@ -342,7 +341,7 @@ public class SearchTokenizer {
       super(null);
     }
     @Override
-    public State nextChar(char c) {
+    public State nextChar(char c) throws SearchTokenizerException {
       if (!noneRws && isWhitespace(c)) {
         return allowed(c);
       } else if (c == CHAR_O) {
@@ -374,7 +373,7 @@ public class SearchTokenizer {
   }
 
   // TODO (mibo): add (new) parse exception
-  public List<SearchQueryToken> tokenize(String searchQuery) {
+  public List<SearchQueryToken> tokenize(String searchQuery) throws SearchTokenizerException {
     char[] chars = searchQuery.toCharArray();
 
     State state = new SearchExpressionState();

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/3eef0bf6/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
new file mode 100644
index 0000000..dd6f71e
--- /dev/null
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerException.java
@@ -0,0 +1,25 @@
+/*
+ * 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 class SearchTokenizerException extends Exception {
+  public SearchTokenizerException(String message) {
+    super(message);
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/3eef0bf6/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java
index 548d3fd..ea3cab9 100644
--- a/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java
+++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java
@@ -150,7 +150,7 @@ public class SearchTokenizerTest {
   }
 
   @Test
-  public void parseImplicitAnd() {
+  public void parseImplicitAnd() throws SearchTokenizerException {
     SearchValidator.init("a b").addExpected(WORD, AND, WORD).validate();
     SearchValidator.init("a b OR c").addExpected(WORD, AND, WORD, OR, WORD).validate();
     SearchValidator.init("a bc OR c").addExpected(WORD, AND, WORD, OR, WORD).validate();
@@ -305,7 +305,7 @@ public class SearchTokenizerTest {
   }
 
   @Test
-  public void moreMixedTests() {
+  public void moreMixedTests() throws SearchTokenizerException {
     validate("abc");
     validate("NOT abc");
 
@@ -340,20 +340,23 @@ public class SearchTokenizerTest {
   }
 
   @Test
-  public void parseInvalid() {
+  public void parseInvalid() throws SearchTokenizerException {
     SearchValidator.init("abc AND OR something").validate();
+    SearchValidator.init("abc AND \"something\" )").validate();
+    //
+    SearchValidator.init("(  abc AND) OR something").validate(SearchTokenizerException.class);
   }
 
-  public boolean validate(String query) {
-    return new SearchValidator(query).validate();
+  public void validate(String query) throws SearchTokenizerException {
+    new SearchValidator(query).validate();
   }
 
-  public boolean validate(String query, SearchQueryToken.Token ... tokens) {
+  public void validate(String query, SearchQueryToken.Token ... tokens) throws SearchTokenizerException {
     SearchValidator sv = new SearchValidator(query);
     for (SearchQueryToken.Token token : tokens) {
       sv.addExpected(token);
     }
-    return sv.validate();
+    sv.validate();
   }
 
   private static class SearchValidator {
@@ -393,7 +396,17 @@ public class SearchTokenizerTest {
       }
       return this;
     }
-    private boolean validate() {
+    private void validate(Class<? extends Exception> 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() throws SearchTokenizerException {
       SearchTokenizer tokenizer = new SearchTokenizer();
       List<SearchQueryToken> result = tokenizer.tokenize(searchQuery);
       Assert.assertNotNull(result);
@@ -412,8 +425,6 @@ public class SearchTokenizerTest {
           }
         }
       }
-
-      return true;
     }
   }
 


[06/23] olingo-odata4 git commit: [OLINGO-568] Improved states

Posted by mi...@apache.org.
[OLINGO-568] Improved states


Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo
Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/43bc49f9
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/43bc49f9
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/43bc49f9

Branch: refs/heads/master
Commit: 43bc49f9631189d1ac49ee5c7ff0875da83b3d73
Parents: b3bbfa6
Author: Michael Bolz <mi...@sap.com>
Authored: Mon Nov 9 15:39:02 2015 +0100
Committer: Michael Bolz <mi...@sap.com>
Committed: Mon Nov 9 15:39:02 2015 +0100

----------------------------------------------------------------------
 .../core/uri/parser/search/SearchTokenizer.java | 68 +++++++++-----------
 .../uri/parser/search/SearchTokenizerTest.java  | 13 +++-
 2 files changed, 40 insertions(+), 41 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/43bc49f9/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 67d4655..f62e0f4 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
@@ -82,6 +82,10 @@ public class SearchTokenizer {
       return token;
     }
 
+    public boolean close() {
+      return nextChar(EOF).isFinished();
+    }
+
     static boolean isAllowedChar(final char character) {
       // TODO mibo: add missing allowed characters
       return CHAR_A <= character && character <= 'Z' // case A..Z
@@ -173,11 +177,9 @@ public class SearchTokenizer {
       if (c == CHAR_OPEN) {
         return new OpenState();
       } else if (isWhitespace(c)) {
-        return new RwsState();
+        return new RwsImplicitAndState();
       } else if(c == CHAR_CLOSE) {
         return new CloseState();
-      } else if(isEof(c)) {
-        return finish();
       } else if(isWhitespace(c)) {
         return new AndState(c);
       } else {
@@ -186,6 +188,11 @@ public class SearchTokenizer {
     }
 
     @Override
+    public boolean close() {
+      return true;
+    }
+
+    @Override
     public State init(char c) {
       return nextChar(c);
     }
@@ -229,7 +236,7 @@ public class SearchTokenizer {
         return new CloseState();
       } else if (isWhitespace(c)) {
         finish();
-        return new RwsState();
+        return new RwsImplicitAndState();
       } else if (isEof(c)) {
         return finish();
       }
@@ -253,10 +260,16 @@ public class SearchTokenizer {
         return allowed(c);
       } else if (c == QUOTATION_MARK) {
         finish();
-        return allowed(c);
+        allowed(c);
+        return new SearchExpressionState();
       } else if (isWhitespace(c)) {
         if(isFinished()) {
-          return new RwsState();
+          return new RwsImplicitAndState();
+        }
+        return allowed(c);
+      } else if (c == CHAR_CLOSE) {
+        if(isFinished()) {
+          return new CloseState();
         }
         return allowed(c);
       } else if (isEof(c)) {
@@ -319,22 +332,6 @@ public class SearchTokenizer {
     }
   }
 
-  private class ImplicitAndState extends LiteralState {
-    private State followingState;
-    public ImplicitAndState(char c) {
-      super(Token.AND);
-      finish();
-      followingState = new SearchExpressionState().init(c);
-    }
-    public State nextState() {
-      return followingState;
-    }
-    @Override
-    public State nextChar(char c) {
-      return followingState.nextChar(c);
-    }
-  }
-
   private class AndState extends LiteralState {
     public AndState(char c) {
       super(Token.AND, c);
@@ -393,9 +390,10 @@ public class SearchTokenizer {
     }
   }
 
-  private class RwsState extends State {
-    public RwsState() {
-      super(Token.RWS);
+  // implicit and
+  private class RwsImplicitAndState extends State {
+    public RwsImplicitAndState() {
+      super(Token.AND);
     }
     @Override
     public State nextChar(char c) {
@@ -406,7 +404,8 @@ public class SearchTokenizer {
       } else if (c == CHAR_A) {
         return new AndState(c);
       } else {
-        return new ImplicitAndState(c);
+        finish();
+        return new SearchExpressionState().init(c);
       }
     }
   }
@@ -417,25 +416,18 @@ public class SearchTokenizer {
 
     State state = new SearchExpressionState();
     List<SearchQueryToken> states = new ArrayList<SearchQueryToken>();
-    State lastAdded = null;
     for (char aChar : chars) {
       State next = state.nextChar(aChar);
-      if (next instanceof ImplicitAndState) {
-        lastAdded = next;
-        states.add(next);
-        next = ((ImplicitAndState)next).nextState();
-      } else if (state.isFinished() && state != lastAdded) {
-        lastAdded = state;
+      if (state.isFinished()) {
         states.add(state);
       }
       state = next;
     }
 
-    final State lastState = state.nextChar(State.EOF);
-    if(lastState.isFinished()) {
-      if(state != lastAdded) {
-        states.add(state);
-      }
+    if(state.close()) {
+     if(state.isFinished()) {
+       states.add(state);
+     }
     } else {
       throw new IllegalStateException("State: " + state + " not finished and list is: " + states.toString());
     }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/43bc49f9/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java
index 9044282..e53b6b2 100644
--- a/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java
+++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java
@@ -86,6 +86,8 @@ public class SearchTokenizerTest {
     SearchTokenizer tokenizer = new SearchTokenizer();
     List<SearchQueryToken> result;
 
+    SearchValidator.init("abc AND \"x-y_z\" AND 123").validate();
+
     //
     result = tokenizer.tokenize("\"abc\"");
     Assert.assertNotNull(result);
@@ -325,12 +327,17 @@ public class SearchTokenizerTest {
     // 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");
     validate("abc AND (def    OR  ghi)");
     validate("abc AND (def        ghi)");
   }
-  
+
+  @Test
+  public void parseInvalid() {
+    SearchValidator.init("abc AND OR something").validate();
+  }
+
   public boolean validate(String query) {
     return new SearchValidator(query).validate();
   }


[16/23] olingo-odata4 git commit: [OLINGO-568] SearchParser negative tests

Posted by mi...@apache.org.
[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/master
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;


[07/23] olingo-odata4 git commit: [OLINGO-568] Removed not necessary code and enums

Posted by mi...@apache.org.
[OLINGO-568] Removed not necessary code and enums


Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo
Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/96483ae5
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/96483ae5
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/96483ae5

Branch: refs/heads/master
Commit: 96483ae559ea8d9dc0624b68876a4045102ba659
Parents: 43bc49f
Author: mibo <mi...@apache.org>
Authored: Mon Nov 9 20:33:05 2015 +0100
Committer: mibo <mi...@apache.org>
Committed: Mon Nov 9 20:33:05 2015 +0100

----------------------------------------------------------------------
 .../uri/parser/search/SearchQueryToken.java     |  2 +-
 .../core/uri/parser/search/SearchTokenizer.java | 61 +++++++-------------
 .../uri/parser/search/SearchTokenizerTest.java  | 21 ++++---
 3 files changed, 36 insertions(+), 48 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/96483ae5/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchQueryToken.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchQueryToken.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchQueryToken.java
index a08c1a2..eb1a009 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchQueryToken.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchQueryToken.java
@@ -19,7 +19,7 @@
 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}
+  enum Token {OPEN, TERM, NOT, AND, OR, WORD, PHRASE, CLOSE}
 
   Token getToken();
   String getLiteral();

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/96483ae5/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 f62e0f4..12af609 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
@@ -44,7 +44,6 @@ public class SearchTokenizer {
     private Token token = null;
     private boolean finished = false;
 
-    protected static final char EOF = 0x03;
     protected static final char QUOTATION_MARK = '\"';
     protected static final char CHAR_N = 'N';
     protected static final char CHAR_O = 'O';
@@ -82,8 +81,8 @@ public class SearchTokenizer {
       return token;
     }
 
-    public boolean close() {
-      return nextChar(EOF).isFinished();
+    public State close() {
+      return this;
     }
 
     static boolean isAllowedChar(final char character) {
@@ -114,10 +113,6 @@ public class SearchTokenizer {
           || character == '=';
     }
 
-    static boolean isEof(final char character) {
-      return character == EOF;
-    }
-
     //BWS =  *( SP / HTAB / "%20" / "%09" )  ; "bad" whitespace
     //RWS = 1*( SP / HTAB / "%20" / "%09" )  ; "required" whitespace
     static boolean isWhitespace(final char character) {
@@ -138,7 +133,7 @@ public class SearchTokenizer {
   }
 
   private static abstract class LiteralState extends State {
-    private final StringBuilder literal = new StringBuilder();
+    protected final StringBuilder literal = new StringBuilder();
     public LiteralState(Token t) {
       super(t);
     }
@@ -170,7 +165,7 @@ public class SearchTokenizer {
 
   private class SearchExpressionState extends LiteralState {
     public SearchExpressionState() {
-      super(Token.SEARCH_EXPRESSION);
+      super(null);
     }
     @Override
     public State nextChar(char c) {
@@ -180,19 +175,12 @@ public class SearchTokenizer {
         return new RwsImplicitAndState();
       } else if(c == CHAR_CLOSE) {
         return new CloseState();
-      } else if(isWhitespace(c)) {
-        return new AndState(c);
       } else {
         return new SearchTermState().init(c);
       }
     }
 
     @Override
-    public boolean close() {
-      return true;
-    }
-
-    @Override
     public State init(char c) {
       return nextChar(c);
     }
@@ -237,11 +225,14 @@ public class SearchTokenizer {
       } else if (isWhitespace(c)) {
         finish();
         return new RwsImplicitAndState();
-      } else if (isEof(c)) {
-        return finish();
       }
       return forbidden(c);
     }
+
+    @Override
+    public State close() {
+      return finish();
+    }
   }
 
   private class SearchPhraseState extends LiteralState {
@@ -254,7 +245,7 @@ public class SearchTokenizer {
 
     @Override
     public State nextChar(char c) {
-      if(isFinished() && !isEof(c)) {
+      if(isFinished()) {
         return new SearchExpressionState().init(c);
       } else if (isAllowedPhrase(c)) {
         return allowed(c);
@@ -272,8 +263,6 @@ public class SearchTokenizer {
           return new CloseState();
         }
         return allowed(c);
-      } else if (isEof(c)) {
-        return finish();
       }
       return forbidden(c);
     }
@@ -302,11 +291,7 @@ public class SearchTokenizer {
 
     @Override
     public State nextChar(char c) {
-      if (isEof(c)) {
-        return finish();
-      } else {
-        return new SearchExpressionState().init(c);
-      }
+      return new SearchExpressionState().init(c);
     }
   }
 
@@ -319,9 +304,9 @@ public class SearchTokenizer {
     }
     @Override
     public State nextChar(char c) {
-      if (getLiteral().length() == 1 && (c == CHAR_O)) {
+      if (getLiteral().length() == 1 && c == CHAR_O) {
         return allowed(c);
-      } else if (getLiteral().length() == 2 && (c == CHAR_T)) {
+      } else if (getLiteral().length() == 2 && c == CHAR_T) {
         return allowed(c);
       } else if(getLiteral().length() == 3 && isWhitespace(c)) {
         finish();
@@ -341,11 +326,11 @@ public class SearchTokenizer {
     }
     @Override
     public State nextChar(char c) {
-      if (getLiteral().length() == 1 && (c == CHAR_N)) {
+      if (literal.length() == 1 && c == CHAR_N) {
         return allowed(c);
-      } else if (getLiteral().length() == 2 && (c == CHAR_D)) {
+      } else if (literal.length() == 2 && c == CHAR_D) {
         return allowed(c);
-      } else if(getLiteral().length() == 3 && isWhitespace(c)) {
+      } else if(literal.length() == 3 && isWhitespace(c)) {
         finish();
         return new BeforeSearchExpressionRwsState();
       } else {
@@ -363,9 +348,9 @@ public class SearchTokenizer {
     }
     @Override
     public State nextChar(char c) {
-      if (getLiteral().length() == 1 && (c == CHAR_R)) {
+      if (literal.length() == 1 && (c == CHAR_R)) {
         return allowed(c);
-      } else if(getLiteral().length() == 2 && isWhitespace(c)) {
+      } else if(literal.length() == 2 && isWhitespace(c)) {
         finish();
         return new BeforeSearchExpressionRwsState();
       } else {
@@ -378,7 +363,7 @@ public class SearchTokenizer {
   // RWS [ 'AND' RWS ] searchExpr
   private class BeforeSearchExpressionRwsState extends State {
     public BeforeSearchExpressionRwsState() {
-      super(Token.RWS);
+      super(null);
     }
     @Override
     public State nextChar(char c) {
@@ -424,12 +409,8 @@ public class SearchTokenizer {
       state = next;
     }
 
-    if(state.close()) {
-     if(state.isFinished()) {
-       states.add(state);
-     }
-    } else {
-      throw new IllegalStateException("State: " + state + " not finished and list is: " + states.toString());
+    if(state.close().isFinished()) {
+      states.add(state);
     }
 
     return states;

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/96483ae5/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java
index e53b6b2..29287cd 100644
--- a/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java
+++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/search/SearchTokenizerTest.java
@@ -287,15 +287,22 @@ public class SearchTokenizerTest {
     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();
+
+    // FIXME (mibo): issue with implicit and
+//    SearchValidator.init("abc ANDsomething").enableLogging()
+//        .addExpected(WORD, AND, WORD).validate();
+
+//    SearchValidator.init("abc ORsomething")
+//        .addExpected(WORD, AND, WORD).validate();
+
+    SearchValidator.init("abc OR orsomething")
+        .addExpected(WORD, OR, WORD).validate();
+
+    SearchValidator.init("abc OR ORsomething")
+        .addExpected(WORD, OR, WORD).validate();
+
   }
 
   @Test


[10/23] olingo-odata4 git commit: [OLINGO-568] Added Search*Impl classes

Posted by mi...@apache.org.
[OLINGO-568] Added Search*Impl classes


Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo
Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/37c5827f
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/37c5827f
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/37c5827f

Branch: refs/heads/master
Commit: 37c5827f9343bfc7554d3bb63bdcf96c14da0cd9
Parents: 3eef0bf
Author: Michael Bolz <mi...@sap.com>
Authored: Wed Nov 11 12:48:09 2015 +0100
Committer: Michael Bolz <mi...@sap.com>
Committed: Wed Nov 11 12:48:09 2015 +0100

----------------------------------------------------------------------
 .../api/uri/queryoption/search/SearchUnary.java |   5 +-
 .../uri/parser/search/SearchBinaryImpl.java     |  56 ++++++++
 .../core/uri/parser/search/SearchParser.java    |  93 +++++++++++-
 .../core/uri/parser/search/SearchTermImpl.java  |  39 +++++
 .../core/uri/parser/search/SearchUnaryImpl.java |  46 ++++++
 .../uri/parser/search/SearchParserTest.java     | 143 ++++++++++++++++++-
 6 files changed, 373 insertions(+), 9 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/37c5827f/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/search/SearchUnary.java
----------------------------------------------------------------------
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 5d8e143..c266308 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();
 
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/37c5827f/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchBinaryImpl.java
----------------------------------------------------------------------
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 0000000..37b03c0
--- /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 + '}';
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/37c5827f/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 ca45037..60fcd4b 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<SearchQueryToken> tokens;
+  private SearchExpression root;
+//  private SearchQueryToken currentToken;
+
   public SearchOption parse(String path, String value) {
     SearchTokenizer tokenizer = new SearchTokenizer();
     try {
-      List<SearchQueryToken> 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();
   }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/37c5827f/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchTermImpl.java
----------------------------------------------------------------------
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 0000000..24b1291
--- /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 + "'}";
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/37c5827f/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/search/SearchUnaryImpl.java
----------------------------------------------------------------------
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 0000000..639540d
--- /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 + '}';
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/37c5827f/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 0c51ba6..fa42293 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<? extends Exception> 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