| /* |
| * 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 & 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 & 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; |
| } |
| } |