1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.jexl3.internal;
18
19 import java.util.Collections;
20 import java.util.Map;
21 import java.util.Set;
22 import java.util.regex.Pattern;
23
24 import org.apache.commons.jexl3.JexlExpression;
25 import org.apache.commons.jexl3.JexlFeatures;
26 import org.apache.commons.jexl3.JexlInfo;
27 import org.apache.commons.jexl3.JexlScript;
28 import org.apache.commons.jexl3.parser.ASTAddNode;
29 import org.apache.commons.jexl3.parser.ASTAndNode;
30 import org.apache.commons.jexl3.parser.ASTAnnotatedStatement;
31 import org.apache.commons.jexl3.parser.ASTAnnotation;
32 import org.apache.commons.jexl3.parser.ASTArguments;
33 import org.apache.commons.jexl3.parser.ASTArrayAccess;
34 import org.apache.commons.jexl3.parser.ASTArrayLiteral;
35 import org.apache.commons.jexl3.parser.ASTAssignment;
36 import org.apache.commons.jexl3.parser.ASTBitwiseAndNode;
37 import org.apache.commons.jexl3.parser.ASTBitwiseComplNode;
38 import org.apache.commons.jexl3.parser.ASTBitwiseOrNode;
39 import org.apache.commons.jexl3.parser.ASTBitwiseXorNode;
40 import org.apache.commons.jexl3.parser.ASTBlock;
41 import org.apache.commons.jexl3.parser.ASTBreak;
42 import org.apache.commons.jexl3.parser.ASTConstructorNode;
43 import org.apache.commons.jexl3.parser.ASTContinue;
44 import org.apache.commons.jexl3.parser.ASTDecrementGetNode;
45 import org.apache.commons.jexl3.parser.ASTDefineVars;
46 import org.apache.commons.jexl3.parser.ASTDivNode;
47 import org.apache.commons.jexl3.parser.ASTDoWhileStatement;
48 import org.apache.commons.jexl3.parser.ASTEQNode;
49 import org.apache.commons.jexl3.parser.ASTEQSNode;
50 import org.apache.commons.jexl3.parser.ASTERNode;
51 import org.apache.commons.jexl3.parser.ASTEWNode;
52 import org.apache.commons.jexl3.parser.ASTEmptyFunction;
53 import org.apache.commons.jexl3.parser.ASTExtendedLiteral;
54 import org.apache.commons.jexl3.parser.ASTFalseNode;
55 import org.apache.commons.jexl3.parser.ASTForeachStatement;
56 import org.apache.commons.jexl3.parser.ASTFunctionNode;
57 import org.apache.commons.jexl3.parser.ASTGENode;
58 import org.apache.commons.jexl3.parser.ASTGTNode;
59 import org.apache.commons.jexl3.parser.ASTGetDecrementNode;
60 import org.apache.commons.jexl3.parser.ASTGetIncrementNode;
61 import org.apache.commons.jexl3.parser.ASTIdentifier;
62 import org.apache.commons.jexl3.parser.ASTIdentifierAccess;
63 import org.apache.commons.jexl3.parser.ASTIfStatement;
64 import org.apache.commons.jexl3.parser.ASTIncrementGetNode;
65 import org.apache.commons.jexl3.parser.ASTInstanceOf;
66 import org.apache.commons.jexl3.parser.ASTJexlLambda;
67 import org.apache.commons.jexl3.parser.ASTJexlScript;
68 import org.apache.commons.jexl3.parser.ASTJxltLiteral;
69 import org.apache.commons.jexl3.parser.ASTLENode;
70 import org.apache.commons.jexl3.parser.ASTLTNode;
71 import org.apache.commons.jexl3.parser.ASTMapEntry;
72 import org.apache.commons.jexl3.parser.ASTMapLiteral;
73 import org.apache.commons.jexl3.parser.ASTMethodNode;
74 import org.apache.commons.jexl3.parser.ASTModNode;
75 import org.apache.commons.jexl3.parser.ASTMulNode;
76 import org.apache.commons.jexl3.parser.ASTNENode;
77 import org.apache.commons.jexl3.parser.ASTNESNode;
78 import org.apache.commons.jexl3.parser.ASTNEWNode;
79 import org.apache.commons.jexl3.parser.ASTNRNode;
80 import org.apache.commons.jexl3.parser.ASTNSWNode;
81 import org.apache.commons.jexl3.parser.ASTNotInstanceOf;
82 import org.apache.commons.jexl3.parser.ASTNotNode;
83 import org.apache.commons.jexl3.parser.ASTNullLiteral;
84 import org.apache.commons.jexl3.parser.ASTNullpNode;
85 import org.apache.commons.jexl3.parser.ASTNumberLiteral;
86 import org.apache.commons.jexl3.parser.ASTOrNode;
87 import org.apache.commons.jexl3.parser.ASTQualifiedIdentifier;
88 import org.apache.commons.jexl3.parser.ASTRangeNode;
89 import org.apache.commons.jexl3.parser.ASTReference;
90 import org.apache.commons.jexl3.parser.ASTReferenceExpression;
91 import org.apache.commons.jexl3.parser.ASTRegexLiteral;
92 import org.apache.commons.jexl3.parser.ASTReturnStatement;
93 import org.apache.commons.jexl3.parser.ASTSWNode;
94 import org.apache.commons.jexl3.parser.ASTSetAddNode;
95 import org.apache.commons.jexl3.parser.ASTSetAndNode;
96 import org.apache.commons.jexl3.parser.ASTSetDivNode;
97 import org.apache.commons.jexl3.parser.ASTSetLiteral;
98 import org.apache.commons.jexl3.parser.ASTSetModNode;
99 import org.apache.commons.jexl3.parser.ASTSetMultNode;
100 import org.apache.commons.jexl3.parser.ASTSetOrNode;
101 import org.apache.commons.jexl3.parser.ASTSetShiftLeftNode;
102 import org.apache.commons.jexl3.parser.ASTSetShiftRightNode;
103 import org.apache.commons.jexl3.parser.ASTSetShiftRightUnsignedNode;
104 import org.apache.commons.jexl3.parser.ASTSetSubNode;
105 import org.apache.commons.jexl3.parser.ASTSetXorNode;
106 import org.apache.commons.jexl3.parser.ASTShiftLeftNode;
107 import org.apache.commons.jexl3.parser.ASTShiftRightNode;
108 import org.apache.commons.jexl3.parser.ASTShiftRightUnsignedNode;
109 import org.apache.commons.jexl3.parser.ASTSizeFunction;
110 import org.apache.commons.jexl3.parser.ASTStringLiteral;
111 import org.apache.commons.jexl3.parser.ASTSubNode;
112 import org.apache.commons.jexl3.parser.ASTTernaryNode;
113 import org.apache.commons.jexl3.parser.ASTThrowStatement;
114 import org.apache.commons.jexl3.parser.ASTTrueNode;
115 import org.apache.commons.jexl3.parser.ASTTryResources;
116 import org.apache.commons.jexl3.parser.ASTTryStatement;
117 import org.apache.commons.jexl3.parser.ASTUnaryMinusNode;
118 import org.apache.commons.jexl3.parser.ASTUnaryPlusNode;
119 import org.apache.commons.jexl3.parser.ASTVar;
120 import org.apache.commons.jexl3.parser.ASTWhileStatement;
121 import org.apache.commons.jexl3.parser.JexlNode;
122 import org.apache.commons.jexl3.parser.ParserVisitor;
123 import org.apache.commons.jexl3.parser.StringParser;
124
125
126
127
128
129
130
131
132
133 public class Debugger extends ParserVisitor implements JexlInfo.Detail {
134
135
136
137 protected static final Pattern QUOTED_IDENTIFIER =
138 Pattern.compile("\\s|\\p{Punct}&&[^@#$_]");
139 private static boolean isLambdaExpr(final ASTJexlLambda lambda) {
140 return lambda.jjtGetNumChildren() == 1 && !isStatement(lambda.jjtGetChild(0));
141 }
142
143
144
145
146
147 private static boolean isStatement(final JexlNode child) {
148 return child instanceof ASTJexlScript
149 || child instanceof ASTBlock
150 || child instanceof ASTIfStatement
151 || child instanceof ASTForeachStatement
152 || child instanceof ASTTryStatement
153 || child instanceof ASTWhileStatement
154 || child instanceof ASTDoWhileStatement
155 || child instanceof ASTAnnotation
156 || child instanceof ASTThrowStatement;
157 }
158
159
160
161
162
163 private static boolean semicolTerminated(final CharSequence cs) {
164 for(int i = cs.length() - 1; i >= 0; --i) {
165 final char c = cs.charAt(i);
166 if (c == ';') {
167 return true;
168 }
169 if (!Character.isWhitespace(c)) {
170 break;
171 }
172 }
173 return false;
174 }
175
176
177
178
179
180 private static void writePragmas(final StringBuilder builder, final Map<String, Object> pragmas) {
181 if (pragmas != null) {
182 for (final Map.Entry<String, Object> pragma : pragmas.entrySet()) {
183 final String key = pragma.getKey();
184 final Object value = pragma.getValue();
185 final Set<Object> values = value instanceof Set<?>
186 ? (Set<Object>) value
187 : Collections.singleton(value);
188 for (final Object pragmaValue : values) {
189 builder.append("#pragma ");
190 builder.append(key);
191 builder.append(' ');
192 builder.append(pragmaValue.toString());
193 builder.append('\n');
194 }
195 }
196 }
197
198 }
199
200 protected final StringBuilder builder = new StringBuilder();
201
202 protected JexlNode cause;
203
204 protected int start;
205
206 protected int end;
207
208 protected int indentLevel;
209
210
211 protected int indent = 2;
212
213
214 protected int depth = Integer.MAX_VALUE;
215
216
217 protected String arrow = "->";
218
219
220 protected String lf = "\n";
221
222
223 protected boolean outputPragmas;
224
225
226
227
228 public Debugger() {
229
230 }
231
232
233
234
235
236
237
238 protected Object accept(final JexlNode node, final Object data) {
239 if (depth <= 0 && builder.length() > 0) {
240 builder.append("...");
241 return data;
242 }
243 if (node == cause) {
244 start = builder.length();
245 }
246 depth -= 1;
247 final Object value = node.jjtAccept(this, data);
248 depth += 1;
249 if (node == cause) {
250 end = builder.length();
251 }
252 return value;
253 }
254
255
256
257
258
259
260
261 protected Object acceptStatement(final JexlNode child, final Object data) {
262 final JexlNode parent = child.jjtGetParent();
263 if (indent > 0 && (parent instanceof ASTBlock || parent instanceof ASTJexlScript)) {
264 for (int i = 0; i < indentLevel; ++i) {
265 for(int s = 0; s < indent; ++s) {
266 builder.append(' ');
267 }
268 }
269 }
270 depth -= 1;
271 final Object value = accept(child, data);
272 depth += 1;
273
274 if (!isStatement(child) && !semicolTerminated(builder)) {
275 builder.append(';');
276 if (indent > 0) {
277 builder.append(lf);
278 } else {
279 builder.append(' ');
280 }
281 }
282 return value;
283 }
284
285
286
287
288
289
290
291
292 protected Object additiveNode(final JexlNode node, final String op, final Object data) {
293
294 final boolean paren = node.jjtGetParent() instanceof ASTMulNode
295 || node.jjtGetParent() instanceof ASTDivNode
296 || node.jjtGetParent() instanceof ASTModNode;
297 final int num = node.jjtGetNumChildren();
298 if (paren) {
299 builder.append('(');
300 }
301 accept(node.jjtGetChild(0), data);
302 for (int i = 1; i < num; ++i) {
303 builder.append(op);
304 accept(node.jjtGetChild(i), data);
305 }
306 if (paren) {
307 builder.append(')');
308 }
309 return data;
310 }
311
312
313
314
315
316
317
318
319 protected Object check(final JexlNode node, final String image, final Object data) {
320 if (node == cause) {
321 start = builder.length();
322 }
323 if (image != null) {
324 builder.append(image);
325 } else {
326 builder.append(node.toString());
327 }
328 if (node == cause) {
329 end = builder.length();
330 }
331 return data;
332 }
333
334
335
336
337
338
339
340 public String data(final JexlNode node) {
341 start = 0;
342 end = 0;
343 indentLevel = 0;
344 setArrowSymbol(node);
345 if (node != null) {
346 builder.setLength(0);
347 cause = node;
348 accept(node, null);
349 }
350 return builder.toString();
351 }
352
353
354
355
356
357
358 public boolean debug(final JexlExpression jscript) {
359 if (jscript instanceof Script) {
360 final Script script = (Script) jscript;
361 return debug(script.script);
362 }
363 return false;
364 }
365
366
367
368
369
370
371 public boolean debug(final JexlNode node) {
372 return debug(node, true);
373 }
374
375
376
377
378
379
380
381 public boolean debug(final JexlNode node, final boolean r) {
382 start = 0;
383 end = 0;
384 indentLevel = 0;
385 setArrowSymbol(node);
386 if (node != null) {
387 builder.setLength(0);
388 cause = node;
389
390 JexlNode walk = node;
391 if (r) {
392 while (walk.jjtGetParent() != null) {
393 walk = walk.jjtGetParent();
394 }
395 }
396 accept(walk, null);
397 }
398 return end > 0;
399 }
400
401
402
403
404
405
406 public boolean debug(final JexlScript jscript) {
407 if (jscript instanceof Script) {
408 final Script script = (Script) jscript;
409 return debug(script.script);
410 }
411 return false;
412 }
413
414
415
416
417
418
419 public Debugger depth(final int rdepth) {
420 this.depth = rdepth;
421 return this;
422 }
423
424
425
426
427 @Override
428 public int end() {
429 return end;
430 }
431
432
433
434
435
436
437 protected JexlFeatures getFeatures(final JexlNode node) {
438 JexlNode walk = node;
439 while(walk != null) {
440 if (walk instanceof ASTJexlScript) {
441 final ASTJexlScript script = (ASTJexlScript) walk;
442 return script.getFeatures();
443 }
444 walk = walk.jjtGetParent();
445 }
446 return null;
447 }
448
449
450
451
452
453
454 public Debugger indentation(final int level) {
455 indent = Math.max(level, 0);
456 indentLevel = 0;
457 return this;
458 }
459
460
461
462
463
464
465
466
467
468
469 protected Object infixChildren(final JexlNode node, final String infix, final boolean paren, final Object data) {
470 final int num = node.jjtGetNumChildren();
471 if (paren) {
472 builder.append('(');
473 }
474 for (int i = 0; i < num; ++i) {
475 if (i > 0) {
476 builder.append(infix);
477 }
478 accept(node.jjtGetChild(i), data);
479 }
480 if (paren) {
481 builder.append(')');
482 }
483 return data;
484 }
485
486
487
488
489
490
491 public Debugger lineFeed(final String lf) {
492 this.lf = lf;
493 return this;
494 }
495
496
497
498
499
500
501 protected boolean needQuotes(final String str) {
502 return QUOTED_IDENTIFIER.matcher(str).find()
503 || "size".equals(str)
504 || "empty".equals(str);
505 }
506
507
508
509
510
511
512 public Debugger outputPragmas(final boolean flag) {
513 this.outputPragmas = flag;
514 return this;
515 }
516
517
518
519
520
521
522
523
524 protected Object postfixChild(final JexlNode node, final String prefix, final Object data) {
525 final boolean paren = node.jjtGetChild(0).jjtGetNumChildren() > 1;
526 if (paren) {
527 builder.append('(');
528 }
529 accept(node.jjtGetChild(0), data);
530 if (paren) {
531 builder.append(')');
532 }
533 builder.append(prefix);
534 return data;
535 }
536
537
538
539
540
541
542
543
544
545 protected Object prefixChild(final JexlNode node, final String prefix, final Object data) {
546 final boolean paren = node.jjtGetChild(0).jjtGetNumChildren() > 1;
547 builder.append(prefix);
548 if (paren) {
549 builder.append('(');
550 }
551 accept(node.jjtGetChild(0), data);
552 if (paren) {
553 builder.append(')');
554 }
555 return data;
556 }
557
558
559
560
561 public void reset() {
562 builder.setLength(0);
563 cause = null;
564 start = 0;
565 end = 0;
566 indentLevel = 0;
567 indent = 2;
568 depth = Integer.MAX_VALUE;
569 }
570
571
572
573
574
575 protected void setArrowSymbol(final JexlNode node) {
576 final JexlFeatures features = getFeatures(node);
577 if (features != null && features.supportsFatArrow() && !features.supportsThinArrow()) {
578 arrow = "=>";
579 } else {
580 arrow = "->";
581 }
582 }
583
584
585
586
587
588 public void setIndentation(final int level) {
589 indentation(level);
590 }
591
592
593
594
595 @Override
596 public int start() {
597 return start;
598 }
599
600
601
602
603 @Override
604 public String toString() {
605 return builder.toString();
606 }
607
608 @Override
609 protected Object visit(final ASTAddNode node, final Object data) {
610 return additiveNode(node, " + ", data);
611 }
612
613 @Override
614 protected Object visit(final ASTAndNode node, final Object data) {
615 return infixChildren(node, " && ", false, data);
616 }
617
618 @Override
619 protected Object visit(final ASTAnnotatedStatement node, final Object data) {
620 final int num = node.jjtGetNumChildren();
621 for (int i = 0; i < num; ++i) {
622 if (i > 0) {
623 builder.append(' ');
624 }
625 final JexlNode child = node.jjtGetChild(i);
626 acceptStatement(child, data);
627 }
628 return data;
629 }
630
631 @Override
632 protected Object visit(final ASTAnnotation node, final Object data) {
633 final int num = node.jjtGetNumChildren();
634 builder.append('@');
635 builder.append(node.getName());
636 if (num > 0) {
637 accept(node.jjtGetChild(0), data);
638 }
639 return null;
640 }
641
642 @Override
643 protected Object visit(final ASTArguments node, final Object data) {
644 final int num = node.jjtGetNumChildren();
645 builder.append("(");
646 if (num > 0) {
647 accept(node.jjtGetChild(0), data);
648 for (int i = 1; i < num; ++i) {
649 builder.append(", ");
650 accept(node.jjtGetChild(i), data);
651 }
652 }
653 builder.append(")");
654 return data;
655 }
656
657 @Override
658 protected Object visit(final ASTArrayAccess node, final Object data) {
659 final int num = node.jjtGetNumChildren();
660 for (int i = 0; i < num; ++i) {
661 if (node.isSafeChild(i)) {
662 builder.append('?');
663 }
664 builder.append('[');
665 accept(node.jjtGetChild(i), data);
666 builder.append(']');
667 }
668 return data;
669 }
670
671 @Override
672 protected Object visit(final ASTArrayLiteral node, final Object data) {
673 final int num = node.jjtGetNumChildren();
674 builder.append("[ ");
675 if (num > 0) {
676 if (depth <= 0) {
677 builder.append("...");
678 } else {
679 accept(node.jjtGetChild(0), data);
680 for (int i = 1; i < num; ++i) {
681 builder.append(", ");
682 accept(node.jjtGetChild(i), data);
683 }
684 }
685 }
686 builder.append(" ]");
687 return data;
688 }
689
690 @Override
691 protected Object visit(final ASTAssignment node, final Object data) {
692 return infixChildren(node, " = ", false, data);
693 }
694
695 @Override
696 protected Object visit(final ASTBitwiseAndNode node, final Object data) {
697 return infixChildren(node, " & ", false, data);
698 }
699
700 @Override
701 protected Object visit(final ASTBitwiseComplNode node, final Object data) {
702 return prefixChild(node, "~", data);
703 }
704
705 @Override
706 protected Object visit(final ASTBitwiseOrNode node, final Object data) {
707 final boolean paren = node.jjtGetParent() instanceof ASTBitwiseAndNode;
708 return infixChildren(node, " | ", paren, data);
709 }
710
711 @Override
712 protected Object visit(final ASTBitwiseXorNode node, final Object data) {
713 final boolean paren = node.jjtGetParent() instanceof ASTBitwiseAndNode;
714 return infixChildren(node, " ^ ", paren, data);
715 }
716
717 @Override
718 protected Object visit(final ASTBlock node, final Object data) {
719 builder.append('{');
720 if (indent > 0) {
721 indentLevel += 1;
722 builder.append(lf);
723 } else {
724 builder.append(' ');
725 }
726 final int num = node.jjtGetNumChildren();
727 for (int i = 0; i < num; ++i) {
728 final JexlNode child = node.jjtGetChild(i);
729 acceptStatement(child, data);
730 }
731 if (indent > 0) {
732 indentLevel -= 1;
733 for (int i = 0; i < indentLevel; ++i) {
734 for(int s = 0; s < indent; ++s) {
735 builder.append(' ');
736 }
737 }
738 }
739 if (!Character.isSpaceChar(builder.charAt(builder.length() - 1))) {
740 builder.append(' ');
741 }
742 builder.append('}');
743 return data;
744 }
745
746 @Override
747 protected Object visit(final ASTBreak node, final Object data) {
748 return check(node, "break", data);
749 }
750
751 @Override
752 protected Object visit(final ASTConstructorNode node, final Object data) {
753 final int num = node.jjtGetNumChildren();
754 builder.append("new");
755 if (num > 0) {
756 final JexlNode c0 = node.jjtGetChild(0);
757 boolean first = true;
758 if (c0 instanceof ASTQualifiedIdentifier) {
759 builder.append(' ');
760 accept(c0, data);
761 builder.append('(');
762 } else {
763 first = false;
764 builder.append('(');
765 accept(c0, data);
766 }
767 for (int i = 1; i < num; ++i) {
768 if (!first) {
769 builder.append(", ");
770 }
771 accept(node.jjtGetChild(i), data);
772 }
773 }
774 builder.append(")");
775 return data;
776 }
777
778 @Override
779 protected Object visit(final ASTContinue node, final Object data) {
780 return check(node, "continue", data);
781 }
782
783 @Override
784 protected Object visit(final ASTDecrementGetNode node, final Object data) {
785 return prefixChild(node, "--", data);
786 }
787
788 @Override
789 protected Object visit(final ASTDefineVars node, final Object data) {
790 final int num = node.jjtGetNumChildren();
791 if (num > 0) {
792
793 accept(node.jjtGetChild(0), data);
794 for (int i = 1; i < num; ++i) {
795 builder.append(", ");
796 final JexlNode child = node.jjtGetChild(i);
797 if (child instanceof ASTAssignment) {
798 final ASTAssignment assign = (ASTAssignment) child;
799 final int nc = assign.jjtGetNumChildren();
800 final ASTVar avar = (ASTVar) assign.jjtGetChild(0);
801 builder.append(avar.getName());
802 if (nc > 1) {
803 builder.append(" = ");
804 accept(assign.jjtGetChild(1), data);
805 }
806 } else if (child instanceof ASTVar) {
807 final ASTVar avar = (ASTVar) child;
808 builder.append(avar.getName());
809 } else {
810
811 accept(child, data);
812 }
813 }
814 }
815 return data;
816 }
817
818 @Override
819 protected Object visit(final ASTDivNode node, final Object data) {
820 return infixChildren(node, " / ", false, data);
821 }
822
823 @Override
824 protected Object visit(final ASTDoWhileStatement node, final Object data) {
825 builder.append("do ");
826 final int nc = node.jjtGetNumChildren();
827 if (nc > 1) {
828 acceptStatement(node.jjtGetChild(0), data);
829 } else {
830 builder.append(";");
831 }
832 builder.append(" while (");
833 accept(node.jjtGetChild(nc - 1), data);
834 builder.append(")");
835 return data;
836 }
837
838 @Override
839 protected Object visit(final ASTEmptyFunction node, final Object data) {
840 builder.append("empty ");
841 accept(node.jjtGetChild(0), data);
842 return data;
843 }
844
845 @Override
846 protected Object visit(final ASTEQNode node, final Object data) {
847 return infixChildren(node, " == ", false, data);
848 }
849
850 @Override
851 protected Object visit(final ASTEQSNode node, final Object data) {
852 return infixChildren(node, " === ", false, data);
853 }
854
855 @Override
856 protected Object visit(final ASTERNode node, final Object data) {
857 return infixChildren(node, " =~ ", false, data);
858 }
859
860 @Override
861 protected Object visit(final ASTEWNode node, final Object data) {
862 return infixChildren(node, " =$ ", false, data);
863 }
864
865 @Override
866 protected Object visit(final ASTExtendedLiteral node, final Object data) {
867 builder.append("...");
868 return data;
869 }
870
871 @Override
872 protected Object visit(final ASTFalseNode node, final Object data) {
873 return check(node, "false", data);
874 }
875
876 @Override
877 protected Object visit(final ASTForeachStatement node, final Object data) {
878 final int form = node.getLoopForm();
879 builder.append("for(");
880 final JexlNode body;
881 if (form == 0) {
882
883 accept(node.jjtGetChild(0), data);
884 builder.append(" : ");
885 accept(node.jjtGetChild(1), data);
886 builder.append(") ");
887 body = node.jjtGetNumChildren() > 2? node.jjtGetChild(2) : null;
888 } else {
889
890 int nc = 0;
891
892 final JexlNode vars = (form & 1) != 0 ? node.jjtGetChild(nc++) : null;
893 final JexlNode predicate = (form & 2) != 0 ? node.jjtGetChild(nc++) : null;
894
895 final JexlNode step = (form & 4) != 0 ? node.jjtGetChild(nc++) : null;
896
897 body = (form & 8) != 0 ? node.jjtGetChild(nc) : null;
898 if (vars != null) {
899 accept(vars, data);
900 }
901 builder.append("; ");
902 if (predicate != null) {
903 accept(predicate, data);
904 }
905 builder.append("; ");
906 if (step != null) {
907 accept(step, data);
908 }
909 builder.append(") ");
910 }
911
912 if (body != null) {
913 accept(body, data);
914 } else {
915 builder.append(';');
916 }
917 return data;
918 }
919
920 @Override
921 protected Object visit(final ASTFunctionNode node, final Object data) {
922 final int num = node.jjtGetNumChildren();
923 if (num == 3) {
924 accept(node.jjtGetChild(0), data);
925 builder.append(":");
926 accept(node.jjtGetChild(1), data);
927 accept(node.jjtGetChild(2), data);
928 } else if (num == 2) {
929 accept(node.jjtGetChild(0), data);
930 accept(node.jjtGetChild(1), data);
931 }
932 return data;
933 }
934
935
936 @Override
937 protected Object visit(final ASTGENode node, final Object data) {
938 return infixChildren(node, " >= ", false, data);
939 }
940
941 @Override
942 protected Object visit(final ASTGetDecrementNode node, final Object data) {
943 return postfixChild(node, "--", data);
944 }
945
946 @Override
947 protected Object visit(final ASTGetIncrementNode node, final Object data) {
948 return postfixChild(node, "++", data);
949 }
950
951 @Override
952 protected Object visit(final ASTGTNode node, final Object data) {
953 return infixChildren(node, " > ", false, data);
954 }
955
956 @Override
957 protected Object visit(final ASTIdentifier node, final Object data) {
958 final String ns = node.getNamespace();
959 final String image = StringParser.escapeIdentifier(node.getName());
960 if (ns == null) {
961 return check(node, image, data);
962 }
963 final String nsid = StringParser.escapeIdentifier(ns) + ":" + image;
964 return check(node, nsid, data);
965 }
966
967 @Override
968 protected Object visit(final ASTIdentifierAccess node, final Object data) {
969 builder.append(node.isSafe() ? "?." : ".");
970 final String image = node.getName();
971 if (node.isExpression()) {
972 builder.append('`');
973 builder.append(image.replace("`", "\\`"));
974 builder.append('`');
975 } else if (needQuotes(image)) {
976
977 builder.append('\'');
978 builder.append(image.replace("'", "\\'"));
979 builder.append('\'');
980 } else {
981 builder.append(image);
982 }
983 return data;
984 }
985
986 @Override
987 protected Object visit(final ASTIfStatement node, final Object data) {
988 final int numChildren = node.jjtGetNumChildren();
989
990 builder.append("if (");
991 accept(node.jjtGetChild(0), data);
992 builder.append(") ");
993 acceptStatement(node.jjtGetChild(1), data);
994
995 for(int c = 2; c < numChildren - 1; c += 2) {
996 builder.append(" else if (");
997 accept(node.jjtGetChild(c), data);
998 builder.append(") ");
999 acceptStatement(node.jjtGetChild(c + 1), data);
1000 }
1001
1002 if ((numChildren & 1) == 1) {
1003 builder.append(" else ");
1004 acceptStatement(node.jjtGetChild(numChildren - 1), data);
1005 }
1006 return data;
1007 }
1008
1009 @Override
1010 protected Object visit(final ASTIncrementGetNode node, final Object data) {
1011 return prefixChild(node, "++", data);
1012 }
1013
1014 @Override
1015 protected Object visit(final ASTInstanceOf node, final Object data) {
1016 return infixChildren(node, " instanceof ", false, data);
1017 }
1018
1019 @Override
1020 protected Object visit(final ASTJexlScript node, final Object arg) {
1021 if (outputPragmas) {
1022 writePragmas(builder, node.getPragmas());
1023 }
1024 Object data = arg;
1025 boolean named = false;
1026
1027 if (node instanceof ASTJexlLambda) {
1028 final ASTJexlLambda lambda = (ASTJexlLambda) node;
1029 final JexlNode parent = node.jjtGetParent();
1030
1031 final boolean expr = isLambdaExpr(lambda);
1032 named = node.jjtGetChild(0) instanceof ASTVar;
1033 final boolean assigned = parent instanceof ASTAssignment || named;
1034 if (assigned && !expr) {
1035 builder.append("function");
1036 if (named) {
1037 final ASTVar avar = (ASTVar) node.jjtGetChild(0);
1038 builder.append(' ');
1039 builder.append(avar.getName());
1040 }
1041 }
1042 builder.append('(');
1043 final String[] params = lambda.getParameters();
1044 if (params != null ) {
1045 final Scope scope = lambda.getScope();
1046 final LexicalScope lexicalScope = lambda.getLexicalScope();
1047 for (int p = 0; p < params.length; ++p) {
1048 if (p > 0) {
1049 builder.append(", ");
1050 }
1051 final String param = params[p];
1052 final int symbol = scope.getSymbol(param);
1053 if (lexicalScope.isConstant(symbol)) {
1054 builder.append("const ");
1055 } else if (scope.isLexical(symbol)) {
1056 builder.append("let ");
1057 }
1058 builder.append(visitParameter(param, data));
1059 }
1060 }
1061 builder.append(')');
1062 if (assigned && !expr) {
1063
1064 builder.append(' ');
1065 } else {
1066 builder.append(arrow);
1067
1068 if (expr) {
1069 builder.append(' ');
1070 }
1071 }
1072 }
1073
1074 final int num = node.jjtGetNumChildren();
1075 if (num == 1 && !(node instanceof ASTJexlLambda)) {
1076 data = accept(node.jjtGetChild(0), data);
1077 } else {
1078 for (int i = named? 1 : 0; i < num; ++i) {
1079 final JexlNode child = node.jjtGetChild(i);
1080 acceptStatement(child, data);
1081 }
1082 }
1083 return data;
1084 }
1085
1086 @Override
1087 protected Object visit(final ASTJxltLiteral node, final Object data) {
1088 final String img = StringParser.escapeString(node.getLiteral(), '`');
1089 return check(node, img, data);
1090 }
1091
1092 @Override
1093 protected Object visit(final ASTLENode node, final Object data) {
1094 return infixChildren(node, " <= ", false, data);
1095 }
1096
1097 @Override
1098 protected Object visit(final ASTLTNode node, final Object data) {
1099 return infixChildren(node, " < ", false, data);
1100 }
1101
1102 @Override
1103 protected Object visit(final ASTMapEntry node, final Object data) {
1104 accept(node.jjtGetChild(0), data);
1105 builder.append(" : ");
1106 accept(node.jjtGetChild(1), data);
1107 return data;
1108 }
1109
1110 @Override
1111 protected Object visit(final ASTMapLiteral node, final Object data) {
1112 final int num = node.jjtGetNumChildren();
1113 builder.append("{ ");
1114 if (num > 0) {
1115 if (depth <= 0) {
1116 builder.append("...");
1117 } else {
1118 accept(node.jjtGetChild(0), data);
1119 for (int i = 1; i < num; ++i) {
1120 builder.append(",");
1121 accept(node.jjtGetChild(i), data);
1122 }
1123 }
1124 } else {
1125 builder.append(':');
1126 }
1127 builder.append(" }");
1128 return data;
1129 }
1130
1131 @Override
1132 protected Object visit(final ASTMethodNode node, final Object data) {
1133 final int num = node.jjtGetNumChildren();
1134 if (num == 2) {
1135 accept(node.jjtGetChild(0), data);
1136 if (depth <= 0) {
1137 builder.append("(...)");
1138 } else {
1139 accept(node.jjtGetChild(1), data);
1140 }
1141 }
1142 return data;
1143 }
1144
1145 @Override
1146 protected Object visit(final ASTModNode node, final Object data) {
1147 return infixChildren(node, " % ", false, data);
1148 }
1149
1150 @Override
1151 protected Object visit(final ASTMulNode node, final Object data) {
1152 return infixChildren(node, " * ", false, data);
1153 }
1154
1155 @Override
1156 protected Object visit(final ASTNENode node, final Object data) {
1157 return infixChildren(node, " != ", false, data);
1158 }
1159
1160 @Override
1161 protected Object visit(final ASTNESNode node, final Object data) {
1162 return infixChildren(node, " !== ", false, data);
1163 }
1164
1165 @Override
1166 protected Object visit(final ASTNEWNode node, final Object data) {
1167 return infixChildren(node, " !$ ", false, data);
1168 }
1169
1170 @Override
1171 protected Object visit(final ASTNotInstanceOf node, final Object data) {
1172 return infixChildren(node, " !instanceof ", false, data);
1173 }
1174
1175 @Override
1176 protected Object visit(final ASTNotNode node, final Object data) {
1177 builder.append("!");
1178 accept(node.jjtGetChild(0), data);
1179 return data;
1180 }
1181
1182 @Override
1183 protected Object visit(final ASTNRNode node, final Object data) {
1184 return infixChildren(node, " !~ ", false, data);
1185 }
1186
1187 @Override
1188 protected Object visit(final ASTNSWNode node, final Object data) {
1189 return infixChildren(node, " !^ ", false, data);
1190 }
1191
1192 @Override
1193 protected Object visit(final ASTNullLiteral node, final Object data) {
1194 check(node, "null", data);
1195 return data;
1196 }
1197
1198 @Override
1199 protected Object visit(final ASTNullpNode node, final Object data) {
1200 accept(node.jjtGetChild(0), data);
1201 builder.append("??");
1202 accept(node.jjtGetChild(1), data);
1203 return data;
1204 }
1205
1206 @Override
1207 protected Object visit(final ASTNumberLiteral node, final Object data) {
1208 return check(node, node.toString(), data);
1209 }
1210
1211 @Override
1212 protected Object visit(final ASTOrNode node, final Object data) {
1213
1214 final boolean paren = node.jjtGetParent() instanceof ASTAndNode;
1215 return infixChildren(node, " || ", paren, data);
1216 }
1217
1218 @Override
1219 protected Object visit(final ASTQualifiedIdentifier node, final Object data) {
1220 final String img = node.getName();
1221 return check(node, img, data);
1222 }
1223
1224 @Override
1225 protected Object visit(final ASTRangeNode node, final Object data) {
1226 if (depth <= 0) {
1227 builder.append("( .. )");
1228 return data;
1229 }
1230 return infixChildren(node, " .. ", false, data);
1231 }
1232
1233 @Override
1234 protected Object visit(final ASTReference node, final Object data) {
1235 final int num = node.jjtGetNumChildren();
1236 for (int i = 0; i < num; ++i) {
1237 accept(node.jjtGetChild(i), data);
1238 }
1239 return data;
1240 }
1241
1242 @Override
1243 protected Object visit(final ASTReferenceExpression node, final Object data) {
1244 final JexlNode first = node.jjtGetChild(0);
1245 builder.append('(');
1246 accept(first, data);
1247 builder.append(')');
1248 final int num = node.jjtGetNumChildren();
1249 for (int i = 1; i < num; ++i) {
1250 builder.append("[");
1251 accept(node.jjtGetChild(i), data);
1252 builder.append("]");
1253 }
1254 return data;
1255 }
1256
1257 @Override
1258 protected Object visit(final ASTRegexLiteral node, final Object data) {
1259 final String img = StringParser.escapeString(node.toString(), '/');
1260 return check(node, "~" + img, data);
1261 }
1262
1263 @Override
1264 protected Object visit(final ASTReturnStatement node, final Object data) {
1265 builder.append("return ");
1266 accept(node.jjtGetChild(0), data);
1267 return data;
1268 }
1269
1270 @Override
1271 protected Object visit(final ASTSetAddNode node, final Object data) {
1272 return infixChildren(node, " += ", false, data);
1273 }
1274
1275 @Override
1276 protected Object visit(final ASTSetAndNode node, final Object data) {
1277 return infixChildren(node, " &= ", false, data);
1278 }
1279
1280 @Override
1281 protected Object visit(final ASTSetDivNode node, final Object data) {
1282 return infixChildren(node, " /= ", false, data);
1283 }
1284
1285 @Override
1286 protected Object visit(final ASTSetLiteral node, final Object data) {
1287 final int num = node.jjtGetNumChildren();
1288 builder.append("{ ");
1289 if (num > 0) {
1290 if (depth <= 0) {
1291 builder.append("...");
1292 } else {
1293 accept(node.jjtGetChild(0), data);
1294 for (int i = 1; i < num; ++i) {
1295 builder.append(",");
1296 accept(node.jjtGetChild(i), data);
1297 }
1298 }
1299 }
1300 builder.append(" }");
1301 return data;
1302 }
1303
1304 @Override
1305 protected Object visit(final ASTSetModNode node, final Object data) {
1306 return infixChildren(node, " %= ", false, data);
1307 }
1308
1309 @Override
1310 protected Object visit(final ASTSetMultNode node, final Object data) {
1311 return infixChildren(node, " *= ", false, data);
1312 }
1313
1314 @Override
1315 protected Object visit(final ASTSetOrNode node, final Object data) {
1316 return infixChildren(node, " |= ", false, data);
1317 }
1318
1319 @Override
1320 protected Object visit(final ASTSetShiftLeftNode node, final Object data) {
1321 return infixChildren(node, " <<= ", false, data);
1322 }
1323
1324 @Override
1325 protected Object visit(final ASTSetShiftRightNode node, final Object data) {
1326 return infixChildren(node, " >>= ", false, data);
1327 }
1328
1329 @Override
1330 protected Object visit(final ASTSetShiftRightUnsignedNode node, final Object data) {
1331 return infixChildren(node, " >>>= ", false, data);
1332 }
1333
1334 @Override
1335 protected Object visit(final ASTSetSubNode node, final Object data) {
1336 return infixChildren(node, " -= ", false, data);
1337 }
1338
1339 @Override
1340 protected Object visit(final ASTSetXorNode node, final Object data) {
1341 return infixChildren(node, " ^= ", false, data);
1342 }
1343
1344 @Override
1345 protected Object visit(final ASTShiftLeftNode node, final Object data) {
1346 return infixChildren(node, " << ", false, data);
1347 }
1348
1349 @Override
1350 protected Object visit(final ASTShiftRightNode node, final Object data) {
1351 return infixChildren(node, " >> ", false, data);
1352 }
1353
1354 @Override
1355 protected Object visit(final ASTShiftRightUnsignedNode node, final Object data) {
1356 return infixChildren(node, " >>> ", false, data);
1357 }
1358
1359 @Override
1360 protected Object visit(final ASTSizeFunction node, final Object data) {
1361 builder.append("size ");
1362 accept(node.jjtGetChild(0), data);
1363 return data;
1364 }
1365
1366 @Override
1367 protected Object visit(final ASTStringLiteral node, final Object data) {
1368 final String img = StringParser.escapeString(node.getLiteral(), '\'');
1369 return check(node, img, data);
1370 }
1371
1372 @Override
1373 protected Object visit(final ASTSubNode node, final Object data) {
1374 return additiveNode(node, " - ", data);
1375 }
1376
1377 @Override
1378 protected Object visit(final ASTSWNode node, final Object data) {
1379 return infixChildren(node, " =^ ", false, data);
1380 }
1381
1382 @Override
1383 protected Object visit(final ASTTernaryNode node, final Object data) {
1384 accept(node.jjtGetChild(0), data);
1385 if (node.jjtGetNumChildren() > 2) {
1386 builder.append("? ");
1387 accept(node.jjtGetChild(1), data);
1388 builder.append(" : ");
1389 accept(node.jjtGetChild(2), data);
1390 } else {
1391 builder.append("?: ");
1392 accept(node.jjtGetChild(1), data);
1393
1394 }
1395 return data;
1396 }
1397
1398 @Override
1399 protected Object visit(final ASTThrowStatement node, final Object data) {
1400 builder.append("throw ");
1401 accept(node.jjtGetChild(0), data);
1402 return data;
1403 }
1404
1405 @Override
1406 protected Object visit(final ASTTrueNode node, final Object data) {
1407 check(node, "true", data);
1408 return data;
1409 }
1410
1411 @Override
1412 protected Object visit(final ASTTryResources node, final Object data) {
1413 final int tryBody = node.jjtGetNumChildren() - 1;
1414 builder.append('(');
1415 accept(node.jjtGetChild(0), data);
1416 for(int c = 1; c < tryBody; ++c) {
1417 builder.append("; ");
1418 accept(node.jjtGetChild(c), data);
1419 }
1420 builder.append(") ");
1421 accept(node.jjtGetChild(tryBody), data);
1422 return data;
1423 }
1424
1425 @Override
1426 protected Object visit(final ASTTryStatement node, final Object data) {
1427 builder.append("try");
1428 int nc = 0;
1429
1430 accept(node.jjtGetChild(nc++), data);
1431
1432 if (node.hasCatchClause()) {
1433 builder.append("catch(");
1434 accept(node.jjtGetChild(nc++), data);
1435 builder.append(") ");
1436 accept(node.jjtGetChild(nc++), data);
1437 }
1438
1439 if (node.hasFinallyClause()) {
1440 builder.append(indent > 0? lf : ' ');
1441 builder.append("finally ");
1442 accept(node.jjtGetChild(nc), data);
1443 }
1444 return data;
1445 }
1446
1447 @Override
1448 protected Object visit(final ASTUnaryMinusNode node, final Object data) {
1449 return prefixChild(node, "-", data);
1450 }
1451
1452 @Override
1453 protected Object visit(final ASTUnaryPlusNode node, final Object data) {
1454 return prefixChild(node, "+", data);
1455 }
1456
1457 @Override
1458 protected Object visit(final ASTVar node, final Object data) {
1459 if (node.isConstant()) {
1460 builder.append("const ");
1461 } else if (node.isLexical()) {
1462 builder.append("let ");
1463 } else {
1464 builder.append("var ");
1465 }
1466 check(node, node.getName(), data);
1467 return data;
1468 }
1469
1470 @Override
1471 protected Object visit(final ASTWhileStatement node, final Object data) {
1472 builder.append("while (");
1473 accept(node.jjtGetChild(0), data);
1474 builder.append(") ");
1475 if (node.jjtGetNumChildren() > 1) {
1476 acceptStatement(node.jjtGetChild(1), data);
1477 } else {
1478 builder.append(';');
1479 }
1480 return data;
1481 }
1482
1483
1484
1485
1486
1487
1488
1489 protected String visitParameter(final String p, final Object data) {
1490 return p;
1491 }
1492 }