use of org.apache.commons.jexl3.parser.ASTArrayAccess in project commons-jexl by apache.
the class Interpreter method visit.
@Override
protected Object visit(final ASTReference node, final Object data) {
cancelCheck(node);
final int numChildren = node.jjtGetNumChildren();
final JexlNode parent = node.jjtGetParent();
// pass first piece of data in and loop through children
Object object = null;
JexlNode objectNode = null;
JexlNode ptyNode = null;
StringBuilder ant = null;
boolean antish = !(parent instanceof ASTReference);
int v = 1;
main: for (int c = 0; c < numChildren; c++) {
objectNode = node.jjtGetChild(c);
if (objectNode instanceof ASTMethodNode) {
antish = false;
if (object == null) {
// we may be performing a method call on an antish var
if (ant != null) {
final JexlNode child = objectNode.jjtGetChild(0);
if (child instanceof ASTIdentifierAccess) {
final int alen = ant.length();
ant.append('.');
ant.append(((ASTIdentifierAccess) child).getName());
object = context.get(ant.toString());
if (object != null) {
object = visit((ASTMethodNode) objectNode, object, context);
continue;
}
// remove method name from antish
ant.delete(alen, ant.length());
ptyNode = objectNode;
}
}
break;
}
} else if (objectNode instanceof ASTArrayAccess) {
antish = false;
if (object == null) {
ptyNode = objectNode;
break;
}
}
// attempt to evaluate the property within the object (visit(ASTIdentifierAccess node))
object = objectNode.jjtAccept(this, object);
cancelCheck(node);
if (object != null) {
// disallow mixing antish variable & bean with same root; avoid ambiguity
antish = false;
} else if (antish) {
// create first from first node
if (ant == null) {
// if we still have a null object, check for an antish variable
final JexlNode first = node.jjtGetChild(0);
if (!(first instanceof ASTIdentifier)) {
// not an identifier, not antish
ptyNode = objectNode;
break main;
}
final ASTIdentifier afirst = (ASTIdentifier) first;
ant = new StringBuilder(afirst.getName());
// *... and continue
if (!options.isAntish()) {
antish = false;
continue;
}
// skip the first node case since it was trialed in jjtAccept above and returned null
if (c == 0) {
continue;
}
}
// catch up to current node
for (; v <= c; ++v) {
final JexlNode child = node.jjtGetChild(v);
if (!(child instanceof ASTIdentifierAccess)) {
// not an identifier, not antish
ptyNode = objectNode;
break main;
}
final ASTIdentifierAccess achild = (ASTIdentifierAccess) child;
if (achild.isSafe() || achild.isExpression()) {
break main;
}
ant.append('.');
ant.append(achild.getName());
}
// solve antish
object = context.get(ant.toString());
} else if (c != numChildren - 1) {
// only the last one may be null
ptyNode = objectNode;
//
break;
}
}
// dealing with null
if (object == null) {
if (ptyNode != null) {
if (ptyNode.isSafeLhs(isSafe())) {
return null;
}
if (ant != null) {
final String aname = ant.toString();
final boolean defined = isVariableDefined(frame, block, aname);
return unsolvableVariable(node, aname, !defined);
}
return unsolvableProperty(node, stringifyProperty(ptyNode), ptyNode == objectNode, null);
}
if (antish) {
if (node.isSafeLhs(isSafe())) {
return null;
}
final String aname = ant != null ? ant.toString() : "?";
final boolean defined = isVariableDefined(frame, block, aname);
// defined but null; arg of a strict operator?
if (defined && !isStrictOperand(node)) {
return null;
}
return unsolvableVariable(node, aname, !defined);
}
}
return object;
}
use of org.apache.commons.jexl3.parser.ASTArrayAccess in project commons-jexl by apache.
the class InterpreterBase method setAttribute.
/**
* Sets an attribute of an object.
*
* @param object to set the value to
* @param attribute the attribute of the object, e.g. an index (1, 0, 2) or key for a map
* @param value the value to assign to the object's attribute
* @param node the node that evaluated as the object
*/
protected void setAttribute(final Object object, final Object attribute, final Object value, final JexlNode node) {
cancelCheck(node);
final JexlOperator operator = node != null && node.jjtGetParent() instanceof ASTArrayAccess ? JexlOperator.ARRAY_SET : JexlOperator.PROPERTY_SET;
final Object result = operators.tryOverload(node, operator, object, attribute, value);
if (result != JexlEngine.TRY_FAILED) {
return;
}
Exception xcause = null;
try {
// attempt to reuse last executor cached in volatile JexlNode.value
if (node != null && cache) {
final Object cached = node.jjtGetValue();
if (cached instanceof JexlPropertySet) {
final JexlPropertySet setter = (JexlPropertySet) cached;
final Object eval = setter.tryInvoke(object, attribute, value);
if (!setter.tryFailed(eval)) {
return;
}
}
}
final List<JexlUberspect.PropertyResolver> resolvers = uberspect.getResolvers(operator, object);
JexlPropertySet vs = uberspect.getPropertySet(resolvers, object, attribute, value);
// if we can't find an exact match, narrow the value argument and try again
if (vs == null) {
// replace all numbers with the smallest type that will fit
final Object[] narrow = { value };
if (arithmetic.narrowArguments(narrow)) {
vs = uberspect.getPropertySet(resolvers, object, attribute, narrow[0]);
}
}
if (vs != null) {
// cache executor in volatile JexlNode.value
vs.invoke(object, value);
if (node != null && cache && vs.isCacheable()) {
node.jjtSetValue(vs);
}
return;
}
} catch (final Exception xany) {
xcause = xany;
}
// lets fail
if (node == null) {
// direct call
final String error = "unable to set object property" + ", class: " + object.getClass().getName() + ", property: " + attribute + ", argument: " + value.getClass().getSimpleName();
throw new UnsupportedOperationException(error, xcause);
}
final String attrStr = attribute != null ? attribute.toString() : null;
unsolvableProperty(node, attrStr, true, xcause);
}
use of org.apache.commons.jexl3.parser.ASTArrayAccess in project commons-jexl by apache.
the class Engine method getVariables.
/**
* Fills up the list of variables accessed by a node.
* @param script the owning script
* @param node the node
* @param collector the variable collector
*/
protected void getVariables(final ASTJexlScript script, final JexlNode node, final VarCollector collector) {
if (node instanceof ASTIdentifier) {
final JexlNode parent = node.jjtGetParent();
if (parent instanceof ASTMethodNode || parent instanceof ASTFunctionNode) {
// skip identifiers for methods and functions
collector.collect(null);
return;
}
final ASTIdentifier identifier = (ASTIdentifier) node;
final int symbol = identifier.getSymbol();
// symbols that are captured are considered "global" variables
if (symbol >= 0 && script != null && !script.isCapturedSymbol(symbol)) {
collector.collect(null);
} else {
// start collecting from identifier
collector.collect(identifier);
collector.add(identifier.getName());
}
} else if (node instanceof ASTIdentifierAccess) {
final JexlNode parent = node.jjtGetParent();
if (parent instanceof ASTMethodNode || parent instanceof ASTFunctionNode) {
// skip identifiers for methods and functions
collector.collect(null);
return;
}
// belt and suspender since an identifier should have been seen first
if (collector.isCollecting()) {
collector.add(((ASTIdentifierAccess) node).getName());
}
} else if (node instanceof ASTArrayAccess && collector.mode > 0) {
final int num = node.jjtGetNumChildren();
// collect only if array access is const and follows an identifier
boolean collecting = collector.isCollecting();
for (int i = 0; i < num; ++i) {
final JexlNode child = node.jjtGetChild(i);
if (collecting && child.isConstant()) {
// collect all constants or only string and number literals
final boolean collect = collector.mode > 1 || (child instanceof ASTStringLiteral || child instanceof ASTNumberLiteral);
if (collect) {
final String image = child.toString();
collector.add(image);
}
} else {
collecting = false;
collector.collect(null);
getVariables(script, child, collector);
collector.collect(null);
}
}
} else {
final int num = node.jjtGetNumChildren();
for (int i = 0; i < num; ++i) {
getVariables(script, node.jjtGetChild(i), collector);
}
collector.collect(null);
}
}
use of org.apache.commons.jexl3.parser.ASTArrayAccess in project commons-jexl by apache.
the class InterpreterBase method getAttribute.
/**
* Gets an attribute of an object.
*
* @param object to retrieve value from
* @param attribute the attribute of the object, e.g. an index (1, 0, 2) or key for a map
* @param node the node that evaluated as the object
* @return the attribute value
*/
protected Object getAttribute(final Object object, final Object attribute, final JexlNode node) {
if (object == null) {
throw new JexlException(node, "object is null");
}
cancelCheck(node);
final JexlOperator operator = node != null && node.jjtGetParent() instanceof ASTArrayAccess ? JexlOperator.ARRAY_GET : JexlOperator.PROPERTY_GET;
final Object result = operators.tryOverload(node, operator, object, attribute);
if (result != JexlEngine.TRY_FAILED) {
return result;
}
Exception xcause = null;
try {
// attempt to reuse last executor cached in volatile JexlNode.value
if (node != null && cache) {
final Object cached = node.jjtGetValue();
if (cached instanceof JexlPropertyGet) {
final JexlPropertyGet vg = (JexlPropertyGet) cached;
final Object value = vg.tryInvoke(object, attribute);
if (!vg.tryFailed(value)) {
return value;
}
}
}
// resolve that property
final List<JexlUberspect.PropertyResolver> resolvers = uberspect.getResolvers(operator, object);
final JexlPropertyGet vg = uberspect.getPropertyGet(resolvers, object, attribute);
if (vg != null) {
final Object value = vg.invoke(object);
// cache executor in volatile JexlNode.value
if (node != null && cache && vg.isCacheable()) {
node.jjtSetValue(vg);
}
return value;
}
} catch (final Exception xany) {
xcause = xany;
}
// lets fail
if (node == null) {
// direct call
final String error = "unable to get object property" + ", class: " + object.getClass().getName() + ", property: " + attribute;
throw new UnsupportedOperationException(error, xcause);
}
final boolean safe = (node instanceof ASTIdentifierAccess) && ((ASTIdentifierAccess) node).isSafe();
if (safe) {
return null;
}
final String attrStr = attribute != null ? attribute.toString() : null;
return unsolvableProperty(node, attrStr, true, xcause);
}
use of org.apache.commons.jexl3.parser.ASTArrayAccess in project commons-jexl by apache.
the class Interpreter method visit.
@Override
protected Object visit(final ASTArrayAccess node, final Object data) {
// first objectNode is the identifier
Object object = data;
// can have multiple nodes - either an expression, integer literal or reference
final int numChildren = node.jjtGetNumChildren();
for (int i = 0; i < numChildren; i++) {
final JexlNode nindex = node.jjtGetChild(i);
if (object == null) {
return unsolvableProperty(nindex, stringifyProperty(nindex), false, null);
}
final Object index = nindex.jjtAccept(this, null);
cancelCheck(node);
object = getAttribute(object, index, nindex);
}
return object;
}
Aggregations