blob: 5ee11df1c17ba215445592129fcb3bf9b4e60d38 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.jexl3.internal;
import org.apache.commons.jexl3.JexlExpression;
import org.apache.commons.jexl3.JexlInfo;
import org.apache.commons.jexl3.JexlScript;
import org.apache.commons.jexl3.parser.ASTAddNode;
import org.apache.commons.jexl3.parser.ASTAndNode;
import org.apache.commons.jexl3.parser.ASTArguments;
import org.apache.commons.jexl3.parser.ASTArrayAccess;
import org.apache.commons.jexl3.parser.ASTArrayLiteral;
import org.apache.commons.jexl3.parser.ASTAssignment;
import org.apache.commons.jexl3.parser.ASTBitwiseAndNode;
import org.apache.commons.jexl3.parser.ASTBitwiseComplNode;
import org.apache.commons.jexl3.parser.ASTBitwiseOrNode;
import org.apache.commons.jexl3.parser.ASTBitwiseXorNode;
import org.apache.commons.jexl3.parser.ASTBlock;
import org.apache.commons.jexl3.parser.ASTBreak;
import org.apache.commons.jexl3.parser.ASTConstructorNode;
import org.apache.commons.jexl3.parser.ASTContinue;
import org.apache.commons.jexl3.parser.ASTDivNode;
import org.apache.commons.jexl3.parser.ASTEQNode;
import org.apache.commons.jexl3.parser.ASTERNode;
import org.apache.commons.jexl3.parser.ASTEWNode;
import org.apache.commons.jexl3.parser.ASTEmptyFunction;
import org.apache.commons.jexl3.parser.ASTEmptyMethod;
import org.apache.commons.jexl3.parser.ASTExtendedLiteral;
import org.apache.commons.jexl3.parser.ASTFalseNode;
import org.apache.commons.jexl3.parser.ASTForeachStatement;
import org.apache.commons.jexl3.parser.ASTFunctionNode;
import org.apache.commons.jexl3.parser.ASTGENode;
import org.apache.commons.jexl3.parser.ASTGTNode;
import org.apache.commons.jexl3.parser.ASTIdentifier;
import org.apache.commons.jexl3.parser.ASTIdentifierAccess;
import org.apache.commons.jexl3.parser.ASTIfStatement;
import org.apache.commons.jexl3.parser.ASTJexlLambda;
import org.apache.commons.jexl3.parser.ASTJexlScript;
import org.apache.commons.jexl3.parser.ASTJxltLiteral;
import org.apache.commons.jexl3.parser.ASTLENode;
import org.apache.commons.jexl3.parser.ASTLTNode;
import org.apache.commons.jexl3.parser.ASTMapEntry;
import org.apache.commons.jexl3.parser.ASTMapLiteral;
import org.apache.commons.jexl3.parser.ASTMethodNode;
import org.apache.commons.jexl3.parser.ASTModNode;
import org.apache.commons.jexl3.parser.ASTMulNode;
import org.apache.commons.jexl3.parser.ASTNENode;
import org.apache.commons.jexl3.parser.ASTNEWNode;
import org.apache.commons.jexl3.parser.ASTNRNode;
import org.apache.commons.jexl3.parser.ASTNSWNode;
import org.apache.commons.jexl3.parser.ASTNotNode;
import org.apache.commons.jexl3.parser.ASTNullLiteral;
import org.apache.commons.jexl3.parser.ASTNumberLiteral;
import org.apache.commons.jexl3.parser.ASTOrNode;
import org.apache.commons.jexl3.parser.ASTRangeNode;
import org.apache.commons.jexl3.parser.ASTReference;
import org.apache.commons.jexl3.parser.ASTReferenceExpression;
import org.apache.commons.jexl3.parser.ASTReturnStatement;
import org.apache.commons.jexl3.parser.ASTSWNode;
import org.apache.commons.jexl3.parser.ASTSetAddNode;
import org.apache.commons.jexl3.parser.ASTSetAndNode;
import org.apache.commons.jexl3.parser.ASTSetDivNode;
import org.apache.commons.jexl3.parser.ASTSetLiteral;
import org.apache.commons.jexl3.parser.ASTSetModNode;
import org.apache.commons.jexl3.parser.ASTSetMultNode;
import org.apache.commons.jexl3.parser.ASTSetOrNode;
import org.apache.commons.jexl3.parser.ASTSetSubNode;
import org.apache.commons.jexl3.parser.ASTSetXorNode;
import org.apache.commons.jexl3.parser.ASTSizeFunction;
import org.apache.commons.jexl3.parser.ASTSizeMethod;
import org.apache.commons.jexl3.parser.ASTStringLiteral;
import org.apache.commons.jexl3.parser.ASTSubNode;
import org.apache.commons.jexl3.parser.ASTTernaryNode;
import org.apache.commons.jexl3.parser.ASTTrueNode;
import org.apache.commons.jexl3.parser.ASTUnaryMinusNode;
import org.apache.commons.jexl3.parser.ASTVar;
import org.apache.commons.jexl3.parser.ASTWhileStatement;
import org.apache.commons.jexl3.parser.JexlNode;
import org.apache.commons.jexl3.parser.ParserVisitor;
import java.util.regex.Pattern;
import org.apache.commons.jexl3.parser.ASTAnnotatedStatement;
import org.apache.commons.jexl3.parser.ASTAnnotation;
/**
* Helps pinpoint the cause of problems in expressions that fail during evaluation.
* <p>
* It rebuilds an expression string from the tree and the start/end offsets of the cause in that string.
* This implies that exceptions during evaluation do always carry the node that's causing the error.
* </p>
* @since 2.0
*/
public class Debugger extends ParserVisitor implements JexlInfo.Detail {
/** The builder to compose messages. */
protected final StringBuilder builder = new StringBuilder();
/** The cause of the issue to debug. */
protected JexlNode cause = null;
/** The starting character location offset of the cause in the builder. */
protected int start = 0;
/** The ending character location offset of the cause in the builder. */
protected int end = 0;
/** The indentation level. */
protected int indentLevel = 0;
/** Perform indentation?. */
protected int indent = 2;
/**
* Creates a Debugger.
*/
public Debugger() {
}
/**
* Position the debugger on the root of an expression.
* @param jscript the expression
* @return true if the expression was a {@link Script} instance, false otherwise
*/
public boolean debug(JexlExpression jscript) {
if (jscript instanceof Script) {
return debug(((Script) jscript).script);
} else {
return false;
}
}
/**
* Position the debugger on the root of a script.
* @param jscript the script
* @return true if the script was a {@link Script} instance, false otherwise
*/
public boolean debug(JexlScript jscript) {
if (jscript instanceof Script) {
return debug(((Script) jscript).script);
} else {
return false;
}
}
/**
* Seeks the location of an error cause (a node) in an expression.
* @param node the node to debug
* @return true if the cause was located, false otherwise
*/
public boolean debug(JexlNode node) {
return debug(node, true);
}
/**
* Seeks the location of an error cause (a node) in an expression.
* @param node the node to debug
* @param r whether we should actively find the root node of the debugged node
* @return true if the cause was located, false otherwise
*/
public boolean debug(JexlNode node, boolean r) {
start = 0;
end = 0;
indentLevel = 0;
if (node != null) {
builder.setLength(0);
cause = node;
// make arg cause become the root cause
JexlNode walk = node;
if (r) {
while (walk.jjtGetParent() != null) {
walk = walk.jjtGetParent();
}
}
accept(walk, null);
}
return end > 0;
}
/**
* @return The rebuilt expression
*/
@Override
public String toString() {
return builder.toString();
}
/**
* Rebuilds an expression from a JEXL node.
* @param node the node to rebuilt from
* @return the rebuilt expression
* @since 3.0
*/
public String data(JexlNode node) {
start = 0;
end = 0;
indentLevel = 0;
if (node != null) {
builder.setLength(0);
cause = node;
accept(node, null);
}
return builder.toString();
}
/**
* @return The starting offset location of the cause in the expression
*/
@Override
public int start() {
return start;
}
/**
* @return The end offset location of the cause in the expression
*/
@Override
public int end() {
return end;
}
/**
* Sets the indentation level.
* @param level the number of spaces for indentation, none if less or equal to zero
*/
public void setIndentation(int level) {
if (level <= 0) {
indent = 0;
} else {
indent = level;
}
indentLevel = 0;
}
/**
* Checks if a child node is the cause to debug &amp; adds its representation to the rebuilt expression.
* @param node the child node
* @param data visitor pattern argument
* @return visitor pattern value
*/
protected Object accept(JexlNode node, Object data) {
if (node == cause) {
start = builder.length();
}
Object value = node.jjtAccept(this, data);
if (node == cause) {
end = builder.length();
}
return value;
}
/**
* Adds a statement node to the rebuilt expression.
* @param child the child node
* @param data visitor pattern argument
* @return visitor pattern value
*/
protected Object acceptStatement(JexlNode child, Object data) {
JexlNode parent = child.jjtGetParent();
if (indent > 0 && (parent instanceof ASTBlock || parent instanceof ASTJexlScript)) {
for (int i = 0; i < indentLevel; ++i) {
for(int s = 0; s < indent; ++s) {
builder.append(' ');
}
}
}
Object value = accept(child, data);
// blocks, if, for & while dont need a ';' at end
if (!(child instanceof ASTJexlScript
|| child instanceof ASTBlock
|| child instanceof ASTIfStatement
|| child instanceof ASTForeachStatement
|| child instanceof ASTWhileStatement)) {
builder.append(';');
if (indent > 0) {
builder.append('\n');
} else {
builder.append(' ');
}
}
return value;
}
/**
* Checks if a terminal node is the the cause to debug &amp; adds its representation to the rebuilt expression.
* @param node the child node
* @param image the child node token image (may be null)
* @param data visitor pattern argument
* @return visitor pattern value
*/
protected Object check(JexlNode node, String image, Object data) {
if (node == cause) {
start = builder.length();
}
if (image != null) {
builder.append(image);
} else {
builder.append(node.toString());
}
if (node == cause) {
end = builder.length();
}
return data;
}
/**
* Checks if the children of a node using infix notation is the cause to debug, adds their representation to the
* rebuilt expression.
* @param node the child node
* @param infix the child node token
* @param paren whether the child should be parenthesized
* @param data visitor pattern argument
* @return visitor pattern value
*/
protected Object infixChildren(JexlNode node, String infix, boolean paren, Object data) {
int num = node.jjtGetNumChildren(); //child.jjtGetNumChildren() > 1;
if (paren) {
builder.append('(');
}
for (int i = 0; i < num; ++i) {
if (i > 0) {
builder.append(infix);
}
accept(node.jjtGetChild(i), data);
}
if (paren) {
builder.append(')');
}
return data;
}
/**
* Checks if the child of a node using prefix notation is the cause to debug, adds their representation to the
* rebuilt expression.
* @param node the node
* @param prefix the node token
* @param data visitor pattern argument
* @return visitor pattern value
*/
protected Object prefixChild(JexlNode node, String prefix, Object data) {
boolean paren = node.jjtGetChild(0).jjtGetNumChildren() > 1;
builder.append(prefix);
if (paren) {
builder.append('(');
}
accept(node.jjtGetChild(0), data);
if (paren) {
builder.append(')');
}
return data;
}
@Override
protected Object visit(ASTAddNode node, Object data) {
return additiveNode(node, " + ", data);
}
@Override
protected Object visit(ASTSubNode node, Object data) {
return additiveNode(node, " - ", data);
}
/**
* Rebuilds an additive expression.
* @param node the node
* @param op the operator
* @param data visitor pattern argument
* @return visitor pattern value
*/
protected Object additiveNode(JexlNode node, String op, Object data) {
// need parenthesis if not in operator precedence order
boolean paren = node.jjtGetParent() instanceof ASTMulNode
|| node.jjtGetParent() instanceof ASTDivNode
|| node.jjtGetParent() instanceof ASTModNode;
int num = node.jjtGetNumChildren();
if (paren) {
builder.append('(');
}
accept(node.jjtGetChild(0), data);
for (int i = 1; i < num; ++i) {
builder.append(op);
accept(node.jjtGetChild(i), data);
}
if (paren) {
builder.append(')');
}
return data;
}
@Override
protected Object visit(ASTAndNode node, Object data) {
return infixChildren(node, " && ", false, data);
}
@Override
protected Object visit(ASTArrayAccess node, Object data) {
int num = node.jjtGetNumChildren();
for (int i = 0; i < num; ++i) {
builder.append('[');
accept(node.jjtGetChild(i), data);
builder.append(']');
}
return data;
}
@Override
protected Object visit(ASTExtendedLiteral node, Object data) {
builder.append("...");
return data;
}
@Override
protected Object visit(ASTArrayLiteral node, Object data) {
int num = node.jjtGetNumChildren();
builder.append("[ ");
if (num > 0) {
accept(node.jjtGetChild(0), data);
for (int i = 1; i < num; ++i) {
builder.append(", ");
accept(node.jjtGetChild(i), data);
}
}
builder.append(" ]");
return data;
}
@Override
protected Object visit(ASTRangeNode node, Object data) {
return infixChildren(node, " .. ", false, data);
}
@Override
protected Object visit(ASTAssignment node, Object data) {
return infixChildren(node, " = ", false, data);
}
@Override
protected Object visit(ASTBitwiseAndNode node, Object data) {
return infixChildren(node, " & ", false, data);
}
@Override
protected Object visit(ASTBitwiseComplNode node, Object data) {
return prefixChild(node, "~", data);
}
@Override
protected Object visit(ASTBitwiseOrNode node, Object data) {
boolean paren = node.jjtGetParent() instanceof ASTBitwiseAndNode;
return infixChildren(node, " | ", paren, data);
}
@Override
protected Object visit(ASTBitwiseXorNode node, Object data) {
boolean paren = node.jjtGetParent() instanceof ASTBitwiseAndNode;
return infixChildren(node, " ^ ", paren, data);
}
@Override
protected Object visit(ASTBlock node, Object data) {
builder.append('{');
if (indent > 0) {
indentLevel += 1;
builder.append('\n');
} else {
builder.append(' ');
}
int num = node.jjtGetNumChildren();
for (int i = 0; i < num; ++i) {
JexlNode child = node.jjtGetChild(i);
acceptStatement(child, data);
}
if (indent > 0) {
indentLevel -= 1;
for (int i = 0; i < indentLevel; ++i) {
for(int s = 0; s < indent; ++s) {
builder.append(' ');
}
}
}
builder.append('}');
return data;
}
@Override
protected Object visit(ASTDivNode node, Object data) {
return infixChildren(node, " / ", false, data);
}
@Override
protected Object visit(ASTEmptyFunction node, Object data) {
builder.append("empty ");
accept(node.jjtGetChild(0), data);
return data;
}
@Override
protected Object visit(ASTEmptyMethod node, Object data) {
accept(node.jjtGetChild(0), data);
check(node, ".empty()", data);
return data;
}
@Override
protected Object visit(ASTEQNode node, Object data) {
return infixChildren(node, " == ", false, data);
}
@Override
protected Object visit(ASTERNode node, Object data) {
return infixChildren(node, " =~ ", false, data);
}
@Override
protected Object visit(ASTSWNode node, Object data) {
return infixChildren(node, " =^ ", false, data);
}
@Override
protected Object visit(ASTEWNode node, Object data) {
return infixChildren(node, " =$ ", false, data);
}
@Override
protected Object visit(ASTNSWNode node, Object data) {
return infixChildren(node, " !^ ", false, data);
}
@Override
protected Object visit(ASTNEWNode node, Object data) {
return infixChildren(node, " !$ ", false, data);
}
@Override
protected Object visit(ASTFalseNode node, Object data) {
return check(node, "false", data);
}
@Override
protected Object visit(ASTContinue node, Object data) {
return check(node, "continue", data);
}
@Override
protected Object visit(ASTBreak node, Object data) {
return check(node, "break", data);
}
@Override
protected Object visit(ASTForeachStatement node, Object data) {
builder.append("for(");
accept(node.jjtGetChild(0), data);
builder.append(" : ");
accept(node.jjtGetChild(1), data);
builder.append(") ");
if (node.jjtGetNumChildren() > 2) {
acceptStatement(node.jjtGetChild(2), data);
} else {
builder.append(';');
}
return data;
}
@Override
protected Object visit(ASTGENode node, Object data) {
return infixChildren(node, " >= ", false, data);
}
@Override
protected Object visit(ASTGTNode node, Object data) {
return infixChildren(node, " > ", false, data);
}
/** Checks identifiers that contain space, quote, double-quotes or backspace. */
protected static final Pattern QUOTED_IDENTIFIER = Pattern.compile("['\"\\s\\\\]");
@Override
protected Object visit(ASTIdentifier node, Object data) {
String image = node.getName();
if (QUOTED_IDENTIFIER.matcher(image).find()) {
// quote it
image = "'" + image.replace("'", "\\'") + "'";
}
return check(node, image, data);
}
@Override
protected Object visit(ASTIdentifierAccess node, Object data) {
builder.append(".");
String image = node.getName();
if (QUOTED_IDENTIFIER.matcher(image).find()) {
// quote it
image = "'" + image.replace("'", "\\'") + "'";
}
builder.append(image);
return data;
}
@Override
protected Object visit(ASTIfStatement node, Object data) {
builder.append("if (");
accept(node.jjtGetChild(0), data);
builder.append(") ");
if (node.jjtGetNumChildren() > 1) {
acceptStatement(node.jjtGetChild(1), data);
if (node.jjtGetNumChildren() > 2) {
builder.append(" else ");
acceptStatement(node.jjtGetChild(2), data);
}
} else {
builder.append(';');
}
return data;
}
@Override
protected Object visit(ASTNumberLiteral node, Object data) {
return check(node, node.toString(), data);
}
/**
* A pseudo visitor for parameters.
* @param p the parameter name
* @param data the visitor argument
* @return the parameter name to use
*/
protected String visitParameter(String p, Object data) {
return p;
}
@Override
protected Object visit(ASTJexlScript node, Object data) {
// if lambda, produce parameters
if (node instanceof ASTJexlLambda) {
JexlNode parent = node.jjtGetParent();
// use lambda syntax if not assigned
boolean named = parent instanceof ASTAssignment;
if (named) {
builder.append("function");
}
builder.append('(');
String[] params = node.getParameters();
if (params != null && params.length > 0) {
builder.append(visitParameter(params[0], data));
for (int p = 1; p < params.length; ++p) {
builder.append(", ");
builder.append(visitParameter(params[p], data));
}
}
builder.append(')');
if (named) {
builder.append(' ');
} else {
builder.append("->");
}
// we will need a block...
}
// no parameters or done with them
int num = node.jjtGetNumChildren();
if (num == 1 && !(node instanceof ASTJexlLambda)) {
data = accept(node.jjtGetChild(0), data);
} else {
for (int i = 0; i < num; ++i) {
JexlNode child = node.jjtGetChild(i);
acceptStatement(child, data);
}
}
return data;
}
@Override
protected Object visit(ASTLENode node, Object data) {
return infixChildren(node, " <= ", false, data);
}
@Override
protected Object visit(ASTLTNode node, Object data) {
return infixChildren(node, " < ", false, data);
}
@Override
protected Object visit(ASTMapEntry node, Object data) {
accept(node.jjtGetChild(0), data);
builder.append(" : ");
accept(node.jjtGetChild(1), data);
return data;
}
@Override
protected Object visit(ASTSetLiteral node, Object data) {
int num = node.jjtGetNumChildren();
builder.append("{ ");
if (num > 0) {
accept(node.jjtGetChild(0), data);
for (int i = 1; i < num; ++i) {
builder.append(",");
accept(node.jjtGetChild(i), data);
}
}
builder.append(" }");
return data;
}
@Override
protected Object visit(ASTMapLiteral node, Object data) {
int num = node.jjtGetNumChildren();
builder.append("{ ");
if (num > 0) {
accept(node.jjtGetChild(0), data);
for (int i = 1; i < num; ++i) {
builder.append(",");
accept(node.jjtGetChild(i), data);
}
} else {
builder.append(':');
}
builder.append(" }");
return data;
}
@Override
protected Object visit(ASTConstructorNode node, Object data) {
int num = node.jjtGetNumChildren();
builder.append("new(");
accept(node.jjtGetChild(0), data);
for (int i = 1; i < num; ++i) {
builder.append(", ");
accept(node.jjtGetChild(i), data);
}
builder.append(")");
return data;
}
@Override
protected Object visit(ASTFunctionNode node, Object data) {
int num = node.jjtGetNumChildren();
if (num == 3) {
accept(node.jjtGetChild(0), data);
builder.append(":");
accept(node.jjtGetChild(1), data);
accept(node.jjtGetChild(2), data);
} else {
accept(node.jjtGetChild(0), data);
accept(node.jjtGetChild(1), data);
}
return data;
}
@Override
protected Object visit(ASTMethodNode node, Object data) {
int num = node.jjtGetNumChildren();
if (num == 2) {
accept(node.jjtGetChild(0), data);
accept(node.jjtGetChild(1), data);
}
return data;
}
@Override
protected Object visit(ASTArguments node, Object data) {
int num = node.jjtGetNumChildren();
builder.append("(");
if (num > 0) {
accept(node.jjtGetChild(0), data);
for (int i = 1; i < num; ++i) {
builder.append(", ");
accept(node.jjtGetChild(i), data);
}
}
builder.append(")");
return data;
}
@Override
protected Object visit(ASTModNode node, Object data) {
return infixChildren(node, " % ", false, data);
}
@Override
protected Object visit(ASTMulNode node, Object data) {
return infixChildren(node, " * ", false, data);
}
@Override
protected Object visit(ASTNENode node, Object data) {
return infixChildren(node, " != ", false, data);
}
@Override
protected Object visit(ASTNRNode node, Object data) {
return infixChildren(node, " !~ ", false, data);
}
@Override
protected Object visit(ASTNotNode node, Object data) {
builder.append("!");
accept(node.jjtGetChild(0), data);
return data;
}
@Override
protected Object visit(ASTNullLiteral node, Object data) {
check(node, "null", data);
return data;
}
@Override
protected Object visit(ASTOrNode node, Object data) {
// need parenthesis if not in operator precedence order
boolean paren = node.jjtGetParent() instanceof ASTAndNode;
return infixChildren(node, " || ", paren, data);
}
@Override
protected Object visit(ASTReference node, Object data) {
int num = node.jjtGetNumChildren();
for (int i = 0; i < num; ++i) {
accept(node.jjtGetChild(i), data);
}
return data;
}
@Override
protected Object visit(ASTReferenceExpression node, Object data) {
JexlNode first = node.jjtGetChild(0);
builder.append('(');
accept(first, data);
builder.append(')');
int num = node.jjtGetNumChildren();
for (int i = 1; i < num; ++i) {
builder.append("[");
accept(node.jjtGetChild(i), data);
builder.append("]");
}
return data;
}
@Override
protected Object visit(ASTReturnStatement node, Object data) {
builder.append("return ");
accept(node.jjtGetChild(0), data);
return data;
}
@Override
protected Object visit(ASTSizeFunction node, Object data) {
builder.append("size ");
accept(node.jjtGetChild(0), data);
return data;
}
@Override
protected Object visit(ASTSizeMethod node, Object data) {
accept(node.jjtGetChild(0), data);
check(node, ".size()", data);
return data;
}
@Override
protected Object visit(ASTStringLiteral node, Object data) {
String img = node.getLiteral().replace("'", "\\'");
return check(node, "'" + img + "'", data);
}
@Override
protected Object visit(ASTTernaryNode node, Object data) {
accept(node.jjtGetChild(0), data);
if (node.jjtGetNumChildren() > 2) {
builder.append("? ");
accept(node.jjtGetChild(1), data);
builder.append(" : ");
accept(node.jjtGetChild(2), data);
} else {
builder.append("?:");
accept(node.jjtGetChild(1), data);
}
return data;
}
@Override
protected Object visit(ASTTrueNode node, Object data) {
check(node, "true", data);
return data;
}
@Override
protected Object visit(ASTUnaryMinusNode node, Object data) {
return prefixChild(node, "-", data);
}
@Override
protected Object visit(ASTVar node, Object data) {
builder.append("var ");
check(node, node.getName(), data);
return data;
}
@Override
protected Object visit(ASTWhileStatement node, Object data) {
builder.append("while (");
accept(node.jjtGetChild(0), data);
builder.append(") ");
if (node.jjtGetNumChildren() > 1) {
acceptStatement(node.jjtGetChild(1), data);
} else {
builder.append(';');
}
return data;
}
@Override
protected Object visit(ASTSetAddNode node, Object data) {
return infixChildren(node, " += ", false, data);
}
@Override
protected Object visit(ASTSetSubNode node, Object data) {
return infixChildren(node, " -= ", false, data);
}
@Override
protected Object visit(ASTSetMultNode node, Object data) {
return infixChildren(node, " *= ", false, data);
}
@Override
protected Object visit(ASTSetDivNode node, Object data) {
return infixChildren(node, " /= ", false, data);
}
@Override
protected Object visit(ASTSetModNode node, Object data) {
return infixChildren(node, " %= ", false, data);
}
@Override
protected Object visit(ASTSetAndNode node, Object data) {
return infixChildren(node, " &= ", false, data);
}
@Override
protected Object visit(ASTSetOrNode node, Object data) {
return infixChildren(node, " |= ", false, data);
}
@Override
protected Object visit(ASTSetXorNode node, Object data) {
return infixChildren(node, " ^= ", false, data);
}
@Override
protected Object visit(ASTJxltLiteral node, Object data) {
String img = node.getLiteral().replace("`", "\\`");
return check(node, "`" + img + "`", data);
}
@Override
protected Object visit(ASTAnnotation node, Object data) {
int num = node.jjtGetNumChildren();
builder.append('@');
builder.append(node.getName());
if (num > 0) {
builder.append("(");
accept(node.jjtGetChild(0), data);
for(int i = 0; i < num; ++i) {
builder.append(", ");
JexlNode child = node.jjtGetChild(i);
acceptStatement(child, data);
}
builder.append(")");
}
return null;
}
@Override
protected Object visit(ASTAnnotatedStatement node, Object data) {
int num = node.jjtGetNumChildren();
for (int i = 0; i < num; ++i) {
JexlNode child = node.jjtGetChild(i);
acceptStatement(child, data);
}
return data;
}
}