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