This project has retired. For details please refer to its Attic page.
ParseTreeWalker 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  
20  package org.apache.chemistry.opencmis.jcr.query;
21  
22  import org.antlr.runtime.tree.Tree;
23  import org.apache.chemistry.opencmis.commons.exceptions.CmisInvalidArgumentException;
24  import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException;
25  import org.apache.chemistry.opencmis.server.support.query.CalendarHelper;
26  import org.apache.chemistry.opencmis.server.support.query.CmisQlStrictLexer;
27  import org.apache.chemistry.opencmis.server.support.query.PredicateWalkerBase;
28  import org.apache.chemistry.opencmis.server.support.query.TextSearchLexer;
29  
30  import java.util.ArrayList;
31  import java.util.List;
32  
33  /**
34   * This implementation of {@link PredicateWalkerBase} traverses the parse tree of a CMIS query.
35   * It uses an {@link Evaluator} to accumulate the result of the traversal. <code>Evaluator</code>
36   * has a corresponding method for each {@link Tree#getType() node type} in the parse tree.
37   * <code>ParseTreeWalker</code> calls these methods while traversing the parse tree passing an
38   * <code>Evaluator</code> for each of the corresponding operation's arguments.
39   * </br>
40   * The {@link #walkPredicate(Tree)} serves as entry point for traversing a parse tree. After
41   * successful traversal, the result is obtained from the {@link #getResult()} method.
42   *
43   * @param <T>  type of the result determined by the <code>Evaluator</code> used.
44   */
45  public class ParseTreeWalker<T> implements PredicateWalkerBase {
46  
47      private final Evaluator<T> evaluator;
48      private T result;
49  
50      /**
51       * Create a new instance for traversing CMIS query parse trees.
52       *
53       * @param evaluator  <code>Evaluator</code> for evaluating the nodes of the parse tree
54       */
55      public ParseTreeWalker(Evaluator<T> evaluator) {
56          this.evaluator = evaluator;
57      }
58  
59      /**
60       * Retrieve the result of a successful traversal.
61       *
62       * @return  result of traversal or <code>null</code> if either not yet traversed, an error occurred
63       *      on traversal or the query has an empty where clause. 
64       */
65      public T getResult() {
66          return result;
67      }
68  
69      //------------------------------------------< PredicateWalkerBase >---
70      
71      public Boolean walkPredicate(Tree node) {
72          result = null;
73          result = walkPredicate(evaluator, node);
74          return false; // Return value is ignored by caller
75      }
76  
77      //------------------------------------------< protected >---
78  
79      /** For extensibility. */
80      protected T walkOtherExpr(Evaluator<?> evaluator, Tree node) {
81          throw new CmisRuntimeException("Unknown node type: " + node.getType() + " (" + node.getText() + ")");
82      }
83  
84      /** For extensibility. */
85      protected T walkOtherPredicate(Evaluator<?> evaluator, Tree node) {
86          throw new CmisRuntimeException("Unknown node type: " + node.getType() + " (" + node.getText() + ")");
87      }
88  
89      //------------------------------------------< private >---
90  
91      private T walkPredicate(Evaluator<T> evaluator, Tree node) {
92          switch (node.getType()) {
93              case CmisQlStrictLexer.NOT:
94                  return evaluator.not(walkPredicate(evaluator.op(), node.getChild(0)));
95              case CmisQlStrictLexer.AND:
96                  return evaluator.and(
97                          walkPredicate(evaluator.op(), node.getChild(0)),
98                          walkPredicate(evaluator.op(), node.getChild(1)));
99              case CmisQlStrictLexer.OR:
100                 return evaluator.or(
101                         walkPredicate(evaluator.op(), node.getChild(0)),
102                         walkPredicate(evaluator.op(), node.getChild(1)));
103             case CmisQlStrictLexer.EQ:
104                 return evaluator.eq(
105                         walkExpr(evaluator.op(), node.getChild(0)),
106                         walkExpr(evaluator.op(), node.getChild(1)));
107             case CmisQlStrictLexer.NEQ:
108                 return evaluator.neq(
109                         walkExpr(evaluator.op(), node.getChild(0)),
110                         walkExpr(evaluator.op(), node.getChild(1)));
111             case CmisQlStrictLexer.GT:
112                 return evaluator.gt(
113                         walkExpr(evaluator.op(), node.getChild(0)),
114                         walkExpr(evaluator.op(), node.getChild(1)));
115             case CmisQlStrictLexer.GTEQ:
116                 return evaluator.gteq(
117                         walkExpr(evaluator.op(), node.getChild(0)),
118                         walkExpr(evaluator.op(), node.getChild(1)));
119             case CmisQlStrictLexer.LT:
120                 return evaluator.lt(
121                         walkExpr(evaluator.op(), node.getChild(0)),
122                         walkExpr(evaluator.op(), node.getChild(1)));
123             case CmisQlStrictLexer.LTEQ:
124                 return evaluator.lteq(
125                         walkExpr(evaluator.op(), node.getChild(0)),
126                         walkExpr(evaluator.op(), node.getChild(1)));
127             case CmisQlStrictLexer.IN:
128                 return evaluator.in(
129                         walkExpr(evaluator.op(), node.getChild(0)),
130                         walkExpr(evaluator.op(), node.getChild(1)));
131             case CmisQlStrictLexer.NOT_IN:
132                 return evaluator.notIn(
133                         walkExpr(evaluator.op(), node.getChild(0)),
134                         walkExpr(evaluator.op(), node.getChild(1)));
135             case CmisQlStrictLexer.IN_ANY:
136                 return evaluator.inAny(
137                         walkExpr(evaluator.op(), node.getChild(0)),
138                         walkExpr(evaluator.op(), node.getChild(1)));
139             case CmisQlStrictLexer.NOT_IN_ANY:
140                 return evaluator.notInAny(
141                         walkExpr(evaluator.op(), node.getChild(0)),
142                         walkExpr(evaluator.op(), node.getChild(1)));
143             case CmisQlStrictLexer.EQ_ANY:
144                 return evaluator.eqAny(
145                         walkExpr(evaluator.op(), node.getChild(0)),
146                         walkExpr(evaluator.op(), node.getChild(1)));
147             case CmisQlStrictLexer.IS_NULL:
148                 return evaluator.isNull(walkExpr(evaluator.op(), node.getChild(0)));
149             case CmisQlStrictLexer.IS_NOT_NULL:
150                 return evaluator.notIsNull(walkExpr(evaluator.op(), node.getChild(0)));
151             case CmisQlStrictLexer.LIKE:
152                 return evaluator.like(
153                         walkExpr(evaluator.op(), node.getChild(0)),
154                         walkExpr(evaluator.op(), node.getChild(1)));
155             case CmisQlStrictLexer.NOT_LIKE:
156                 return evaluator.notLike(
157                         walkExpr(evaluator.op(), node.getChild(0)),
158                         walkExpr(evaluator.op(), node.getChild(1)));
159             case CmisQlStrictLexer.CONTAINS:
160                 if (node.getChildCount() == 1) {
161                     return evaluator.contains(
162                             null,
163                             walkExprTextSearch(evaluator.op(), node.getChild(0)));
164                 }
165                 else {
166                     return evaluator.contains(
167                             walkExpr(evaluator.op(), node.getChild(0)),
168                             walkExpr(evaluator.op(), node.getChild(1)));
169                 }
170             case CmisQlStrictLexer.IN_FOLDER:
171                 if (node.getChildCount() == 1) {
172                     return evaluator.inFolder(
173                             null,
174                             walkExpr(evaluator.op(), node.getChild(0)));
175                 }
176                 else {
177                     return evaluator.inFolder(
178                             walkExpr(evaluator.op(), node.getChild(0)),
179                             walkExpr(evaluator.op(), node.getChild(1)));
180                 }
181             case CmisQlStrictLexer.IN_TREE:
182                 if (node.getChildCount() == 1) {
183                     return evaluator.inTree(
184                             null,
185                             walkExpr(evaluator.op(), node.getChild(0)));
186                 }
187                 else {
188                     return evaluator.inTree(
189                             walkExpr(evaluator.op(), node.getChild(0)),
190                             walkExpr(evaluator.op(), node.getChild(1)));
191                 }
192             default:
193                 return walkOtherPredicate(evaluator, node);
194         }
195     }
196 
197     private T walkExpr(Evaluator<T> evaluator, Tree node) {
198         switch (node.getType()) {
199             case CmisQlStrictLexer.BOOL_LIT:
200                 return walkBoolean(evaluator, node);
201             case CmisQlStrictLexer.NUM_LIT:
202                 return walkNumber(evaluator, node);
203             case CmisQlStrictLexer.STRING_LIT:
204                 return walkString(evaluator, node);
205             case CmisQlStrictLexer.TIME_LIT:
206                 return walkTimestamp(evaluator, node);
207             case CmisQlStrictLexer.IN_LIST:
208                 return evaluator.list(walkList(evaluator, node));
209             case CmisQlStrictLexer.COL:
210                 return walkCol(evaluator, node);
211             default:
212                 return walkOtherExpr(evaluator, node);
213         }
214     }
215     
216     private T walkExprTextSearch(Evaluator<T> evaluator, Tree node) {
217         switch (node.getType()) {
218             case TextSearchLexer.TEXT_AND:
219                 return walkTextAnd(evaluator, node);
220             case TextSearchLexer.TEXT_OR:
221                 return walkTextOr(evaluator, node);
222             case TextSearchLexer.TEXT_MINUS:
223                 return walkTextMinus(evaluator, node);
224             case TextSearchLexer.TEXT_SEARCH_WORD_LIT:
225                 return walkTextWord(evaluator, node);
226             case TextSearchLexer.TEXT_SEARCH_PHRASE_STRING_LIT:
227                 return walkTextPhrase(evaluator, node);
228             default:
229                 return walkOtherExpr(evaluator, node);
230         }
231     }
232 
233     private List<T> walkList(Evaluator<T> evaluator, Tree node) {
234         int n = node.getChildCount();
235         List<T> result = new ArrayList<T>(n);
236         for (int i = 0; i < n; i++) {
237             result.add(walkExpr(evaluator.op(), node.getChild(i)));
238         }
239         return result;
240     }
241 
242     private T walkBoolean(Evaluator<T> evaluator, Tree node) {
243         String s = node.getText();
244 
245         if ("true".equalsIgnoreCase(s)) {
246             return evaluator.value(true);
247         }
248         else if ("false".equalsIgnoreCase(s)) {
249             return evaluator.value(false);
250         }
251         else {
252             throw new CmisInvalidArgumentException("Not a boolean: " + s);
253         }
254     }
255 
256     private T walkNumber(Evaluator<T> evaluator, Tree node) {
257         String s = node.getText();
258         try {
259             return s.contains(".") || s.contains("e") || s.contains("E")
260                     ? evaluator.value(Double.valueOf(s))
261                     : evaluator.value(Long.valueOf(s));
262         }
263         catch (NumberFormatException e) {
264             throw new CmisInvalidArgumentException("Not a number: " + s);
265         }
266     }
267 
268     private T walkString(Evaluator<T> evaluator, Tree node) {
269         String s = node.getText();
270         s = s.substring(1, s.length() - 1);
271         return evaluator.value(s.replace("''", "'"));  // un-escape quotes
272     }
273 
274     private T walkTimestamp(Evaluator<T> evaluator, Tree node) {
275         String s = node.getText();
276         s = s.substring(s.indexOf('\'') + 1, s.length() - 1);
277         try {
278             return evaluator.value(CalendarHelper.fromString(s));
279         }
280         catch (IllegalArgumentException e) {
281             throw new CmisInvalidArgumentException("Not a date time value: " + s);
282         }
283     }
284 
285     private T walkCol(Evaluator<T> evaluator, Tree node) {
286         return evaluator.col(node.getChild(0).getText());
287     }
288 
289     private T walkTextAnd(Evaluator<T> evaluator2, Tree node) {
290         // TODO Auto-generated method stub
291         return null;
292     }
293     
294     private T walkTextOr(Evaluator<T> evaluator2, Tree node) {
295         // TODO Auto-generated method stub
296         return null;
297     }
298     
299     private T walkTextMinus(Evaluator<T> evaluator2, Tree node) {
300         // TODO Auto-generated method stub
301         return null;
302     }
303     
304     private T walkTextWord(Evaluator<T> evaluator2, Tree node) {
305         // TODO Auto-generated method stub
306         return null;
307     }
308     
309     private T walkTextPhrase(Evaluator<T> evaluator2, Tree node) {
310         // TODO Auto-generated method stub
311         return null;
312     }
313 
314 }