package org.apache.lucene.queryparser.flexible.aqp.nodes; import java.util.ArrayList; import java.util.List; import org.antlr.runtime.CommonToken; import org.apache.lucene.queryparser.flexible.core.nodes.QueryNode; import org.apache.lucene.queryparser.flexible.core.nodes.QueryNodeImpl; import org.apache.lucene.queryparser.flexible.core.parser.EscapeQuerySyntax; import org.apache.lucene.queryparser.flexible.aqp.util.AqpCommonTree; /** * When Aqp parser starts processing the AST (abstract syntax tree) * every node in the tree is made of {@link AqpANTLRNode} and it wraps * the {@link AqpCommonTree} through which you can access information * about the string, its position, type etc... these are courtesy of * ANTLR. * * We provide a few utility methods for setting different attributes * of the original ANTLR object. * */ public class AqpANTLRNode extends QueryNodeImpl { private static final long serialVersionUID = 5128762709928473351L; private AqpCommonTree tree; private int tokenType; private String tokenLabel; private String tokenName; private String tokenInput = null; private boolean isCloning; /** * @param node * - AST node */ public AqpANTLRNode(AqpCommonTree node) { tree = node; String input = node.getTokenInput(); if (input != null) { setTokenInput(input); } setTokenLabel(node.getTokenLabel()); setTokenType(node.getTokenType()); setTokenName(node.getTypeLabel()); if (node.getChildCount() > 0) { setLeaf(false); allocate(); } } public CharSequence toQueryString(EscapeQuerySyntax escaper) { if (getTokenInput() != null) { return "(" + getTokenLabel() + ":" + getTokenInput() + ")"; } else { return getTokenLabel(); } } public String escapeXmlVal(String v) { return v.replace("&", "&").replace("\"", """).replace("'", "'").replace("<", "<").replace(">", ">"); } public String escapeJsonVal(String v) { return v.replace("\"", "\\\""); //replace("\\", "\\\\").replace("\"", "\\\"").replace("'", "\\'").replace("\t", "\\t").replace("\n", "\\n"); } /** * Method to transform the tree into JSON - so that we can send it to the * javascript clients * * @return json representation of the node */ public String toJson() { return toJson(0); } public String toJson(int level) { StringBuffer buf = new StringBuffer(); buf.append("\n"); for (int i = 0; i < level; i++) { buf.append(" "); } buf.append("{\"name\":\""); buf.append(getTokenName()); buf.append("\""); if (getTokenInput() != null) { buf.append(", \"input\":\""); buf.append(escapeJsonVal(getTokenInput())); buf.append("\", \"start\":" + getTokenStart()); buf.append(", \"end\":" + getTokenEnd()); } else { buf.append(", \"label\":\""); buf.append(getTokenLabel()); buf.append("\""); } List<QueryNode> children = this.getChildren(); if (children != null) { buf.append(", \"children\": ["); boolean notFirst = false; for (QueryNode child : children) { if (notFirst) buf.append(","); if (child instanceof AqpANTLRNode) { buf.append(((AqpANTLRNode) child).toJson(level + 4)); } else { buf.append("{\"xvalue\":\""); buf.append(escapeJsonVal(child.toString())); buf.append("\"}"); } notFirst = true; } buf.append("]"); } if (isLeaf()) { buf.append("}"); } else { buf.append("\n"); for (int i = 0; i < level; i++) { buf.append(" "); } buf.append("}"); } return buf.toString(); } public String toStringNodeOnly() { if (getTokenInput() != null) { return "<ast" + getTokenName() + " value=\"" + escapeXmlVal(getTokenInput()) + "\" start=\"" + getTokenStart() + "\" end=\"" + getTokenEnd() + "\" />"; } else { return "<ast" + getTokenName() + " type=\"" + getTokenLabel() + "\" />"; } } public String toString() { return toString(0); } public String toString(int level) { StringBuffer buf = new StringBuffer(); buf.append("\n"); for (int i = 0; i < level; i++) { buf.append(" "); } buf.append("<ast" + getTokenName() + " "); if (getTokenInput() != null) { buf.append("value=\"" + escapeXmlVal(getTokenInput()) + "\" start=\"" + getTokenStart() + "\" end=\"" + getTokenEnd() + "\""); } else { buf.append("label=\"" + getTokenLabel() + "\""); } buf.append(" name=\"" + getTokenName() + "\"" + " type=\"" + getTokenType() + "\" "); List<QueryNode> children = this.getChildren(); if (children != null) { buf.append(">"); for (QueryNode child : children) { if (child instanceof AqpANTLRNode) { buf.append(((AqpANTLRNode) child).toString(level + 4)); } else { buf.append(child.toString()); } } } if (isLeaf()) { buf.append("/>"); } else { buf.append("\n"); for (int i = 0; i < level; i++) { buf.append(" "); } buf.append("</ast" + getTokenName() + ">"); } return buf.toString(); } public int getTokenType() { return tokenType; } public void setTokenType(int tokenType) { this.tokenType = tokenType; } /** * Label is what is displayed in the AST tree, for example and, And, AND will * all have label=AND * * (But their internal name is an 'OPERATOR') * * @return string, token label */ public String getTokenLabel() { return tokenLabel; } /** * Label is what is displayed in the AST tree, for example and, And, AND will * all have label=AND * * (But their internal name is an 'OPERATOR') * */ public void setTokenLabel(String tokenLabel) { this.tokenLabel = tokenLabel; } /** * Name is the internal token name, for example and, And, AND will * all have name=OPERATOR */ public String getTokenName() { return tokenName; } /** * Name is the name of the group, ie. 'AND' is an OPERATOR (but its label * says: 'AND') * * @param tokenName * string to set */ public void setTokenName(String tokenName) { this.tokenName = tokenName; } public String getTokenInput() { return tokenInput; } public void setTokenInput(String tokenInput) { this.tokenInput = tokenInput; } public int getTokenStart() { return tree.getStartIndex(); } /* * I'm confused, this method added just now, * there should be only one method for setting */ public void setTokenStart(int start) { tree.setStartIndex(start); ((CommonToken) tree.getToken()).setCharPositionInLine(start); } public int getTokenEnd() { return tree.getStopIndex(); } public AqpCommonTree getTree() { return tree; } public int getInputTokenStart() { return ((CommonToken) tree.getToken()).getCharPositionInLine();// getStartIndex(); } public int getInputTokenEnd() { return ((CommonToken) tree.getToken()).getStopIndex(); } public void setInputTokenEnd(int stop) { ((CommonToken) tree.getToken()).setStopIndex(stop); } public void setInputTokenStart(int start) { ((CommonToken) tree.getToken()).setStartIndex(start); } public AqpANTLRNode getChild(String tokenLabel) { List<QueryNode> children = getChildren(); if (children != null) { for (QueryNode child : children) { AqpANTLRNode n = (AqpANTLRNode) child; if (n.getTokenLabel().equals(tokenLabel)) { return n; } } } return null; } public int hasTokenName(String tokenName, int level) { List<QueryNode> children = getChildren(); if (children != null) { for (QueryNode child : children) { if (child instanceof AqpANTLRNode) { AqpANTLRNode n = (AqpANTLRNode) child; if (n.getTokenLabel().equals(tokenName)) { return level+1; } int nl = n.hasTokenName(tokenName, level+1); if (nl > level+1) return nl; } } } return level; } public AqpANTLRNode findChild(String tokenLabel) { ArrayList<QueryNode> lst = new ArrayList<QueryNode>(); findChild(this, tokenLabel, lst); if (lst.size() == 1) { return (AqpANTLRNode) lst.get(0); } else if (lst.size() > 1) { throw new RuntimeException( "This method is not meant to search for n>1 nodes"); } return null; } private void findChild(QueryNode node, String tokenLabel, ArrayList<QueryNode> lst) { if (((AqpANTLRNode) node).getTokenLabel().equals(tokenLabel)) { lst.add(node); } else { if (!node.isLeaf()) { for (QueryNode child : node.getChildren()) { findChild(child, tokenLabel, lst); } } } } public Float getTokenInputFloat() { if (this.tokenInput != null) { return Float.valueOf(this.tokenInput); } return null; } }