/* Generated By:JJTree: Do not edit this line. SimpleNode.java */
/*****************************************************************
* 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.cayenne.exp.parser;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.apache.cayenne.CayenneRuntimeException;
import org.apache.cayenne.ObjectId;
import org.apache.cayenne.Persistent;
import org.apache.cayenne.exp.Expression;
import org.apache.cayenne.exp.ExpressionException;
import org.apache.cayenne.util.Util;
/**
* Superclass of AST* expressions that implements Node interface defined by JavaCC framework.
* <p>
* Some parts of the parser are based on OGNL parser,
* copyright (c) 2002, Drew Davidson and Luke Blanshard.
* </p>
*
* @since 1.1
*/
public abstract class SimpleNode extends Expression implements Node {
private static final long serialVersionUID = 4471832357335707557L;
protected Node parent;
protected Node[] children;
protected int id;
/**
* <p>
* This is a utility method that can represent the supplied scalar as either
* an EJBQL literal into the supplied {@link java.io.PrintWriter} or is able
* to add the scalar to the parameters and to instead write a positional
* parameter to the EJBQL written to the {@link java.io.PrintWriter}. If the
* parameters are null and the scalar object is not able to be represented
* as an EJBQL literal then the method will throw a runtime exception to
* indicate that it has failed to produce valid EJBQL.
* </p>
*/
protected static void encodeScalarAsEJBQL(List<Object> parameterAccumulator, Appendable out, Object scalar)
throws IOException {
if (null == scalar) {
out.append("null");
return;
}
if (scalar instanceof Boolean) {
if ((Boolean) scalar) {
out.append("true");
} else {
out.append("false");
}
return;
}
if (null != parameterAccumulator) {
parameterAccumulator.add(scalar);
out.append('?');
out.append(Integer.toString(parameterAccumulator.size())); // parameters start at 1
return;
}
if (scalar instanceof Integer || scalar instanceof Long || scalar instanceof Float || scalar instanceof Double) {
out.append(scalar.toString());
return;
}
if (scalar instanceof Persistent) {
ObjectId id = ((Persistent) scalar).getObjectId();
Object encode = (id != null) ? id : scalar;
appendAsEscapedString(out, String.valueOf(encode));
return;
}
if (scalar instanceof Enum<?>) {
Enum<?> e = (Enum<?>) scalar;
out.append("enum:");
out.append(e.getClass().getName()).append(".").append(e.name());
return;
}
if (scalar instanceof String) {
out.append('\'');
appendAsEscapedString(out, scalar.toString());
out.append('\'');
return;
}
throw new IllegalStateException("the scalar type '" + scalar.getClass().getSimpleName()
+ "' is not supported as a scalar type in EJBQL");
}
/**
* Utility method that encodes an object that is not an expression Node to
* String.
*/
protected static void appendScalarAsString(Appendable out, Object scalar, char quoteChar) throws IOException {
boolean quote = scalar instanceof String;
if (quote) {
out.append(quoteChar);
}
// encode only ObjectId for Persistent, ensure that the order of keys is predictable....
// TODO: should we use UUID here?
if (scalar instanceof Persistent) {
ObjectId id = ((Persistent) scalar).getObjectId();
Object encode = (id != null) ? id : scalar;
appendAsEscapedString(out, String.valueOf(encode));
} else if (scalar instanceof Enum<?>) {
Enum<?> e = (Enum<?>) scalar;
out.append("enum:");
out.append(e.getClass().getName()).append(".").append(e.name());
} else {
appendAsEscapedString(out, String.valueOf(scalar));
}
if (quote) {
out.append(quoteChar);
}
}
/**
* Utility method that prints a string to the provided Appendable, escaping special characters.
*/
protected static void appendAsEscapedString(Appendable out, String source) throws IOException {
int len = source.length();
for (int i = 0; i < len; i++) {
char c = source.charAt(i);
switch (c) {
case '\n':
out.append("\\n");
continue;
case '\r':
out.append("\\r");
continue;
case '\t':
out.append("\\t");
continue;
case '\b':
out.append("\\b");
continue;
case '\f':
out.append("\\f");
continue;
case '\\':
out.append("\\\\");
continue;
case '\'':
out.append("\\'");
continue;
case '\"':
out.append("\\\"");
continue;
default:
out.append(c);
}
}
}
protected SimpleNode(int i) {
id = i;
}
/**
* Always returns empty map.
*
* @since 3.0
*/
@Override
public Map<String, String> getPathAliases() {
return Collections.emptyMap();
}
protected abstract String getExpressionOperator(int index);
/**
* Returns operator for EJBQL statements, which can differ for Cayenne expression operator
*/
protected String getEJBQLExpressionOperator(int index) {
return getExpressionOperator(index);
}
@Override
protected boolean pruneNodeForPrunedChild(Object prunedChild) {
return true;
}
/**
* Implemented for backwards compatibility with exp package.
*/
@Override
public String expName() {
return ExpressionParserTreeConstants.jjtNodeName[id];
}
/**
* Flattens the tree under this node by eliminating any children that are of
* the same class as this node and copying their children to this node.
*/
@Override
protected void flattenTree() {
boolean shouldFlatten = false;
int newSize = 0;
for (Node child : children) {
if (child.getClass() == getClass()) {
shouldFlatten = true;
newSize += child.jjtGetNumChildren();
} else {
newSize++;
}
}
if (shouldFlatten) {
Node[] newChildren = new Node[newSize];
int j = 0;
for (Node c : children) {
if (c.getClass() == getClass()) {
for (int k = 0; k < c.jjtGetNumChildren(); ++k) {
newChildren[j++] = c.jjtGetChild(k);
}
} else {
newChildren[j++] = c;
}
}
if (j != newSize) {
throw new ExpressionException("Assertion error: " + j + " != " + newSize);
}
this.children = newChildren;
}
}
/**
* @since 4.0
*/
@Override
public void appendAsString(Appendable out) throws IOException {
if (parent != null) {
out.append("(");
}
if ((children != null) && (children.length > 0)) {
for (int i = 0; i < children.length; ++i) {
if (i > 0) {
out.append(' ');
out.append(getExpressionOperator(i));
out.append(' ');
}
if (children[i] == null) {
out.append("null");
} else {
((SimpleNode) children[i]).appendAsString(out);
}
}
}
if (parent != null) {
out.append(')');
}
}
/**
* @deprecated since 4.0 use {@link #appendAsString(Appendable)}.
*/
@Override
@Deprecated
public void encodeAsString(PrintWriter pw) {
try {
appendAsString(pw);
} catch (IOException e) {
throw new CayenneRuntimeException("Unexpected IO exception appending to PrintWriter", e);
}
}
@Override
public Object getOperand(int index) {
Node child = jjtGetChild(index);
// unwrap ASTScalar nodes - this is likely a temporary thing to keep it compatible
// with QualifierTranslator. In the future we might want to keep scalar nodes
// for the purpose of expression evaluation.
return unwrapChild(child);
}
protected Node wrapChild(Object child) {
// when child is null, there's no way of telling whether this is a scalar or not... fuzzy...
// maybe we should stop using this method - it is too generic
return (child instanceof Node || child == null) ? (Node) child : new ASTScalar(child);
}
protected Object unwrapChild(Node child) {
return (child instanceof ASTScalar) ? ((ASTScalar) child).getValue() : child;
}
@Override
public int getOperandCount() {
return jjtGetNumChildren();
}
@Override
public void setOperand(int index, Object value) {
Node node = (value == null || value instanceof Node) ? (Node) value : new ASTScalar(value);
jjtAddChild(node, index);
// set the parent, as jjtAddChild doesn't do it...
if (node != null) {
node.jjtSetParent(this);
}
}
public void jjtOpen() {
}
public void jjtClose() {
}
public void jjtSetParent(Node n) {
parent = n;
}
public Node jjtGetParent() {
return parent;
}
public void jjtAddChild(Node n, int i) {
if (children == null) {
children = new Node[i + 1];
} else if (i >= children.length) {
Node c[] = new Node[i + 1];
System.arraycopy(children, 0, c, 0, children.length);
children = c;
}
children[i] = n;
}
public Node jjtGetChild(int i) {
return children[i];
}
public final int jjtGetNumChildren() {
return (children == null) ? 0 : children.length;
}
/**
* Evaluates itself with object, pushing result on the stack.
*/
protected abstract Object evaluateNode(Object o) throws Exception;
/**
* Sets the parent to this for all children.
*
* @since 3.0
*/
protected void connectChildren() {
if (children != null) {
for (Node child : children) {
// although nulls are expected to be wrapped in scalar,
// still doing a check here to make it more robust
if (child != null) {
child.jjtSetParent(this);
}
}
}
}
protected Object evaluateChild(int index, Object o) throws Exception {
SimpleNode node = (SimpleNode) jjtGetChild(index);
return node != null ? node.evaluate(o) : null;
}
@Override
public Expression notExp() {
return new ASTNot(this);
}
@Override
public Object evaluate(Object o) {
// wrap in try/catch to provide unified exception processing
try {
return evaluateNode(o);
} catch (Throwable th) {
String string = this.toString();
throw new ExpressionException("Error evaluating expression '%s'",
string, Util.unwindException(th), string);
}
}
/**
* @since 3.0
* @deprecated since 4.0 use {@link #appendAsEJBQL(Appendable, String)}.
*/
@Override
@Deprecated
public void encodeAsEJBQL(PrintWriter pw, String rootId) {
try {
appendAsEJBQL(pw, rootId);
} catch (IOException e) {
throw new CayenneRuntimeException("Unexpected IO exception appending to PrintWriter", e);
}
}
/**
* @since 4.0
*/
public void appendAsEJBQL(Appendable out, String rootId) throws IOException {
appendAsEJBQL(null, out, rootId);
}
/**
* @since 4.0
*/
@Override
public void appendAsEJBQL(List<Object> parameterAccumulator, Appendable out, String rootId) throws IOException {
if (parent != null) {
out.append("(");
}
if ((children != null) && (children.length > 0)) {
appendChildrenAsEJBQL(parameterAccumulator, out, rootId);
}
if (parent != null) {
out.append(')');
}
}
/**
* Encodes child of this node with specified index to EJBQL
*/
protected void appendChildrenAsEJBQL(List<Object> parameterAccumulator, Appendable out, String rootId)
throws IOException {
for (int i = 0; i < children.length; ++i) {
if (i > 0) {
out.append(' ');
out.append(getEJBQLExpressionOperator(i));
out.append(' ');
}
if (children[i] == null) {
out.append("null");
} else {
((SimpleNode) children[i]).appendAsEJBQL(parameterAccumulator, out, rootId);
}
}
}
}