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