Repository: zeppelin
Updated Branches:
  refs/heads/branch-0.8 3fd2fec0d -> e27e8d4ff


http://git-wip-us.apache.org/repos/asf/zeppelin/blob/e27e8d4f/sap/src/main/java/org/apache/zeppelin/sap/universe/UniverseUtil.java
----------------------------------------------------------------------
diff --git 
a/sap/src/main/java/org/apache/zeppelin/sap/universe/UniverseUtil.java 
b/sap/src/main/java/org/apache/zeppelin/sap/universe/UniverseUtil.java
new file mode 100644
index 0000000..dc9099d
--- /dev/null
+++ b/sap/src/main/java/org/apache/zeppelin/sap/universe/UniverseUtil.java
@@ -0,0 +1,643 @@
+/*
+ * 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.zeppelin.sap.universe;
+
+import org.apache.commons.lang.StringUtils;
+
+import java.util.*;
+
+/**
+ * Util class for convert request from Zeppelin to SAP
+ */
+public class UniverseUtil {
+
+  private static final String COMPRASION_START_TEMPLATE = "<comparisonFilter 
path=\"%s\" " +
+      "operator=\"%s\" id=\"%s\">\n";
+  private static final String COMPRASION_END_TEMPLATE = 
"</comparisonFilter>\n";
+  private static final String COMPARISON_FILTER = "<comparisonFilter id=\"%s\" 
path=\"%s\" " +
+      "operator=\"%s\"/>\n";
+  private static final String CONST_OPERAND_START_TEMPLATE = 
"<constantOperand>\n";
+  private static final String CONST_OPERAND_END_TEMPLATE = 
"</constantOperand>\n";
+  private static final String CONST_OPERAND_VALUE_TEMPLATE = "<value>\n" +
+      "<caption type=\"%s\">%s</caption>\n</value>\n";
+  private static final String PREDEFINED_FILTER_TEMPLATE = "<predefinedFilter 
path=\"%s\"" +
+      " id=\"%s\"/>\n";
+  private static final String OBJECT_OPERAND_TEMPLATE = "<objectOperand 
id=\"%s\" path=\"%s\"/>\n";
+  private static final String RESULT_START_TEMPLATE = "<resultObjects>\n";
+  private static final String RESULT_END_TEMPLATE = "</resultObjects>\n";
+  private static final String RESULT_OBJ_TEMPLATE = "<resultObject path=\"%s\" 
id=\"%s\"/>\n";
+
+  private static final String MARKER_EQUAL = "#EqualTo#";
+  private static final String MARKER_LESS_EQUAL = "#LessThanOrEqualTo#";
+  private static final String MARKER_NOT_EQUAL = "#NotEqualTo#";
+  private static final String MARKER_LESS = "#LessThan#";
+  private static final String MARKER_GREATER_EQUALS = "#GreaterThanOrEqualTo#";
+  private static final String MARKER_GREATER = "#GreaterThan#";
+  private static final String MARKER_IN = "#InList#";
+  private static final String MARKER_NOT_IN = "#NotInList#";
+  private static final String MARKER_NULL = "#IsNull#";
+  private static final String MARKER_NOT_NULL = "#IsNotNull#";
+  private static final String MARKER_FILTER = "#filter#";
+  private static final String MARKER_AND = "#and#";
+  private static final String MARKER_OR = "#or#";
+  private static final String MARKER_BACKSPACE = "#backspace#";
+  private static final String MARKER_LEFT_BRACE = "#left_brace#";
+  private static final String MARKER_RIGHT_BRACE = "#right_brace#";
+
+
+  private static final String LEFT_BRACE = "(";
+  private static final String RIGHT_BRACE = ")";
+
+  public static final Map<String, Integer> OPERATIONS;
+
+  static {
+    OPERATIONS = new HashMap<>();
+    OPERATIONS.put(MARKER_EQUAL, 1);
+    OPERATIONS.put(MARKER_LESS_EQUAL, 1);
+    OPERATIONS.put(MARKER_NOT_EQUAL, 1);
+    OPERATIONS.put(MARKER_LESS, 1);
+    OPERATIONS.put(MARKER_GREATER_EQUALS, 1);
+    OPERATIONS.put(MARKER_GREATER, 1);
+    OPERATIONS.put(MARKER_IN, 1);
+    OPERATIONS.put(MARKER_NOT_IN, 1);
+    OPERATIONS.put(MARKER_NULL, 1);
+    OPERATIONS.put(MARKER_NOT_NULL, 1);
+    OPERATIONS.put(MARKER_FILTER, 1);
+    OPERATIONS.put(MARKER_AND, 2);
+    OPERATIONS.put(MARKER_OR, 3);
+  }
+
+  public UniverseQuery convertQuery(String text, UniverseClient client, String 
token)
+      throws UniverseException {
+    StringBuilder select = new StringBuilder();
+    StringBuilder universe = new StringBuilder();
+    StringBuilder buf = new StringBuilder();
+    StringBuilder resultObj = new StringBuilder();
+    StringBuilder whereBuf = new StringBuilder();
+    UniverseInfo universeInfo = null;
+    String where = null;
+    boolean singleQuoteClosed = true;
+    boolean pathClosed = true;
+    boolean universePart = false;
+    boolean selectPart = false;
+    boolean wherePart = false;
+    boolean listOperator = false;
+    boolean operatorPosition = false;
+    Map<String, UniverseNodeInfo> nodeInfos = null;
+
+    char[] array = text.toCharArray();
+    for (int i = 0; i < array.length; i++) {
+      char c = array[i];
+      buf.append(c);
+      if (c == '\'') {
+        if (i == 0 || array[i - 1] != '\\') {
+          singleQuoteClosed = !singleQuoteClosed;
+        }
+      }
+      if (c == '[' && pathClosed && singleQuoteClosed) {
+        pathClosed = false;
+        if (wherePart) {
+          operatorPosition = false;
+        }
+      }
+      if (c == ']' && !pathClosed && singleQuoteClosed) {
+        pathClosed = true;
+        if (wherePart) {
+          operatorPosition = true;
+          if (i + 1 == array.length || (array[i + 1] != '.'
+              && isFilter(String.format("%s]", whereBuf.toString()), 
text.substring(i + 1)))) {
+            whereBuf.append(c);
+            whereBuf.append(MARKER_FILTER);
+            if (i + 1 == array.length) {
+              wherePart = false;
+              where = parseWhere(whereBuf.toString(), nodeInfos);
+            }
+            continue;
+          }
+        }
+      }
+      if (c == '(' && wherePart && pathClosed && singleQuoteClosed) {
+        if (listOperator) {
+          whereBuf.append(MARKER_LEFT_BRACE);
+          continue;
+        } else {
+          whereBuf.append(c);
+          continue;
+        }
+      }
+      if (c == ')' && wherePart && pathClosed && singleQuoteClosed) {
+        if (listOperator) {
+          whereBuf.append(MARKER_RIGHT_BRACE);
+          listOperator = false;
+          continue;
+        } else {
+          whereBuf.append(c);
+          continue;
+        }
+      }
+
+      if (!universePart && singleQuoteClosed
+          && buf.toString().toLowerCase().endsWith("universe")) {
+        universePart = true;
+        continue;
+      }
+
+      if (universePart) {
+        if (c == ';' && singleQuoteClosed) {
+          universePart = false;
+          if (universe.toString().trim().length() > 2) {
+            String universeName =
+                universe.toString().trim().substring(1, 
universe.toString().trim().length() - 1);
+            universeInfo = client.getUniverseInfo(universeName);
+            nodeInfos = client.getUniverseNodesInfo(token, universeName);
+          }
+        } else {
+          universe.append(c);
+        }
+        continue;
+      }
+
+      if (!selectPart && pathClosed && singleQuoteClosed
+          && buf.toString().toLowerCase().endsWith("select")) {
+        if (StringUtils.isBlank(universe.toString())) {
+          throw new UniverseException("Not found universe name");
+        }
+        selectPart = true;
+        select.append(RESULT_START_TEMPLATE);
+        continue;
+      }
+
+      if (!wherePart && pathClosed && singleQuoteClosed) {
+        if (buf.toString().toLowerCase().endsWith("where")) {
+          wherePart = true;
+        }
+        if (buf.toString().toLowerCase().endsWith("where") || i == 
array.length - 1) {
+          selectPart = false;
+          
select.append(parseResultObj(resultObj.toString().replaceAll("(?i)wher$", ""), 
nodeInfos));
+          select.append(RESULT_END_TEMPLATE);
+          continue;
+        }
+      }
+
+      if (selectPart) {
+        if (pathClosed && singleQuoteClosed && c == ',') {
+          select.append(parseResultObj(resultObj.toString(), nodeInfos));
+          resultObj = new StringBuilder();
+        } else {
+          resultObj.append(c);
+        }
+        continue;
+      }
+
+      if (wherePart) {
+        if (c == ';' && pathClosed && singleQuoteClosed) {
+          wherePart = false;
+          where = parseWhere(whereBuf.toString(), nodeInfos);
+        } else {
+          if (!singleQuoteClosed || !pathClosed) {
+            switch (c) {
+              case ' ':
+              case '\n':
+                whereBuf.append(MARKER_BACKSPACE);
+                break;
+              case '(':
+                whereBuf.append(MARKER_LEFT_BRACE);
+                break;
+              case ')':
+                whereBuf.append(MARKER_RIGHT_BRACE);
+                break;
+              default:
+                whereBuf.append(c);
+            }
+          } else if (pathClosed) {
+            if ((c == 'a' || c == 'A') && i < array.length - 2 &&
+                text.substring(i, i + 3).equalsIgnoreCase("and")) {
+              i += 2;
+              whereBuf.append(MARKER_AND);
+              operatorPosition = false;
+              continue;
+            }
+            if ((c == 'o' || c == 'O') && i < array.length - 1 &&
+                text.substring(i, i + 2).equalsIgnoreCase("or")) {
+              i += 1;
+              whereBuf.append(MARKER_OR);
+              operatorPosition = false;
+              continue;
+            }
+            if (operatorPosition) {
+              switch (c) {
+                case '=':
+                  whereBuf.append(MARKER_EQUAL);
+                  operatorPosition = false;
+                  break;
+                case '<':
+                  if (i + 1 < array.length) {
+                    if (array[i + 1] == '=') {
+                      whereBuf.append(MARKER_LESS_EQUAL);
+                      operatorPosition = false;
+                      i++;
+                      break;
+                    } else if (array[i + 1] == '>') {
+                      whereBuf.append(MARKER_NOT_EQUAL);
+                      operatorPosition = false;
+                      i++;
+                      break;
+                    }
+                  }
+                  operatorPosition = false;
+                  whereBuf.append(MARKER_LESS);
+                  break;
+                case '>':
+                  if (i + 1 < array.length) {
+                    if (array[i + 1] == '=') {
+                      whereBuf.append(MARKER_GREATER_EQUALS);
+                      operatorPosition = false;
+                      i++;
+                      break;
+                    }
+                  }
+                  operatorPosition = false;
+                  whereBuf.append(MARKER_GREATER);
+                  break;
+                case 'i':
+                case 'I':
+                  boolean whileI = true;
+                  StringBuilder operI = new StringBuilder();
+                  operI.append(c);
+                  while (whileI) {
+                    i++;
+                    if (i >= array.length) {
+                      whileI = false;
+                    }
+
+                    if (array[i] != ' ' && array[i] != '\n') {
+                      operI.append(array[i]);
+                    } else {
+                      continue;
+                    }
+                    String tmp = operI.toString().toLowerCase();
+                    if (tmp.equals("in")) {
+                      whereBuf.append(MARKER_IN);
+                      listOperator = true;
+                      whileI = false;
+                      operatorPosition = false;
+                    } else if (tmp.equals("isnull")) {
+                      whereBuf.append(MARKER_NULL);
+                      whileI = false;
+                      operatorPosition = false;
+                    } else if (tmp.equals("isnotnull")) {
+                      whereBuf.append(MARKER_NOT_NULL);
+                      whileI = false;
+                      operatorPosition = false;
+                    }
+                    // longest 9 - isnotnull
+                    if (tmp.length() > 8) {
+                      whileI = false;
+                    }
+                  }
+                  break;
+                case 'n':
+                case 'N':
+                  boolean whileN = true;
+                  StringBuilder operN = new StringBuilder();
+                  operN.append(c);
+                  while (whileN) {
+                    i++;
+                    if (i >= array.length) {
+                      whileN = false;
+                    }
+
+                    if (array[i] != ' ' && array[i] != '\n') {
+                      operN.append(array[i]);
+                    } else {
+                      continue;
+                    }
+
+                    String tmp = operN.toString().toLowerCase();
+
+                    if (tmp.equals("notin")) {
+                      whereBuf.append(MARKER_NOT_IN);
+                      listOperator = true;
+                      whileN = false;
+                      operatorPosition = false;
+                    }
+
+                    // longest 5 - notin
+                    if (tmp.length() > 4) {
+                      whileN = false;
+                    }
+                  }
+                  break;
+                default:
+                  whereBuf.append(c);
+              }
+            } else {
+              whereBuf.append(c);
+            }
+          } else {
+            whereBuf.append(c);
+          }
+        }
+      }
+    }
+
+    if (wherePart && StringUtils.isBlank(where)) {
+      throw new UniverseException("Incorrect block where");
+    }
+
+    UniverseQuery universeQuery = new UniverseQuery(select.toString().trim(),
+        where, universeInfo);
+
+    if (!universeQuery.isCorrect()) {
+      throw new UniverseException("Incorrect query");
+    }
+
+    return universeQuery;
+  }
+
+  private String parseWhere(String where, Map<String, UniverseNodeInfo> 
nodeInfos)
+      throws UniverseException {
+    List<String> out = new ArrayList<>();
+    Stack<String> stack = new Stack<>();
+
+    where = where.replaceAll("\\s*", "");
+
+    Set<String> operationSymbols = new HashSet<>(OPERATIONS.keySet());
+    operationSymbols.add(LEFT_BRACE);
+    operationSymbols.add(RIGHT_BRACE);
+
+    int index = 0;
+
+    boolean findNext = true;
+    while (findNext) {
+      int nextOperationIndex = where.length();
+      String nextOperation = "";
+      for (String operation : operationSymbols) {
+        int i = where.indexOf(operation, index);
+        if (i >= 0 && i < nextOperationIndex) {
+          nextOperation = operation;
+          nextOperationIndex = i;
+        }
+      }
+      if (nextOperationIndex == where.length()) {
+        findNext = false;
+      } else {
+        if (index != nextOperationIndex) {
+          out.add(where.substring(index, nextOperationIndex));
+        }
+        if (nextOperation.equals(LEFT_BRACE)) {
+          stack.push(nextOperation);
+        }
+        else if (nextOperation.equals(RIGHT_BRACE)) {
+          while (!stack.peek().equals(LEFT_BRACE)) {
+            out.add(stack.pop());
+            if (stack.empty()) {
+              throw new UniverseException("Unmatched brackets");
+            }
+          }
+          stack.pop();
+        }
+        else {
+          while (!stack.empty() && !stack.peek().equals(LEFT_BRACE) &&
+              (OPERATIONS.get(nextOperation) >= OPERATIONS.get(stack.peek()))) 
{
+            out.add(stack.pop());
+          }
+          stack.push(nextOperation);
+        }
+        index = nextOperationIndex + nextOperation.length();
+      }
+    }
+    if (index != where.length()) {
+      out.add(where.substring(index));
+    }
+    while (!stack.empty()) {
+      out.add(stack.pop());
+    }
+    StringBuffer result = new StringBuffer();
+    if (!out.isEmpty())
+      result.append(out.remove(0));
+    while (!out.isEmpty())
+      result.append(" ").append(out.remove(0));
+
+    // result contains the reverse polish notation
+    return convertWhereToXml(result.toString(), nodeInfos);
+  }
+
+  private String parseResultObj(String resultObj, Map<String, 
UniverseNodeInfo> nodeInfos)
+      throws UniverseException {
+    if (StringUtils.isNotBlank(resultObj)) {
+      UniverseNodeInfo nodeInfo = nodeInfos.get(resultObj.trim());
+      if (nodeInfo != null) {
+        return String.format(RESULT_OBJ_TEMPLATE, nodeInfo.getNodePath(), 
nodeInfo.getId());
+      }
+      throw new UniverseException(String.format("Not found information about: 
\"%s\"",
+          resultObj.trim()));
+    }
+
+    return StringUtils.EMPTY;
+  }
+
+  private String convertWhereToXml(String rpn, Map<String, UniverseNodeInfo> 
nodeInfos)
+      throws UniverseException {
+    StringTokenizer tokenizer = new StringTokenizer(rpn, " ");
+
+    Stack<String> stack = new Stack();
+
+    while (tokenizer.hasMoreTokens()) {
+      StringBuilder tmp = new StringBuilder();
+      String token = tokenizer.nextToken();
+      if (!OPERATIONS.keySet().contains(token)) {
+        stack.push(token.trim());
+      } else {
+        String rightOperand = revertReplace(stack.pop());
+        String operator = token.replaceAll("^#|#$", "");
+
+        if (token.equalsIgnoreCase(MARKER_NOT_NULL) || 
token.equalsIgnoreCase(MARKER_NULL)) {
+          UniverseNodeInfo rightOperandInfo = nodeInfos.get(rightOperand);
+          stack.push(String.format(COMPARISON_FILTER, rightOperandInfo.getId(),
+              rightOperandInfo.getNodePath(), operator));
+          continue;
+        }
+
+        if (token.equalsIgnoreCase(MARKER_FILTER)) {
+          UniverseNodeInfo rightOperandInfo = nodeInfos.get(rightOperand);
+          stack.push(String.format(PREDEFINED_FILTER_TEMPLATE, 
rightOperandInfo.getNodePath(),
+              rightOperandInfo.getId()));
+          continue;
+        }
+
+        String leftOperand = stack.empty() ? null : revertReplace(stack.pop());
+
+        if (token.equalsIgnoreCase(MARKER_AND) || 
token.equalsIgnoreCase(MARKER_OR)) {
+          if (rightOperand.matches("^\\[.*\\]$")) {
+            UniverseNodeInfo rightOperandInfo = nodeInfos.get(rightOperand);
+            if (rightOperandInfo == null) {
+              throw new UniverseException(String.format("Not found information 
about: \"%s\"",
+                  rightOperand));
+            }
+            rightOperand = String.format(PREDEFINED_FILTER_TEMPLATE,
+                rightOperandInfo.getNodePath(), rightOperandInfo.getId());
+          }
+          if (leftOperand.matches("^\\[.*\\]$")) {
+            UniverseNodeInfo leftOperandInfo = nodeInfos.get(leftOperand);
+            if (leftOperandInfo == null) {
+              throw new UniverseException(String.format("Not found information 
about: \"%s\"",
+                  leftOperand));
+            }
+            leftOperand = String.format(PREDEFINED_FILTER_TEMPLATE, 
leftOperandInfo.getNodePath(),
+                leftOperandInfo.getId());
+          }
+          tmp.append(String.format("<%s>\n", operator));
+          tmp.append(leftOperand);
+          tmp.append("\n");
+          tmp.append(rightOperand);
+          tmp.append("\n");
+          tmp.append(String.format("</%s>\n", operator));
+          stack.push(tmp.toString());
+          continue;
+        }
+
+        UniverseNodeInfo leftOperandInfo = nodeInfos.get(leftOperand);
+        if (leftOperandInfo == null) {
+          throw new UniverseException(String.format("Not found information 
about: \"%s\"",
+              leftOperand));
+        }
+        if (token.equalsIgnoreCase(MARKER_IN) || 
token.equalsIgnoreCase(MARKER_NOT_IN)) {
+          String listValues = rightOperand.replaceAll("^\\(|\\)$", "").trim();
+          boolean startItem = false;
+          List<String> values = new ArrayList<>();
+          StringBuilder value = new StringBuilder();
+          boolean isNumericList = false;
+          if (listValues.charAt(0) != '\'') {
+            isNumericList = true;
+          }
+          if (isNumericList) {
+            String[] nums = listValues.split(",");
+            for (String num : nums) {
+              values.add(num.trim());
+            }
+          } else {
+            for (int i = 0; i < listValues.length(); i++) {
+              char c = listValues.charAt(i);
+              if (c == '\'' && (i == 0 || listValues.charAt(i - 1) != '\\')) {
+                startItem = !startItem;
+                if (!startItem) {
+                  values.add(value.toString());
+                  value = new StringBuilder();
+                }
+                continue;
+              }
+              if (startItem) {
+                value.append(c);
+              }
+            }
+          }
+
+          if (!values.isEmpty()) {
+            tmp.append(String.format(COMPRASION_START_TEMPLATE, 
leftOperandInfo.getNodePath(),
+                operator, leftOperandInfo.getId()));
+            tmp.append(CONST_OPERAND_START_TEMPLATE);
+            String type = isNumericList ? "Numeric" : "String";
+            for (String v : values) {
+              tmp.append(String.format(CONST_OPERAND_VALUE_TEMPLATE, type, v));
+            }
+            tmp.append(CONST_OPERAND_END_TEMPLATE);
+            tmp.append(COMPRASION_END_TEMPLATE);
+            stack.push(tmp.toString());
+          }
+          continue;
+        }
+
+        // EqualTo, LessThanOrEqualTo, NotEqualTo, LessThan, 
GreaterThanOrEqualTo, GreaterThan
+        UniverseNodeInfo rightOperandInfo = null;
+        if (rightOperand.startsWith("[") && rightOperand.endsWith("]")) {
+          rightOperandInfo = nodeInfos.get(rightOperand);
+          if (rightOperandInfo == null) {
+            throw new UniverseException(String.format("Not found information 
about: \"%s\"",
+                rightOperand));
+          }
+        }
+        if (OPERATIONS.containsKey(token)) {
+          if (rightOperandInfo != null) {
+            tmp.append(String.format(COMPRASION_START_TEMPLATE, 
leftOperandInfo.getNodePath(),
+                operator, leftOperandInfo.getId()));
+            tmp.append(String.format(OBJECT_OPERAND_TEMPLATE, 
rightOperandInfo.getId(),
+                rightOperandInfo.getNodePath()));
+            tmp.append(COMPRASION_END_TEMPLATE);
+          } else {
+            String type = rightOperand.startsWith("'") ? "String" : "Numeric";
+            String value = rightOperand.replaceAll("^'|'$", "");
+            tmp.append(String.format(COMPRASION_START_TEMPLATE, 
leftOperandInfo.getNodePath(),
+                operator, leftOperandInfo.getId()));
+            tmp.append(CONST_OPERAND_START_TEMPLATE);
+            tmp.append(String.format(CONST_OPERAND_VALUE_TEMPLATE, type, 
value));
+            tmp.append(CONST_OPERAND_END_TEMPLATE);
+            tmp.append(COMPRASION_END_TEMPLATE);
+          }
+          stack.push(tmp.toString());
+          continue;
+        }
+        throw new UniverseException(String.format("Incorrect syntax after: 
\"%s\"", leftOperand));
+      }
+    }
+
+    return stack.pop();
+  }
+
+  private String revertReplace(String s) {
+    return s.replaceAll(MARKER_BACKSPACE, " ")
+        .replaceAll(MARKER_LEFT_BRACE, "(")
+        .replaceAll(MARKER_RIGHT_BRACE, ")");
+  }
+
+  private boolean isFilter(String buf, String after) {
+    boolean result = false;
+    String[] parts = buf.trim().split("\\s");
+    if (parts[parts.length - 1].matches("^\\[.*\\]$")) {
+      // check before
+      if (parts.length == 1) {
+        result = true;
+      } else {
+        int count = parts.length - 2;
+        Set<String> operations = new HashSet(OPERATIONS.keySet());
+        operations.remove(MARKER_AND);
+        operations.remove(MARKER_OR);
+        while (count >= 0) {
+          String p = parts[count];
+          if (StringUtils.isNotBlank(p)) {
+            if (!operations.contains(p)) {
+              result = true;
+              break;
+            } else {
+              return false;
+            }
+          }
+          count--;
+        }
+      }
+      after = after.trim();
+      // check after
+      if (result && !after.startsWith("and") && !after.startsWith("or") &&
+          !after.startsWith(";") && StringUtils.isNotBlank(after)) {
+        result = false;
+      }
+    }
+
+    return result;
+  }
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/e27e8d4f/sap/src/main/resources/interpreter-setting.json
----------------------------------------------------------------------
diff --git a/sap/src/main/resources/interpreter-setting.json 
b/sap/src/main/resources/interpreter-setting.json
new file mode 100644
index 0000000..cb5cf94
--- /dev/null
+++ b/sap/src/main/resources/interpreter-setting.json
@@ -0,0 +1,42 @@
+[
+  {
+    "group": "sap",
+    "name": "universe",
+    "className": "org.apache.zeppelin.sap.UniverseInterpreter",
+    "defaultInterpreter": true,
+    "properties": {
+      "universe.api.url": {
+        "envName": null,
+        "propertyName": "universe.api.url",
+        "defaultValue": "http://localhost:6405/biprws";,
+        "description": "API url of Universe",
+        "type": "url"
+      },
+      "universe.user": {
+        "envName": null,
+        "propertyName": "universe.user",
+        "defaultValue": "",
+        "description": "Username for API of Universe",
+        "type": "string"
+      },
+      "universe.password": {
+        "envName": null,
+        "propertyName": "universe.password",
+        "defaultValue": "",
+        "description": "Password for API of Universe",
+        "type": "password"
+      },
+      "universe.authType": {
+        "envName": null,
+        "propertyName": "universe.password",
+        "defaultValue": "secEnterprise",
+        "description": "Type of authentication for API of Universe. Available 
values: secEnterprise, secLDAP, secWinAD, secSAPR3",
+        "type": "string"
+      }
+    },
+    "editor": {
+      "editOnDblClick": false,
+      "completionKey": "TAB"
+    }
+  }
+]

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/e27e8d4f/sap/src/main/resources/universe.keywords
----------------------------------------------------------------------
diff --git a/sap/src/main/resources/universe.keywords 
b/sap/src/main/resources/universe.keywords
new file mode 100644
index 0000000..0811bfb
--- /dev/null
+++ b/sap/src/main/resources/universe.keywords
@@ -0,0 +1 @@
+universe,select,where,and,or,is null,is not null,in
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/e27e8d4f/sap/src/test/java/org/apache/zeppelin/sap/universe/UniverseCompleterTest.java
----------------------------------------------------------------------
diff --git 
a/sap/src/test/java/org/apache/zeppelin/sap/universe/UniverseCompleterTest.java 
b/sap/src/test/java/org/apache/zeppelin/sap/universe/UniverseCompleterTest.java
new file mode 100644
index 0000000..91a4217
--- /dev/null
+++ 
b/sap/src/test/java/org/apache/zeppelin/sap/universe/UniverseCompleterTest.java
@@ -0,0 +1,134 @@
+/**
+ * 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.zeppelin.sap.universe;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.zeppelin.completer.CachedCompleter;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.*;
+
+import static org.junit.Assert.*;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * Universe completer unit tests
+ */
+public class UniverseCompleterTest {
+
+  private UniverseCompleter universeCompleter;
+  private UniverseUtil universeUtil;
+  private UniverseClient universeClient;
+
+  @Before
+  public void beforeTest() throws UniverseException {
+    universeCompleter = new UniverseCompleter(0);
+    universeUtil = new UniverseUtil();
+    Map<String, UniverseInfo> universes = new HashMap<>();
+
+    universes.put("testUniverse", new UniverseInfo("1", "testUniverse", 
"uvx"));
+    universes.put("test with space", new UniverseInfo("2", "test with space", 
"uvx"));
+    universes.put("(GLOBAL) universe", new UniverseInfo("3", "(GLOBAL) 
universe", "uvx"));
+    UniverseInfo universeInfo = new UniverseInfo("1", "testUniverse", "uvx");
+    Map<String, UniverseNodeInfo> testUniverseNodes = new HashMap<>();
+    testUniverseNodes.put("[Dimension].[Test].[name1]",
+        new UniverseNodeInfo("name1id", "name1", "dimension", 
"Dimension\\Test",
+            "Dimension|folder\\Test|folder\\name1|dimension"));
+    testUniverseNodes.put("[Dimension].[Test].[name2]",
+        new UniverseNodeInfo("name2id", "name2", "dimension", 
"Dimension\\Test",
+            "Dimension|folder\\Test|folder\\name2|dimension"));
+    testUniverseNodes.put("[Filter].[name3]",
+        new UniverseNodeInfo("name3id", "name3", "filter", "Filter",
+            "Filter|folder\\name3|filter"));
+    testUniverseNodes.put("[Filter].[name4]",
+        new UniverseNodeInfo("name4id", "name4", "filter", "Filter",
+            "Filter|folder\\name4|filter"));
+    testUniverseNodes.put("[Measure].[name5]",
+        new UniverseNodeInfo("name5id", "name5", "measure", "Measure",
+            "Measure|folder\\name5|measure"));
+
+    universeClient = mock(UniverseClient.class);
+    when(universeClient.getUniverseInfo(anyString())).thenReturn(universeInfo);
+    when(universeClient.getUniverseNodesInfo(anyString(), anyString()))
+        .thenReturn(testUniverseNodes);
+    when(universeClient.getUniversesMap()).thenReturn(universes);
+  }
+
+  @Test
+  public void testCreateUniverseNameCompleter() {
+    String buffer = "universe [";
+    List<CharSequence> candidates = new ArrayList<>();
+    universeCompleter.createOrUpdate(universeClient, null, buffer, 9);
+    CachedCompleter completer = universeCompleter.getUniverseCompleter();
+    assertNull(completer);
+    universeCompleter.createOrUpdate(universeClient, null, buffer, 10);
+    completer = universeCompleter.getUniverseCompleter();
+    assertNotNull(completer);
+
+    completer.getCompleter().complete(StringUtils.EMPTY, 0, candidates);
+    assertEquals(3, candidates.size());
+  }
+
+  @Test
+  public void testCreateUniverseNodesCompleter() {
+    String buffer = "universe [testUniverse]; select [";
+    List<CharSequence> candidates = new ArrayList<>();
+    universeCompleter.createOrUpdate(universeClient, null, buffer, 32);
+    Map<String, CachedCompleter> completerMap = 
universeCompleter.getUniverseInfoCompletersMap();
+    assertFalse(completerMap.containsKey("testUniverse"));
+    universeCompleter.createOrUpdate(universeClient, null, buffer, 33);
+    completerMap = universeCompleter.getUniverseInfoCompletersMap();
+    assertTrue(completerMap.containsKey("testUniverse"));
+    CachedCompleter completer = completerMap.get("testUniverse");
+
+    completer.getCompleter().complete(StringUtils.EMPTY, 0, candidates);
+    assertEquals(3, candidates.size());
+    List<String> candidatesStrings = new ArrayList<>();
+    for (Object o : candidates) {
+      UniverseNodeInfo info = (UniverseNodeInfo) o;
+      candidatesStrings.add(info.getName());
+    }
+    List<String> expected = Arrays.asList("Filter", "Measure", "Dimension");
+    Collections.sort(candidatesStrings);
+    Collections.sort(expected);
+    assertEquals(expected, candidatesStrings);
+  }
+
+  @Test
+  public void testNestedUniverseNodes() {
+    String buffer = "universe [testUniverse]; select [Dimension].[Test].[n";
+    List<CharSequence> candidates = new ArrayList<>();
+
+    universeCompleter.createOrUpdate(universeClient, null, buffer, 53);
+    Map<String, CachedCompleter> completerMap = 
universeCompleter.getUniverseInfoCompletersMap();
+    assertTrue(completerMap.containsKey("testUniverse"));
+    CachedCompleter completer = completerMap.get("testUniverse");
+
+    completer.getCompleter().complete("[Dimension].[Test].[n", 21, candidates);
+    assertEquals(2, candidates.size());
+    List<String> candidatesStrings = new ArrayList<>();
+    for (Object o : candidates) {
+      UniverseNodeInfo info = (UniverseNodeInfo) o;
+      candidatesStrings.add(info.getName());
+    }
+    List<String> expected = Arrays.asList("name1", "name2");
+    Collections.sort(candidatesStrings);
+    Collections.sort(expected);
+    assertEquals(expected, candidatesStrings);
+  }
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/e27e8d4f/sap/src/test/java/org/apache/zeppelin/sap/universe/UniverseUtilTest.java
----------------------------------------------------------------------
diff --git 
a/sap/src/test/java/org/apache/zeppelin/sap/universe/UniverseUtilTest.java 
b/sap/src/test/java/org/apache/zeppelin/sap/universe/UniverseUtilTest.java
new file mode 100644
index 0000000..81a027e
--- /dev/null
+++ b/sap/src/test/java/org/apache/zeppelin/sap/universe/UniverseUtilTest.java
@@ -0,0 +1,371 @@
+/*
+ * 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.zeppelin.sap.universe;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class UniverseUtilTest {
+
+  private UniverseClient universeClient;
+  private UniverseUtil universeUtil;
+
+  @Before
+  public void beforeTest() throws UniverseException {
+      universeUtil = new UniverseUtil();
+      UniverseInfo universeInfo = new UniverseInfo("1", "testUniverse", "uvx");
+      Map<String, UniverseNodeInfo> testUniverseNodes = new HashMap<>();
+      testUniverseNodes.put("[Dimension].[Test].[name1]",
+          new UniverseNodeInfo("name1id", "name1", "dimension", 
"Dimension\\Test",
+              "Dimension|folder\\Test|folder\\name1|dimension"));
+      testUniverseNodes.put("[Dimension].[Test].[name2]",
+          new UniverseNodeInfo("name2id", "name2", "dimension", "Filter\\Test",
+              "Dimension|folder\\Test|folder\\name2|dimension"));
+      testUniverseNodes.put("[Filter].[name3]",
+          new UniverseNodeInfo("name3id", "name3", "filter", "Filter",
+              "Filter|folder\\name3|filter"));
+      testUniverseNodes.put("[Filter].[name4]",
+          new UniverseNodeInfo("name4id", "name4", "filter", "Filter",
+              "Filter|folder\\name4|filter"));
+      testUniverseNodes.put("[Measure].[name5]",
+          new UniverseNodeInfo("name5id", "name5", "measure", "Measure",
+              "Measure|folder\\name5|measure"));
+
+      universeClient = mock(UniverseClient.class);
+      
when(universeClient.getUniverseInfo(anyString())).thenReturn(universeInfo);
+      when(universeClient.getUniverseNodesInfo(anyString(), anyString()))
+          .thenReturn(testUniverseNodes);
+  }
+
+  @Test
+  public void testForConvert() throws UniverseException {
+      String request = "universe [testUniverse];\n" +
+          "select [Measure].[name5]\n" +
+          "where [Filter].[name3] and [Dimension].[Test].[name2] > 1;";
+      UniverseQuery universeQuery = universeUtil.convertQuery(request, 
universeClient, null);
+      assertNotNull(universeQuery);
+      assertNotNull(universeQuery.getUniverseInfo());
+      assertEquals("<resultObjects>\n" +
+                                       "<resultObject 
path=\"Measure|folder\\name5|measure\" id=\"name5id\"/>\n" +
+                                       "</resultObjects>", 
universeQuery.getSelect());
+      assertEquals("<and>\n" +
+          "<predefinedFilter path=\"Filter|folder\\name3|filter\" 
id=\"name3id\"/>\n" +
+          "\n<comparisonFilter 
path=\"Dimension|folder\\Test|folder\\name2|dimension\"" +
+          " operator=\"GreaterThan\" id=\"name2id\">\n" +
+          "<constantOperand>\n" +
+          "<value>\n" +
+          "<caption type=\"Numeric\">1</caption>\n" +
+          "</value>\n" +
+          "</constantOperand>\n" +
+          "</comparisonFilter>\n\n" +
+          "</and>\n", universeQuery.getWhere());
+      assertEquals("testUniverse", universeQuery.getUniverseInfo().getName());
+  }
+
+  @Test
+  public void testConvertConditions() throws UniverseException {
+    String request = "universe [testUniverse];\n" +
+        "select [Measure].[name5]\n" +
+        "where [Filter].[name3] " +
+        "and [Dimension].[Test].[name2] >= 1 " +
+        "and [Dimension].[Test].[name2] < 20 " +
+        "and [Dimension].[Test].[name1] <> 'test' " +
+        "and [Dimension].[Test].[name1] is not null " +
+        "and [Measure].[name5] is null" +
+        "and [Dimension].[Test].[name1] in ('var1', 'v a r 2') " +
+        "and [Dimension].[Test].[name1] in ('var1','withoutspaces')" +
+        "and [Dimension].[Test].[name1] in ('one value')" +
+        "and [Dimension].[Test].[name2] in (1,3,4);";
+    UniverseQuery universeQuery = universeUtil.convertQuery(request, 
universeClient, null);
+    assertNotNull(universeQuery);
+    assertEquals("<and>\n" +
+            "<and>\n" +
+            "<and>\n" +
+            "<and>\n" +
+            "<and>\n" +
+            "<and>\n" +
+            "<and>\n" +
+            "<and>\n" +
+            "<and>\n" +
+            "<predefinedFilter path=\"Filter|folder\\name3|filter\" 
id=\"name3id\"/>\n\n" +
+            "<comparisonFilter 
path=\"Dimension|folder\\Test|folder\\name2|dimension\"" +
+            " operator=\"GreaterThanOrEqualTo\" id=\"name2id\">\n" +
+            "<constantOperand>\n" +
+            "<value>\n" +
+            "<caption type=\"Numeric\">1</caption>\n" +
+            "</value>\n" +
+            "</constantOperand>\n" +
+            "</comparisonFilter>\n\n" +
+            "</and>\n\n" +
+            "<comparisonFilter 
path=\"Dimension|folder\\Test|folder\\name2|dimension\"" +
+            " operator=\"LessThan\" id=\"name2id\">\n" +
+            "<constantOperand>\n" +
+            "<value>\n" +
+            "<caption type=\"Numeric\">20</caption>\n" +
+            "</value>\n" +
+            "</constantOperand>\n" +
+            "</comparisonFilter>\n\n" +
+            "</and>\n\n" +
+            "<comparisonFilter 
path=\"Dimension|folder\\Test|folder\\name1|dimension\"" +
+            " operator=\"NotEqualTo\" id=\"name1id\">\n" +
+            "<constantOperand>\n" +
+            "<value>\n" +
+            "<caption type=\"String\">test</caption>\n" +
+            "</value>\n" +
+            "</constantOperand>\n" +
+            "</comparisonFilter>\n\n" +
+            "</and>\n\n" +
+            "<comparisonFilter id=\"name1id\" 
path=\"Dimension|folder\\Test|folder\\name1|dimension\"" +
+            " operator=\"IsNotNull\"/>\n\n" +
+            "</and>\n\n" +
+            "<comparisonFilter id=\"name5id\" 
path=\"Measure|folder\\name5|measure\" operator=\"IsNull\"/>\n\n" +
+            "</and>\n\n" +
+            "<comparisonFilter 
path=\"Dimension|folder\\Test|folder\\name1|dimension\"" +
+            " operator=\"InList\" id=\"name1id\">\n" +
+            "<constantOperand>\n" +
+            "<value>\n" +
+            "<caption type=\"String\">var1</caption>\n" +
+            "</value>\n" +
+            "<value>\n" +
+            "<caption type=\"String\">v a r 2</caption>\n" +
+            "</value>\n" +
+            "</constantOperand>\n" +
+            "</comparisonFilter>\n\n" +
+            "</and>\n\n" +
+            "<comparisonFilter 
path=\"Dimension|folder\\Test|folder\\name1|dimension\"" +
+            " operator=\"InList\" id=\"name1id\">\n" +
+            "<constantOperand>\n" +
+            "<value>\n" +
+            "<caption type=\"String\">var1</caption>\n" +
+            "</value>\n" +
+            "<value>\n" +
+            "<caption type=\"String\">withoutspaces</caption>\n" +
+            "</value>\n" +
+            "</constantOperand>\n" +
+            "</comparisonFilter>\n\n" +
+            "</and>\n\n" +
+            "<comparisonFilter 
path=\"Dimension|folder\\Test|folder\\name1|dimension\"" +
+            " operator=\"InList\" id=\"name1id\">\n" +
+            "<constantOperand>\n" +
+            "<value>\n" +
+            "<caption type=\"String\">one value</caption>\n" +
+            "</value>\n" +
+            "</constantOperand>\n" +
+            "</comparisonFilter>\n\n" +
+            "</and>\n\n" +
+            "<comparisonFilter 
path=\"Dimension|folder\\Test|folder\\name2|dimension\"" +
+            " operator=\"InList\" id=\"name2id\">\n" +
+            "<constantOperand>\n" +
+            "<value>\n" +
+            "<caption type=\"Numeric\">1</caption>\n" +
+            "</value>\n" +
+            "<value>\n" +
+            "<caption type=\"Numeric\">3</caption>\n" +
+            "</value>\n" +
+            "<value>\n" +
+            "<caption type=\"Numeric\">4</caption>\n" +
+            "</value>\n" +
+            "</constantOperand>\n" +
+            "</comparisonFilter>\n\n" +
+            "</and>\n",
+        universeQuery.getWhere());
+  }
+
+  @Test(expected = UniverseException.class)
+  public void testFailConvertWithoutUniverse() throws UniverseException {
+    String request = "universe ;\n" +
+        "select [Measure].[name5]\n" +
+        "where [Filter].[name3] and [Dimension].[Test].[name2] > 1;";
+    universeUtil.convertQuery(request, universeClient, null);
+  }
+
+  @Test(expected = UniverseException.class)
+  public void testFailConvertWithIncorrectSelect() throws UniverseException {
+    String request = "universe [testUniverse];\n" +
+        "select [not].[exist];";
+    universeUtil.convertQuery(request, universeClient, null);
+  }
+
+
+  @Test(expected = UniverseException.class)
+  public void testFailConvertWithIncorrectCondition() throws UniverseException 
{
+    String request = "universe [testUniverse];\n" +
+        "select [Measure].[name5]\n" +
+        "where [Filter].[name;";
+    universeUtil.convertQuery(request, universeClient, null);
+  }
+
+  @Test
+  public void testFiltersConditions() throws UniverseException {
+    String request1 = "universe [testUniverse];\n" +
+        "select [Measure].[name5]\n" +
+        "where [Filter].[name3];";
+    String request2 = "universe [testUniverse];\n" +
+        "select [Measure].[name5]\n" +
+        "where [Measure].[name5] > 2 and [Filter].[name3];";
+    String request3 = "universe [testUniverse];\n" +
+        "select [Measure].[name5]\n" +
+        "where [Filter].[name3] or [Measure].[name5];";
+    String request4 = "universe [testUniverse];\n" +
+        "select [Measure].[name5]\n" +
+        "where [Filter].[name3] and [Measure].[name5] is null;";
+    UniverseQuery universeQuery = universeUtil.convertQuery(request1, 
universeClient, null);
+    assertEquals("<predefinedFilter path=\"Filter|folder\\name3|filter\" 
id=\"name3id\"/>\n",
+        universeQuery.getWhere());
+    universeQuery = universeUtil.convertQuery(request2, universeClient, null);
+    assertEquals("<and>\n" +
+            "<comparisonFilter path=\"Measure|folder\\name5|measure\" 
operator=\"GreaterThan\" id=\"name5id\">\n" +
+            "<constantOperand>\n" +
+            "<value>\n" +
+            "<caption type=\"Numeric\">2</caption>\n" +
+            "</value>\n" +
+            "</constantOperand>\n" +
+            "</comparisonFilter>\n\n" +
+            "<predefinedFilter path=\"Filter|folder\\name3|filter\" 
id=\"name3id\"/>\n\n" +
+            "</and>\n",
+        universeQuery.getWhere());
+    universeQuery = universeUtil.convertQuery(request3, universeClient, null);
+    assertEquals("<or>\n" +
+            "<predefinedFilter path=\"Filter|folder\\name3|filter\" 
id=\"name3id\"/>\n\n" +
+            "<predefinedFilter path=\"Measure|folder\\name5|measure\" 
id=\"name5id\"/>\n\n" +
+            "</or>\n",
+        universeQuery.getWhere());
+    universeQuery = universeUtil.convertQuery(request4, universeClient, null);
+    assertEquals("<and>\n" +
+            "<predefinedFilter path=\"Filter|folder\\name3|filter\" 
id=\"name3id\"/>\n\n" +
+            "<comparisonFilter id=\"name5id\" 
path=\"Measure|folder\\name5|measure\" operator=\"IsNull\"/>\n\n" +
+            "</and>\n",
+        universeQuery.getWhere());
+  }
+
+  @Test
+  public void testNestedConditions() throws UniverseException {
+    String request = "universe [testUniverse];\n" +
+        "select [Dimension].[Test].[name2]\n" +
+        "where ([Measure].[name5] = 'text' or ([Dimension].[Test].[name1] in 
('1','2', '3') and\n" +
+        "[Dimension].[Test].[name2] is not null)) and ([Filter].[name4] or 
[Measure].[name5] >=12)\n" +
+        "or [Dimension].[Test].[name2] not in (31, 65, 77);";
+    UniverseQuery universeQuery = universeUtil.convertQuery(request, 
universeClient, null);
+    assertEquals("<or>\n" +
+            "<and>\n" +
+            "<or>\n" +
+            "<comparisonFilter path=\"Measure|folder\\name5|measure\" 
operator=\"EqualTo\" id=\"name5id\">\n" +
+            "<constantOperand>\n" +
+            "<value>\n" +
+            "<caption type=\"String\">text</caption>\n" +
+            "</value>\n" +
+            "</constantOperand>\n" +
+            "</comparisonFilter>\n\n" +
+            "<and>\n" +
+            "<comparisonFilter 
path=\"Dimension|folder\\Test|folder\\name1|dimension\" operator=\"InList\" 
id=\"name1id\">\n" +
+            "<constantOperand>\n" +
+            "<value>\n" +
+            "<caption type=\"String\">1</caption>\n" +
+            "</value>\n" +
+            "<value>\n" +
+            "<caption type=\"String\">2</caption>\n" +
+            "</value>\n" +
+            "<value>\n" +
+            "<caption type=\"String\">3</caption>\n" +
+            "</value>\n" +
+            "</constantOperand>\n" +
+            "</comparisonFilter>\n\n" +
+            "<comparisonFilter id=\"name2id\" 
path=\"Dimension|folder\\Test|folder\\name2|dimension\" 
operator=\"IsNotNull\"/>\n\n" +
+            "</and>\n\n" +
+            "</or>\n\n" +
+            "<or>\n" +
+            "<predefinedFilter path=\"Filter|folder\\name4|filter\" 
id=\"name4id\"/>\n\n" +
+            "<comparisonFilter path=\"Measure|folder\\name5|measure\" 
operator=\"GreaterThanOrEqualTo\" id=\"name5id\">\n" +
+            "<constantOperand>\n" +
+            "<value>\n" +
+            "<caption type=\"Numeric\">12</caption>\n" +
+            "</value>\n" +
+            "</constantOperand>\n" +
+            "</comparisonFilter>\n\n" +
+            "</or>\n\n" +
+            "</and>\n\n" +
+            "<comparisonFilter 
path=\"Dimension|folder\\Test|folder\\name2|dimension\" operator=\"NotInList\" 
id=\"name2id\">\n" +
+            "<constantOperand>\n" +
+            "<value>\n" +
+            "<caption type=\"Numeric\">31</caption>\n" +
+            "</value>\n" +
+            "<value>\n" +
+            "<caption type=\"Numeric\">65</caption>\n" +
+            "</value>\n" +
+            "<value>\n" +
+            "<caption type=\"Numeric\">77</caption>\n" +
+            "</value>\n" +
+            "</constantOperand>\n" +
+            "</comparisonFilter>\n\n" +
+            "</or>\n",
+        universeQuery.getWhere());
+  }
+
+  @Test
+  public void testWithoutConditions() throws UniverseException {
+    String request = "universe [testUniverse];\n" +
+        "select [Dimension].[Test].[name2], [Measure].[name5],\n" +
+        "[Dimension].[Test].[name1] ;";
+    UniverseQuery universeQuery = universeUtil.convertQuery(request, 
universeClient, null);
+    assertNull(universeQuery.getWhere());
+    assertEquals("<resultObjects>\n" +
+            "<resultObject 
path=\"Dimension|folder\\Test|folder\\name2|dimension\" id=\"name2id\"/>\n" +
+            "<resultObject path=\"Measure|folder\\name5|measure\" 
id=\"name5id\"/>\n" +
+            "<resultObject 
path=\"Dimension|folder\\Test|folder\\name1|dimension\" id=\"name1id\"/>\n" +
+            "</resultObjects>",
+        universeQuery.getSelect());
+  }
+
+  @Test
+  public void testCaseSensitive() throws UniverseException {
+    String request = "uniVersE [testUniverse];\n" +
+        "seLEct [Dimension].[Test].[name2], [Measure].[name5]\n" +
+        "whERE [Dimension].[Test].[name2] Is NULl Or [Measure].[name5] IN 
(1,2) aNd [Measure].[name5] is NOT nUll;";
+    UniverseQuery universeQuery = universeUtil.convertQuery(request, 
universeClient, null);
+    assertEquals("<resultObjects>\n" +
+            "<resultObject 
path=\"Dimension|folder\\Test|folder\\name2|dimension\" id=\"name2id\"/>\n" +
+            "<resultObject path=\"Measure|folder\\name5|measure\" 
id=\"name5id\"/>\n" +
+            "</resultObjects>",
+        universeQuery.getSelect());
+    assertEquals("<or>\n" +
+            "<comparisonFilter id=\"name2id\" 
path=\"Dimension|folder\\Test|folder\\name2|dimension\" 
operator=\"IsNull\"/>\n\n" +
+            "<and>\n" +
+            "<comparisonFilter path=\"Measure|folder\\name5|measure\" 
operator=\"InList\" id=\"name5id\">\n" +
+            "<constantOperand>\n" + "<value>\n" + "<caption 
type=\"Numeric\">1</caption>\n" +
+            "</value>\n" +
+            "<value>\n" +
+            "<caption type=\"Numeric\">2</caption>\n" +
+            "</value>\n" +
+            "</constantOperand>\n" +
+            "</comparisonFilter>\n\n" +
+            "<comparisonFilter id=\"name5id\" 
path=\"Measure|folder\\name5|measure\" operator=\"IsNotNull\"/>\n\n" +
+            "</and>\n\n" +
+            "</or>\n",
+        universeQuery.getWhere());
+  }
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/e27e8d4f/zeppelin-interpreter/src/main/java/org/apache/zeppelin/completer/CompletionType.java
----------------------------------------------------------------------
diff --git 
a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/completer/CompletionType.java
 
b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/completer/CompletionType.java
index 20cceda..fc5f380 100644
--- 
a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/completer/CompletionType.java
+++ 
b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/completer/CompletionType.java
@@ -24,5 +24,6 @@ public enum CompletionType {
   setting,
   command,
   keyword,
-  path
+  path,
+  universe
 }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/e27e8d4f/zeppelin-interpreter/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java
----------------------------------------------------------------------
diff --git 
a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java
 
b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java
index 725d94b..db70637 100644
--- 
a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java
+++ 
b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java
@@ -692,7 +692,8 @@ public class ZeppelinConfiguration extends XMLConfiguration 
{
         + "org.apache.zeppelin.beam.BeamInterpreter,"
         + "org.apache.zeppelin.scio.ScioInterpreter,"
         + "org.apache.zeppelin.groovy.GroovyInterpreter,"
-        + "org.apache.zeppelin.neo4j.Neo4jCypherInterpreter"
+        + "org.apache.zeppelin.neo4j.Neo4jCypherInterpreter,"
+        + "org.apache.zeppelin.sap.UniverseInterpreter"
         ),
     ZEPPELIN_INTERPRETER_JSON("zeppelin.interpreter.setting", 
"interpreter-setting.json"),
     ZEPPELIN_INTERPRETER_DIR("zeppelin.interpreter.dir", "interpreter"),

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/e27e8d4f/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js 
b/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js
index 971257c..ee17130 100644
--- a/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js
+++ b/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js
@@ -800,7 +800,7 @@ function ParagraphCtrl($scope, $rootScope, $route, $window, 
$routeParams, $locat
                     name: v.name,
                     value: v.value,
                     meta: v.meta,
-                    caption: computeCaption(v.value, v.meta),
+                    caption: computeCaption(v.name, v.meta),
                     score: 300,
                   });
                 }

Reply via email to