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.StringUtil;
46 import com.healthmarketscience.jackcess.impl.expr.ExpressionTokenizer.Token;
47 import com.healthmarketscience.jackcess.impl.expr.ExpressionTokenizer.TokenType;
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<>();
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<>(
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 return parse(exprType, exprStr, resultType, context, true);
414 }
415
416
417
418
419
420
421 public static Expression parse(Type exprType, String exprStr,
422 Value.Type resultType,
423 ParseContext context,
424 boolean allowLiteralDefault) {
425
426 List<Token> tokens = trimSpaces(
427 ExpressionTokenizer.tokenize(exprType, exprStr, context));
428
429 if(tokens == null) {
430 throw new ParseException("null/empty expression");
431 }
432
433 TokBuf buf = new TokBuf(exprType, tokens, context);
434
435 if(isLiteralDefaultValue(buf, resultType, exprStr, allowLiteralDefault)) {
436
437
438
439 return new ExprWrapper(exprStr,
440 new ELiteralValue(Value.Type.STRING, exprStr), resultType);
441 }
442
443
444 Expr expr = parseExpression(buf, false);
445
446 if((exprType == Type.FIELD_VALIDATOR) && !expr.isValidationExpr()) {
447
448
449
450 expr = new EImplicitCompOp(expr);
451 }
452
453 switch(exprType) {
454 case DEFAULT_VALUE:
455 case EXPRESSION:
456 return (expr.isConstant() ?
457
458
459 new MemoizedExprWrapper(exprStr, expr, resultType) :
460 new ExprWrapper(exprStr, expr, resultType));
461 case FIELD_VALIDATOR:
462 case RECORD_VALIDATOR:
463 return (expr.isConstant() ?
464
465
466 new MemoizedCondExprWrapper(exprStr, expr) :
467 new CondExprWrapper(exprStr, expr));
468 default:
469 throw new ParseException("unexpected expression type " + exprType);
470 }
471 }
472
473 private static List<Token> trimSpaces(List<Token> tokens) {
474 if(tokens == null) {
475 return null;
476 }
477
478
479
480
481 for(int i = 1; i < (tokens.size() - 1); ++i) {
482 Token t = tokens.get(i);
483 if(t.getType() == TokenType.SPACE) {
484 if((tokens.get(i - 1).getType() == TokenType.STRING) &&
485 isDelim(tokens.get(i + 1), FUNC_START_DELIM)) {
486
487 } else {
488 tokens.remove(i);
489 --i;
490 }
491 }
492 }
493 return tokens;
494 }
495
496 private static Expr parseExpression(TokBuf buf, boolean singleExpr)
497 {
498 while(buf.hasNext()) {
499 Token t = buf.next();
500
501 switch(t.getType()) {
502 case OBJ_NAME:
503
504 parseObjectRefExpression(t, buf);
505 break;
506
507 case LITERAL:
508
509 buf.setPendingExpr(new ELiteralValue(t.getValueType(), t.getValue()));
510 break;
511
512 case OP:
513
514 WordType wordType = getWordType(t);
515 if(wordType == null) {
516
517 throw new ParseException("Invalid operator " + t);
518 }
519
520
521
522 switch(wordType) {
523 case OP:
524 parseOperatorExpression(t, buf);
525 break;
526
527 case COMP:
528
529 parseCompOpExpression(t, buf);
530 break;
531
532 default:
533 throw new ParseException("Unexpected OP word type " + wordType);
534 }
535
536 break;
537
538 case DELIM:
539
540 parseDelimExpression(t, buf);
541 break;
542
543 case STRING:
544
545
546 wordType = getWordType(t);
547 if(wordType == null) {
548
549
550 if(!maybeParseFuncCallExpression(t, buf)) {
551
552
553 Token next = buf.peekNext();
554 if((next != null) && isObjNameSep(next)) {
555
556 parseObjectRefExpression(t, buf);
557
558 } else {
559
560
561 throw new UnsupportedOperationException("FIXME");
562 }
563 }
564
565 } else {
566
567
568
569 switch(wordType) {
570 case OP:
571
572 parseOperatorExpression(t, buf);
573 break;
574
575 case LOG_OP:
576
577 parseLogicalOpExpression(t, buf);
578 break;
579
580 case CONST:
581
582 parseConstExpression(t, buf);
583 break;
584
585 case SPEC_OP_PREFIX:
586
587 parseSpecOpExpression(t, buf);
588 break;
589
590 default:
591 throw new ParseException("Unexpected STRING word type "
592 + wordType);
593 }
594 }
595
596 break;
597
598 case SPACE:
599
600 break;
601
602 default:
603 throw new ParseException("unknown token type " + t);
604 }
605
606 if(singleExpr && buf.hasPendingExpr()) {
607 break;
608 }
609 }
610
611 Expr expr = buf.takePendingExpr();
612 if(expr == null) {
613 throw new ParseException("No expression found? " + buf);
614 }
615
616 return expr;
617 }
618
619 private static void parseObjectRefExpression(Token firstTok, TokBuf buf) {
620
621
622
623
624
625
626
627
628 Deque<String> objNames = new LinkedList<>();
629 objNames.add(firstTok.getValueStr());
630
631 Token t = null;
632 boolean atSep = false;
633 while((t = buf.peekNext()) != null) {
634 if(!atSep) {
635 if(isObjNameSep(t)) {
636 buf.next();
637 atSep = true;
638 continue;
639 }
640 } else {
641 if((t.getType() == TokenType.OBJ_NAME) ||
642 (t.getType() == TokenType.STRING)) {
643 buf.next();
644
645 objNames.addFirst(t.getValueStr());
646 atSep = false;
647 continue;
648 }
649 }
650 break;
651 }
652
653 int numNames = objNames.size();
654 if(atSep || (numNames > 3)) {
655 throw new ParseException("Invalid object reference " + buf);
656 }
657
658
659 String propName = null;
660 if(numNames == 3) {
661 propName = objNames.poll();
662 }
663 String objName = objNames.poll();
664 String collectionName = objNames.poll();
665
666 buf.setPendingExpr(
667 new EObjValue(new Identifier(collectionName, objName, propName)));
668 }
669
670 private static void parseDelimExpression(Token firstTok, TokBuf buf) {
671
672
673 if(!isDelim(firstTok, OPEN_PAREN) || buf.hasPendingExpr()) {
674 throw new ParseException("Unexpected delimiter " +
675 firstTok.getValue() + " " + buf);
676 }
677
678 Expr subExpr = findParenExprs(buf, false).get(0);
679 buf.setPendingExpr(new EParen(subExpr));
680 }
681
682 private static boolean maybeParseFuncCallExpression(
683 Token firstTok, TokBuf buf) {
684
685 int startPos = buf.curPos();
686 boolean foundFunc = false;
687
688 try {
689 Token t = buf.peekNext();
690 if((t != null) && (t.getType() == TokenType.SPACE)) {
691
692
693
694
695 buf.next();
696 t = buf.peekNext();
697 }
698 if(!isDelim(t, FUNC_START_DELIM)) {
699
700 return false;
701 }
702
703 buf.next();
704 List<Expr> params = findParenExprs(buf, true);
705 String funcName = firstTok.getValueStr();
706 Function func = buf.getFunction(funcName);
707 if(func == null) {
708 throw new ParseException("Could not find function '" +
709 funcName + "' " + buf);
710 }
711 buf.setPendingExpr(new EFunc(func, params));
712 foundFunc = true;
713 return true;
714
715 } finally {
716 if(!foundFunc) {
717 buf.reset(startPos);
718 }
719 }
720 }
721
722 private static List<Expr> findParenExprs(
723 TokBuf buf, boolean allowMulti) {
724
725 if(allowMulti) {
726
727 Token t = buf.peekNext();
728 if(isDelim(t, CLOSE_PAREN)) {
729 buf.next();
730 return Collections.emptyList();
731 }
732 }
733
734
735 List<Expr> exprs = new ArrayList<>(3);
736 int level = 1;
737 int startPos = buf.curPos();
738 while(buf.hasNext()) {
739
740 Token t = buf.next();
741
742 if(isDelim(t, OPEN_PAREN)) {
743
744 ++level;
745
746 } else if(isDelim(t, CLOSE_PAREN)) {
747
748 --level;
749 if(level == 0) {
750 TokBuf subBuf = buf.subBuf(startPos, buf.prevPos());
751 exprs.add(parseExpression(subBuf, false));
752 return exprs;
753 }
754
755 } else if(allowMulti && (level == 1) && isDelim(t, FUNC_PARAM_SEP)) {
756
757 TokBuf subBuf = buf.subBuf(startPos, buf.prevPos());
758 exprs.add(parseExpression(subBuf, false));
759 startPos = buf.curPos();
760 }
761 }
762
763 throw new ParseException("Missing closing '" + CLOSE_PAREN
764 + " " + buf);
765 }
766
767 private static void parseOperatorExpression(Token t, TokBuf buf) {
768
769
770
771 if(buf.hasPendingExpr()) {
772 parseBinaryOpExpression(t, buf);
773 } else if(isEitherOp(t, "-", "+")) {
774 parseUnaryOpExpression(t, buf);
775 } else {
776 throw new ParseException(
777 "Missing left expression for binary operator " + t.getValue() +
778 " " + buf);
779 }
780 }
781
782 private static void parseBinaryOpExpression(Token firstTok, TokBuf buf) {
783 BinaryOp op = getOpType(firstTok, BinaryOp.class);
784 Expr leftExpr = buf.takePendingExpr();
785 Expr rightExpr = parseExpression(buf, true);
786
787 buf.setPendingExpr(new EBinaryOp(op, leftExpr, rightExpr));
788 }
789
790 private static void parseUnaryOpExpression(Token firstTok, TokBuf buf) {
791 UnaryOp op = getOpType(firstTok, UnaryOp.class);
792
793 UnaryOp numOp = op.getUnaryNumOp();
794 if(numOp != null) {
795
796
797 Token nextTok = buf.peekNext();
798 if((nextTok != null) && (nextTok.getType() == TokenType.LITERAL) &&
799 nextTok.getValueType().isNumeric()) {
800 op = numOp;
801 }
802 }
803
804 Expr val = parseExpression(buf, true);
805
806 buf.setPendingExpr(new EUnaryOp(op, val));
807 }
808
809 private static void parseCompOpExpression(Token firstTok, TokBuf buf) {
810
811 if(!buf.hasPendingExpr()) {
812 if(buf.getExprType() == Type.FIELD_VALIDATOR) {
813
814
815 buf.setPendingExpr(THIS_COL_VALUE);
816 } else {
817 throw new ParseException(
818 "Missing left expression for comparison operator " +
819 firstTok.getValue() + " " + buf);
820 }
821 }
822
823 CompOp op = getOpType(firstTok, CompOp.class);
824 Expr leftExpr = buf.takePendingExpr();
825 Expr rightExpr = parseExpression(buf, true);
826
827 buf.setPendingExpr(new ECompOp(op, leftExpr, rightExpr));
828 }
829
830 private static void parseLogicalOpExpression(Token firstTok, TokBuf buf) {
831
832 if(!buf.hasPendingExpr()) {
833 throw new ParseException(
834 "Missing left expression for logical operator " +
835 firstTok.getValue() + " " + buf);
836 }
837
838 LogOp op = getOpType(firstTok, LogOp.class);
839 Expr leftExpr = buf.takePendingExpr();
840 Expr rightExpr = parseExpression(buf, true);
841
842 buf.setPendingExpr(new ELogicalOp(op, leftExpr, rightExpr));
843 }
844
845 private static void parseSpecOpExpression(Token firstTok, TokBuf buf) {
846
847 SpecOp specOp = getSpecialOperator(firstTok, buf);
848
849 if(specOp == SpecOp.NOT) {
850
851 parseUnaryOpExpression(firstTok, buf);
852 return;
853 }
854
855 if(!buf.hasPendingExpr()) {
856 if(buf.getExprType() == Type.FIELD_VALIDATOR) {
857
858
859 buf.setPendingExpr(THIS_COL_VALUE);
860 } else {
861 throw new ParseException(
862 "Missing left expression for comparison operator " +
863 specOp + " " + buf);
864 }
865 }
866
867 Expr expr = buf.takePendingExpr();
868
869 Expr specOpExpr = null;
870 switch(specOp) {
871 case IS_NULL:
872 case IS_NOT_NULL:
873 specOpExpr = new ENullOp(specOp, expr);
874 break;
875
876 case LIKE:
877 case NOT_LIKE:
878 Token t = buf.peekNext();
879 boolean literalPattern = false;
880 if((t != null) && (t.getType() == TokenType.LITERAL) &&
881 (t.getValueType() == Value.Type.STRING)) {
882 buf.next();
883
884
885 literalPattern = !isArithmeticOp(buf.peekNext());
886 if(!literalPattern) {
887 buf.reset(buf.prevPos());
888 }
889 }
890 if(literalPattern) {
891
892 specOpExpr = new ELikeOp(specOp, expr, t.getValueStr());
893 } else {
894
895
896 specOpExpr = new EDynamicLikeOp(specOp, expr,
897 parseExpression(buf, true));
898 }
899 break;
900
901 case BETWEEN:
902 case NOT_BETWEEN:
903
904
905
906
907 Expr startRangeExpr = null;
908 while(true) {
909
910 Expr tmpExpr = parseExpression(buf, true);
911 Token tmpT = buf.peekNext();
912
913 if(tmpT == null) {
914
915 throw new ParseException(
916 "Missing 'And' for 'Between' expression " + buf);
917 }
918
919 if(isString(tmpT, "and")) {
920 buf.next();
921 startRangeExpr = tmpExpr;
922 break;
923 }
924
925
926 buf.restorePendingExpr(tmpExpr);
927 }
928
929 Expr endRangeExpr = parseExpression(buf, true);
930
931 specOpExpr = new EBetweenOp(specOp, expr, startRangeExpr, endRangeExpr);
932 break;
933
934 case IN:
935 case NOT_IN:
936
937
938 t = buf.next();
939 if(t.getType() == TokenType.SPACE) {
940 t = buf.next();
941 }
942 if(!isDelim(t, OPEN_PAREN)) {
943 throw new ParseException("Malformed 'In' expression " + buf);
944 }
945
946 List<Expr> exprs = findParenExprs(buf, true);
947 specOpExpr = new EInOp(specOp, expr, exprs);
948 break;
949
950 default:
951 throw new ParseException("Unexpected special op " + specOp);
952 }
953
954 buf.setPendingExpr(specOpExpr);
955 }
956
957 private static SpecOp getSpecialOperator(Token firstTok, TokBuf buf) {
958 String opStr = StringUtil.toLowerCase(firstTok.getValueStr());
959
960 if("is".equals(opStr)) {
961 Token t = buf.peekNext();
962 if(isString(t, "null")) {
963 buf.next();
964 return SpecOp.IS_NULL;
965 } else if(isString(t, "not")) {
966 buf.next();
967 t = buf.peekNext();
968 if(isString(t, "null")) {
969 buf.next();
970 return SpecOp.IS_NOT_NULL;
971 }
972 }
973 } else if("like".equals(opStr)) {
974 return SpecOp.LIKE;
975 } else if("between".equals(opStr)) {
976 return SpecOp.BETWEEN;
977 } else if("in".equals(opStr)) {
978 return SpecOp.IN;
979 } else if("not".equals(opStr)) {
980 Token t = buf.peekNext();
981 if(isString(t, "between")) {
982 buf.next();
983 return SpecOp.NOT_BETWEEN;
984 } else if(isString(t, "in")) {
985 buf.next();
986 return SpecOp.NOT_IN;
987 } else if(isString(t, "like")) {
988 buf.next();
989 return SpecOp.NOT_LIKE;
990 }
991 return SpecOp.NOT;
992 }
993
994 throw new ParseException(
995 "Malformed special operator " + opStr + " " + buf);
996 }
997
998 private static void parseConstExpression(Token firstTok, TokBuf buf) {
999 Expr constExpr = null;
1000 String tokStr = StringUtil.toLowerCase(firstTok.getValueStr());
1001 if(TRUE_STRS.contains(tokStr)) {
1002 constExpr = TRUE_VALUE;
1003 } else if(FALSE_STRS.contains(tokStr)) {
1004 constExpr = FALSE_VALUE;
1005 } else if("null".equals(tokStr)) {
1006 constExpr = NULL_VALUE;
1007 } else {
1008 throw new ParseException("Unexpected CONST word "
1009 + firstTok.getValue());
1010 }
1011 buf.setPendingExpr(constExpr);
1012 }
1013
1014 private static boolean isObjNameSep(Token t) {
1015 return (isDelim(t, ".") || isDelim(t, "!"));
1016 }
1017
1018 private static boolean isOp(Token t, String opStr) {
1019 return ((t != null) && (t.getType() == TokenType.OP) &&
1020 opStr.equalsIgnoreCase(t.getValueStr()));
1021 }
1022
1023 private static boolean isEitherOp(Token t, String opStr1, String opStr2) {
1024 return ((t != null) && (t.getType() == TokenType.OP) &&
1025 (opStr1.equalsIgnoreCase(t.getValueStr()) ||
1026 opStr2.equalsIgnoreCase(t.getValueStr())));
1027 }
1028
1029 private static boolean isDelim(Token t, String opStr) {
1030 return ((t != null) && (t.getType() == TokenType.DELIM) &&
1031 opStr.equalsIgnoreCase(t.getValueStr()));
1032 }
1033
1034 private static boolean isString(Token t, String opStr) {
1035 return ((t != null) && (t.getType() == TokenType.STRING) &&
1036 opStr.equalsIgnoreCase(t.getValueStr()));
1037 }
1038
1039 private static WordType getWordType(Token t) {
1040 return WORD_TYPES.get(StringUtil.toLowerCase(t.getValueStr()));
1041 }
1042
1043
1044
1045
1046
1047
1048
1049 private static boolean isArithmeticOp(Token t) {
1050 if((t == null) ||
1051 ((t.getType() != TokenType.OP) && (t.getType() != TokenType.STRING))) {
1052 return false;
1053 }
1054 return (getWordType(t) == WordType.OP);
1055 }
1056
1057 private static void setWordType(WordType type, String... words) {
1058 for(String w : words) {
1059 WORD_TYPES.put(w, type);
1060 }
1061 }
1062
1063 private static <T extends Enum<T>> T getOpType(Token t, Class<T> opClazz) {
1064 String str = t.getValueStr();
1065 for(T op : opClazz.getEnumConstants()) {
1066 if(str.equalsIgnoreCase(op.toString())) {
1067 return op;
1068 }
1069 }
1070 throw new ParseException("Unexpected op string " + t.getValueStr());
1071 }
1072
1073 private static StringBuilder appendLeadingExpr(
1074 Expr expr, LocaleContext ctx, StringBuilder sb, boolean isDebug)
1075 {
1076 int len = sb.length();
1077 expr.toString(ctx, sb, isDebug);
1078 if(sb.length() > len) {
1079
1080 sb.append(" ");
1081 }
1082 return sb;
1083 }
1084
1085 private static final class TokBuf
1086 {
1087 private final Type _exprType;
1088 private final List<Token> _tokens;
1089 private final TokBuf _parent;
1090 private final int _parentOff;
1091 private final ParseContext _ctx;
1092 private int _pos;
1093 private Expr _pendingExpr;
1094
1095 private TokBuf(Type exprType, List<Token> tokens, ParseContext context) {
1096 this(exprType, tokens, null, 0, context);
1097 }
1098
1099 private TokBuf(List<Token> tokens, TokBuf parent, int parentOff) {
1100 this(parent._exprType, tokens, parent, parentOff, parent._ctx);
1101 }
1102
1103 private TokBuf(Type exprType, List<Token> tokens, TokBuf parent,
1104 int parentOff, ParseContext context) {
1105 _exprType = exprType;
1106 _tokens = tokens;
1107 _parent = parent;
1108 _parentOff = parentOff;
1109 _ctx = context;
1110 }
1111
1112 public Type getExprType() {
1113 return _exprType;
1114 }
1115
1116 public int curPos() {
1117 return _pos;
1118 }
1119
1120 public int prevPos() {
1121 return _pos - 1;
1122 }
1123
1124 public boolean hasNext() {
1125 return (_pos < _tokens.size());
1126 }
1127
1128 public Token peekNext() {
1129 if(!hasNext()) {
1130 return null;
1131 }
1132 return _tokens.get(_pos);
1133 }
1134
1135 public Token next() {
1136 if(!hasNext()) {
1137 throw new ParseException(
1138 "Unexpected end of expression " + this);
1139 }
1140 return _tokens.get(_pos++);
1141 }
1142
1143 public void reset(int pos) {
1144 _pos = pos;
1145 }
1146
1147 public TokBuf subBuf(int start, int end) {
1148 return new TokBuf(_tokens.subList(start, end), this, start);
1149 }
1150
1151 public void setPendingExpr(Expr expr) {
1152 if(_pendingExpr != null) {
1153 throw new ParseException(
1154 "Found multiple expressions with no operator " + this);
1155 }
1156 _pendingExpr = expr.resolveOrderOfOperations();
1157 }
1158
1159 public void restorePendingExpr(Expr expr) {
1160
1161 _pendingExpr = expr;
1162 }
1163
1164 public Expr takePendingExpr() {
1165 Expr expr = _pendingExpr;
1166 _pendingExpr = null;
1167 return expr;
1168 }
1169
1170 public boolean hasPendingExpr() {
1171 return (_pendingExpr != null);
1172 }
1173
1174 private Map.Entry<Integer,List<Token>> getTopPos() {
1175 int pos = _pos;
1176 List<Token> toks = _tokens;
1177 TokBuf cur = this;
1178 while(cur._parent != null) {
1179 pos += cur._parentOff;
1180 cur = cur._parent;
1181 toks = cur._tokens;
1182 }
1183 return ExpressionTokenizer.newEntry(pos, toks);
1184 }
1185
1186 public Function getFunction(String funcName) {
1187 return _ctx.getFunctionLookup().getFunction(funcName);
1188 }
1189
1190 @Override
1191 public String toString() {
1192
1193 Map.Entry<Integer,List<Token>> e = getTopPos();
1194
1195
1196 StringBuilder sb = new StringBuilder()
1197 .append("[token ").append(e.getKey()).append("] (");
1198
1199 for(Iterator<Token> iter = e.getValue().iterator(); iter.hasNext(); ) {
1200 Token t = iter.next();
1201 sb.append("'").append(t.getValueStr()).append("'");
1202 if(iter.hasNext()) {
1203 sb.append(",");
1204 }
1205 }
1206
1207 sb.append(")");
1208
1209 if(_pendingExpr != null) {
1210 sb.append(" [pending '").append(_pendingExpr.toDebugString(_ctx))
1211 .append("']");
1212 }
1213
1214 return sb.toString();
1215 }
1216 }
1217
1218 private static boolean isHigherPrecendence(OpType op1, OpType op2) {
1219 int prec1 = PRECENDENCE.get(op1);
1220 int prec2 = PRECENDENCE.get(op2);
1221
1222
1223 return (prec1 < prec2);
1224 }
1225
1226 private static final Map<OpType, Integer> buildPrecedenceMap(
1227 OpType[]... opArrs) {
1228 Map<OpType, Integer> prec = new HashMap<>();
1229
1230 int level = 0;
1231 for(OpType[] ops : opArrs) {
1232 for(OpType op : ops) {
1233 prec.put(op, level);
1234 }
1235 ++level;
1236 }
1237
1238 return prec;
1239 }
1240
1241 private static void exprListToString(
1242 List<Expr> exprs, String sep, LocaleContext ctx, StringBuilder sb,
1243 boolean isDebug) {
1244 Iterator<Expr> iter = exprs.iterator();
1245 iter.next().toString(ctx, sb, isDebug);
1246 while(iter.hasNext()) {
1247 sb.append(sep);
1248 iter.next().toString(ctx, sb, isDebug);
1249 }
1250 }
1251
1252 private static Value[] exprListToValues(
1253 List<Expr> exprs, EvalContext ctx) {
1254 Valueess/expr/Value.html#Value">Value[] paramVals = new Value[exprs.size()];
1255 for(int i = 0; i < exprs.size(); ++i) {
1256 paramVals[i] = exprs.get(i).eval(ctx);
1257 }
1258 return paramVals;
1259 }
1260
1261 private static Value[] exprListToDelayedValues(
1262 List<Expr> exprs, EvalContext ctx) {
1263 Valueess/expr/Value.html#Value">Value[] paramVals = new Value[exprs.size()];
1264 for(int i = 0; i < exprs.size(); ++i) {
1265 paramVals[i] = new DelayedValue(exprs.get(i), ctx);
1266 }
1267 return paramVals;
1268 }
1269
1270 private static boolean areConstant(List<Expr> exprs) {
1271 for(Expr expr : exprs) {
1272 if(!expr.isConstant()) {
1273 return false;
1274 }
1275 }
1276 return true;
1277 }
1278
1279 private static boolean areConstant(Expr... exprs) {
1280 for(Expr expr : exprs) {
1281 if(!expr.isConstant()) {
1282 return false;
1283 }
1284 }
1285 return true;
1286 }
1287
1288 private static void literalStrToString(String str, StringBuilder sb) {
1289 sb.append("\"")
1290 .append(StringUtil.replace(str, "\"", "\"\""))
1291 .append("\"");
1292 }
1293
1294
1295
1296
1297
1298 public static Pattern likePatternToRegex(String pattern) {
1299
1300 StringBuilder sb = new StringBuilder(pattern.length());
1301
1302
1303
1304
1305
1306
1307
1308 for(int i = 0; i < pattern.length(); ++i) {
1309 char c = pattern.charAt(i);
1310
1311 if(c == '*') {
1312 sb.append(".*");
1313 } else if(c == '?') {
1314 sb.append('.');
1315 } else if(c == '#') {
1316 sb.append("\\d");
1317 } else if(c == '[') {
1318
1319
1320 int startPos = i + 1;
1321 int endPos = -1;
1322 for(int j = startPos; j < pattern.length(); ++j) {
1323 if(pattern.charAt(j) == ']') {
1324 endPos = j;
1325 break;
1326 }
1327 }
1328
1329
1330 if(endPos == -1) {
1331 return UNMATCHABLE_REGEX;
1332 }
1333
1334 String charClass = pattern.substring(startPos, endPos);
1335
1336 if((charClass.length() > 0) && (charClass.charAt(0) == '!')) {
1337
1338 charClass = '^' + charClass.substring(1);
1339 }
1340
1341 sb.append('[').append(charClass).append(']');
1342 i += (endPos - startPos) + 1;
1343
1344 } else if(isRegexSpecialChar(c)) {
1345
1346 sb.append('\\').append(c);
1347 } else {
1348 sb.append(c);
1349 }
1350 }
1351
1352 try {
1353 return Pattern.compile(sb.toString(),
1354 Pattern.CASE_INSENSITIVE | Pattern.DOTALL |
1355 Pattern.UNICODE_CASE);
1356 } catch(PatternSyntaxException ignored) {
1357 return UNMATCHABLE_REGEX;
1358 }
1359 }
1360
1361 public static boolean isRegexSpecialChar(char c) {
1362 return REGEX_SPEC_CHARS.contains(c);
1363 }
1364
1365 private static Value toLiteralValue(Value.Type valType, Object value) {
1366 switch(valType) {
1367 case STRING:
1368 return ValueSupport.toValue((String)value);
1369 case DATE:
1370 case TIME:
1371 case DATE_TIME:
1372 return ValueSupport.toValue(valType, (LocalDateTime)value);
1373 case LONG:
1374 return ValueSupport.toValue((Integer)value);
1375 case DOUBLE:
1376 return ValueSupport.toValue((Double)value);
1377 case BIG_DEC:
1378 return ValueSupport.toValue((BigDecimal)value);
1379 default:
1380 throw new ParseException("unexpected literal type " + valType);
1381 }
1382 }
1383
1384 private static boolean isLiteralDefaultValue(
1385 TokBuf buf, Value.Type resultType, String exprStr,
1386 boolean allowLiteralDefault) {
1387
1388
1389
1390
1391
1392 if(buf.getExprType() != Type.DEFAULT_VALUE) {
1393 return false;
1394 }
1395
1396
1397
1398 if(isOp(buf.peekNext(), "=")) {
1399 buf.next();
1400 return false;
1401 }
1402
1403 return(allowLiteralDefault && (resultType == Value.Type.STRING) &&
1404 ((exprStr.length() == 0) ||
1405 (exprStr.charAt(0) != ExpressionTokenizer.QUOTED_STR_CHAR)));
1406 }
1407
1408 private interface LeftAssocExpr {
1409 public OpType getOp();
1410 public Expr getLeft();
1411 public void setLeft(Expr left);
1412 }
1413
1414 private interface RightAssocExpr {
1415 public OpType getOp();
1416 public Expr getRight();
1417 public void setRight(Expr right);
1418 }
1419
1420 private static final class DelayedValue extends BaseDelayedValue
1421 {
1422 private final Expr _expr;
1423 private final EvalContext _ctx;
1424
1425 private DelayedValue(Expr expr, EvalContext ctx) {
1426 _expr = expr;
1427 _ctx = ctx;
1428 }
1429
1430 @Override
1431 public Value eval() {
1432 return _expr.eval(_ctx);
1433 }
1434 }
1435
1436
1437 private static abstract class Expr
1438 {
1439 public String toCleanString(LocaleContext ctx) {
1440 return toString(ctx, new StringBuilder(), false).toString();
1441 }
1442
1443 public String toDebugString(LocaleContext ctx) {
1444 return toString(ctx, new StringBuilder(), true).toString();
1445 }
1446
1447 protected boolean isValidationExpr() {
1448 return false;
1449 }
1450
1451 protected StringBuilder toString(
1452 LocaleContext ctx, StringBuilder sb, boolean isDebug) {
1453 if(isDebug) {
1454 sb.append("<").append(getClass().getSimpleName()).append(">{");
1455 }
1456 toExprString(ctx, sb, isDebug);
1457 if(isDebug) {
1458 sb.append("}");
1459 }
1460 return sb;
1461 }
1462
1463 protected Expr resolveOrderOfOperations() {
1464
1465 if(!(this instanceof LeftAssocExpr)) {
1466
1467 return this;
1468 }
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479 Expr outerExpr = this;
1480 final LeftAssocExpr thisExpr = (LeftAssocExpr)this;
1481 final Expr thisLeft = thisExpr.getLeft();
1482
1483
1484 if(thisLeft instanceof RightAssocExpr) {
1485
1486 RightAssocExpr leftOp = (RightAssocExpr)thisLeft;
1487
1488
1489
1490 thisExpr.setLeft(leftOp.getRight());
1491
1492
1493
1494
1495 leftOp.setRight(resolveOrderOfOperations());
1496 outerExpr = thisLeft;
1497
1498
1499
1500
1501
1502
1503
1504
1505 if((leftOp.getRight() == this) &&
1506 !isHigherPrecendence(thisExpr.getOp(), leftOp.getOp())) {
1507
1508
1509
1510 leftOp.setRight(thisExpr.getLeft());
1511 thisExpr.setLeft(thisLeft);
1512 outerExpr = this;
1513 }
1514 }
1515
1516 return outerExpr;
1517 }
1518
1519 public abstract boolean isConstant();
1520
1521 public abstract Value eval(EvalContext ctx);
1522
1523 public abstract void collectIdentifiers(Collection<Identifier> identifiers);
1524
1525 protected abstract void toExprString(
1526 LocaleContext ctx, StringBuilder sb, boolean isDebug);
1527 }
1528
1529 private static final class EConstValue extends Expr
1530 {
1531 private final Value _val;
1532 private final String _str;
1533
1534 private EConstValue(Value val, String str) {
1535 _val = val;
1536 _str = str;
1537 }
1538
1539 @Override
1540 public boolean isConstant() {
1541 return true;
1542 }
1543
1544 @Override
1545 public Value eval(EvalContext ctx) {
1546 return _val;
1547 }
1548
1549 @Override
1550 public void collectIdentifiers(Collection<Identifier> identifiers) {
1551
1552 }
1553
1554 @Override
1555 protected void toExprString(
1556 LocaleContext ctx, StringBuilder sb, boolean isDebug) {
1557 sb.append(_str);
1558 }
1559 }
1560
1561 private static final class EThisValue extends Expr
1562 {
1563 @Override
1564 public boolean isConstant() {
1565 return false;
1566 }
1567 @Override
1568 public Value eval(EvalContext ctx) {
1569 return ctx.getThisColumnValue();
1570 }
1571 @Override
1572 public void collectIdentifiers(Collection<Identifier> identifiers) {
1573
1574 }
1575 @Override
1576 protected void toExprString(
1577 LocaleContext ctx, StringBuilder sb, boolean isDebug) {
1578 if(isDebug) {
1579 sb.append("<THIS_COL>");
1580 }
1581 }
1582 }
1583
1584 private static final class ELiteralValue extends Expr
1585 {
1586 private final Value _val;
1587
1588 private ELiteralValue(Value.Type valType, Object value) {
1589 _val = toLiteralValue(valType, value);
1590 }
1591
1592 @Override
1593 public boolean isConstant() {
1594 return true;
1595 }
1596
1597 @Override
1598 public Value eval(EvalContext ctx) {
1599 return _val;
1600 }
1601
1602 @Override
1603 public void collectIdentifiers(Collection<Identifier> identifiers) {
1604
1605 }
1606
1607 @Override
1608 protected void toExprString(
1609 LocaleContext ctx, StringBuilder sb, boolean isDebug) {
1610 if(_val.getType() == Value.Type.STRING) {
1611 literalStrToString((String)_val.get(), sb);
1612 } else if(_val.getType().isTemporal()) {
1613 sb.append("#").append(_val.getAsString(ctx)).append("#");
1614 } else {
1615 sb.append(_val.get());
1616 }
1617 }
1618 }
1619
1620 private static final class EObjValue extends Expr
1621 {
1622 private final Identifier _identifier;
1623
1624 private EObjValue(Identifier identifier) {
1625 _identifier = identifier;
1626 }
1627
1628 @Override
1629 public boolean isConstant() {
1630 return false;
1631 }
1632
1633 @Override
1634 public Value eval(EvalContext ctx) {
1635 return ctx.getIdentifierValue(_identifier);
1636 }
1637
1638 @Override
1639 public void collectIdentifiers(Collection<Identifier> identifiers) {
1640 identifiers.add(_identifier);
1641 }
1642
1643 @Override
1644 protected void toExprString(
1645 LocaleContext ctx, StringBuilder sb, boolean isDebug) {
1646 sb.append(_identifier);
1647 }
1648 }
1649
1650 private static class EParen extends Expr
1651 {
1652 private final Expr _expr;
1653
1654 private EParen(Expr expr) {
1655 _expr = expr;
1656 }
1657
1658 @Override
1659 public boolean isConstant() {
1660 return _expr.isConstant();
1661 }
1662
1663 @Override
1664 protected boolean isValidationExpr() {
1665 return _expr.isValidationExpr();
1666 }
1667
1668 @Override
1669 public Value eval(EvalContext ctx) {
1670 return _expr.eval(ctx);
1671 }
1672
1673 @Override
1674 public void collectIdentifiers(Collection<Identifier> identifiers) {
1675 _expr.collectIdentifiers(identifiers);
1676 }
1677
1678 @Override
1679 protected void toExprString(
1680 LocaleContext ctx, StringBuilder sb, boolean isDebug) {
1681 sb.append("(");
1682 _expr.toString(ctx, sb, isDebug);
1683 sb.append(")");
1684 }
1685 }
1686
1687 private static class EFunc extends Expr
1688 {
1689 private final Function _func;
1690 private final List<Expr> _params;
1691
1692 private EFunc(Function func, List<Expr> params) {
1693 _func = func;
1694 _params = params;
1695 }
1696
1697 @Override
1698 public boolean isConstant() {
1699 return _func.isPure() && areConstant(_params);
1700 }
1701
1702 @Override
1703 public Value eval(EvalContext ctx) {
1704 return _func.eval(ctx, exprListToValues(_params, ctx));
1705 }
1706
1707 @Override
1708 public void collectIdentifiers(Collection<Identifier> identifiers) {
1709 for(Expr param : _params) {
1710 param.collectIdentifiers(identifiers);
1711 }
1712 }
1713
1714 @Override
1715 protected void toExprString(
1716 LocaleContext ctx, StringBuilder sb, boolean isDebug) {
1717 sb.append(_func.getName()).append("(");
1718
1719 if(!_params.isEmpty()) {
1720 exprListToString(_params, ",", ctx, sb, isDebug);
1721 }
1722
1723 sb.append(")");
1724 }
1725 }
1726
1727 private static abstract class EBaseBinaryOp extends Expr
1728 implements LeftAssocExpr, RightAssocExpr
1729 {
1730 protected final OpType _op;
1731 protected Expr _left;
1732 protected Expr _right;
1733
1734 private EBaseBinaryOp(OpType op, Expr left, Expr right) {
1735 _op = op;
1736 _left = left;
1737 _right = right;
1738 }
1739
1740 @Override
1741 public boolean isConstant() {
1742 return areConstant(_left, _right);
1743 }
1744
1745 @Override
1746 public OpType getOp() {
1747 return _op;
1748 }
1749
1750 @Override
1751 public Expr getLeft() {
1752 return _left;
1753 }
1754
1755 @Override
1756 public void setLeft(Expr left) {
1757 _left = left;
1758 }
1759
1760 @Override
1761 public Expr getRight() {
1762 return _right;
1763 }
1764
1765 @Override
1766 public void setRight(Expr right) {
1767 _right = right;
1768 }
1769
1770 @Override
1771 public void collectIdentifiers(Collection<Identifier> identifiers) {
1772 _left.collectIdentifiers(identifiers);
1773 _right.collectIdentifiers(identifiers);
1774 }
1775
1776 @Override
1777 protected void toExprString(
1778 LocaleContext ctx, StringBuilder sb, boolean isDebug) {
1779 appendLeadingExpr(_left, ctx, sb, isDebug)
1780 .append(_op).append(" ");
1781 _right.toString(ctx, sb, isDebug);
1782 }
1783 }
1784
1785 private static class EBinaryOp extends EBaseBinaryOp
1786 {
1787 private EBinaryOp(BinaryOp op, Expr left, Expr right) {
1788 super(op, left, right);
1789 }
1790
1791 @Override
1792 public Value eval(EvalContext ctx) {
1793 return ((BinaryOp)_op).eval(ctx, _left.eval(ctx), _right.eval(ctx));
1794 }
1795 }
1796
1797 private static class EUnaryOp extends Expr
1798 implements RightAssocExpr
1799 {
1800 private final OpType _op;
1801 private Expr _expr;
1802
1803 private EUnaryOp(UnaryOp op, Expr expr) {
1804 _op = op;
1805 _expr = expr;
1806 }
1807
1808 @Override
1809 public boolean isConstant() {
1810 return _expr.isConstant();
1811 }
1812
1813 @Override
1814 public OpType getOp() {
1815 return _op;
1816 }
1817
1818 @Override
1819 public Expr getRight() {
1820 return _expr;
1821 }
1822
1823 @Override
1824 public void setRight(Expr right) {
1825 _expr = right;
1826 }
1827
1828 @Override
1829 public Value eval(EvalContext ctx) {
1830 return ((UnaryOp)_op).eval(ctx, _expr.eval(ctx));
1831 }
1832
1833 @Override
1834 public void collectIdentifiers(Collection<Identifier> identifiers) {
1835 _expr.collectIdentifiers(identifiers);
1836 }
1837
1838 @Override
1839 protected void toExprString(
1840 LocaleContext ctx, StringBuilder sb, boolean isDebug) {
1841 sb.append(_op);
1842 if(isDebug || ((UnaryOp)_op).needsSpace()) {
1843 sb.append(" ");
1844 }
1845 _expr.toString(ctx, sb, isDebug);
1846 }
1847 }
1848
1849 private static class ECompOp extends EBaseBinaryOp
1850 {
1851 private ECompOp(CompOp op, Expr left, Expr right) {
1852 super(op, left, right);
1853 }
1854
1855 @Override
1856 protected boolean isValidationExpr() {
1857 return true;
1858 }
1859
1860 @Override
1861 public Value eval(EvalContext ctx) {
1862 return ((CompOp)_op).eval(ctx, _left.eval(ctx), _right.eval(ctx));
1863 }
1864 }
1865
1866 private static class EImplicitCompOp extends ECompOp
1867 {
1868 private EImplicitCompOp(Expr right) {
1869 super(CompOp.EQ, THIS_COL_VALUE, right);
1870 }
1871
1872 @Override
1873 protected void toExprString(
1874 LocaleContext ctx, StringBuilder sb, boolean isDebug) {
1875
1876 if(isDebug) {
1877 super.toExprString(ctx, sb, isDebug);
1878 } else {
1879
1880 _right.toString(ctx, sb, isDebug);
1881 }
1882 }
1883 }
1884
1885 private static class ELogicalOp extends EBaseBinaryOp
1886 {
1887 private ELogicalOp(LogOp op, Expr left, Expr right) {
1888 super(op, left, right);
1889 }
1890
1891 @Override
1892 protected boolean isValidationExpr() {
1893 return true;
1894 }
1895
1896 @Override
1897 public Value eval(final EvalContext ctx) {
1898
1899
1900
1901 return ((LogOp)_op).eval(ctx, new DelayedValue(_left, ctx),
1902 new DelayedValue(_right, ctx));
1903 }
1904 }
1905
1906 private static abstract class ESpecOp extends Expr
1907 implements LeftAssocExpr
1908 {
1909 protected final SpecOp _op;
1910 protected Expr _expr;
1911
1912 private ESpecOp(SpecOp op, Expr expr) {
1913 _op = op;
1914 _expr = expr;
1915 }
1916
1917 @Override
1918 public boolean isConstant() {
1919 return _expr.isConstant();
1920 }
1921
1922 @Override
1923 public OpType getOp() {
1924 return _op;
1925 }
1926
1927 @Override
1928 public Expr getLeft() {
1929 return _expr;
1930 }
1931
1932 @Override
1933 public void setLeft(Expr left) {
1934 _expr = left;
1935 }
1936
1937 @Override
1938 public void collectIdentifiers(Collection<Identifier> identifiers) {
1939 _expr.collectIdentifiers(identifiers);
1940 }
1941
1942 @Override
1943 protected boolean isValidationExpr() {
1944 return true;
1945 }
1946 }
1947
1948 private static class ENullOp extends ESpecOp
1949 {
1950 private ENullOp(SpecOp op, Expr expr) {
1951 super(op, expr);
1952 }
1953
1954 @Override
1955 public Value eval(EvalContext ctx) {
1956 return _op.eval(ctx, _expr.eval(ctx), null, null);
1957 }
1958
1959 @Override
1960 protected void toExprString(
1961 LocaleContext ctx, StringBuilder sb, boolean isDebug) {
1962 appendLeadingExpr(_expr, ctx, sb, isDebug)
1963 .append(_op);
1964 }
1965 }
1966
1967 private static class ELikeOp extends ESpecOp
1968 {
1969 private final String _patternStr;
1970 private Pattern _pattern;
1971
1972 private ELikeOp(SpecOp op, Expr expr, String patternStr) {
1973 super(op, expr);
1974 _patternStr = patternStr;
1975 }
1976
1977 private Pattern getPattern()
1978 {
1979 if(_pattern == null) {
1980 _pattern = likePatternToRegex(_patternStr);
1981 }
1982 return _pattern;
1983 }
1984
1985 @Override
1986 public Value eval(EvalContext ctx) {
1987 return _op.eval(ctx, _expr.eval(ctx), getPattern(), null);
1988 }
1989
1990 @Override
1991 protected void toExprString(
1992 LocaleContext ctx, StringBuilder sb, boolean isDebug) {
1993 appendLeadingExpr(_expr, ctx, sb, isDebug)
1994 .append(_op).append(" ");
1995 literalStrToString(_patternStr, sb);
1996 if(isDebug) {
1997 sb.append("(").append(getPattern()).append(")");
1998 }
1999 }
2000 }
2001
2002
2003
2004
2005
2006
2007
2008 private static class EDynamicLikeOp extends ESpecOp
2009 implements RightAssocExpr
2010 {
2011 private Expr _patternExpr;
2012
2013
2014 private volatile CompiledPattern _compiled;
2015
2016 private EDynamicLikeOp(SpecOp op, Expr expr, Expr patternExpr) {
2017 super(op, expr);
2018 _patternExpr = patternExpr;
2019 }
2020
2021 @Override
2022 public boolean isConstant() {
2023 return super.isConstant() && _patternExpr.isConstant();
2024 }
2025
2026 @Override
2027 public Expr getRight() {
2028 return _patternExpr;
2029 }
2030
2031 @Override
2032 public void setRight(Expr right) {
2033 _patternExpr = right;
2034 }
2035
2036 @Override
2037 public Value eval(EvalContext ctx) {
2038 Value patternVal = _patternExpr.eval(ctx);
2039 if(patternVal.isNull()) {
2040 return ValueSupport.NULL_VAL;
2041 }
2042 String patternStr = patternVal.getAsString(ctx);
2043 CompiledPattern compiled = _compiled;
2044 if((compiled == null) || !patternStr.equals(compiled._patternStr)) {
2045 compiled = new CompiledPattern(patternStr,
2046 likePatternToRegex(patternStr));
2047 _compiled = compiled;
2048 }
2049 return _op.eval(ctx, _expr.eval(ctx), compiled._pattern, null);
2050 }
2051
2052 @Override
2053 public void collectIdentifiers(Collection<Identifier> identifiers) {
2054 super.collectIdentifiers(identifiers);
2055 _patternExpr.collectIdentifiers(identifiers);
2056 }
2057
2058 @Override
2059 protected void toExprString(
2060 LocaleContext ctx, StringBuilder sb, boolean isDebug) {
2061 appendLeadingExpr(_expr, ctx, sb, isDebug)
2062 .append(_op).append(" ");
2063 _patternExpr.toString(ctx, sb, isDebug);
2064 }
2065 }
2066
2067
2068 private static final class CompiledPattern
2069 {
2070 private final String _patternStr;
2071 private final Pattern _pattern;
2072
2073 private CompiledPattern(String patternStr, Pattern pattern) {
2074 _patternStr = patternStr;
2075 _pattern = pattern;
2076 }
2077 }
2078
2079 private static class EInOp extends ESpecOp
2080 {
2081 private final List<Expr> _exprs;
2082
2083 private EInOp(SpecOp op, Expr expr, List<Expr> exprs) {
2084 super(op, expr);
2085 _exprs = exprs;
2086 }
2087
2088 @Override
2089 public boolean isConstant() {
2090 return super.isConstant() && areConstant(_exprs);
2091 }
2092
2093 @Override
2094 public Value eval(EvalContext ctx) {
2095 return _op.eval(ctx, _expr.eval(ctx),
2096 exprListToDelayedValues(_exprs, ctx), null);
2097 }
2098
2099 @Override
2100 public void collectIdentifiers(Collection<Identifier> identifiers) {
2101 for(Expr expr : _exprs) {
2102 expr.collectIdentifiers(identifiers);
2103 }
2104 }
2105
2106 @Override
2107 protected void toExprString(
2108 LocaleContext ctx, StringBuilder sb, boolean isDebug) {
2109 appendLeadingExpr(_expr, ctx, sb, isDebug)
2110 .append(_op).append(" (");
2111 exprListToString(_exprs, ",", ctx, sb, isDebug);
2112 sb.append(")");
2113 }
2114 }
2115
2116 private static class EBetweenOp extends ESpecOp
2117 implements RightAssocExpr
2118 {
2119 private final Expr _startRangeExpr;
2120 private Expr _endRangeExpr;
2121
2122 private EBetweenOp(SpecOp op, Expr expr, Expr startRangeExpr,
2123 Expr endRangeExpr) {
2124 super(op, expr);
2125 _startRangeExpr = startRangeExpr;
2126 _endRangeExpr = endRangeExpr;
2127 }
2128
2129 @Override
2130 public boolean isConstant() {
2131 return _expr.isConstant() && areConstant(_startRangeExpr, _endRangeExpr);
2132 }
2133
2134 @Override
2135 public Expr getRight() {
2136 return _endRangeExpr;
2137 }
2138
2139 @Override
2140 public void setRight(Expr right) {
2141 _endRangeExpr = right;
2142 }
2143
2144 @Override
2145 public Value eval(EvalContext ctx) {
2146 return _op.eval(ctx, _expr.eval(ctx),
2147 new DelayedValue(_startRangeExpr, ctx),
2148 new DelayedValue(_endRangeExpr, ctx));
2149 }
2150
2151 @Override
2152 public void collectIdentifiers(Collection<Identifier> identifiers) {
2153 super.collectIdentifiers(identifiers);
2154 _startRangeExpr.collectIdentifiers(identifiers);
2155 _endRangeExpr.collectIdentifiers(identifiers);
2156 }
2157
2158 @Override
2159 protected void toExprString(
2160 LocaleContext ctx, StringBuilder sb, boolean isDebug) {
2161 appendLeadingExpr(_expr, ctx, sb, isDebug)
2162 .append(_op).append(" ");
2163 _startRangeExpr.toString(ctx, sb, isDebug);
2164 sb.append(" And ");
2165 _endRangeExpr.toString(ctx, sb, isDebug);
2166 }
2167 }
2168
2169
2170
2171
2172 private static abstract class BaseExprWrapper implements Expression
2173 {
2174 private final String _rawExprStr;
2175 private final Expr _expr;
2176
2177 private BaseExprWrapper(String rawExprStr, Expr expr) {
2178 _rawExprStr = rawExprStr;
2179 _expr = expr;
2180 }
2181
2182 @Override
2183 public String toDebugString(LocaleContext ctx) {
2184 return _expr.toDebugString(ctx);
2185 }
2186
2187 @Override
2188 public String toRawString() {
2189 return _rawExprStr;
2190 }
2191
2192 @Override
2193 public String toCleanString(LocaleContext ctx) {
2194 return _expr.toCleanString(ctx);
2195 }
2196
2197 @Override
2198 public boolean isConstant() {
2199 return _expr.isConstant();
2200 }
2201
2202 @Override
2203 public void collectIdentifiers(Collection<Identifier> identifiers) {
2204 _expr.collectIdentifiers(identifiers);
2205 }
2206
2207 @Override
2208 public String toString() {
2209 return toRawString();
2210 }
2211
2212 protected Object evalValue(Value.Type resultType, EvalContext ctx) {
2213 Value val = _expr.eval(ctx);
2214
2215 if(val.isNull()) {
2216 return null;
2217 }
2218
2219 if(resultType == null) {
2220
2221 return val.get();
2222 }
2223
2224
2225 switch(resultType) {
2226 case STRING:
2227 return val.getAsString(ctx);
2228 case DATE:
2229 case TIME:
2230 case DATE_TIME:
2231 return val.getAsLocalDateTime(ctx);
2232 case LONG:
2233 return val.getAsLongInt(ctx);
2234 case DOUBLE:
2235 return val.getAsDouble(ctx);
2236 case BIG_DEC:
2237 return val.getAsBigDecimal(ctx);
2238 default:
2239 throw new IllegalStateException("unexpected result type " + resultType);
2240 }
2241 }
2242
2243 protected Boolean evalCondition(EvalContext ctx) {
2244 Value val = _expr.eval(ctx);
2245
2246 if(val.isNull()) {
2247
2248 throw new EvalException("Condition evaluated to Null");
2249 }
2250
2251 return val.getAsBoolean(ctx);
2252 }
2253 }
2254
2255
2256
2257
2258 private static class ExprWrapper extends BaseExprWrapper
2259 {
2260 private final Value.Type _resultType;
2261
2262 private ExprWrapper(String rawExprStr, Expr expr, Value.Type resultType) {
2263 super(rawExprStr, expr);
2264 _resultType = resultType;
2265 }
2266
2267 @Override
2268 public Object eval(EvalContext ctx) {
2269 return evalValue(_resultType, ctx);
2270 }
2271 }
2272
2273
2274
2275
2276
2277 private static class CondExprWrapper extends BaseExprWrapper
2278 {
2279 private CondExprWrapper(String rawExprStr, Expr expr) {
2280 super(rawExprStr, expr);
2281 }
2282
2283 @Override
2284 public Object eval(EvalContext ctx) {
2285 return evalCondition(ctx);
2286 }
2287 }
2288
2289
2290
2291
2292
2293 private static final class MemoizedExprWrapper extends ExprWrapper
2294 {
2295 private Object _val;
2296
2297 private MemoizedExprWrapper(String rawExprStr, Expr expr,
2298 Value.Type resultType) {
2299 super(rawExprStr, expr, resultType);
2300 }
2301
2302 @Override
2303 public Object eval(EvalContext ctx) {
2304 if(_val == null) {
2305 _val = super.eval(ctx);
2306 }
2307 return _val;
2308 }
2309 }
2310
2311
2312
2313
2314
2315 private static final class MemoizedCondExprWrapper extends CondExprWrapper
2316 {
2317 private Object _val;
2318
2319 private MemoizedCondExprWrapper(String rawExprStr, Expr expr) {
2320 super(rawExprStr, expr);
2321 }
2322
2323 @Override
2324 public Object eval(EvalContext ctx) {
2325 if(_val == null) {
2326 _val = super.eval(ctx);
2327 }
2328 return _val;
2329 }
2330 }
2331 }