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