1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.healthmarketscience.jackcess.impl.expr;
18
19 import java.math.BigDecimal;
20 import java.time.LocalDateTime;
21 import java.util.ArrayList;
22 import java.util.Arrays;
23 import java.util.Collection;
24 import java.util.Collections;
25 import java.util.Deque;
26 import java.util.HashMap;
27 import java.util.HashSet;
28 import java.util.Iterator;
29 import java.util.LinkedList;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.Set;
33 import java.util.regex.Pattern;
34 import java.util.regex.PatternSyntaxException;
35
36 import com.healthmarketscience.jackcess.expr.EvalContext;
37 import com.healthmarketscience.jackcess.expr.EvalException;
38 import com.healthmarketscience.jackcess.expr.Expression;
39 import com.healthmarketscience.jackcess.expr.Function;
40 import com.healthmarketscience.jackcess.expr.FunctionLookup;
41 import com.healthmarketscience.jackcess.expr.Identifier;
42 import com.healthmarketscience.jackcess.expr.LocaleContext;
43 import com.healthmarketscience.jackcess.expr.ParseException;
44 import com.healthmarketscience.jackcess.expr.Value;
45 import com.healthmarketscience.jackcess.impl.expr.ExpressionTokenizer.Token;
46 import com.healthmarketscience.jackcess.impl.expr.ExpressionTokenizer.TokenType;
47 import org.apache.commons.lang3.StringUtils;
48
49
50
51
52
53
54 public class Expressionator
55 {
56
57
58
59
60
61
62
63 public enum Type {
64 DEFAULT_VALUE, EXPRESSION, FIELD_VALIDATOR, RECORD_VALIDATOR;
65 }
66
67 public interface ParseContext extends LocaleContext {
68 public FunctionLookup getFunctionLookup();
69 }
70
71 private enum WordType {
72 OP, COMP, LOG_OP, CONST, SPEC_OP_PREFIX, DELIM;
73 }
74
75 private static final String FUNC_START_DELIM = "(";
76 private static final String OPEN_PAREN = "(";
77 private static final String CLOSE_PAREN = ")";
78 private static final String FUNC_PARAM_SEP = ",";
79
80 private static final Map<String,WordType> WORD_TYPES =
81 new HashMap<String,WordType>();
82
83 static {
84 setWordType(WordType.OP, "+", "-", "*", "/", "\\", "^", "&", "mod");
85 setWordType(WordType.COMP, "<", "<=", ">", ">=", "=", "<>");
86 setWordType(WordType.LOG_OP, "and", "or", "eqv", "xor", "imp");
87 setWordType(WordType.CONST, "true", "false", "null", "on", "off",
88 "yes", "no");
89 setWordType(WordType.SPEC_OP_PREFIX, "is", "like", "between", "in", "not");
90
91
92
93 setWordType(WordType.DELIM, ".", "!", ",", "(", ")");
94 }
95
96 private static final Collection<String> TRUE_STRS =
97 Arrays.asList("true", "yes", "on");
98 private static final Collection<String> FALSE_STRS =
99 Arrays.asList("false", "no", "off");
100
101 private interface OpType {}
102
103 private enum UnaryOp implements OpType {
104 NEG("-", false) {
105 @Override public Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Value param1) {
106 return BuiltinOperators.negate(ctx, param1);
107 }
108 @Override public UnaryOp getUnaryNumOp() {
109 return UnaryOp.NEG_NUM;
110 }
111 },
112 POS("+", false) {
113 @Override public Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Value param1) {
114
115 return param1;
116 }
117 @Override public UnaryOp getUnaryNumOp() {
118 return UnaryOp.POS_NUM;
119 }
120 },
121 NOT("Not", true) {
122 @Override public Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Value param1) {
123 return BuiltinOperators.not(ctx, param1);
124 }
125 },
126
127 NEG_NUM("-", false) {
128 @Override public Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Value param1) {
129 return BuiltinOperators.negate(ctx, param1);
130 }
131 },
132
133 POS_NUM("+", false) {
134 @Override public Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Value param1) {
135
136 return param1;
137 }
138 };
139
140 private final String _str;
141 private final boolean _needSpace;
142
143 private UnaryOp(String str, boolean needSpace) {
144 _str = str;
145 _needSpace = needSpace;
146 }
147
148 public boolean needsSpace() {
149 return _needSpace;
150 }
151
152 @Override
153 public String toString() {
154 return _str;
155 }
156
157 public UnaryOp getUnaryNumOp() {
158 return null;
159 }
160
161 public abstract Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Value param1);
162 }
163
164 private enum BinaryOp implements OpType {
165 PLUS("+") {
166 @Override public Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value param1, Value param2) {
167 return BuiltinOperators.add(ctx, param1, param2);
168 }
169 },
170 MINUS("-") {
171 @Override public Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value param1, Value param2) {
172 return BuiltinOperators.subtract(ctx, param1, param2);
173 }
174 },
175 MULT("*") {
176 @Override public Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value param1, Value param2) {
177 return BuiltinOperators.multiply(ctx, param1, param2);
178 }
179 },
180 DIV("/") {
181 @Override public Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value param1, Value param2) {
182 return BuiltinOperators.divide(ctx, param1, param2);
183 }
184 },
185 INT_DIV("\\") {
186 @Override public Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value param1, Value param2) {
187 return BuiltinOperators.intDivide(ctx, param1, param2);
188 }
189 },
190 EXP("^") {
191 @Override public Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value param1, Value param2) {
192 return BuiltinOperators.exp(ctx, param1, param2);
193 }
194 },
195 CONCAT("&") {
196 @Override public Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value param1, Value param2) {
197 return BuiltinOperators.concat(ctx, param1, param2);
198 }
199 },
200 MOD("Mod") {
201 @Override public Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value param1, Value param2) {
202 return BuiltinOperators.mod(ctx, param1, param2);
203 }
204 };
205
206 private final String _str;
207
208 private BinaryOp(String str) {
209 _str = str;
210 }
211
212 @Override
213 public String toString() {
214 return _str;
215 }
216
217 public abstract Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value param1, Value param2);
218 }
219
220 private enum CompOp implements OpType {
221 LT("<") {
222 @Override public Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value param1, Value param2) {
223 return BuiltinOperators.lessThan(ctx, param1, param2);
224 }
225 },
226 LTE("<=") {
227 @Override public Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value param1, Value param2) {
228 return BuiltinOperators.lessThanEq(ctx, param1, param2);
229 }
230 },
231 GT(">") {
232 @Override public Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value param1, Value param2) {
233 return BuiltinOperators.greaterThan(ctx, param1, param2);
234 }
235 },
236 GTE(">=") {
237 @Override public Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value param1, Value param2) {
238 return BuiltinOperators.greaterThanEq(ctx, param1, param2);
239 }
240 },
241 EQ("=") {
242 @Override public Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value param1, Value param2) {
243 return BuiltinOperators.equals(ctx, param1, param2);
244 }
245 },
246 NE("<>") {
247 @Override public Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value param1, Value param2) {
248 return BuiltinOperators.notEquals(ctx, param1, param2);
249 }
250 };
251
252 private final String _str;
253
254 private CompOp(String str) {
255 _str = str;
256 }
257
258 @Override
259 public String toString() {
260 return _str;
261 }
262
263 public abstract Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value param1, Value param2);
264 }
265
266 private enum LogOp implements OpType {
267 AND("And") {
268 @Override public Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value param1, Value param2) {
269 return BuiltinOperators.and(ctx, param1, param2);
270 }
271 },
272 OR("Or") {
273 @Override public Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value param1, Value param2) {
274 return BuiltinOperators.or(ctx, param1, param2);
275 }
276 },
277 EQV("Eqv") {
278 @Override public Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value param1, Value param2) {
279 return BuiltinOperators.eqv(ctx, param1, param2);
280 }
281 },
282 XOR("Xor") {
283 @Override public Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value param1, Value param2) {
284 return BuiltinOperators.xor(ctx, param1, param2);
285 }
286 },
287 IMP("Imp") {
288 @Override public Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value param1, Value param2) {
289 return BuiltinOperators.imp(ctx, param1, param2);
290 }
291 };
292
293 private final String _str;
294
295 private LogOp(String str) {
296 _str = str;
297 }
298
299 @Override
300 public String toString() {
301 return _str;
302 }
303
304 public abstract Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value param1, Value param2);
305 }
306
307 private enum SpecOp implements OpType {
308
309
310 NOT("Not") {
311 @Override public Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Value param1, Object param2, Object param3) {
312 throw new UnsupportedOperationException();
313 }
314 },
315 IS_NULL("Is Null") {
316 @Override public Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Value param1, Object param2, Object param3) {
317 return BuiltinOperators.isNull(param1);
318 }
319 },
320 IS_NOT_NULL("Is Not Null") {
321 @Override public Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Value param1, Object param2, Object param3) {
322 return BuiltinOperators.isNotNull(param1);
323 }
324 },
325 LIKE("Like") {
326 @Override public Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Value param1, Object param2, Object param3) {
327 return BuiltinOperators.like(ctx, param1, (Pattern)param2);
328 }
329 },
330 NOT_LIKE("Not Like") {
331 @Override public Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Value param1, Object param2, Object param3) {
332 return BuiltinOperators.notLike(ctx, param1, (Pattern)param2);
333 }
334 },
335 BETWEEN("Between") {
336 @Override public Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Value param1, Object param2, Object param3) {
337 return BuiltinOperators.between(ctx, param1, (Value="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value)param2, (Value)param3);
338 }
339 },
340 NOT_BETWEEN("Not Between") {
341 @Override public Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Value param1, Object param2, Object param3) {
342 return BuiltinOperators.notBetween(ctx, param1, (Value="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value)param2, (Value)param3);
343 }
344 },
345 IN("In") {
346 @Override public Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Value param1, Object param2, Object param3) {
347 return BuiltinOperators.in(ctx, param1, (Value[])param2);
348 }
349 },
350 NOT_IN("Not In") {
351 @Override public Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Value param1, Object param2, Object param3) {
352 return BuiltinOperators.notIn(ctx, param1, (Value[])param2);
353 }
354 };
355
356 private final String _str;
357
358 private SpecOp(String str) {
359 _str = str;
360 }
361
362 @Override
363 public String toString() {
364 return _str;
365 }
366
367 public abstract Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Value param1, Object param2, Object param3);
368 }
369
370 private static final Map<OpType, Integer> PRECENDENCE =
371 buildPrecedenceMap(
372 new OpType[]{UnaryOp.NEG_NUM, UnaryOp.POS_NUM},
373 new OpType[]{BinaryOp.EXP},
374 new OpType[]{UnaryOp.NEG, UnaryOp.POS},
375 new OpType[]{BinaryOp.MULT, BinaryOp.DIV},
376 new OpType[]{BinaryOp.INT_DIV},
377 new OpType[]{BinaryOp.MOD},
378 new OpType[]{BinaryOp.PLUS, BinaryOp.MINUS},
379 new OpType[]{BinaryOp.CONCAT},
380 new OpType[]{CompOp.LT, CompOp.GT, CompOp.NE, CompOp.LTE, CompOp.GTE,
381 CompOp.EQ, SpecOp.LIKE, SpecOp.NOT_LIKE,
382 SpecOp.IS_NULL, SpecOp.IS_NOT_NULL},
383 new OpType[]{UnaryOp.NOT},
384 new OpType[]{LogOp.AND},
385 new OpType[]{LogOp.OR},
386 new OpType[]{LogOp.XOR},
387 new OpType[]{LogOp.EQV},
388 new OpType[]{LogOp.IMP},
389 new OpType[]{SpecOp.IN, SpecOp.NOT_IN, SpecOp.BETWEEN,
390 SpecOp.NOT_BETWEEN});
391
392 private static final Set<Character> REGEX_SPEC_CHARS = new HashSet<Character>(
393 Arrays.asList('\\','.','%','=','+', '$','^','|','(',')','{','}','&',
394 '[',']','*','?'));
395
396 private static final Pattern UNMATCHABLE_REGEX = Pattern.compile("(?!)");
397
398 private static final Expr THIS_COL_VALUE = new EThisValue();
399
400 private static final Expr NULL_VALUE = new EConstValue(
401 ValueSupport.NULL_VAL, "Null");
402 private static final Expr TRUE_VALUE = new EConstValue(
403 ValueSupport.TRUE_VAL, "True");
404 private static final Expr FALSE_VALUE = new EConstValue(
405 ValueSupport.FALSE_VAL, "False");
406
407
408 private Expressionator() {}
409
410 public static Expression parse(Type exprType, String exprStr,
411 Value.Type resultType,
412 ParseContext context) {
413
414 List<Token> tokens = trimSpaces(
415 ExpressionTokenizer.tokenize(exprType, exprStr, context));
416
417 if(tokens == null) {
418 throw new ParseException("null/empty expression");
419 }
420
421 TokBuf buf = new TokBuf(exprType, tokens, context);
422
423 if(isLiteralDefaultValue(buf, resultType, exprStr)) {
424
425
426
427 return new ExprWrapper(exprStr,
428 new ELiteralValue(Value.Type.STRING, exprStr), resultType);
429 }
430
431
432 Expr expr = parseExpression(buf, false);
433
434 if((exprType == Type.FIELD_VALIDATOR) && !expr.isValidationExpr()) {
435
436
437
438 expr = new EImplicitCompOp(expr);
439 }
440
441 switch(exprType) {
442 case DEFAULT_VALUE:
443 case EXPRESSION:
444 return (expr.isConstant() ?
445
446
447 new MemoizedExprWrapper(exprStr, expr, resultType) :
448 new ExprWrapper(exprStr, expr, resultType));
449 case FIELD_VALIDATOR:
450 case RECORD_VALIDATOR:
451 return (expr.isConstant() ?
452
453
454 new MemoizedCondExprWrapper(exprStr, expr) :
455 new CondExprWrapper(exprStr, expr));
456 default:
457 throw new ParseException("unexpected expression type " + exprType);
458 }
459 }
460
461 private static List<Token> trimSpaces(List<Token> tokens) {
462 if(tokens == null) {
463 return null;
464 }
465
466
467
468
469 for(int i = 1; i < (tokens.size() - 1); ++i) {
470 Token t = tokens.get(i);
471 if(t.getType() == TokenType.SPACE) {
472 if((tokens.get(i - 1).getType() == TokenType.STRING) &&
473 isDelim(tokens.get(i + 1), FUNC_START_DELIM)) {
474
475 } else {
476 tokens.remove(i);
477 --i;
478 }
479 }
480 }
481 return tokens;
482 }
483
484 private static Expr parseExpression(TokBuf buf, boolean singleExpr)
485 {
486 while(buf.hasNext()) {
487 Token t = buf.next();
488
489 switch(t.getType()) {
490 case OBJ_NAME:
491
492 parseObjectRefExpression(t, buf);
493 break;
494
495 case LITERAL:
496
497 buf.setPendingExpr(new ELiteralValue(t.getValueType(), t.getValue()));
498 break;
499
500 case OP:
501
502 WordType wordType = getWordType(t);
503 if(wordType == null) {
504
505 throw new ParseException("Invalid operator " + t);
506 }
507
508
509
510 switch(wordType) {
511 case OP:
512 parseOperatorExpression(t, buf);
513 break;
514
515 case COMP:
516
517 parseCompOpExpression(t, buf);
518 break;
519
520 default:
521 throw new ParseException("Unexpected OP word type " + wordType);
522 }
523
524 break;
525
526 case DELIM:
527
528 parseDelimExpression(t, buf);
529 break;
530
531 case STRING:
532
533
534 wordType = getWordType(t);
535 if(wordType == null) {
536
537
538 if(!maybeParseFuncCallExpression(t, buf)) {
539
540
541 Token next = buf.peekNext();
542 if((next != null) && isObjNameSep(next)) {
543
544 parseObjectRefExpression(t, buf);
545
546 } else {
547
548
549 throw new UnsupportedOperationException("FIXME");
550 }
551 }
552
553 } else {
554
555
556
557 switch(wordType) {
558 case OP:
559
560 parseOperatorExpression(t, buf);
561 break;
562
563 case LOG_OP:
564
565 parseLogicalOpExpression(t, buf);
566 break;
567
568 case CONST:
569
570 parseConstExpression(t, buf);
571 break;
572
573 case SPEC_OP_PREFIX:
574
575 parseSpecOpExpression(t, buf);
576 break;
577
578 default:
579 throw new ParseException("Unexpected STRING word type "
580 + wordType);
581 }
582 }
583
584 break;
585
586 case SPACE:
587
588 break;
589
590 default:
591 throw new ParseException("unknown token type " + t);
592 }
593
594 if(singleExpr && buf.hasPendingExpr()) {
595 break;
596 }
597 }
598
599 Expr expr = buf.takePendingExpr();
600 if(expr == null) {
601 throw new ParseException("No expression found? " + buf);
602 }
603
604 return expr;
605 }
606
607 private static void parseObjectRefExpression(Token firstTok, TokBuf buf) {
608
609
610
611
612
613
614
615
616 Deque<String> objNames = new LinkedList<String>();
617 objNames.add(firstTok.getValueStr());
618
619 Token t = null;
620 boolean atSep = false;
621 while((t = buf.peekNext()) != null) {
622 if(!atSep) {
623 if(isObjNameSep(t)) {
624 buf.next();
625 atSep = true;
626 continue;
627 }
628 } else {
629 if((t.getType() == TokenType.OBJ_NAME) ||
630 (t.getType() == TokenType.STRING)) {
631 buf.next();
632
633 objNames.addFirst(t.getValueStr());
634 atSep = false;
635 continue;
636 }
637 }
638 break;
639 }
640
641 int numNames = objNames.size();
642 if(atSep || (numNames > 3)) {
643 throw new ParseException("Invalid object reference " + buf);
644 }
645
646
647 String propName = null;
648 if(numNames == 3) {
649 propName = objNames.poll();
650 }
651 String objName = objNames.poll();
652 String collectionName = objNames.poll();
653
654 buf.setPendingExpr(
655 new EObjValue(new Identifier(collectionName, objName, propName)));
656 }
657
658 private static void parseDelimExpression(Token firstTok, TokBuf buf) {
659
660
661 if(!isDelim(firstTok, OPEN_PAREN) || buf.hasPendingExpr()) {
662 throw new ParseException("Unexpected delimiter " +
663 firstTok.getValue() + " " + buf);
664 }
665
666 Expr subExpr = findParenExprs(buf, false).get(0);
667 buf.setPendingExpr(new EParen(subExpr));
668 }
669
670 private static boolean maybeParseFuncCallExpression(
671 Token firstTok, TokBuf buf) {
672
673 int startPos = buf.curPos();
674 boolean foundFunc = false;
675
676 try {
677 Token t = buf.peekNext();
678 if(!isDelim(t, FUNC_START_DELIM)) {
679
680 return false;
681 }
682
683 buf.next();
684 List<Expr> params = findParenExprs(buf, true);
685 String funcName = firstTok.getValueStr();
686 Function func = buf.getFunction(funcName);
687 if(func == null) {
688 throw new ParseException("Could not find function '" +
689 funcName + "' " + buf);
690 }
691 buf.setPendingExpr(new EFunc(func, params));
692 foundFunc = true;
693 return true;
694
695 } finally {
696 if(!foundFunc) {
697 buf.reset(startPos);
698 }
699 }
700 }
701
702 private static List<Expr> findParenExprs(
703 TokBuf buf, boolean allowMulti) {
704
705 if(allowMulti) {
706
707 Token t = buf.peekNext();
708 if(isDelim(t, CLOSE_PAREN)) {
709 buf.next();
710 return Collections.emptyList();
711 }
712 }
713
714
715 List<Expr> exprs = new ArrayList<Expr>(3);
716 int level = 1;
717 int startPos = buf.curPos();
718 while(buf.hasNext()) {
719
720 Token t = buf.next();
721
722 if(isDelim(t, OPEN_PAREN)) {
723
724 ++level;
725
726 } else if(isDelim(t, CLOSE_PAREN)) {
727
728 --level;
729 if(level == 0) {
730 TokBuf subBuf = buf.subBuf(startPos, buf.prevPos());
731 exprs.add(parseExpression(subBuf, false));
732 return exprs;
733 }
734
735 } else if(allowMulti && (level == 1) && isDelim(t, FUNC_PARAM_SEP)) {
736
737 TokBuf subBuf = buf.subBuf(startPos, buf.prevPos());
738 exprs.add(parseExpression(subBuf, false));
739 startPos = buf.curPos();
740 }
741 }
742
743 throw new ParseException("Missing closing '" + CLOSE_PAREN
744 + " " + buf);
745 }
746
747 private static void parseOperatorExpression(Token t, TokBuf buf) {
748
749
750
751 if(buf.hasPendingExpr()) {
752 parseBinaryOpExpression(t, buf);
753 } else if(isEitherOp(t, "-", "+")) {
754 parseUnaryOpExpression(t, buf);
755 } else {
756 throw new ParseException(
757 "Missing left expression for binary operator " + t.getValue() +
758 " " + buf);
759 }
760 }
761
762 private static void parseBinaryOpExpression(Token firstTok, TokBuf buf) {
763 BinaryOp op = getOpType(firstTok, BinaryOp.class);
764 Expr leftExpr = buf.takePendingExpr();
765 Expr rightExpr = parseExpression(buf, true);
766
767 buf.setPendingExpr(new EBinaryOp(op, leftExpr, rightExpr));
768 }
769
770 private static void parseUnaryOpExpression(Token firstTok, TokBuf buf) {
771 UnaryOp op = getOpType(firstTok, UnaryOp.class);
772
773 UnaryOp numOp = op.getUnaryNumOp();
774 if(numOp != null) {
775
776
777 Token nextTok = buf.peekNext();
778 if((nextTok != null) && (nextTok.getType() == TokenType.LITERAL) &&
779 nextTok.getValueType().isNumeric()) {
780 op = numOp;
781 }
782 }
783
784 Expr val = parseExpression(buf, true);
785
786 buf.setPendingExpr(new EUnaryOp(op, val));
787 }
788
789 private static void parseCompOpExpression(Token firstTok, TokBuf buf) {
790
791 if(!buf.hasPendingExpr()) {
792 if(buf.getExprType() == Type.FIELD_VALIDATOR) {
793
794
795 buf.setPendingExpr(THIS_COL_VALUE);
796 } else {
797 throw new ParseException(
798 "Missing left expression for comparison operator " +
799 firstTok.getValue() + " " + buf);
800 }
801 }
802
803 CompOp op = getOpType(firstTok, CompOp.class);
804 Expr leftExpr = buf.takePendingExpr();
805 Expr rightExpr = parseExpression(buf, true);
806
807 buf.setPendingExpr(new ECompOp(op, leftExpr, rightExpr));
808 }
809
810 private static void parseLogicalOpExpression(Token firstTok, TokBuf buf) {
811
812 if(!buf.hasPendingExpr()) {
813 throw new ParseException(
814 "Missing left expression for logical operator " +
815 firstTok.getValue() + " " + buf);
816 }
817
818 LogOp op = getOpType(firstTok, LogOp.class);
819 Expr leftExpr = buf.takePendingExpr();
820 Expr rightExpr = parseExpression(buf, true);
821
822 buf.setPendingExpr(new ELogicalOp(op, leftExpr, rightExpr));
823 }
824
825 private static void parseSpecOpExpression(Token firstTok, TokBuf buf) {
826
827 SpecOp specOp = getSpecialOperator(firstTok, buf);
828
829 if(specOp == SpecOp.NOT) {
830
831 parseUnaryOpExpression(firstTok, buf);
832 return;
833 }
834
835 if(!buf.hasPendingExpr()) {
836 if(buf.getExprType() == Type.FIELD_VALIDATOR) {
837
838
839 buf.setPendingExpr(THIS_COL_VALUE);
840 } else {
841 throw new ParseException(
842 "Missing left expression for comparison operator " +
843 specOp + " " + buf);
844 }
845 }
846
847 Expr expr = buf.takePendingExpr();
848
849 Expr specOpExpr = null;
850 switch(specOp) {
851 case IS_NULL:
852 case IS_NOT_NULL:
853 specOpExpr = new ENullOp(specOp, expr);
854 break;
855
856 case LIKE:
857 case NOT_LIKE:
858 Token t = buf.next();
859 if((t.getType() != TokenType.LITERAL) ||
860 (t.getValueType() != Value.Type.STRING)) {
861 throw new ParseException("Missing Like pattern " + buf);
862 }
863 String patternStr = t.getValueStr();
864 specOpExpr = new ELikeOp(specOp, expr, patternStr);
865 break;
866
867 case BETWEEN:
868 case NOT_BETWEEN:
869
870
871
872
873 Expr startRangeExpr = null;
874 while(true) {
875
876 Expr tmpExpr = parseExpression(buf, true);
877 Token tmpT = buf.peekNext();
878
879 if(tmpT == null) {
880
881 throw new ParseException(
882 "Missing 'And' for 'Between' expression " + buf);
883 }
884
885 if(isString(tmpT, "and")) {
886 buf.next();
887 startRangeExpr = tmpExpr;
888 break;
889 }
890
891
892 buf.restorePendingExpr(tmpExpr);
893 }
894
895 Expr endRangeExpr = parseExpression(buf, true);
896
897 specOpExpr = new EBetweenOp(specOp, expr, startRangeExpr, endRangeExpr);
898 break;
899
900 case IN:
901 case NOT_IN:
902
903
904 t = buf.next();
905 if(t.getType() == TokenType.SPACE) {
906 t = buf.next();
907 }
908 if(!isDelim(t, OPEN_PAREN)) {
909 throw new ParseException("Malformed 'In' expression " + buf);
910 }
911
912 List<Expr> exprs = findParenExprs(buf, true);
913 specOpExpr = new EInOp(specOp, expr, exprs);
914 break;
915
916 default:
917 throw new ParseException("Unexpected special op " + specOp);
918 }
919
920 buf.setPendingExpr(specOpExpr);
921 }
922
923 private static SpecOp getSpecialOperator(Token firstTok, TokBuf buf) {
924 String opStr = firstTok.getValueStr().toLowerCase();
925
926 if("is".equals(opStr)) {
927 Token t = buf.peekNext();
928 if(isString(t, "null")) {
929 buf.next();
930 return SpecOp.IS_NULL;
931 } else if(isString(t, "not")) {
932 buf.next();
933 t = buf.peekNext();
934 if(isString(t, "null")) {
935 buf.next();
936 return SpecOp.IS_NOT_NULL;
937 }
938 }
939 } else if("like".equals(opStr)) {
940 return SpecOp.LIKE;
941 } else if("between".equals(opStr)) {
942 return SpecOp.BETWEEN;
943 } else if("in".equals(opStr)) {
944 return SpecOp.IN;
945 } else if("not".equals(opStr)) {
946 Token t = buf.peekNext();
947 if(isString(t, "between")) {
948 buf.next();
949 return SpecOp.NOT_BETWEEN;
950 } else if(isString(t, "in")) {
951 buf.next();
952 return SpecOp.NOT_IN;
953 } else if(isString(t, "like")) {
954 buf.next();
955 return SpecOp.NOT_LIKE;
956 }
957 return SpecOp.NOT;
958 }
959
960 throw new ParseException(
961 "Malformed special operator " + opStr + " " + buf);
962 }
963
964 private static void parseConstExpression(Token firstTok, TokBuf buf) {
965 Expr constExpr = null;
966 String tokStr = firstTok.getValueStr().toLowerCase();
967 if(TRUE_STRS.contains(tokStr)) {
968 constExpr = TRUE_VALUE;
969 } else if(FALSE_STRS.contains(tokStr)) {
970 constExpr = FALSE_VALUE;
971 } else if("null".equals(tokStr)) {
972 constExpr = NULL_VALUE;
973 } else {
974 throw new ParseException("Unexpected CONST word "
975 + firstTok.getValue());
976 }
977 buf.setPendingExpr(constExpr);
978 }
979
980 private static boolean isObjNameSep(Token t) {
981 return (isDelim(t, ".") || isDelim(t, "!"));
982 }
983
984 private static boolean isOp(Token t, String opStr) {
985 return ((t != null) && (t.getType() == TokenType.OP) &&
986 opStr.equalsIgnoreCase(t.getValueStr()));
987 }
988
989 private static boolean isEitherOp(Token t, String opStr1, String opStr2) {
990 return ((t != null) && (t.getType() == TokenType.OP) &&
991 (opStr1.equalsIgnoreCase(t.getValueStr()) ||
992 opStr2.equalsIgnoreCase(t.getValueStr())));
993 }
994
995 private static boolean isDelim(Token t, String opStr) {
996 return ((t != null) && (t.getType() == TokenType.DELIM) &&
997 opStr.equalsIgnoreCase(t.getValueStr()));
998 }
999
1000 private static boolean isString(Token t, String opStr) {
1001 return ((t != null) && (t.getType() == TokenType.STRING) &&
1002 opStr.equalsIgnoreCase(t.getValueStr()));
1003 }
1004
1005 private static WordType getWordType(Token t) {
1006 return WORD_TYPES.get(t.getValueStr().toLowerCase());
1007 }
1008
1009 private static void setWordType(WordType type, String... words) {
1010 for(String w : words) {
1011 WORD_TYPES.put(w, type);
1012 }
1013 }
1014
1015 private static <T extends Enum<T>> T getOpType(Token t, Class<T> opClazz) {
1016 String str = t.getValueStr();
1017 for(T op : opClazz.getEnumConstants()) {
1018 if(str.equalsIgnoreCase(op.toString())) {
1019 return op;
1020 }
1021 }
1022 throw new ParseException("Unexpected op string " + t.getValueStr());
1023 }
1024
1025 private static StringBuilder appendLeadingExpr(
1026 Expr expr, LocaleContext ctx, StringBuilder sb, boolean isDebug)
1027 {
1028 int len = sb.length();
1029 expr.toString(ctx, sb, isDebug);
1030 if(sb.length() > len) {
1031
1032 sb.append(" ");
1033 }
1034 return sb;
1035 }
1036
1037 private static final class TokBuf
1038 {
1039 private final Type _exprType;
1040 private final List<Token> _tokens;
1041 private final TokBuf _parent;
1042 private final int _parentOff;
1043 private final ParseContext _ctx;
1044 private int _pos;
1045 private Expr _pendingExpr;
1046
1047 private TokBuf(Type exprType, List<Token> tokens, ParseContext context) {
1048 this(exprType, tokens, null, 0, context);
1049 }
1050
1051 private TokBuf(List<Token> tokens, TokBuf parent, int parentOff) {
1052 this(parent._exprType, tokens, parent, parentOff, parent._ctx);
1053 }
1054
1055 private TokBuf(Type exprType, List<Token> tokens, TokBuf parent,
1056 int parentOff, ParseContext context) {
1057 _exprType = exprType;
1058 _tokens = tokens;
1059 _parent = parent;
1060 _parentOff = parentOff;
1061 _ctx = context;
1062 }
1063
1064 public Type getExprType() {
1065 return _exprType;
1066 }
1067
1068 public int curPos() {
1069 return _pos;
1070 }
1071
1072 public int prevPos() {
1073 return _pos - 1;
1074 }
1075
1076 public boolean hasNext() {
1077 return (_pos < _tokens.size());
1078 }
1079
1080 public Token peekNext() {
1081 if(!hasNext()) {
1082 return null;
1083 }
1084 return _tokens.get(_pos);
1085 }
1086
1087 public Token next() {
1088 if(!hasNext()) {
1089 throw new ParseException(
1090 "Unexpected end of expression " + this);
1091 }
1092 return _tokens.get(_pos++);
1093 }
1094
1095 public void reset(int pos) {
1096 _pos = pos;
1097 }
1098
1099 public TokBuf subBuf(int start, int end) {
1100 return new TokBuf(_tokens.subList(start, end), this, start);
1101 }
1102
1103 public void setPendingExpr(Expr expr) {
1104 if(_pendingExpr != null) {
1105 throw new ParseException(
1106 "Found multiple expressions with no operator " + this);
1107 }
1108 _pendingExpr = expr.resolveOrderOfOperations();
1109 }
1110
1111 public void restorePendingExpr(Expr expr) {
1112
1113 _pendingExpr = expr;
1114 }
1115
1116 public Expr takePendingExpr() {
1117 Expr expr = _pendingExpr;
1118 _pendingExpr = null;
1119 return expr;
1120 }
1121
1122 public boolean hasPendingExpr() {
1123 return (_pendingExpr != null);
1124 }
1125
1126 private Map.Entry<Integer,List<Token>> getTopPos() {
1127 int pos = _pos;
1128 List<Token> toks = _tokens;
1129 TokBuf cur = this;
1130 while(cur._parent != null) {
1131 pos += cur._parentOff;
1132 cur = cur._parent;
1133 toks = cur._tokens;
1134 }
1135 return ExpressionTokenizer.newEntry(pos, toks);
1136 }
1137
1138 public Function getFunction(String funcName) {
1139 return _ctx.getFunctionLookup().getFunction(funcName);
1140 }
1141
1142 @Override
1143 public String toString() {
1144
1145 Map.Entry<Integer,List<Token>> e = getTopPos();
1146
1147
1148 StringBuilder sb = new StringBuilder()
1149 .append("[token ").append(e.getKey()).append("] (");
1150
1151 for(Iterator<Token> iter = e.getValue().iterator(); iter.hasNext(); ) {
1152 Token t = iter.next();
1153 sb.append("'").append(t.getValueStr()).append("'");
1154 if(iter.hasNext()) {
1155 sb.append(",");
1156 }
1157 }
1158
1159 sb.append(")");
1160
1161 if(_pendingExpr != null) {
1162 sb.append(" [pending '").append(_pendingExpr.toDebugString(_ctx))
1163 .append("']");
1164 }
1165
1166 return sb.toString();
1167 }
1168 }
1169
1170 private static boolean isHigherPrecendence(OpType op1, OpType op2) {
1171 int prec1 = PRECENDENCE.get(op1);
1172 int prec2 = PRECENDENCE.get(op2);
1173
1174
1175 return (prec1 < prec2);
1176 }
1177
1178 private static final Map<OpType, Integer> buildPrecedenceMap(
1179 OpType[]... opArrs) {
1180 Map<OpType, Integer> prec = new HashMap<OpType, Integer>();
1181
1182 int level = 0;
1183 for(OpType[] ops : opArrs) {
1184 for(OpType op : ops) {
1185 prec.put(op, level);
1186 }
1187 ++level;
1188 }
1189
1190 return prec;
1191 }
1192
1193 private static void exprListToString(
1194 List<Expr> exprs, String sep, LocaleContext ctx, StringBuilder sb,
1195 boolean isDebug) {
1196 Iterator<Expr> iter = exprs.iterator();
1197 iter.next().toString(ctx, sb, isDebug);
1198 while(iter.hasNext()) {
1199 sb.append(sep);
1200 iter.next().toString(ctx, sb, isDebug);
1201 }
1202 }
1203
1204 private static Value[] exprListToValues(
1205 List<Expr> exprs, EvalContext ctx) {
1206 Valueess/expr/Value.html#Value">Value[] paramVals = new Value[exprs.size()];
1207 for(int i = 0; i < exprs.size(); ++i) {
1208 paramVals[i] = exprs.get(i).eval(ctx);
1209 }
1210 return paramVals;
1211 }
1212
1213 private static Value[] exprListToDelayedValues(
1214 List<Expr> exprs, EvalContext ctx) {
1215 Valueess/expr/Value.html#Value">Value[] paramVals = new Value[exprs.size()];
1216 for(int i = 0; i < exprs.size(); ++i) {
1217 paramVals[i] = new DelayedValue(exprs.get(i), ctx);
1218 }
1219 return paramVals;
1220 }
1221
1222 private static boolean areConstant(List<Expr> exprs) {
1223 for(Expr expr : exprs) {
1224 if(!expr.isConstant()) {
1225 return false;
1226 }
1227 }
1228 return true;
1229 }
1230
1231 private static boolean areConstant(Expr... exprs) {
1232 for(Expr expr : exprs) {
1233 if(!expr.isConstant()) {
1234 return false;
1235 }
1236 }
1237 return true;
1238 }
1239
1240 private static void literalStrToString(String str, StringBuilder sb) {
1241 sb.append("\"")
1242 .append(StringUtils.replace(str, "\"", "\"\""))
1243 .append("\"");
1244 }
1245
1246
1247
1248
1249
1250 public static Pattern likePatternToRegex(String pattern) {
1251
1252 StringBuilder sb = new StringBuilder(pattern.length());
1253
1254
1255
1256
1257
1258
1259
1260 for(int i = 0; i < pattern.length(); ++i) {
1261 char c = pattern.charAt(i);
1262
1263 if(c == '*') {
1264 sb.append(".*");
1265 } else if(c == '?') {
1266 sb.append('.');
1267 } else if(c == '#') {
1268 sb.append("\\d");
1269 } else if(c == '[') {
1270
1271
1272 int startPos = i + 1;
1273 int endPos = -1;
1274 for(int j = startPos; j < pattern.length(); ++j) {
1275 if(pattern.charAt(j) == ']') {
1276 endPos = j;
1277 break;
1278 }
1279 }
1280
1281
1282 if(endPos == -1) {
1283 return UNMATCHABLE_REGEX;
1284 }
1285
1286 String charClass = pattern.substring(startPos, endPos);
1287
1288 if((charClass.length() > 0) && (charClass.charAt(0) == '!')) {
1289
1290 charClass = '^' + charClass.substring(1);
1291 }
1292
1293 sb.append('[').append(charClass).append(']');
1294 i += (endPos - startPos) + 1;
1295
1296 } else if(isRegexSpecialChar(c)) {
1297
1298 sb.append('\\').append(c);
1299 } else {
1300 sb.append(c);
1301 }
1302 }
1303
1304 try {
1305 return Pattern.compile(sb.toString(),
1306 Pattern.CASE_INSENSITIVE | Pattern.DOTALL |
1307 Pattern.UNICODE_CASE);
1308 } catch(PatternSyntaxException ignored) {
1309 return UNMATCHABLE_REGEX;
1310 }
1311 }
1312
1313 public static boolean isRegexSpecialChar(char c) {
1314 return REGEX_SPEC_CHARS.contains(c);
1315 }
1316
1317 private static Value toLiteralValue(Value.Type valType, Object value) {
1318 switch(valType) {
1319 case STRING:
1320 return ValueSupport.toValue((String)value);
1321 case DATE:
1322 case TIME:
1323 case DATE_TIME:
1324 return ValueSupport.toValue(valType, (LocalDateTime)value);
1325 case LONG:
1326 return ValueSupport.toValue((Integer)value);
1327 case DOUBLE:
1328 return ValueSupport.toValue((Double)value);
1329 case BIG_DEC:
1330 return ValueSupport.toValue((BigDecimal)value);
1331 default:
1332 throw new ParseException("unexpected literal type " + valType);
1333 }
1334 }
1335
1336 private static boolean isLiteralDefaultValue(
1337 TokBuf buf, Value.Type resultType, String exprStr) {
1338
1339
1340
1341
1342
1343 if(buf.getExprType() != Type.DEFAULT_VALUE) {
1344 return false;
1345 }
1346
1347
1348
1349 if(isOp(buf.peekNext(), "=")) {
1350 buf.next();
1351 return false;
1352 }
1353
1354 return((resultType == Value.Type.STRING) &&
1355 ((exprStr.length() == 0) ||
1356 (exprStr.charAt(0) != ExpressionTokenizer.QUOTED_STR_CHAR)));
1357 }
1358
1359 private interface LeftAssocExpr {
1360 public OpType getOp();
1361 public Expr getLeft();
1362 public void setLeft(Expr left);
1363 }
1364
1365 private interface RightAssocExpr {
1366 public OpType getOp();
1367 public Expr getRight();
1368 public void setRight(Expr right);
1369 }
1370
1371 private static final class DelayedValue extends BaseDelayedValue
1372 {
1373 private final Expr _expr;
1374 private final EvalContext _ctx;
1375
1376 private DelayedValue(Expr expr, EvalContext ctx) {
1377 _expr = expr;
1378 _ctx = ctx;
1379 }
1380
1381 @Override
1382 public Value eval() {
1383 return _expr.eval(_ctx);
1384 }
1385 }
1386
1387
1388 private static abstract class Expr
1389 {
1390 public String toCleanString(LocaleContext ctx) {
1391 return toString(ctx, new StringBuilder(), false).toString();
1392 }
1393
1394 public String toDebugString(LocaleContext ctx) {
1395 return toString(ctx, new StringBuilder(), true).toString();
1396 }
1397
1398 protected boolean isValidationExpr() {
1399 return false;
1400 }
1401
1402 protected StringBuilder toString(
1403 LocaleContext ctx, StringBuilder sb, boolean isDebug) {
1404 if(isDebug) {
1405 sb.append("<").append(getClass().getSimpleName()).append(">{");
1406 }
1407 toExprString(ctx, sb, isDebug);
1408 if(isDebug) {
1409 sb.append("}");
1410 }
1411 return sb;
1412 }
1413
1414 protected Expr resolveOrderOfOperations() {
1415
1416 if(!(this instanceof LeftAssocExpr)) {
1417
1418 return this;
1419 }
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430 Expr outerExpr = this;
1431 final LeftAssocExpr thisExpr = (LeftAssocExpr)this;
1432 final Expr thisLeft = thisExpr.getLeft();
1433
1434
1435 if(thisLeft instanceof RightAssocExpr) {
1436
1437 RightAssocExpr leftOp = (RightAssocExpr)thisLeft;
1438
1439
1440
1441 thisExpr.setLeft(leftOp.getRight());
1442
1443
1444
1445
1446 leftOp.setRight(resolveOrderOfOperations());
1447 outerExpr = thisLeft;
1448
1449
1450
1451
1452
1453
1454
1455
1456 if((leftOp.getRight() == this) &&
1457 !isHigherPrecendence(thisExpr.getOp(), leftOp.getOp())) {
1458
1459
1460
1461 leftOp.setRight(thisExpr.getLeft());
1462 thisExpr.setLeft(thisLeft);
1463 outerExpr = this;
1464 }
1465 }
1466
1467 return outerExpr;
1468 }
1469
1470 public abstract boolean isConstant();
1471
1472 public abstract Value eval(EvalContext ctx);
1473
1474 public abstract void collectIdentifiers(Collection<Identifier> identifiers);
1475
1476 protected abstract void toExprString(
1477 LocaleContext ctx, StringBuilder sb, boolean isDebug);
1478 }
1479
1480 private static final class EConstValue extends Expr
1481 {
1482 private final Value _val;
1483 private final String _str;
1484
1485 private EConstValue(Value val, String str) {
1486 _val = val;
1487 _str = str;
1488 }
1489
1490 @Override
1491 public boolean isConstant() {
1492 return true;
1493 }
1494
1495 @Override
1496 public Value eval(EvalContext ctx) {
1497 return _val;
1498 }
1499
1500 @Override
1501 public void collectIdentifiers(Collection<Identifier> identifiers) {
1502
1503 }
1504
1505 @Override
1506 protected void toExprString(
1507 LocaleContext ctx, StringBuilder sb, boolean isDebug) {
1508 sb.append(_str);
1509 }
1510 }
1511
1512 private static final class EThisValue extends Expr
1513 {
1514 @Override
1515 public boolean isConstant() {
1516 return false;
1517 }
1518 @Override
1519 public Value eval(EvalContext ctx) {
1520 return ctx.getThisColumnValue();
1521 }
1522 @Override
1523 public void collectIdentifiers(Collection<Identifier> identifiers) {
1524
1525 }
1526 @Override
1527 protected void toExprString(
1528 LocaleContext ctx, StringBuilder sb, boolean isDebug) {
1529 if(isDebug) {
1530 sb.append("<THIS_COL>");
1531 }
1532 }
1533 }
1534
1535 private static final class ELiteralValue extends Expr
1536 {
1537 private final Value _val;
1538
1539 private ELiteralValue(Value.Type valType, Object value) {
1540 _val = toLiteralValue(valType, value);
1541 }
1542
1543 @Override
1544 public boolean isConstant() {
1545 return true;
1546 }
1547
1548 @Override
1549 public Value eval(EvalContext ctx) {
1550 return _val;
1551 }
1552
1553 @Override
1554 public void collectIdentifiers(Collection<Identifier> identifiers) {
1555
1556 }
1557
1558 @Override
1559 protected void toExprString(
1560 LocaleContext ctx, StringBuilder sb, boolean isDebug) {
1561 if(_val.getType() == Value.Type.STRING) {
1562 literalStrToString((String)_val.get(), sb);
1563 } else if(_val.getType().isTemporal()) {
1564 sb.append("#").append(_val.getAsString(ctx)).append("#");
1565 } else {
1566 sb.append(_val.get());
1567 }
1568 }
1569 }
1570
1571 private static final class EObjValue extends Expr
1572 {
1573 private final Identifier _identifier;
1574
1575 private EObjValue(Identifier identifier) {
1576 _identifier = identifier;
1577 }
1578
1579 @Override
1580 public boolean isConstant() {
1581 return false;
1582 }
1583
1584 @Override
1585 public Value eval(EvalContext ctx) {
1586 return ctx.getIdentifierValue(_identifier);
1587 }
1588
1589 @Override
1590 public void collectIdentifiers(Collection<Identifier> identifiers) {
1591 identifiers.add(_identifier);
1592 }
1593
1594 @Override
1595 protected void toExprString(
1596 LocaleContext ctx, StringBuilder sb, boolean isDebug) {
1597 sb.append(_identifier);
1598 }
1599 }
1600
1601 private static class EParen extends Expr
1602 {
1603 private final Expr _expr;
1604
1605 private EParen(Expr expr) {
1606 _expr = expr;
1607 }
1608
1609 @Override
1610 public boolean isConstant() {
1611 return _expr.isConstant();
1612 }
1613
1614 @Override
1615 protected boolean isValidationExpr() {
1616 return _expr.isValidationExpr();
1617 }
1618
1619 @Override
1620 public Value eval(EvalContext ctx) {
1621 return _expr.eval(ctx);
1622 }
1623
1624 @Override
1625 public void collectIdentifiers(Collection<Identifier> identifiers) {
1626 _expr.collectIdentifiers(identifiers);
1627 }
1628
1629 @Override
1630 protected void toExprString(
1631 LocaleContext ctx, StringBuilder sb, boolean isDebug) {
1632 sb.append("(");
1633 _expr.toString(ctx, sb, isDebug);
1634 sb.append(")");
1635 }
1636 }
1637
1638 private static class EFunc extends Expr
1639 {
1640 private final Function _func;
1641 private final List<Expr> _params;
1642
1643 private EFunc(Function func, List<Expr> params) {
1644 _func = func;
1645 _params = params;
1646 }
1647
1648 @Override
1649 public boolean isConstant() {
1650 return _func.isPure() && areConstant(_params);
1651 }
1652
1653 @Override
1654 public Value eval(EvalContext ctx) {
1655 return _func.eval(ctx, exprListToValues(_params, ctx));
1656 }
1657
1658 @Override
1659 public void collectIdentifiers(Collection<Identifier> identifiers) {
1660 for(Expr param : _params) {
1661 param.collectIdentifiers(identifiers);
1662 }
1663 }
1664
1665 @Override
1666 protected void toExprString(
1667 LocaleContext ctx, StringBuilder sb, boolean isDebug) {
1668 sb.append(_func.getName()).append("(");
1669
1670 if(!_params.isEmpty()) {
1671 exprListToString(_params, ",", ctx, sb, isDebug);
1672 }
1673
1674 sb.append(")");
1675 }
1676 }
1677
1678 private static abstract class EBaseBinaryOp extends Expr
1679 implements LeftAssocExpr, RightAssocExpr
1680 {
1681 protected final OpType _op;
1682 protected Expr _left;
1683 protected Expr _right;
1684
1685 private EBaseBinaryOp(OpType op, Expr left, Expr right) {
1686 _op = op;
1687 _left = left;
1688 _right = right;
1689 }
1690
1691 @Override
1692 public boolean isConstant() {
1693 return areConstant(_left, _right);
1694 }
1695
1696 @Override
1697 public OpType getOp() {
1698 return _op;
1699 }
1700
1701 @Override
1702 public Expr getLeft() {
1703 return _left;
1704 }
1705
1706 @Override
1707 public void setLeft(Expr left) {
1708 _left = left;
1709 }
1710
1711 @Override
1712 public Expr getRight() {
1713 return _right;
1714 }
1715
1716 @Override
1717 public void setRight(Expr right) {
1718 _right = right;
1719 }
1720
1721 @Override
1722 public void collectIdentifiers(Collection<Identifier> identifiers) {
1723 _left.collectIdentifiers(identifiers);
1724 _right.collectIdentifiers(identifiers);
1725 }
1726
1727 @Override
1728 protected void toExprString(
1729 LocaleContext ctx, StringBuilder sb, boolean isDebug) {
1730 appendLeadingExpr(_left, ctx, sb, isDebug)
1731 .append(_op).append(" ");
1732 _right.toString(ctx, sb, isDebug);
1733 }
1734 }
1735
1736 private static class EBinaryOp extends EBaseBinaryOp
1737 {
1738 private EBinaryOp(BinaryOp op, Expr left, Expr right) {
1739 super(op, left, right);
1740 }
1741
1742 @Override
1743 public Value eval(EvalContext ctx) {
1744 return ((BinaryOp)_op).eval(ctx, _left.eval(ctx), _right.eval(ctx));
1745 }
1746 }
1747
1748 private static class EUnaryOp extends Expr
1749 implements RightAssocExpr
1750 {
1751 private final OpType _op;
1752 private Expr _expr;
1753
1754 private EUnaryOp(UnaryOp op, Expr expr) {
1755 _op = op;
1756 _expr = expr;
1757 }
1758
1759 @Override
1760 public boolean isConstant() {
1761 return _expr.isConstant();
1762 }
1763
1764 @Override
1765 public OpType getOp() {
1766 return _op;
1767 }
1768
1769 @Override
1770 public Expr getRight() {
1771 return _expr;
1772 }
1773
1774 @Override
1775 public void setRight(Expr right) {
1776 _expr = right;
1777 }
1778
1779 @Override
1780 public Value eval(EvalContext ctx) {
1781 return ((UnaryOp)_op).eval(ctx, _expr.eval(ctx));
1782 }
1783
1784 @Override
1785 public void collectIdentifiers(Collection<Identifier> identifiers) {
1786 _expr.collectIdentifiers(identifiers);
1787 }
1788
1789 @Override
1790 protected void toExprString(
1791 LocaleContext ctx, StringBuilder sb, boolean isDebug) {
1792 sb.append(_op);
1793 if(isDebug || ((UnaryOp)_op).needsSpace()) {
1794 sb.append(" ");
1795 }
1796 _expr.toString(ctx, sb, isDebug);
1797 }
1798 }
1799
1800 private static class ECompOp extends EBaseBinaryOp
1801 {
1802 private ECompOp(CompOp op, Expr left, Expr right) {
1803 super(op, left, right);
1804 }
1805
1806 @Override
1807 protected boolean isValidationExpr() {
1808 return true;
1809 }
1810
1811 @Override
1812 public Value eval(EvalContext ctx) {
1813 return ((CompOp)_op).eval(ctx, _left.eval(ctx), _right.eval(ctx));
1814 }
1815 }
1816
1817 private static class EImplicitCompOp extends ECompOp
1818 {
1819 private EImplicitCompOp(Expr right) {
1820 super(CompOp.EQ, THIS_COL_VALUE, right);
1821 }
1822
1823 @Override
1824 protected void toExprString(
1825 LocaleContext ctx, StringBuilder sb, boolean isDebug) {
1826
1827 if(isDebug) {
1828 super.toExprString(ctx, sb, isDebug);
1829 } else {
1830
1831 _right.toString(ctx, sb, isDebug);
1832 }
1833 }
1834 }
1835
1836 private static class ELogicalOp extends EBaseBinaryOp
1837 {
1838 private ELogicalOp(LogOp op, Expr left, Expr right) {
1839 super(op, left, right);
1840 }
1841
1842 @Override
1843 protected boolean isValidationExpr() {
1844 return true;
1845 }
1846
1847 @Override
1848 public Value eval(final EvalContext ctx) {
1849
1850
1851
1852 return ((LogOp)_op).eval(ctx, new DelayedValue(_left, ctx),
1853 new DelayedValue(_right, ctx));
1854 }
1855 }
1856
1857 private static abstract class ESpecOp extends Expr
1858 implements LeftAssocExpr
1859 {
1860 protected final SpecOp _op;
1861 protected Expr _expr;
1862
1863 private ESpecOp(SpecOp op, Expr expr) {
1864 _op = op;
1865 _expr = expr;
1866 }
1867
1868 @Override
1869 public boolean isConstant() {
1870 return _expr.isConstant();
1871 }
1872
1873 @Override
1874 public OpType getOp() {
1875 return _op;
1876 }
1877
1878 @Override
1879 public Expr getLeft() {
1880 return _expr;
1881 }
1882
1883 @Override
1884 public void setLeft(Expr left) {
1885 _expr = left;
1886 }
1887
1888 @Override
1889 public void collectIdentifiers(Collection<Identifier> identifiers) {
1890 _expr.collectIdentifiers(identifiers);
1891 }
1892
1893 @Override
1894 protected boolean isValidationExpr() {
1895 return true;
1896 }
1897 }
1898
1899 private static class ENullOp extends ESpecOp
1900 {
1901 private ENullOp(SpecOp op, Expr expr) {
1902 super(op, expr);
1903 }
1904
1905 @Override
1906 public Value eval(EvalContext ctx) {
1907 return _op.eval(ctx, _expr.eval(ctx), null, null);
1908 }
1909
1910 @Override
1911 protected void toExprString(
1912 LocaleContext ctx, StringBuilder sb, boolean isDebug) {
1913 appendLeadingExpr(_expr, ctx, sb, isDebug)
1914 .append(_op);
1915 }
1916 }
1917
1918 private static class ELikeOp extends ESpecOp
1919 {
1920 private final String _patternStr;
1921 private Pattern _pattern;
1922
1923 private ELikeOp(SpecOp op, Expr expr, String patternStr) {
1924 super(op, expr);
1925 _patternStr = patternStr;
1926 }
1927
1928 private Pattern getPattern()
1929 {
1930 if(_pattern == null) {
1931 _pattern = likePatternToRegex(_patternStr);
1932 }
1933 return _pattern;
1934 }
1935
1936 @Override
1937 public Value eval(EvalContext ctx) {
1938 return _op.eval(ctx, _expr.eval(ctx), getPattern(), null);
1939 }
1940
1941 @Override
1942 protected void toExprString(
1943 LocaleContext ctx, StringBuilder sb, boolean isDebug) {
1944 appendLeadingExpr(_expr, ctx, sb, isDebug)
1945 .append(_op).append(" ");
1946 literalStrToString(_patternStr, sb);
1947 if(isDebug) {
1948 sb.append("(").append(getPattern()).append(")");
1949 }
1950 }
1951 }
1952
1953 private static class EInOp extends ESpecOp
1954 {
1955 private final List<Expr> _exprs;
1956
1957 private EInOp(SpecOp op, Expr expr, List<Expr> exprs) {
1958 super(op, expr);
1959 _exprs = exprs;
1960 }
1961
1962 @Override
1963 public boolean isConstant() {
1964 return super.isConstant() && areConstant(_exprs);
1965 }
1966
1967 @Override
1968 public Value eval(EvalContext ctx) {
1969 return _op.eval(ctx, _expr.eval(ctx),
1970 exprListToDelayedValues(_exprs, ctx), null);
1971 }
1972
1973 @Override
1974 public void collectIdentifiers(Collection<Identifier> identifiers) {
1975 for(Expr expr : _exprs) {
1976 expr.collectIdentifiers(identifiers);
1977 }
1978 }
1979
1980 @Override
1981 protected void toExprString(
1982 LocaleContext ctx, StringBuilder sb, boolean isDebug) {
1983 appendLeadingExpr(_expr, ctx, sb, isDebug)
1984 .append(_op).append(" (");
1985 exprListToString(_exprs, ",", ctx, sb, isDebug);
1986 sb.append(")");
1987 }
1988 }
1989
1990 private static class EBetweenOp extends ESpecOp
1991 implements RightAssocExpr
1992 {
1993 private final Expr _startRangeExpr;
1994 private Expr _endRangeExpr;
1995
1996 private EBetweenOp(SpecOp op, Expr expr, Expr startRangeExpr,
1997 Expr endRangeExpr) {
1998 super(op, expr);
1999 _startRangeExpr = startRangeExpr;
2000 _endRangeExpr = endRangeExpr;
2001 }
2002
2003 @Override
2004 public boolean isConstant() {
2005 return _expr.isConstant() && areConstant(_startRangeExpr, _endRangeExpr);
2006 }
2007
2008 @Override
2009 public Expr getRight() {
2010 return _endRangeExpr;
2011 }
2012
2013 @Override
2014 public void setRight(Expr right) {
2015 _endRangeExpr = right;
2016 }
2017
2018 @Override
2019 public Value eval(EvalContext ctx) {
2020 return _op.eval(ctx, _expr.eval(ctx),
2021 new DelayedValue(_startRangeExpr, ctx),
2022 new DelayedValue(_endRangeExpr, ctx));
2023 }
2024
2025 @Override
2026 public void collectIdentifiers(Collection<Identifier> identifiers) {
2027 super.collectIdentifiers(identifiers);
2028 _startRangeExpr.collectIdentifiers(identifiers);
2029 _endRangeExpr.collectIdentifiers(identifiers);
2030 }
2031
2032 @Override
2033 protected void toExprString(
2034 LocaleContext ctx, StringBuilder sb, boolean isDebug) {
2035 appendLeadingExpr(_expr, ctx, sb, isDebug)
2036 .append(_op).append(" ");
2037 _startRangeExpr.toString(ctx, sb, isDebug);
2038 sb.append(" And ");
2039 _endRangeExpr.toString(ctx, sb, isDebug);
2040 }
2041 }
2042
2043
2044
2045
2046 private static abstract class BaseExprWrapper implements Expression
2047 {
2048 private final String _rawExprStr;
2049 private final Expr _expr;
2050
2051 private BaseExprWrapper(String rawExprStr, Expr expr) {
2052 _rawExprStr = rawExprStr;
2053 _expr = expr;
2054 }
2055
2056 @Override
2057 public String toDebugString(LocaleContext ctx) {
2058 return _expr.toDebugString(ctx);
2059 }
2060
2061 @Override
2062 public String toRawString() {
2063 return _rawExprStr;
2064 }
2065
2066 @Override
2067 public String toCleanString(LocaleContext ctx) {
2068 return _expr.toCleanString(ctx);
2069 }
2070
2071 @Override
2072 public boolean isConstant() {
2073 return _expr.isConstant();
2074 }
2075
2076 @Override
2077 public void collectIdentifiers(Collection<Identifier> identifiers) {
2078 _expr.collectIdentifiers(identifiers);
2079 }
2080
2081 @Override
2082 public String toString() {
2083 return toRawString();
2084 }
2085
2086 protected Object evalValue(Value.Type resultType, EvalContext ctx) {
2087 Value val = _expr.eval(ctx);
2088
2089 if(val.isNull()) {
2090 return null;
2091 }
2092
2093 if(resultType == null) {
2094
2095 return val.get();
2096 }
2097
2098
2099 switch(resultType) {
2100 case STRING:
2101 return val.getAsString(ctx);
2102 case DATE:
2103 case TIME:
2104 case DATE_TIME:
2105 return val.getAsLocalDateTime(ctx);
2106 case LONG:
2107 return val.getAsLongInt(ctx);
2108 case DOUBLE:
2109 return val.getAsDouble(ctx);
2110 case BIG_DEC:
2111 return val.getAsBigDecimal(ctx);
2112 default:
2113 throw new IllegalStateException("unexpected result type " + resultType);
2114 }
2115 }
2116
2117 protected Boolean evalCondition(EvalContext ctx) {
2118 Value val = _expr.eval(ctx);
2119
2120 if(val.isNull()) {
2121
2122 throw new EvalException("Condition evaluated to Null");
2123 }
2124
2125 return val.getAsBoolean(ctx);
2126 }
2127 }
2128
2129
2130
2131
2132 private static class ExprWrapper extends BaseExprWrapper
2133 {
2134 private final Value.Type _resultType;
2135
2136 private ExprWrapper(String rawExprStr, Expr expr, Value.Type resultType) {
2137 super(rawExprStr, expr);
2138 _resultType = resultType;
2139 }
2140
2141 @Override
2142 public Object eval(EvalContext ctx) {
2143 return evalValue(_resultType, ctx);
2144 }
2145 }
2146
2147
2148
2149
2150
2151 private static class CondExprWrapper extends BaseExprWrapper
2152 {
2153 private CondExprWrapper(String rawExprStr, Expr expr) {
2154 super(rawExprStr, expr);
2155 }
2156
2157 @Override
2158 public Object eval(EvalContext ctx) {
2159 return evalCondition(ctx);
2160 }
2161 }
2162
2163
2164
2165
2166
2167 private static final class MemoizedExprWrapper extends ExprWrapper
2168 {
2169 private Object _val;
2170
2171 private MemoizedExprWrapper(String rawExprStr, Expr expr,
2172 Value.Type resultType) {
2173 super(rawExprStr, expr, resultType);
2174 }
2175
2176 @Override
2177 public Object eval(EvalContext ctx) {
2178 if(_val == null) {
2179 _val = super.eval(ctx);
2180 }
2181 return _val;
2182 }
2183 }
2184
2185
2186
2187
2188
2189 private static final class MemoizedCondExprWrapper extends CondExprWrapper
2190 {
2191 private Object _val;
2192
2193 private MemoizedCondExprWrapper(String rawExprStr, Expr expr) {
2194 super(rawExprStr, expr);
2195 }
2196
2197 @Override
2198 public Object eval(EvalContext ctx) {
2199 if(_val == null) {
2200 _val = super.eval(ctx);
2201 }
2202 return _val;
2203 }
2204 }
2205 }