1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.healthmarketscience.jackcess.impl.expr;
18
19 import java.math.BigDecimal;
20 import java.time.LocalDateTime;
21 import java.util.ArrayList;
22 import java.util.Arrays;
23 import java.util.Collection;
24 import java.util.Collections;
25 import java.util.Deque;
26 import java.util.HashMap;
27 import java.util.HashSet;
28 import java.util.Iterator;
29 import java.util.LinkedList;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.Set;
33 import java.util.regex.Pattern;
34 import java.util.regex.PatternSyntaxException;
35
36 import com.healthmarketscience.jackcess.expr.EvalContext;
37 import com.healthmarketscience.jackcess.expr.EvalException;
38 import com.healthmarketscience.jackcess.expr.Expression;
39 import com.healthmarketscience.jackcess.expr.Function;
40 import com.healthmarketscience.jackcess.expr.FunctionLookup;
41 import com.healthmarketscience.jackcess.expr.Identifier;
42 import com.healthmarketscience.jackcess.expr.LocaleContext;
43 import com.healthmarketscience.jackcess.expr.ParseException;
44 import com.healthmarketscience.jackcess.expr.Value;
45 import com.healthmarketscience.jackcess.impl.expr.ExpressionTokenizer.Token;
46 import com.healthmarketscience.jackcess.impl.expr.ExpressionTokenizer.TokenType;
47 import org.apache.commons.lang3.StringUtils;
48
49
50
51
52
53
54 public class Expressionator
55 {
56
57
58
59
60
61
62
63 public enum Type {
64 DEFAULT_VALUE, EXPRESSION, FIELD_VALIDATOR, RECORD_VALIDATOR;
65 }
66
67 public interface ParseContext extends LocaleContext {
68 public FunctionLookup getFunctionLookup();
69 }
70
71 private enum WordType {
72 OP, COMP, LOG_OP, CONST, SPEC_OP_PREFIX, DELIM;
73 }
74
75 private static final String FUNC_START_DELIM = "(";
76 private static final String OPEN_PAREN = "(";
77 private static final String CLOSE_PAREN = ")";
78 private static final String FUNC_PARAM_SEP = ",";
79
80 private static final Map<String,WordType> WORD_TYPES =
81 new HashMap<String,WordType>();
82
83 static {
84 setWordType(WordType.OP, "+", "-", "*", "/", "\\", "^", "&", "mod");
85 setWordType(WordType.COMP, "<", "<=", ">", ">=", "=", "<>");
86 setWordType(WordType.LOG_OP, "and", "or", "eqv", "xor", "imp");
87 setWordType(WordType.CONST, "true", "false", "null", "on", "off",
88 "yes", "no");
89 setWordType(WordType.SPEC_OP_PREFIX, "is", "like", "between", "in", "not");
90
91
92
93 setWordType(WordType.DELIM, ".", "!", ",", "(", ")");
94 }
95
96 private static final Collection<String> TRUE_STRS =
97 Arrays.asList("true", "yes", "on");
98 private static final Collection<String> FALSE_STRS =
99 Arrays.asList("false", "no", "off");
100
101 private interface OpType {}
102
103 private enum UnaryOp implements OpType {
104 NEG("-", false) {
105 @Override public Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Value param1) {
106 return BuiltinOperators.negate(ctx, param1);
107 }
108 @Override public UnaryOp getUnaryNumOp() {
109 return UnaryOp.NEG_NUM;
110 }
111 },
112 POS("+", false) {
113 @Override public Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Value param1) {
114
115 return param1;
116 }
117 @Override public UnaryOp getUnaryNumOp() {
118 return UnaryOp.POS_NUM;
119 }
120 },
121 NOT("Not", true) {
122 @Override public Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Value param1) {
123 return BuiltinOperators.not(ctx, param1);
124 }
125 },
126
127 NEG_NUM("-", false) {
128 @Override public Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Value param1) {
129 return BuiltinOperators.negate(ctx, param1);
130 }
131 },
132
133 POS_NUM("+", false) {
134 @Override public Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Value param1) {
135
136 return param1;
137 }
138 };
139
140 private final String _str;
141 private final boolean _needSpace;
142
143 private UnaryOp(String str, boolean needSpace) {
144 _str = str;
145 _needSpace = needSpace;
146 }
147
148 public boolean needsSpace() {
149 return _needSpace;
150 }
151
152 @Override
153 public String toString() {
154 return _str;
155 }
156
157 public UnaryOp getUnaryNumOp() {
158 return null;
159 }
160
161 public abstract Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Value param1);
162 }
163
164 private enum BinaryOp implements OpType {
165 PLUS("+") {
166 @Override public Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value param1, Value param2) {
167 return BuiltinOperators.add(ctx, param1, param2);
168 }
169 },
170 MINUS("-") {
171 @Override public Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value param1, Value param2) {
172 return BuiltinOperators.subtract(ctx, param1, param2);
173 }
174 },
175 MULT("*") {
176 @Override public Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value param1, Value param2) {
177 return BuiltinOperators.multiply(ctx, param1, param2);
178 }
179 },
180 DIV("/") {
181 @Override public Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value param1, Value param2) {
182 return BuiltinOperators.divide(ctx, param1, param2);
183 }
184 },
185 INT_DIV("\\") {
186 @Override public Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value param1, Value param2) {
187 return BuiltinOperators.intDivide(ctx, param1, param2);
188 }
189 },
190 EXP("^") {
191 @Override public Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value param1, Value param2) {
192 return BuiltinOperators.exp(ctx, param1, param2);
193 }
194 },
195 CONCAT("&") {
196 @Override public Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value param1, Value param2) {
197 return BuiltinOperators.concat(ctx, param1, param2);
198 }
199 },
200 MOD("Mod") {
201 @Override public Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value param1, Value param2) {
202 return BuiltinOperators.mod(ctx, param1, param2);
203 }
204 };
205
206 private final String _str;
207
208 private BinaryOp(String str) {
209 _str = str;
210 }
211
212 @Override
213 public String toString() {
214 return _str;
215 }
216
217 public abstract Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value param1, Value param2);
218 }
219
220 private enum CompOp implements OpType {
221 LT("<") {
222 @Override public Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value param1, Value param2) {
223 return BuiltinOperators.lessThan(ctx, param1, param2);
224 }
225 },
226 LTE("<=") {
227 @Override public Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value param1, Value param2) {
228 return BuiltinOperators.lessThanEq(ctx, param1, param2);
229 }
230 },
231 GT(">") {
232 @Override public Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value param1, Value param2) {
233 return BuiltinOperators.greaterThan(ctx, param1, param2);
234 }
235 },
236 GTE(">=") {
237 @Override public Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value param1, Value param2) {
238 return BuiltinOperators.greaterThanEq(ctx, param1, param2);
239 }
240 },
241 EQ("=") {
242 @Override public Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value param1, Value param2) {
243 return BuiltinOperators.equals(ctx, param1, param2);
244 }
245 },
246 NE("<>") {
247 @Override public Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value param1, Value param2) {
248 return BuiltinOperators.notEquals(ctx, param1, param2);
249 }
250 };
251
252 private final String _str;
253
254 private CompOp(String str) {
255 _str = str;
256 }
257
258 @Override
259 public String toString() {
260 return _str;
261 }
262
263 public abstract Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value param1, Value param2);
264 }
265
266 private enum LogOp implements OpType {
267 AND("And") {
268 @Override public Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value param1, Value param2) {
269 return BuiltinOperators.and(ctx, param1, param2);
270 }
271 },
272 OR("Or") {
273 @Override public Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value param1, Value param2) {
274 return BuiltinOperators.or(ctx, param1, param2);
275 }
276 },
277 EQV("Eqv") {
278 @Override public Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value param1, Value param2) {
279 return BuiltinOperators.eqv(ctx, param1, param2);
280 }
281 },
282 XOR("Xor") {
283 @Override public Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value param1, Value param2) {
284 return BuiltinOperators.xor(ctx, param1, param2);
285 }
286 },
287 IMP("Imp") {
288 @Override public Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value param1, Value param2) {
289 return BuiltinOperators.imp(ctx, param1, param2);
290 }
291 };
292
293 private final String _str;
294
295 private LogOp(String str) {
296 _str = str;
297 }
298
299 @Override
300 public String toString() {
301 return _str;
302 }
303
304 public abstract Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Valuef="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value param1, Value param2);
305 }
306
307 private enum SpecOp implements OpType {
308
309
310 NOT("Not") {
311 @Override public Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Value param1, Object param2, Object param3) {
312 throw new UnsupportedOperationException();
313 }
314 },
315 IS_NULL("Is Null") {
316 @Override public Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Value param1, Object param2, Object param3) {
317 return BuiltinOperators.isNull(param1);
318 }
319 },
320 IS_NOT_NULL("Is Not Null") {
321 @Override public Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Value param1, Object param2, Object param3) {
322 return BuiltinOperators.isNotNull(param1);
323 }
324 },
325 LIKE("Like") {
326 @Override public Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Value param1, Object param2, Object param3) {
327 return BuiltinOperators.like(ctx, param1, (Pattern)param2);
328 }
329 },
330 NOT_LIKE("Not Like") {
331 @Override public Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Value param1, Object param2, Object param3) {
332 return BuiltinOperators.notLike(ctx, param1, (Pattern)param2);
333 }
334 },
335 BETWEEN("Between") {
336 @Override public Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Value param1, Object param2, Object param3) {
337 return BuiltinOperators.between(ctx, param1, (Value="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value)param2, (Value)param3);
338 }
339 },
340 NOT_BETWEEN("Not Between") {
341 @Override public Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Value param1, Object param2, Object param3) {
342 return BuiltinOperators.notBetween(ctx, param1, (Value="../../../../../com/healthmarketscience/jackcess/expr/Value.html#Value">Value)param2, (Value)param3);
343 }
344 },
345 IN("In") {
346 @Override public Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Value param1, Object param2, Object param3) {
347 return BuiltinOperators.in(ctx, param1, (Value[])param2);
348 }
349 },
350 NOT_IN("Not In") {
351 @Override public Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Value param1, Object param2, Object param3) {
352 return BuiltinOperators.notIn(ctx, param1, (Value[])param2);
353 }
354 };
355
356 private final String _str;
357
358 private SpecOp(String str) {
359 _str = str;
360 }
361
362 @Override
363 public String toString() {
364 return _str;
365 }
366
367 public abstract Value/../com/healthmarketscience/jackcess/expr/Value.html#Value">Value eval(EvalContext ctx, Value param1, Object param2, Object param3);
368 }
369
370 private static final Map<OpType, Integer> PRECENDENCE =
371 buildPrecedenceMap(
372 new OpType[]{UnaryOp.NEG_NUM, UnaryOp.POS_NUM},
373 new OpType[]{BinaryOp.EXP},
374 new OpType[]{UnaryOp.NEG, UnaryOp.POS},
375 new OpType[]{BinaryOp.MULT, BinaryOp.DIV},
376 new OpType[]{BinaryOp.INT_DIV},
377 new OpType[]{BinaryOp.MOD},
378 new OpType[]{BinaryOp.PLUS, BinaryOp.MINUS},
379 new OpType[]{BinaryOp.CONCAT},
380 new OpType[]{CompOp.LT, CompOp.GT, CompOp.NE, CompOp.LTE, CompOp.GTE,
381 CompOp.EQ, SpecOp.LIKE, SpecOp.NOT_LIKE,
382 SpecOp.IS_NULL, SpecOp.IS_NOT_NULL},
383 new OpType[]{UnaryOp.NOT},
384 new OpType[]{LogOp.AND},
385 new OpType[]{LogOp.OR},
386 new OpType[]{LogOp.XOR},
387 new OpType[]{LogOp.EQV},
388 new OpType[]{LogOp.IMP},
389 new OpType[]{SpecOp.IN, SpecOp.NOT_IN, SpecOp.BETWEEN,
390 SpecOp.NOT_BETWEEN});
391
392 private static final Set<Character> REGEX_SPEC_CHARS = new HashSet<Character>(
393 Arrays.asList('\\','.','%','=','+', '$','^','|','(',')','{','}','&',
394 '[',']','*','?'));
395
396 private static final Pattern UNMATCHABLE_REGEX = Pattern.compile("(?!)");
397
398 private static final Expr THIS_COL_VALUE = new EThisValue();
399
400 private static final Expr NULL_VALUE = new EConstValue(
401 ValueSupport.NULL_VAL, "Null");
402 private static final Expr TRUE_VALUE = new EConstValue(
403 ValueSupport.TRUE_VAL, "True");
404 private static final Expr FALSE_VALUE = new EConstValue(
405 ValueSupport.FALSE_VAL, "False");
406
407
408 private Expressionator() {}
409
410 public static Expression parse(Type exprType, String exprStr,
411 Value.Type resultType,
412 ParseContext context) {
413
414 List<Token> tokens = trimSpaces(
415 ExpressionTokenizer.tokenize(exprType, exprStr, context));
416
417 if(tokens == null) {
418 throw new ParseException("null/empty expression");
419 }
420
421 TokBuf buf = new TokBuf(exprType, tokens, context);
422
423 if(isLiteralDefaultValue(buf, resultType, exprStr)) {
424
425
426
427 return new ExprWrapper(exprStr,
428 new ELiteralValue(Value.Type.STRING, exprStr), resultType);
429 }
430
431
432 Expr expr = parseExpression(buf, false);
433
434 if((exprType == Type.FIELD_VALIDATOR) && !expr.isValidationExpr()) {
435
436
437
438 expr = new EImplicitCompOp(expr);
439 }
440
441 switch(exprType) {
442 case DEFAULT_VALUE:
443 case EXPRESSION:
444 return (expr.isConstant() ?
445
446
447 new MemoizedExprWrapper(exprStr, expr, resultType) :
448 new ExprWrapper(exprStr, expr, resultType));
449 case FIELD_VALIDATOR:
450 case RECORD_VALIDATOR:
451 return (expr.isConstant() ?
452
453
454 new MemoizedCondExprWrapper(exprStr, expr) :
455 new CondExprWrapper(exprStr, expr));
456 default:
457 throw new ParseException("unexpected expression type " + exprType);
458 }
459 }
460
461 private static List<Token> trimSpaces(List<Token> tokens) {
462 if(tokens == null) {
463 return null;
464 }
465
466
467
468
469 for(int i = 1; i < (tokens.size() - 1); ++i) {
470 Token t = tokens.get(i);
471 if(t.getType() == TokenType.SPACE) {
472 if((tokens.get(i - 1).getType() == TokenType.STRING) &&
473 isDelim(tokens.get(i + 1), FUNC_START_DELIM)) {
474
475 } else {
476 tokens.remove(i);
477 --i;
478 }
479 }
480 }
481 return tokens;
482 }
483
484 private static Expr parseExpression(TokBuf buf, boolean singleExpr)
485 {
486 while(buf.hasNext()) {
487 Token t = buf.next();
488
489 switch(t.getType()) {
490 case OBJ_NAME:
491
492 parseObjectRefExpression(t, buf);
493 break;
494
495 case LITERAL:
496
497 buf.setPendingExpr(new ELiteralValue(t.getValueType(), t.getValue()));
498 break;
499
500 case OP:
501
502 WordType wordType = getWordType(t);
503 if(wordType == null) {
504
505 throw new ParseException("Invalid operator " + t);
506 }
507
508
509
510 switch(wordType) {
511 case OP:
512 parseOperatorExpression(t, buf);
513 break;
514
515 case COMP:
516
517 parseCompOpExpression(t, buf);
518 break;
519
520 default:
521 throw new ParseException("Unexpected OP word type " + wordType);
522 }
523
524 break;
525
526 case DELIM:
527
528 parseDelimExpression(t, buf);
529 break;
530
531 case STRING:
532
533
534 wordType = getWordType(t);
535 if(wordType == null) {
536
537
538 if(!maybeParseFuncCallExpression(t, buf)) {
539
540
541 Token next = buf.peekNext();
542 if((next != null) && isObjNameSep(next)) {
543
544 parseObjectRefExpression(t, buf);
545
546 } else {
547
548
549 throw new UnsupportedOperationException("FIXME");
550 }
551 }
552
553 } else {
554
555
556
557 switch(wordType) {
558 case OP:
559
560 parseOperatorExpression(t, buf);
561 break;
562
563 case LOG_OP:
564
565 parseLogicalOpExpression(t, buf);
566 break;
567
568 case CONST:
569
570 parseConstExpression(t, buf);
571 break;
572
573 case SPEC_OP_PREFIX:
574
575 parseSpecOpExpression(t, buf);
576 break;
577
578 default:
579 throw new ParseException("Unexpected STRING word type "
580 + wordType);
581 }
582 }
583
584 break;
585
586 case SPACE:
587
588 break;
589
590 default:
591 throw new ParseException("unknown token type " + t);
592 }
593
594 if(singleExpr && buf.hasPendingExpr()) {
595 break;
596 }
597 }
598
599 Expr expr = buf.takePendingExpr();
600 if(expr == null) {
601 throw new ParseException("No expression found? " + buf);
602 }
603
604 return expr;
605 }
606
607 private static void parseObjectRefExpression(Token firstTok, TokBuf buf) {
608
609
610
611
612
613
614
615
616 Deque<String> objNames = new LinkedList<String>();
617 objNames.add(firstTok.getValueStr());
618
619 Token t = null;
620 boolean atSep = false;
621 while((t = buf.peekNext()) != null) {
622 if(!atSep) {
623 if(isObjNameSep(t)) {
624 buf.next();
625 atSep = true;
626 continue;
627 }
628 } else {
629 if((t.getType() == TokenType.OBJ_NAME) ||
630 (t.getType() == TokenType.STRING)) {
631 buf.next();
632
633 objNames.addFirst(t.getValueStr());
634 atSep = false;
635 continue;
636 }
637 }
638 break;
639 }
640
641 int numNames = objNames.size();
642 if(atSep || (numNames > 3)) {
643 throw new ParseException("Invalid object reference " + buf);
644 }
645
646
647 String propName = null;
648 if(numNames == 3) {
649 propName = objNames.poll();
650 }
651 String objName = objNames.poll();
652 String collectionName = objNames.poll();
653
654 buf.setPendingExpr(
655 new EObjValue(new Identifier(collectionName, objName, propName)));
656 }
657
658 private static void parseDelimExpression(Token firstTok, TokBuf buf) {
659
660
661 if(!isDelim(firstTok, OPEN_PAREN) || buf.hasPendingExpr()) {
662 throw new ParseException("Unexpected delimiter " +
663 firstTok.getValue() + " " + buf);
664 }
665
666 Expr subExpr = findParenExprs(buf, false).get(0);
667 buf.setPendingExpr(new EParen(subExpr));
668 }
669
670 private static boolean maybeParseFuncCallExpression(
671 Token firstTok, TokBuf buf) {
672
673 int startPos = buf.curPos();
674 boolean foundFunc = false;
675
676 try {
677 Token t = buf.peekNext();
678 if(!isDelim(t, FUNC_START_DELIM)) {
679
680 return false;
681 }
682
683 buf.next();
684 List<Expr> params = findParenExprs(buf, true);
685 String funcName = firstTok.getValueStr();
686 Function func = buf.getFunction(funcName);
687 if(func == null) {
688 throw new ParseException("Could not find function '" +
689 funcName + "' " + buf);
690 }
691 buf.setPendingExpr(new EFunc(func, params));
692 foundFunc = true;
693 return true;
694
695 } finally {
696 if(!foundFunc) {
697 buf.reset(startPos);
698 }
699 }
700 }
701
702 private static List<Expr> findParenExprs(
703 TokBuf buf, boolean allowMulti) {
704
705 if(allowMulti) {
706
707 Token t = buf.peekNext();
708 if(isDelim(t, CLOSE_PAREN)) {
709 buf.next();
710 return Collections.emptyList();
711 }
712 }
713
714
715 List<Expr> exprs = new ArrayList<Expr>(3);
716 int level = 1;
717 int startPos = buf.curPos();
718 while(buf.hasNext()) {
719
720 Token t = buf.next();
721
722 if(isDelim(t, OPEN_PAREN)) {
723
724 ++level;
725
726 } else if(isDelim(t, CLOSE_PAREN)) {
727
728 --level;
729 if(level == 0) {
730 TokBuf subBuf = buf.subBuf(startPos, buf.prevPos());
731 exprs.add(parseExpression(subBuf, false));
732 return exprs;
733 }
734
735 } else if(allowMulti && (level == 1) && isDelim(t, FUNC_PARAM_SEP)) {
736
737 TokBuf subBuf = buf.subBuf(startPos, buf.prevPos());
738 exprs.add(parseExpression(subBuf, false));
739 startPos = buf.curPos();
740 }
741 }
742
743 throw new ParseException("Missing closing '" + CLOSE_PAREN
744 + " " + buf);
745 }
746
747 private static void parseOperatorExpression(Token t, TokBuf buf) {
748
749
750
751 if(buf.hasPendingExpr()) {
752 parseBinaryOpExpression(t, buf);
753 } else if(isEitherOp(t, "-", "+")) {
754 parseUnaryOpExpression(t, buf);
755 } else {
756 throw new ParseException(
757 "Missing left expression for binary operator " + t.getValue() +
758 " " + buf);
759 }
760 }
761
762 private static void parseBinaryOpExpression(Token firstTok, TokBuf buf) {
763 BinaryOp op = getOpType(firstTok, BinaryOp.class);
764 Expr leftExpr = buf.takePendingExpr();
765 Expr rightExpr = parseExpression(buf, true);
766
767 buf.setPendingExpr(new EBinaryOp(op, leftExpr, rightExpr));
768 }
769
770 private static void parseUnaryOpExpression(Token firstTok, TokBuf buf) {
771 UnaryOp op = getOpType(firstTok, UnaryOp.class);
772
773 UnaryOp numOp = op.getUnaryNumOp();
774 if(numOp != null) {
775
776
777 Token nextTok = buf.peekNext();
778 if((nextTok != null) && (nextTok.getType() == TokenType.LITERAL) &&
779 nextTok.getValueType().isNumeric()) {
780 op = numOp;
781 }
782 }
783
784 Expr val = parseExpression(buf, true);
785
786 buf.setPendingExpr(new EUnaryOp(op, val));
787 }
788
789 private static void parseCompOpExpression(Token firstTok, TokBuf buf) {
790
791 if(!buf.hasPendingExpr()) {
792 if(buf.getExprType() == Type.FIELD_VALIDATOR) {
793
794
795 buf.setPendingExpr(THIS_COL_VALUE);
796 } else {
797 throw new ParseException(
798 "Missing left expression for comparison operator " +
799 firstTok.getValue() + " " + buf);
800 }
801 }
802
803 CompOp op = getOpType(firstTok, CompOp.class);
804 Expr leftExpr = buf.takePendingExpr();
805 Expr rightExpr = parseExpression(buf, true);
806
807 buf.setPendingExpr(new ECompOp(op, leftExpr, rightExpr));
808 }
809
810 private static void parseLogicalOpExpression(Token firstTok, TokBuf buf) {
811
812 if(!buf.hasPendingExpr()) {
813 throw new ParseException(
814 "Missing left expression for logical operator " +
815 firstTok.getValue() + " " + buf);
816 }
817
818 LogOp op = getOpType(firstTok, LogOp.class);
819 Expr leftExpr = buf.takePendingExpr();
820 Expr rightExpr = parseExpression(buf, true);
821
822 buf.setPendingExpr(new ELogicalOp(op, leftExpr, rightExpr));
823 }
824
825 private static void parseSpecOpExpression(Token firstTok, TokBuf buf) {
826
827 SpecOp specOp = getSpecialOperator(firstTok, buf);
828
829 if(specOp == SpecOp.NOT) {
830
831 parseUnaryOpExpression(firstTok, buf);
832 return;
833 }
834
835 if(!buf.hasPendingExpr()) {
836 if(buf.getExprType() == Type.FIELD_VALIDATOR) {
837
838
839 buf.setPendingExpr(THIS_COL_VALUE);
840 } else {
841 throw new ParseException(
842 "Missing left expression for comparison operator " +
843 specOp + " " + buf);
844 }
845 }
846
847 Expr expr = buf.takePendingExpr();
848
849 Expr specOpExpr = null;
850 switch(specOp) {
851 case IS_NULL:
852 case IS_NOT_NULL:
853 specOpExpr = new ENullOp(specOp, expr);
854 break;
855
856 case LIKE:
857 case NOT_LIKE:
858 Token t = buf.next();
859 if((t.getType() != TokenType.LITERAL) ||
860 (t.getValueType() != Value.Type.STRING)) {
861 throw new ParseException("Missing Like pattern " + buf);
862 }
863 String patternStr = t.getValueStr();
864 specOpExpr = new ELikeOp(specOp, expr, patternStr);
865 break;
866
867 case BETWEEN:
868 case NOT_BETWEEN:
869
870
871
872
873 Expr startRangeExpr = null;
874 while(true) {
875
876 Expr tmpExpr = parseExpression(buf, true);
877 Token tmpT = buf.peekNext();
878
879 if(tmpT == null) {
880
881 throw new ParseException(
882 "Missing 'And' for 'Between' expression " + buf);
883 }
884
885 if(isString(tmpT, "and")) {
886 buf.next();
887 startRangeExpr = tmpExpr;
888 break;
889 }
890
891
892 buf.restorePendingExpr(tmpExpr);
893 }
894
895 Expr endRangeExpr = parseExpression(buf, true);
896
897 specOpExpr = new EBetweenOp(specOp, expr, startRangeExpr, endRangeExpr);
898 break;
899
900 case IN:
901 case NOT_IN:
902
903
904 t = buf.next();
905 if(t.getType() == TokenType.SPACE) {
906 t = buf.next();
907 }
908 if(!isDelim(t, OPEN_PAREN)) {
909 throw new ParseException("Malformed 'In' expression " + buf);
910 }
911
912 List<Expr> exprs = findParenExprs(buf, true);
913 specOpExpr = new EInOp(specOp, expr, exprs);
914 break;
915
916 default:
917 throw new ParseException("Unexpected special op " + specOp);
918 }
919
920 buf.setPendingExpr(specOpExpr);
921 }
922
923 private static SpecOp getSpecialOperator(Token firstTok, TokBuf buf) {
924 String opStr = firstTok.getValueStr().toLowerCase();
925
926 if("is".equals(opStr)) {
927 Token t = buf.peekNext();
928 if(isString(t, "null")) {
929 buf.next();
930 return SpecOp.IS_NULL;
931 } else if(isString(t, "not")) {
932 buf.next();
933 t = buf.peekNext();
934 if(isString(t, "null")) {
935 buf.next();
936 return SpecOp.IS_NOT_NULL;
937 }
938 }
939 } else if("like".equals(opStr)) {
940 return SpecOp.LIKE;
941 } else if("between".equals(opStr)) {
942 return SpecOp.BETWEEN;
943 } else if("in".equals(opStr)) {
944 return SpecOp.IN;
945 } else if("not".equals(opStr)) {
946 Token t = buf.peekNext();
947 if(isString(t, "between")) {
948 buf.next();
949 return SpecOp.NOT_BETWEEN;
950 } else if(isString(t, "in")) {
951 buf.next();
952 return SpecOp.NOT_IN;
953 } else if(isString(t, "like")) {
954 buf.next();
955 return SpecOp.NOT_LIKE;
956 }
957 return SpecOp.NOT;
958 }
959
960 throw new ParseException(
961 "Malformed special operator " + opStr + " " + buf);
962 }
963
964 private static void parseConstExpression(Token firstTok, TokBuf buf) {
965 Expr constExpr = null;
966 String tokStr = firstTok.getValueStr().toLowerCase();
967 if(TRUE_STRS.contains(tokStr)) {
968 constExpr = TRUE_VALUE;
969 } else if(FALSE_STRS.contains(tokStr)) {
970 constExpr = FALSE_VALUE;
971 } else if("null".equals(tokStr)) {
972 constExpr = NULL_VALUE;
973 } else {
974 throw new ParseException("Unexpected CONST word "
975 + firstTok.getValue());
976 }
977 buf.setPendingExpr(constExpr);
978 }
979
980 private static boolean isObjNameSep(Token t) {
981 return (isDelim(t, ".") || isDelim(t, "!"));
982 }
983
984 private static boolean isOp(Token t, String opStr) {
985 return ((t != null) && (t.getType() == TokenType.OP) &&
986 opStr.equalsIgnoreCase(t.getValueStr()));
987 }
988
989 private static boolean isEitherOp(Token t, String opStr1, String opStr2) {
990 return ((t != null) && (t.getType() == TokenType.OP) &&
991 (opStr1.equalsIgnoreCase(t.getValueStr()) ||
992 opStr2.equalsIgnoreCase(t.getValueStr())));
993 }
994
995 private static boolean isDelim(Token t, String opStr) {
996 return ((t != null) && (t.getType() == TokenType.DELIM) &&
997 opStr.equalsIgnoreCase(t.getValueStr()));
998 }
999
1000 private static boolean isString(Token t, String opStr) {
1001 return ((t != null) && (t.getType() == TokenType.STRING) &&
1002 opStr.equalsIgnoreCase(t.getValueStr()));
1003 }
1004
1005 private static WordType getWordType(Token t) {
1006 return WORD_TYPES.get(t.getValueStr().toLowerCase());
1007 }
1008
1009 private static void setWordType(WordType type, String... words) {
1010 for(String w : words) {
1011 WORD_TYPES.put(w, type);
1012 }
1013 }
1014
1015 private static <T extends Enum<T>> T getOpType(Token t, Class<T> opClazz) {
1016 String str = t.getValueStr();
1017 for(T op : opClazz.getEnumConstants()) {
1018 if(str.equalsIgnoreCase(op.toString())) {
1019 return op;
1020 }
1021 }
1022 throw new ParseException("Unexpected op string " + t.getValueStr());
1023 }
1024
1025 private static StringBuilder appendLeadingExpr(
1026 Expr expr, LocaleContext ctx, StringBuilder sb, boolean isDebug)
1027 {
1028 int len = sb.length();
1029 expr.toString(ctx, sb, isDebug);
1030 if(sb.length() > len) {
1031
1032 sb.append(" ");
1033 }
1034 return sb;
1035 }
1036
1037 private static final class TokBuf
1038 {
1039 private final Type _exprType;
1040 private final List<Token> _tokens;
1041 private final TokBuf _parent;
1042 private final int _parentOff;
1043 private final ParseContext _ctx;
1044 private int _pos;
1045 private Expr _pendingExpr;
1046
1047 private TokBuf(Type exprType, List<Token> tokens, ParseContext context) {
1048 this(exprType, tokens, null, 0, context);
1049 }
1050
1051 private TokBuf(List<Token> tokens, TokBuf parent, int parentOff) {
1052 this(parent._exprType, tokens, parent, parentOff, parent._ctx);
1053 }
1054
1055 private TokBuf(Type exprType, List<Token> tokens, TokBuf parent,
1056 int parentOff, ParseContext context) {
1057 _exprType = exprType;
1058 _tokens = tokens;
1059 _parent = parent;
1060 _parentOff = parentOff;
1061 _ctx = context;
1062 }
1063
1064 public Type getExprType() {
1065 return _exprType;
1066 }
1067
1068 public int curPos() {
1069 return _pos;
1070 }
1071
1072 public int prevPos() {
1073 return _pos - 1;
1074 }
1075
1076 public boolean hasNext() {
1077 return (_pos < _tokens.size());
1078 }
1079
1080 public Token peekNext() {
1081 if(!hasNext()) {
1082 return null;
1083 }
1084 return _tokens.get(_pos);
1085 }
1086
1087 public Token next() {
1088 if(!hasNext()) {
1089 throw new ParseException(
1090 "Unexpected end of expression " + this);
1091 }
1092 return _tokens.get(_pos++);
1093 }
1094
1095 public void reset(int pos) {
1096 _pos = pos;
1097 }
1098
1099 public TokBuf subBuf(int start, int end) {
1100 return new TokBuf(_tokens.subList(start, end), this, start);
1101 }
1102
1103 public void setPendingExpr(Expr expr) {
1104 if(_pendingExpr != null) {
1105 throw new ParseException(
1106 "Found multiple expressions with no operator " + this);
1107 }
1108 _pendingExpr = expr.resolveOrderOfOperations();
1109 }
1110
1111 public void restorePendingExpr(Expr expr) {
1112
1113 _pendingExpr = expr;
1114 }
1115
1116 public Expr takePendingExpr() {
1117 Expr expr = _pendingExpr;
1118 _pendingExpr = null;
1119 return expr;
1120 }
1121
1122 public boolean hasPendingExpr() {
1123 return (_pendingExpr != null);
1124 }
1125
1126 private Map.Entry<Integer,List<Token>> getTopPos() {
1127 int pos = _pos;
1128 List<Token> toks = _tokens;
1129 TokBuf cur = this;
1130 while(cur._parent != null) {
1131 pos += cur._parentOff;
1132 cur = cur._parent;
1133 toks = cur._tokens;
1134 }
1135 return ExpressionTokenizer.newEntry(pos, toks);
1136 }
1137
1138 public Function getFunction(String funcName) {
1139 return _ctx.getFunctionLookup().getFunction(funcName);
1140 }
1141
1142 @Override
1143 public String toString() {
1144
1145 Map.Entry<Integer,List<Token>> e = getTopPos();
1146
1147
1148 StringBuilder sb = new StringBuilder()
1149 .append("[token ").append(e.getKey()).append("] (");
1150
1151 for(Iterator<Token> iter = e.getValue().iterator(); iter.hasNext(); ) {
1152 Token t = iter.next();
1153 sb.append("'").append(t.getValueStr()).append("'");
1154 if(iter.hasNext()) {
1155 sb.append(",");
1156 }
1157 }
1158
1159 sb.append(")");
1160
1161 if(_pendingExpr != null) {
1162 sb.append(" [pending '").append(_pendingExpr.toDebugString(_ctx))
1163 .append("']");
1164 }
1165
1166 return sb.toString();
1167 }
1168 }
1169
1170 private static boolean isHigherPrecendence(OpType op1, OpType op2) {
1171 int prec1 = PRECENDENCE.get(op1);
1172 int prec2 = PRECENDENCE.get(op2);
1173
1174
1175 return (prec1 < prec2);
1176 }
1177
1178 private static final Map<OpType, Integer> buildPrecedenceMap(
1179 OpType[]... opArrs) {
1180 Map<OpType, Integer> prec = new HashMap<OpType, Integer>();
1181
1182 int level = 0;
1183 for(OpType[] ops : opArrs) {
1184 for(OpType op : ops) {
1185 prec.put(op, level);
1186 }
1187 ++level;
1188 }
1189
1190 return prec;
1191 }
1192
1193 private static void exprListToString(
1194 List<Expr> exprs, String sep, LocaleContext ctx, StringBuilder sb,
1195 boolean isDebug) {
1196 Iterator<Expr> iter = exprs.iterator();
1197 iter.next().toString(ctx, sb, isDebug);
1198 while(iter.hasNext()) {
1199 sb.append(sep);
1200 iter.next().toString(ctx, sb, isDebug);
1201 }
1202 }
1203
1204 private static Value[] exprListToValues(
1205 List<Expr> exprs, EvalContext ctx) {
1206 Valueess/expr/Value.html#Value">Value[] paramVals = new Value[exprs.size()];
1207 for(int i = 0; i < exprs.size(); ++i) {
1208 paramVals[i] = exprs.get(i).eval(ctx);
1209 }
1210 return paramVals;
1211 }
1212
1213 private static Value[] exprListToDelayedValues(
1214 List<Expr> exprs, EvalContext ctx) {
1215 Valueess/expr/Value.html#Value">Value[] paramVals = new Value[exprs.size()];
1216 for(int i = 0; i < exprs.size(); ++i) {
1217 paramVals[i] = new DelayedValue(exprs.get(i), ctx);
1218 }
1219 return paramVals;
1220 }
1221
1222 private static boolean areConstant(List<Expr> exprs) {
1223 for(Expr expr : exprs) {
1224 if(!expr.isConstant()) {
1225 return false;
1226 }
1227 }
1228 return true;
1229 }
1230
1231 private static boolean areConstant(Expr... exprs) {
1232 for(Expr expr : exprs) {
1233 if(!expr.isConstant()) {
1234 return false;
1235 }
1236 }
1237 return true;
1238 }
1239
1240 @SuppressWarnings("deprecation")
1241 private static void literalStrToString(String str, StringBuilder sb) {
1242 sb.append("\"")
1243 .append(StringUtils.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 }