| /* |
| * 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.chemistry.opencmis.inmemory.query; |
| |
| import java.util.ArrayList; |
| import java.util.GregorianCalendar; |
| import java.util.List; |
| |
| import org.antlr.runtime.tree.Tree; |
| import org.apache.chemistry.opencmis.server.support.query.CalendarHelper; |
| import org.apache.chemistry.opencmis.server.support.query.CmisQlStrictLexer; |
| import org.apache.chemistry.opencmis.server.support.query.PredicateWalkerBase; |
| import org.apache.chemistry.opencmis.server.support.query.StringUtil; |
| import org.apache.chemistry.opencmis.server.support.query.TextSearchLexer; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| public abstract class AbstractQueryConditionProcessor implements PredicateWalkerBase { |
| |
| private static final Logger LOG = LoggerFactory.getLogger(ProcessQueryTest.class); |
| |
| protected abstract void onStartProcessing(Tree whereNode); |
| |
| protected abstract void onStopProcessing(); |
| |
| // Compare operators |
| protected void onPreEquals(Tree eqNode, Tree leftNode, Tree rightNode) { |
| } |
| |
| protected abstract void onEquals(Tree eqNode, Tree leftNode, Tree rightNode); |
| |
| protected void onPostEquals(Tree eqNode, Tree leftNode, Tree rightNode) { |
| } |
| |
| protected void onPreNotEquals(Tree neNode, Tree leftNode, Tree rightNode) { |
| } |
| |
| protected abstract void onNotEquals(Tree neNode, Tree leftNode, Tree rightNode); |
| |
| protected void onPostNotEquals(Tree neNode, Tree leftNode, Tree rightNode) { |
| } |
| |
| protected void onPreGreaterThan(Tree gtNode, Tree leftNode, Tree rightNode) { |
| } |
| |
| protected abstract void onGreaterThan(Tree gtNode, Tree leftNode, Tree rightNode); |
| |
| protected void onPostGreaterThan(Tree gtNode, Tree leftNode, Tree rightNode) { |
| } |
| |
| protected void onPreGreaterOrEquals(Tree geNode, Tree leftNode, Tree rightNode) { |
| } |
| |
| protected abstract void onGreaterOrEquals(Tree geNode, Tree leftNode, Tree rightNode); |
| |
| protected void onPostGreaterOrEquals(Tree geNode, Tree leftNode, Tree rightNode) { |
| } |
| |
| protected void onPreLessThan(Tree ltNode, Tree leftNode, Tree rightNode) { |
| } |
| |
| protected abstract void onLessThan(Tree ltNode, Tree leftNode, Tree rightNode); |
| |
| protected void onPostLessThan(Tree ltNode, Tree leftNode, Tree rightNode) { |
| } |
| |
| protected void onPreLessOrEquals(Tree leqNode, Tree leftNode, Tree rightNode) { |
| } |
| |
| protected abstract void onLessOrEquals(Tree leqNode, Tree leftNode, Tree rightNode); |
| |
| protected void onPostLessOrEquals(Tree leqNode, Tree leftNode, Tree rightNode) { |
| } |
| |
| // Boolean operators |
| protected abstract void onNot(Tree opNode, Tree leftNode); |
| |
| protected void onPostNot(Tree opNode, Tree leftNode) { |
| } |
| |
| protected void onPreAnd(Tree opNode, Tree leftNode, Tree rightNode) { |
| } |
| |
| protected abstract void onAnd(Tree opNode, Tree leftNode, Tree rightNode); |
| |
| protected void onPostAnd(Tree opNode, Tree leftNode, Tree rightNode) { |
| } |
| |
| protected void onPreOr(Tree opNode, Tree leftNode, Tree rightNode) { |
| } |
| |
| protected abstract void onOr(Tree opNode, Tree leftNode, Tree rightNode); |
| |
| protected void onPostOr(Tree opNode, Tree leftNode, Tree rightNode) { |
| } |
| |
| // Multi-value: |
| protected void onPreIn(Tree node, Tree colNode, Tree listNode) { |
| } |
| |
| protected abstract void onIn(Tree node, Tree colNode, Tree listNode); |
| |
| protected void onPostIn(Tree node, Tree colNode, Tree listNode) { |
| } |
| |
| protected void onPreNotIn(Tree node, Tree colNode, Tree listNode) { |
| } |
| |
| protected abstract void onNotIn(Tree node, Tree colNode, Tree listNode); |
| |
| protected void onPostNotIn(Tree node, Tree colNode, Tree listNode) { |
| } |
| |
| protected void onPreInAny(Tree node, Tree colNode, Tree listNode) { |
| } |
| |
| protected abstract void onInAny(Tree node, Tree colNode, Tree listNode); |
| |
| protected void onPostInAny(Tree node, Tree colNode, Tree listNode) { |
| } |
| |
| protected void onPreNotInAny(Tree node, Tree colNode, Tree listNode) { |
| } |
| |
| protected abstract void onNotInAny(Tree node, Tree literalNode, Tree colNode); |
| |
| protected void onPostNotInAny(Tree node, Tree colNode, Tree listNode) { |
| } |
| |
| protected void onPreEqAny(Tree node, Tree literalNode, Tree colNode) { |
| } |
| |
| protected abstract void onEqAny(Tree node, Tree literalNode, Tree colNode); |
| |
| protected void onPostEqAny(Tree node, Tree literalNode, Tree colNode) { |
| } |
| |
| // Null comparisons: |
| protected abstract void onIsNull(Tree nullNode, Tree colNode); |
| |
| protected void onPostIsNull(Tree nullNode, Tree colNode) { |
| } |
| |
| protected abstract void onIsNotNull(Tree notNullNode, Tree colNode); |
| |
| protected void onPostIsNotNull(Tree notNullNode, Tree colNode) { |
| } |
| |
| // String matching: |
| protected void onPreIsLike(Tree node, Tree colNode, Tree stringNode) { |
| } |
| |
| protected abstract void onIsLike(Tree node, Tree colNode, Tree stringNode); |
| |
| protected void onPostIsLike(Tree node, Tree colNode, Tree stringNode) { |
| } |
| |
| protected void onPreIsNotLike(Tree node, Tree colNode, Tree stringNode) { |
| } |
| |
| protected abstract void onIsNotLike(Tree node, Tree colNode, Tree stringNode); |
| |
| protected void onPostIsNotLike(Tree node, Tree colNode, Tree stringNode) { |
| } |
| |
| protected abstract void onInFolder(Tree node, Tree colNode, Tree paramNode); |
| |
| protected void onBetweenInFolder(Tree node, Tree colNode, Tree paramNode) { |
| } |
| |
| protected void onPostInFolder(Tree node, Tree colNode, Tree paramNode) { |
| } |
| |
| protected abstract void onInTree(Tree node, Tree colNode, Tree paramNode); |
| |
| protected void onBetweenInTree(Tree node, Tree colNode, Tree paramNode) { |
| } |
| |
| protected void onPostInTree(Tree node, Tree colNode, Tree paramNode) { |
| } |
| |
| protected abstract void onScore(Tree node); |
| |
| protected abstract void onColNode(Tree node); |
| |
| protected void onPreTextAnd(Tree node, List<Tree> conjunctionNodes) { |
| } |
| |
| protected abstract void onTextAnd(Tree node, List<Tree> conjunctionNodes, int index); |
| |
| protected void onPostTextAnd(Tree node, List<Tree> conjunctionNodes) { |
| } |
| |
| protected void onPreTextOr(Tree node, List<Tree> termNodes) { |
| } |
| |
| protected abstract void onTextOr(Tree node, List<Tree> termNodes, int index); |
| |
| protected void onPostTextOr(Tree node, List<Tree> termNodes) { |
| } |
| |
| protected abstract void onTextMinus(Tree node, Tree notNode); |
| |
| protected void onPostTextMinus(Tree node, Tree notNode) { |
| } |
| |
| protected abstract void onTextWord(String word); |
| |
| protected abstract void onTextPhrase(String phrase); |
| |
| // Base interface called from query parser |
| @Override |
| public Boolean walkPredicate(Tree whereNode) { |
| if (null != whereNode) { |
| onStartProcessing(whereNode); |
| evalWhereNode(whereNode); |
| onStopProcessing(); |
| } |
| return null; // unused |
| } |
| |
| // /////////////////////////////////////////////////////// |
| // Processing the WHERE clause |
| |
| protected void evalWhereNode(Tree node) { |
| // Ensure that we receive only valid tokens and nodes in the where |
| // clause: |
| LOG.debug("evaluating node: " + node.toString()); |
| switch (node.getType()) { |
| case CmisQlStrictLexer.WHERE: |
| break; // ignore |
| case CmisQlStrictLexer.EQ: |
| onPreEquals(node, node.getChild(0), node.getChild(1)); |
| evalWhereNode(node.getChild(0)); |
| onEquals(node, node.getChild(0), node.getChild(1)); |
| evalWhereNode(node.getChild(1)); |
| onPostEquals(node, node.getChild(0), node.getChild(1)); |
| break; |
| case CmisQlStrictLexer.NEQ: |
| onPreNotEquals(node, node.getChild(0), node.getChild(1)); |
| evalWhereNode(node.getChild(0)); |
| onNotEquals(node, node.getChild(0), node.getChild(1)); |
| evalWhereNode(node.getChild(1)); |
| onPostNotEquals(node, node.getChild(0), node.getChild(1)); |
| break; |
| case CmisQlStrictLexer.GT: |
| onPreGreaterThan(node, node.getChild(0), node.getChild(1)); |
| evalWhereNode(node.getChild(0)); |
| onGreaterThan(node, node.getChild(0), node.getChild(1)); |
| evalWhereNode(node.getChild(1)); |
| onPostGreaterThan(node, node.getChild(0), node.getChild(1)); |
| break; |
| case CmisQlStrictLexer.GTEQ: |
| onPreGreaterOrEquals(node, node.getChild(0), node.getChild(1)); |
| evalWhereNode(node.getChild(0)); |
| onGreaterOrEquals(node, node.getChild(0), node.getChild(1)); |
| evalWhereNode(node.getChild(1)); |
| onPostGreaterOrEquals(node, node.getChild(0), node.getChild(1)); |
| break; |
| case CmisQlStrictLexer.LT: |
| onPreLessThan(node, node.getChild(0), node.getChild(1)); |
| evalWhereNode(node.getChild(0)); |
| onLessThan(node, node.getChild(0), node.getChild(1)); |
| evalWhereNode(node.getChild(1)); |
| onPostLessThan(node, node.getChild(0), node.getChild(1)); |
| break; |
| case CmisQlStrictLexer.LTEQ: |
| onPreLessOrEquals(node, node.getChild(0), node.getChild(1)); |
| evalWhereNode(node.getChild(0)); |
| onLessOrEquals(node, node.getChild(0), node.getChild(1)); |
| evalWhereNode(node.getChild(1)); |
| onPostLessOrEquals(node, node.getChild(0), node.getChild(1)); |
| break; |
| |
| case CmisQlStrictLexer.NOT: |
| onNot(node, node.getChild(0)); |
| evalWhereNode(node.getChild(0)); |
| onPostNot(node, node.getChild(0)); |
| break; |
| case CmisQlStrictLexer.AND: |
| onPreAnd(node, node.getChild(0), node.getChild(1)); |
| evalWhereNode(node.getChild(0)); |
| onAnd(node, node.getChild(0), node.getChild(1)); |
| evalWhereNode(node.getChild(1)); |
| onPostAnd(node, node.getChild(0), node.getChild(1)); |
| break; |
| case CmisQlStrictLexer.OR: |
| onPreOr(node, node.getChild(0), node.getChild(1)); |
| evalWhereNode(node.getChild(0)); |
| onOr(node, node.getChild(0), node.getChild(1)); |
| evalWhereNode(node.getChild(1)); |
| onPostOr(node, node.getChild(0), node.getChild(1)); |
| break; |
| |
| // Multi-value: |
| case CmisQlStrictLexer.IN: |
| onPreIn(node, node.getChild(0), node.getChild(1)); |
| evalWhereNode(node.getChild(0)); |
| onIn(node, node.getChild(0), node.getChild(1)); |
| evalWhereNode(node.getChild(1)); |
| onPostIn(node, node.getChild(0), node.getChild(1)); |
| break; |
| case CmisQlStrictLexer.NOT_IN: |
| onPreNotIn(node, node.getChild(0), node.getChild(1)); |
| evalWhereNode(node.getChild(0)); |
| onNotIn(node, node.getChild(0), node.getChild(1)); |
| evalWhereNode(node.getChild(1)); |
| onPostNotIn(node, node.getChild(0), node.getChild(1)); |
| break; |
| case CmisQlStrictLexer.IN_ANY: |
| onPreInAny(node, node.getChild(0), node.getChild(1)); |
| evalWhereNode(node.getChild(0)); |
| onInAny(node, node.getChild(0), node.getChild(1)); |
| evalWhereNode(node.getChild(1)); |
| onPostInAny(node, node.getChild(0), node.getChild(1)); |
| break; |
| case CmisQlStrictLexer.NOT_IN_ANY: |
| onPreNotInAny(node, node.getChild(0), node.getChild(1)); |
| evalWhereNode(node.getChild(0)); |
| onNotInAny(node, node.getChild(0), node.getChild(1)); |
| evalWhereNode(node.getChild(1)); |
| onPostNotInAny(node, node.getChild(0), node.getChild(1)); |
| break; |
| case CmisQlStrictLexer.EQ_ANY: |
| onPreEqAny(node, node.getChild(0), node.getChild(1)); |
| evalWhereNode(node.getChild(0)); |
| onEqAny(node, node.getChild(0), node.getChild(1)); |
| evalWhereNode(node.getChild(1)); |
| onPostEqAny(node, node.getChild(0), node.getChild(1)); |
| break; |
| |
| // Null comparisons: |
| case CmisQlStrictLexer.IS_NULL: |
| onIsNull(node, node.getChild(0)); |
| evalWhereNode(node.getChild(0)); |
| onPostIsNull(node, node.getChild(0)); |
| break; |
| case CmisQlStrictLexer.IS_NOT_NULL: |
| onIsNotNull(node, node.getChild(0)); |
| evalWhereNode(node.getChild(0)); |
| onPostIsNotNull(node, node.getChild(0)); |
| break; |
| |
| // String matching |
| case CmisQlStrictLexer.LIKE: |
| onPreIsLike(node, node.getChild(0), node.getChild(1)); |
| evalWhereNode(node.getChild(0)); |
| onIsLike(node, node.getChild(0), node.getChild(1)); |
| evalWhereNode(node.getChild(1)); |
| onPostIsLike(node, node.getChild(0), node.getChild(1)); |
| break; |
| case CmisQlStrictLexer.NOT_LIKE: |
| onPreIsNotLike(node, node.getChild(0), node.getChild(1)); |
| evalWhereNode(node.getChild(0)); |
| onIsNotLike(node, node.getChild(0), node.getChild(1)); |
| evalWhereNode(node.getChild(1)); |
| onPostIsNotLike(node, node.getChild(0), node.getChild(1)); |
| break; |
| |
| // Functions |
| case CmisQlStrictLexer.CONTAINS: |
| Tree typeNode = node.getChildCount() == 1 ? null : node.getChild(0); |
| Tree textSearchNode = node.getChildCount() == 1 ? node.getChild(0) : node.getChild(1); |
| |
| onPreContains(node, typeNode, textSearchNode); |
| if (node.getChildCount() > 1) { |
| evalWhereNode(typeNode); |
| onBetweenContains(node, typeNode, textSearchNode); |
| } |
| onContains(node, typeNode, textSearchNode); |
| break; |
| case CmisQlStrictLexer.IN_FOLDER: |
| if (node.getChildCount() == 1) { |
| onInFolder(node, null, node.getChild(0)); |
| evalWhereNode(node.getChild(0)); |
| onPostInFolder(node, null, node.getChild(0)); |
| } else { |
| onInFolder(node, node.getChild(0), node.getChild(1)); |
| evalWhereNode(node.getChild(0)); |
| onBetweenInFolder(node, node.getChild(0), node.getChild(1)); |
| evalWhereNode(node.getChild(1)); |
| onPostInFolder(node, node.getChild(0), node.getChild(1)); |
| } |
| break; |
| case CmisQlStrictLexer.IN_TREE: |
| if (node.getChildCount() == 1) { |
| onInTree(node, null, node.getChild(0)); |
| evalWhereNode(node.getChild(0)); |
| onPostInTree(node, null, node.getChild(0)); |
| } else { |
| onInTree(node, node.getChild(0), node.getChild(1)); |
| evalWhereNode(node.getChild(0)); |
| onBetweenInTree(node, node.getChild(0), node.getChild(1)); |
| evalWhereNode(node.getChild(1)); |
| onPostInTree(node, node.getChild(0), node.getChild(1)); |
| } |
| break; |
| case CmisQlStrictLexer.SCORE: |
| onScore(node); |
| break; |
| case CmisQlStrictLexer.COL: |
| onColNode(node); |
| break; |
| case CmisQlStrictLexer.BOOL_LIT: |
| case CmisQlStrictLexer.NUM_LIT: |
| case CmisQlStrictLexer.STRING_LIT: |
| case CmisQlStrictLexer.TIME_LIT: |
| onLiteral(node); |
| break; |
| case CmisQlStrictLexer.IN_LIST: |
| onLiteralList(node); |
| break; |
| case CmisQlStrictLexer.ID: |
| onId(node); |
| break; |
| default: |
| // do nothing; |
| } |
| } |
| |
| protected void onPreContains(Tree node, Tree typeNode, Tree searchExprNode) { |
| } |
| |
| protected void onContains(Tree node, Tree typeNode, Tree searchExprNode) { |
| evalTextSearchNode(typeNode, searchExprNode); |
| } |
| |
| protected void onBetweenContains(Tree node, Tree typeNode, Tree searchExprNode) { |
| } |
| |
| protected void evalTextSearchNode(Tree typeNode, Tree node) { |
| // Ensure that we receive only valid tokens and nodes in the where |
| // clause: |
| LOG.debug("evaluating node: " + node.toString()); |
| switch (node.getType()) { |
| case TextSearchLexer.TEXT_AND: |
| List<Tree> children = getChildrenAsList(node); |
| onPreTextAnd(node, children); |
| int i = 0; |
| for (Tree child : children) { |
| evalTextSearchNode(typeNode, child); |
| onTextAnd(node, children, i++); |
| } |
| onPostTextAnd(node, children); |
| break; |
| case TextSearchLexer.TEXT_OR: |
| children = getChildrenAsList(node); |
| onPreTextOr(node, children); |
| int j = 0; |
| for (Tree child : children) { |
| evalTextSearchNode(typeNode, child); |
| onTextOr(node, children, j++); |
| } |
| onPostTextOr(node, children); |
| break; |
| case TextSearchLexer.TEXT_MINUS: |
| onTextMinus(node, node.getChild(0)); |
| evalTextSearchNode(typeNode, node.getChild(0)); |
| onPostTextMinus(node, node.getChild(0)); |
| break; |
| case TextSearchLexer.TEXT_SEARCH_PHRASE_STRING_LIT: |
| onTextPhrase(onTextLiteral(node)); |
| break; |
| case TextSearchLexer.TEXT_SEARCH_WORD_LIT: |
| onTextWord(onTextLiteral(node)); |
| break; |
| } |
| } |
| |
| // helper functions that are needed by most query tree walkers |
| |
| protected Object getLiteral(Tree node) { |
| int type = node.getType(); |
| String text = node.getText(); |
| switch (type) { |
| case CmisQlStrictLexer.BOOL_LIT: |
| return Boolean.parseBoolean(node.getText()); |
| case CmisQlStrictLexer.NUM_LIT: |
| if (text.contains(".") || text.contains("e") || text.contains("E")) { |
| return Double.parseDouble(text); |
| } else { |
| return Long.parseLong(text); |
| } |
| case CmisQlStrictLexer.STRING_LIT: |
| return text.substring(1, text.length() - 1); |
| case CmisQlStrictLexer.TIME_LIT: |
| GregorianCalendar gc = CalendarHelper.fromString(text.substring(text.indexOf('\'') + 1, |
| text.lastIndexOf('\''))); |
| return gc; |
| default: |
| throw new RuntimeException("Unknown literal. " + node); |
| } |
| } |
| |
| protected Object onLiteral(Tree node) { |
| return getLiteral(node); |
| } |
| |
| protected String onId(Tree node) { |
| return node.getText(); |
| } |
| |
| protected String onTextLiteral(Tree node) { |
| int type = node.getType(); |
| String text = node.getText(); |
| switch (type) { |
| case TextSearchLexer.TEXT_SEARCH_PHRASE_STRING_LIT: |
| return StringUtil.unescape(text.substring(1, text.length() - 1), null); |
| case TextSearchLexer.TEXT_SEARCH_WORD_LIT: |
| return StringUtil.unescape(text, null); |
| default: |
| throw new RuntimeException("Unknown text literal. " + node); |
| } |
| } |
| |
| protected List<Object> onLiteralList(Tree node) { |
| List<Object> res = new ArrayList<Object>(node.getChildCount()); |
| for (int i = 0; i < node.getChildCount(); i++) { |
| Tree literal = node.getChild(i); |
| res.add(getLiteral(literal)); |
| } |
| return res; |
| } |
| |
| protected List<Tree> getChildrenAsList(Tree node) { |
| List<Tree> res = new ArrayList<Tree>(node.getChildCount()); |
| for (int i = 0; i < node.getChildCount(); i++) { |
| Tree childNnode = node.getChild(i); |
| res.add(childNnode); |
| } |
| return res; |
| } |
| } |