This project has retired. For details please refer to its Attic page.
AbstractQueryConditionProcessor xref

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.chemistry.opencmis.inmemory.query;
20  
21  import java.io.ByteArrayInputStream;
22  import java.io.IOException;
23  import java.io.UnsupportedEncodingException;
24  import java.util.ArrayList;
25  import java.util.GregorianCalendar;
26  import java.util.List;
27  
28  import org.antlr.runtime.ANTLRInputStream;
29  import org.antlr.runtime.CharStream;
30  import org.antlr.runtime.CommonTokenStream;
31  import org.antlr.runtime.RecognitionException;
32  import org.antlr.runtime.TokenSource;
33  import org.antlr.runtime.TokenStream;
34  import org.antlr.runtime.tree.CommonTree;
35  import org.antlr.runtime.tree.CommonTreeNodeStream;
36  import org.antlr.runtime.tree.Tree;
37  import org.apache.chemistry.opencmis.server.support.query.CalendarHelper;
38  import org.apache.chemistry.opencmis.server.support.query.CmisQlStrictLexer;
39  import org.apache.chemistry.opencmis.server.support.query.CmisQlStrictParser;
40  import org.apache.chemistry.opencmis.server.support.query.CmisQlStrictParser_CmisBaseGrammar.query_return;
41  import org.apache.chemistry.opencmis.server.support.query.CmisQueryWalker;
42  import org.apache.chemistry.opencmis.server.support.query.StringUtil;
43  import org.apache.chemistry.opencmis.server.support.query.TextSearchLexer;
44  import org.apache.commons.logging.Log;
45  import org.apache.commons.logging.LogFactory;
46  
47  public abstract class AbstractQueryConditionProcessor implements QueryConditionProcessor {
48  
49      private static final Log LOG = LogFactory.getLog(ProcessQueryTest.class);
50      
51      public abstract void onStartProcessing(Tree whereNode);
52      public abstract void onStopProcessing();
53  
54      // Compare operators
55      public abstract void onEquals(Tree eqNode, Tree leftNode, Tree rightNode);
56      public abstract void onNotEquals(Tree neNode, Tree leftNode, Tree rightNode);
57      public abstract void onGreaterThan(Tree gtNode, Tree leftNode, Tree rightNode);
58      public abstract void onGreaterOrEquals(Tree geNode, Tree leftNode, Tree rightNode);
59      public abstract void onLessThan(Tree ltNode, Tree leftNode, Tree rightNode);
60      public abstract void onLessOrEquals(Tree leqNode, Tree leftNode, Tree rightNode);
61  
62      // Boolean operators
63      public void onPreNot(Tree opNode, Tree leftNode) {
64      }
65      public abstract void onNot(Tree opNode, Tree leftNode);
66      public void onPostNot(Tree opNode, Tree leftNode) {
67      }
68      public void onPreAnd(Tree opNode, Tree leftNode, Tree rightNode) {
69      }
70      public abstract void onAnd(Tree opNode, Tree leftNode, Tree rightNode);
71      public void onPostAnd(Tree opNode, Tree leftNode, Tree rightNode) {
72      }
73      public void onPreOr(Tree opNode, Tree leftNode, Tree rightNode) {
74      }
75      public abstract void onOr(Tree opNode, Tree leftNode, Tree rightNode);
76      public void onPostOr(Tree opNode, Tree leftNode, Tree rightNode) {
77      }
78  
79      // Multi-value:
80      public abstract void onIn(Tree node, Tree colNode, Tree listNode);
81      public abstract void onNotIn(Tree node, Tree colNode, Tree listNode);
82      public abstract void onInAny(Tree node, Tree colNode, Tree listNode);
83      public abstract void onNotInAny(Tree node, Tree colNode, Tree listNode);
84      public abstract void onEqAny(Tree node, Tree literalNode, Tree colNode);
85  
86      // Null comparisons:
87      public abstract void onIsNull(Tree nullNode, Tree colNode);
88      public abstract void onIsNotNull(Tree notNullNode, Tree colNode);
89  
90      // String matching:
91      public abstract void onIsLike(Tree node, Tree colNode, Tree stringNode);
92      public abstract void onIsNotLike(Tree node, Tree colNode, Tree stringNode);
93  
94      // Functions:
95      public abstract void onInFolder(Tree node, Tree colNode, Tree paramNode);
96      public abstract void onInTree(Tree node, Tree colNode, Tree paramNode);
97      public abstract void onScore(Tree node);
98      
99      public void onPreTextAnd(Tree node, List<Tree> conjunctionNodes) {
100     }
101     public abstract void onTextAnd(Tree node, List<Tree> conjunctionNodes);
102     public void onPostTextAnd(Tree node, List<Tree> conjunctionNodes) {
103     }
104     public void onPreTextOr(Tree node, List<Tree> termNodes) {
105     }
106     public abstract void onTextOr(Tree node, List<Tree> termNodes);
107     public void onPostTextOr(Tree node, List<Tree> termNodes) {
108     }
109     public abstract void onTextMinus(Tree node, Tree notNode);
110     public abstract void onTextWord(String word);
111     public abstract void onTextPhrase(String phrase);
112 
113     // convenience method because everybody needs this piece of code
114     public static CmisQueryWalker getWalker(String statement) throws UnsupportedEncodingException, IOException, RecognitionException {
115         CharStream input = new ANTLRInputStream(new ByteArrayInputStream(statement.getBytes("UTF-8")));
116         TokenSource lexer = new CmisQlStrictLexer(input);
117         TokenStream tokens = new CommonTokenStream(lexer);
118         CmisQlStrictParser parser = new CmisQlStrictParser(tokens);
119         CommonTree parserTree; // the ANTLR tree after parsing phase
120 
121         query_return parsedStatement = parser.query();
122 //        if (parser.errorMessage != null) {
123 //            throw new RuntimeException("Cannot parse query: " + statement + " (" + parser.errorMessage + ")");
124 //        }
125         parserTree = (CommonTree) parsedStatement.getTree();
126 
127         CommonTreeNodeStream nodes = new CommonTreeNodeStream(parserTree);
128         nodes.setTokenStream(tokens);
129         CmisQueryWalker walker = new CmisQueryWalker(nodes);
130         return walker;
131     }
132 
133 
134     // Base interface called from query parser
135     public Boolean walkPredicate(Tree whereNode) {
136         if (null != whereNode) {
137             onStartProcessing(whereNode);
138             evalWhereNode(whereNode);
139             onStopProcessing();
140         }
141         return null; // unused
142     }
143 
144     // ///////////////////////////////////////////////////////
145     // Processing the WHERE clause
146 
147     // default implementation for ^ains
148     public void onContains(Tree node, Tree typeNode, Tree searchExprNode) {
149         LOG.debug("evaluating text search node: " + searchExprNode);
150         evalTextSearchNode(searchExprNode);        
151     }
152 
153     protected void evalWhereNode(Tree node) {
154         // Ensure that we receive only valid tokens and nodes in the where
155         // clause:
156         LOG.debug("evaluating node: " + node.toString());
157         switch (node.getType()) {
158         case CmisQlStrictLexer.WHERE:
159             break; // ignore
160         case CmisQlStrictLexer.EQ:
161             evalWhereNode(node.getChild(0));
162             onEquals(node, node.getChild(0), node.getChild(1));
163             evalWhereNode(node.getChild(1));
164             break;
165         case CmisQlStrictLexer.NEQ:
166             evalWhereNode(node.getChild(0));
167             onNotEquals(node, node.getChild(0), node.getChild(1));
168             evalWhereNode(node.getChild(1));
169             break;
170         case CmisQlStrictLexer.GT:
171             evalWhereNode(node.getChild(0));
172             onGreaterThan(node, node.getChild(0), node.getChild(1));
173             evalWhereNode(node.getChild(1));
174             break;
175         case CmisQlStrictLexer.GTEQ:
176             evalWhereNode(node.getChild(0));
177             onGreaterOrEquals(node, node.getChild(0), node.getChild(1));
178             evalWhereNode(node.getChild(1));
179             break;
180         case CmisQlStrictLexer.LT:
181             evalWhereNode(node.getChild(0));
182             onLessThan(node, node.getChild(0), node.getChild(1));
183             evalWhereNode(node.getChild(1));
184             break;
185         case CmisQlStrictLexer.LTEQ:
186             evalWhereNode(node.getChild(0));
187             onLessOrEquals(node, node.getChild(0), node.getChild(1));
188             evalWhereNode(node.getChild(1));
189             break;
190 
191         case CmisQlStrictLexer.NOT:
192             onPreNot(node, node.getChild(0));
193             onNot(node, node.getChild(0));
194             evalWhereNode(node.getChild(0));
195             onPostNot(node, node.getChild(0));
196             break;
197         case CmisQlStrictLexer.AND:
198             onPreAnd(node, node.getChild(0), node.getChild(1));
199             evalWhereNode(node.getChild(0));
200             onAnd(node, node.getChild(0), node.getChild(1));
201             evalWhereNode(node.getChild(1));
202             onPostAnd(node, node.getChild(0), node.getChild(1));
203             break;
204         case CmisQlStrictLexer.OR:
205             onPreOr(node, node.getChild(0), node.getChild(1));
206             evalWhereNode(node.getChild(0));
207             onOr(node, node.getChild(0), node.getChild(1));
208             evalWhereNode(node.getChild(1));
209             onPostOr(node, node.getChild(0), node.getChild(1));
210             break;
211 
212         // Multi-value:
213         case CmisQlStrictLexer.IN:
214             evalWhereNode(node.getChild(0));
215             onIn(node, node.getChild(0), node.getChild(1));
216             evalWhereNode(node.getChild(1));
217             break;
218         case CmisQlStrictLexer.NOT_IN:
219             evalWhereNode(node.getChild(0));
220             onNotIn(node, node.getChild(0), node.getChild(1));
221             evalWhereNode(node.getChild(1));
222             break;
223         case CmisQlStrictLexer.IN_ANY:
224             evalWhereNode(node.getChild(0));
225             onInAny(node, node.getChild(0), node.getChild(1));
226             evalWhereNode(node.getChild(1));
227             break;
228         case CmisQlStrictLexer.NOT_IN_ANY:
229             evalWhereNode(node.getChild(0));
230             onNotInAny(node, node.getChild(0), node.getChild(1));
231             evalWhereNode(node.getChild(1));
232             break;
233         case CmisQlStrictLexer.EQ_ANY:
234             evalWhereNode(node.getChild(0));
235             onEqAny(node, node.getChild(0), node.getChild(1));
236             evalWhereNode(node.getChild(1));
237             break;
238 
239         // Null comparisons:
240         case CmisQlStrictLexer.IS_NULL:
241             onIsNull(node, node.getChild(0));
242             evalWhereNode(node.getChild(0));
243             break;
244         case CmisQlStrictLexer.IS_NOT_NULL:
245             onIsNotNull(node, node.getChild(0));
246             evalWhereNode(node.getChild(0));
247             break;
248 
249         // String matching
250         case CmisQlStrictLexer.LIKE:
251             evalWhereNode(node.getChild(0));
252             onIsLike(node, node.getChild(0), node.getChild(1));
253             evalWhereNode(node.getChild(1));
254             break;
255         case CmisQlStrictLexer.NOT_LIKE:
256             evalWhereNode(node.getChild(0));
257             onIsNotLike(node, node.getChild(0), node.getChild(1));
258             evalWhereNode(node.getChild(1));
259             break;
260 
261         // Functions
262         case CmisQlStrictLexer.CONTAINS:
263             onContains(node, null, node.getChild(0));
264             break;
265         case CmisQlStrictLexer.IN_FOLDER:
266             if (node.getChildCount() == 1) {
267                 onInFolder(node, null, node.getChild(0));
268                 evalWhereNode(node.getChild(0));
269             } else {
270                 evalWhereNode(node.getChild(0));
271                 onInFolder(node, node.getChild(0), node.getChild(1));
272                 evalWhereNode(node.getChild(1));
273             }
274             break;
275         case CmisQlStrictLexer.IN_TREE:
276             if (node.getChildCount() == 1) {
277                 onInTree(node, null, node.getChild(0));
278                 evalWhereNode(node.getChild(0));
279             } else {
280                 evalWhereNode(node.getChild(0));
281                 onInTree(node, node.getChild(0), node.getChild(1));
282                 evalWhereNode(node.getChild(1));
283             }
284             break;
285         case CmisQlStrictLexer.SCORE:
286             onScore(node);
287             break;
288 
289         default:
290             // do nothing;
291         }
292     }
293 
294     protected void evalTextSearchNode(Tree node) {
295         // Ensure that we receive only valid tokens and nodes in the where
296         // clause:
297         LOG.debug("evaluating node: " + node.toString());
298         switch (node.getType()) {
299         case TextSearchLexer.TEXT_AND:
300             List<Tree> children = getChildrenAsList(node);
301             onPreTextAnd(node, children);
302             for (Tree child : children)
303                 evalTextSearchNode(child);
304             onTextAnd(node, children);
305             onPostTextAnd(node, children);
306             break;
307         case TextSearchLexer.TEXT_OR:
308             children = getChildrenAsList(node);
309             onPreTextOr(node, children);
310             for (Tree child : children)
311                 evalTextSearchNode(child);
312             onTextOr(node, children);
313             onPostTextOr(node, children);
314             break;
315         case TextSearchLexer.TEXT_MINUS:
316             onTextMinus(node, node.getChild(0));
317             break;
318         case TextSearchLexer.TEXT_SEARCH_PHRASE_STRING_LIT:
319             onTextPhrase(onTextLiteral(node));
320             break;
321         case TextSearchLexer.TEXT_SEARCH_WORD_LIT:
322             onTextWord(onTextLiteral(node));
323             break;
324         }
325     }
326         
327     // helper functions that are needed by most query tree walkers
328 
329     protected Object onLiteral(Tree node) {
330         int type = node.getType();
331         String text = node.getText();
332         switch (type) {
333         case CmisQlStrictLexer.BOOL_LIT:
334             return Boolean.parseBoolean(node.getText());
335         case CmisQlStrictLexer.NUM_LIT:
336             if (text.contains(".") || text.contains("e") || text.contains("E")) {
337                 return Double.parseDouble(text);
338             } else {
339                 return Long.parseLong(text);
340             }
341         case CmisQlStrictLexer.STRING_LIT:
342             return text.substring(1, text.length()-1);
343         case CmisQlStrictLexer.TIME_LIT:
344             GregorianCalendar gc = CalendarHelper.fromString(text.substring(text.indexOf('\'')+1, text.lastIndexOf('\'')));
345             return gc;
346         default:
347             throw new RuntimeException("Unknown literal. " + node);
348         }
349     }
350     
351     protected String onTextLiteral(Tree node) {
352         int type = node.getType();
353         String text = node.getText();
354         switch (type) {
355         case TextSearchLexer.TEXT_SEARCH_PHRASE_STRING_LIT:
356             return StringUtil.unescape(text.substring(1, text.length()-1), null);
357         case TextSearchLexer.TEXT_SEARCH_WORD_LIT:
358             return StringUtil.unescape(text, null);
359         default:
360             throw new RuntimeException("Unknown text literal. " + node);
361         }
362 
363     }
364 
365     protected List<Object> onLiteralList(Tree node) {
366         List<Object> res = new ArrayList<Object>(node.getChildCount());
367         for (int i=0; i<node.getChildCount(); i++) {
368             Tree literal =  node.getChild(i);
369             res.add(onLiteral(literal));
370         }
371         return res;
372     }
373     
374     protected List<Tree> getChildrenAsList(Tree node) {
375         List<Tree> res = new ArrayList<Tree>(node.getChildCount());
376         for (int i=0; i<node.getChildCount(); i++) {
377             Tree childNnode =  node.getChild(i);
378             res.add(childNnode);
379         }
380         return res;
381     }
382 }