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 char lastChar = builder.charAt(builder.length() - 1);
740 if (!Character.isSpaceChar(lastChar) && lastChar != '\n') {
741 builder.append(' ');
742 }
743 builder.append('}');
744 return data;
745 }
746
747 @Override
748 protected Object visit(final ASTBreak node, final Object data) {
749 return check(node, "break", data);
750 }
751
752 @Override
753 protected Object visit(final ASTConstructorNode node, final Object data) {
754 final int num = node.jjtGetNumChildren();
755 builder.append("new");
756 if (num > 0) {
757 final JexlNode c0 = node.jjtGetChild(0);
758 boolean first = true;
759 if (c0 instanceof ASTQualifiedIdentifier) {
760 builder.append(' ');
761 accept(c0, data);
762 builder.append('(');
763 } else {
764 first = false;
765 builder.append('(');
766 accept(c0, data);
767 }
768 for (int i = 1; i < num; ++i) {
769 if (!first) {
770 builder.append(", ");
771 }
772 accept(node.jjtGetChild(i), data);
773 }
774 }
775 builder.append(")");
776 return data;
777 }
778
779 @Override
780 protected Object visit(final ASTContinue node, final Object data) {
781 return check(node, "continue", data);
782 }
783
784 @Override
785 protected Object visit(final ASTDecrementGetNode node, final Object data) {
786 return prefixChild(node, "--", data);
787 }
788
789 @Override
790 protected Object visit(final ASTDefineVars node, final Object data) {
791 final int num = node.jjtGetNumChildren();
792 if (num > 0) {
793
794 accept(node.jjtGetChild(0), data);
795 for (int i = 1; i < num; ++i) {
796 builder.append(", ");
797 final JexlNode child = node.jjtGetChild(i);
798 if (child instanceof ASTAssignment) {
799 final ASTAssignment assign = (ASTAssignment) child;
800 final int nc = assign.jjtGetNumChildren();
801 final ASTVar avar = (ASTVar) assign.jjtGetChild(0);
802 builder.append(avar.getName());
803 if (nc > 1) {
804 builder.append(" = ");
805 accept(assign.jjtGetChild(1), data);
806 }
807 } else if (child instanceof ASTVar) {
808 final ASTVar avar = (ASTVar) child;
809 builder.append(avar.getName());
810 } else {
811
812 accept(child, data);
813 }
814 }
815 }
816 return data;
817 }
818
819 @Override
820 protected Object visit(final ASTDivNode node, final Object data) {
821 return infixChildren(node, " / ", false, data);
822 }
823
824 @Override
825 protected Object visit(final ASTDoWhileStatement node, final Object data) {
826 builder.append("do ");
827 final int nc = node.jjtGetNumChildren();
828 if (nc > 1) {
829 acceptStatement(node.jjtGetChild(0), data);
830 } else {
831 builder.append(";");
832 }
833 builder.append(" while (");
834 accept(node.jjtGetChild(nc - 1), data);
835 builder.append(")");
836 return data;
837 }
838
839 @Override
840 protected Object visit(final ASTEmptyFunction node, final Object data) {
841 builder.append("empty ");
842 accept(node.jjtGetChild(0), data);
843 return data;
844 }
845
846 @Override
847 protected Object visit(final ASTEQNode node, final Object data) {
848 return infixChildren(node, " == ", false, data);
849 }
850
851 @Override
852 protected Object visit(final ASTEQSNode node, final Object data) {
853 return infixChildren(node, " === ", false, data);
854 }
855
856 @Override
857 protected Object visit(final ASTERNode node, final Object data) {
858 return infixChildren(node, " =~ ", false, data);
859 }
860
861 @Override
862 protected Object visit(final ASTEWNode node, final Object data) {
863 return infixChildren(node, " =$ ", false, data);
864 }
865
866 @Override
867 protected Object visit(final ASTExtendedLiteral node, final Object data) {
868 builder.append("...");
869 return data;
870 }
871
872 @Override
873 protected Object visit(final ASTFalseNode node, final Object data) {
874 return check(node, "false", data);
875 }
876
877 @Override
878 protected Object visit(final ASTForeachStatement node, final Object data) {
879 final int form = node.getLoopForm();
880 builder.append("for (");
881 final JexlNode body;
882 if (form == 0) {
883
884 accept(node.jjtGetChild(0), data);
885 builder.append(" : ");
886 accept(node.jjtGetChild(1), data);
887 builder.append(") ");
888 body = node.jjtGetNumChildren() > 2? node.jjtGetChild(2) : null;
889 } else {
890
891 int nc = 0;
892
893 final JexlNode vars = (form & 1) != 0 ? node.jjtGetChild(nc++) : null;
894 final JexlNode predicate = (form & 2) != 0 ? node.jjtGetChild(nc++) : null;
895
896 final JexlNode step = (form & 4) != 0 ? node.jjtGetChild(nc++) : null;
897
898 body = (form & 8) != 0 ? node.jjtGetChild(nc) : null;
899 if (vars != null) {
900 accept(vars, data);
901 }
902 builder.append("; ");
903 if (predicate != null) {
904 accept(predicate, data);
905 }
906 builder.append("; ");
907 if (step != null) {
908 accept(step, data);
909 }
910 builder.append(") ");
911 }
912
913 if (body != null) {
914 accept(body, data);
915 } else {
916 builder.append(';');
917 }
918 return data;
919 }
920
921 @Override
922 protected Object visit(final ASTFunctionNode node, final Object data) {
923 final int num = node.jjtGetNumChildren();
924 if (num == 3) {
925 accept(node.jjtGetChild(0), data);
926 builder.append(":");
927 accept(node.jjtGetChild(1), data);
928 accept(node.jjtGetChild(2), data);
929 } else if (num == 2) {
930 accept(node.jjtGetChild(0), data);
931 accept(node.jjtGetChild(1), data);
932 }
933 return data;
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 if (node.jjtGetNumChildren() > 0) {
1267 builder.append(' ');
1268 accept(node.jjtGetChild(0), data);
1269 }
1270 return data;
1271 }
1272
1273 @Override
1274 protected Object visit(final ASTSetAddNode node, final Object data) {
1275 return infixChildren(node, " += ", false, data);
1276 }
1277
1278 @Override
1279 protected Object visit(final ASTSetAndNode node, final Object data) {
1280 return infixChildren(node, " &= ", false, data);
1281 }
1282
1283 @Override
1284 protected Object visit(final ASTSetDivNode node, final Object data) {
1285 return infixChildren(node, " /= ", false, data);
1286 }
1287
1288 @Override
1289 protected Object visit(final ASTSetLiteral node, final Object data) {
1290 final int num = node.jjtGetNumChildren();
1291 builder.append("{ ");
1292 if (num > 0) {
1293 if (depth <= 0) {
1294 builder.append("...");
1295 } else {
1296 accept(node.jjtGetChild(0), data);
1297 for (int i = 1; i < num; ++i) {
1298 builder.append(",");
1299 accept(node.jjtGetChild(i), data);
1300 }
1301 }
1302 }
1303 builder.append(" }");
1304 return data;
1305 }
1306
1307 @Override
1308 protected Object visit(final ASTSetModNode node, final Object data) {
1309 return infixChildren(node, " %= ", false, data);
1310 }
1311
1312 @Override
1313 protected Object visit(final ASTSetMultNode node, final Object data) {
1314 return infixChildren(node, " *= ", false, data);
1315 }
1316
1317 @Override
1318 protected Object visit(final ASTSetOrNode node, final Object data) {
1319 return infixChildren(node, " |= ", false, data);
1320 }
1321
1322 @Override
1323 protected Object visit(final ASTSetShiftLeftNode node, final Object data) {
1324 return infixChildren(node, " <<= ", false, data);
1325 }
1326
1327 @Override
1328 protected Object visit(final ASTSetShiftRightNode node, final Object data) {
1329 return infixChildren(node, " >>= ", false, data);
1330 }
1331
1332 @Override
1333 protected Object visit(final ASTSetShiftRightUnsignedNode node, final Object data) {
1334 return infixChildren(node, " >>>= ", false, data);
1335 }
1336
1337 @Override
1338 protected Object visit(final ASTSetSubNode node, final Object data) {
1339 return infixChildren(node, " -= ", false, data);
1340 }
1341
1342 @Override
1343 protected Object visit(final ASTSetXorNode node, final Object data) {
1344 return infixChildren(node, " ^= ", false, data);
1345 }
1346
1347 @Override
1348 protected Object visit(final ASTShiftLeftNode node, final Object data) {
1349 return infixChildren(node, " << ", false, data);
1350 }
1351
1352 @Override
1353 protected Object visit(final ASTShiftRightNode node, final Object data) {
1354 return infixChildren(node, " >> ", false, data);
1355 }
1356
1357 @Override
1358 protected Object visit(final ASTShiftRightUnsignedNode node, final Object data) {
1359 return infixChildren(node, " >>> ", false, data);
1360 }
1361
1362 @Override
1363 protected Object visit(final ASTSizeFunction node, final Object data) {
1364 builder.append("size ");
1365 accept(node.jjtGetChild(0), data);
1366 return data;
1367 }
1368
1369 @Override
1370 protected Object visit(final ASTStringLiteral node, final Object data) {
1371 final String img = StringParser.escapeString(node.getLiteral(), '\'');
1372 return check(node, img, data);
1373 }
1374
1375 @Override
1376 protected Object visit(final ASTSubNode node, final Object data) {
1377 return additiveNode(node, " - ", data);
1378 }
1379
1380 @Override
1381 protected Object visit(final ASTSWNode node, final Object data) {
1382 return infixChildren(node, " =^ ", false, data);
1383 }
1384
1385 @Override
1386 protected Object visit(final ASTTernaryNode node, final Object data) {
1387 accept(node.jjtGetChild(0), data);
1388 if (node.jjtGetNumChildren() > 2) {
1389 builder.append("? ");
1390 accept(node.jjtGetChild(1), data);
1391 builder.append(" : ");
1392 accept(node.jjtGetChild(2), data);
1393 } else {
1394 builder.append("?: ");
1395 accept(node.jjtGetChild(1), data);
1396
1397 }
1398 return data;
1399 }
1400
1401 @Override
1402 protected Object visit(final ASTThrowStatement node, final Object data) {
1403 builder.append("throw ");
1404 accept(node.jjtGetChild(0), data);
1405 return data;
1406 }
1407
1408 @Override
1409 protected Object visit(final ASTTrueNode node, final Object data) {
1410 check(node, "true", data);
1411 return data;
1412 }
1413
1414 @Override
1415 protected Object visit(final ASTTryResources node, final Object data) {
1416 final int tryBody = node.jjtGetNumChildren() - 1;
1417 builder.append(" (");
1418 accept(node.jjtGetChild(0), data);
1419 for(int c = 1; c < tryBody; ++c) {
1420 builder.append("; ");
1421 accept(node.jjtGetChild(c), data);
1422 }
1423 builder.append(") ");
1424 accept(node.jjtGetChild(tryBody), data);
1425 return data;
1426 }
1427
1428 @Override
1429 protected Object visit(final ASTTryStatement node, final Object data) {
1430 builder.append("try");
1431 int nc = 0;
1432
1433 accept(node.jjtGetChild(nc++), data);
1434
1435 if (node.hasCatchClause()) {
1436 builder.append("catch (");
1437 accept(node.jjtGetChild(nc++), data);
1438 builder.append(") ");
1439 accept(node.jjtGetChild(nc++), data);
1440 }
1441
1442 if (node.hasFinallyClause()) {
1443 builder.append(indent > 0? lf : ' ');
1444 builder.append("finally ");
1445 accept(node.jjtGetChild(nc), data);
1446 }
1447 return data;
1448 }
1449
1450 @Override
1451 protected Object visit(final ASTUnaryMinusNode node, final Object data) {
1452 return prefixChild(node, "-", data);
1453 }
1454
1455 @Override
1456 protected Object visit(final ASTUnaryPlusNode node, final Object data) {
1457 return prefixChild(node, "+", data);
1458 }
1459
1460 @Override
1461 protected Object visit(final ASTVar node, final Object data) {
1462 if (node.isConstant()) {
1463 builder.append("const ");
1464 } else if (node.isLexical()) {
1465 builder.append("let ");
1466 } else {
1467 builder.append("var ");
1468 }
1469 check(node, node.getName(), data);
1470 return data;
1471 }
1472
1473 @Override
1474 protected Object visit(final ASTWhileStatement node, final Object data) {
1475 builder.append("while (");
1476 accept(node.jjtGetChild(0), data);
1477 builder.append(") ");
1478 if (node.jjtGetNumChildren() > 1) {
1479 acceptStatement(node.jjtGetChild(1), data);
1480 } else {
1481 builder.append(';');
1482 }
1483 return data;
1484 }
1485
1486
1487
1488
1489
1490
1491
1492 protected String visitParameter(final String p, final Object data) {
1493 return p;
1494 }
1495 }