use of org.apache.commons.jexl3.JexlOperator 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.JexlOperator in project commons-jexl by apache.
the class Operators method tryOverload.
/**
* Attempts to call an operator.
* <p>
* This takes care of finding and caching the operator method when appropriate
* @param node the syntactic node
* @param operator the operator
* @param args the arguments
* @return the result of the operator evaluation or TRY_FAILED
*/
protected Object tryOverload(final JexlNode node, final JexlOperator operator, final Object... args) {
if (operators != null && operators.overloads(operator)) {
final JexlArithmetic arithmetic = interpreter.arithmetic;
final boolean cache = interpreter.cache;
try {
if (cache) {
final Object cached = node.jjtGetValue();
if (cached instanceof JexlMethod) {
final JexlMethod me = (JexlMethod) cached;
final Object eval = me.tryInvoke(operator.getMethodName(), arithmetic, args);
if (!me.tryFailed(eval)) {
return eval;
}
}
}
final JexlMethod vm = operators.getOperator(operator, args);
if (vm != null && !isArithmetic(vm)) {
final Object result = vm.invoke(arithmetic, args);
if (cache) {
node.jjtSetValue(vm);
}
return result;
}
} catch (final Exception xany) {
return interpreter.operatorError(node, operator, xany);
}
}
return JexlEngine.TRY_FAILED;
}
use of org.apache.commons.jexl3.JexlOperator in project commons-jexl by apache.
the class Operators method tryAssignOverload.
/**
* Evaluates an assign operator.
* <p>
* This takes care of finding and caching the operator method when appropriate.
* If an overloads returns Operator.ASSIGN, it means the side-effect is complete.
* Otherwise, a += b <=> a = a + b
* </p>
* @param node the syntactic node
* @param operator the operator
* @param args the arguments, the first one being the target of assignment
* @return JexlOperator.ASSIGN if operation assignment has been performed,
* JexlEngine.TRY_FAILED if no operation was performed,
* the value to use as the side effect argument otherwise
*/
protected Object tryAssignOverload(final JexlNode node, final JexlOperator operator, final Object... args) {
final JexlArithmetic arithmetic = interpreter.arithmetic;
if (args.length != operator.getArity()) {
return JexlEngine.TRY_FAILED;
}
// try to call overload with side effect
Object result = tryOverload(node, operator, args);
if (result != JexlEngine.TRY_FAILED) {
return result;
}
// call base operator
final JexlOperator base = operator.getBaseOperator();
if (base == null) {
throw new IllegalArgumentException("must be called with a side-effect operator");
}
if (operators != null && operators.overloads(base)) {
// in case there is an overload on the base operator
try {
final JexlMethod vm = operators.getOperator(base, args);
if (vm != null) {
result = vm.invoke(arithmetic, args);
if (result != JexlEngine.TRY_FAILED) {
return result;
}
}
} catch (final Exception xany) {
interpreter.operatorError(node, base, xany);
}
}
// base eval
try {
switch(operator) {
case SELF_ADD:
return arithmetic.add(args[0], args[1]);
case SELF_SUBTRACT:
return arithmetic.subtract(args[0], args[1]);
case SELF_MULTIPLY:
return arithmetic.multiply(args[0], args[1]);
case SELF_DIVIDE:
return arithmetic.divide(args[0], args[1]);
case SELF_MOD:
return arithmetic.mod(args[0], args[1]);
case SELF_AND:
return arithmetic.and(args[0], args[1]);
case SELF_OR:
return arithmetic.or(args[0], args[1]);
case SELF_XOR:
return arithmetic.xor(args[0], args[1]);
default:
// unexpected, new operator added?
throw new UnsupportedOperationException(operator.getOperatorSymbol());
}
} catch (final Exception xany) {
interpreter.operatorError(node, base, xany);
}
return JexlEngine.TRY_FAILED;
}
use of org.apache.commons.jexl3.JexlOperator 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.JexlOperator in project commons-jexl by apache.
the class Interpreter method executeAssign.
/**
* Executes an assignment with an optional side-effect operator.
* @param node the node
* @param assignop the assignment operator or null if simply assignment
* @param data the data
* @return the left hand side
*/
protected Object executeAssign(final JexlNode node, final JexlOperator assignop, final Object data) {
// CSOFF: MethodLength
cancelCheck(node);
// left contains the reference to assign to
final JexlNode left = node.jjtGetChild(0);
ASTIdentifier var = null;
Object object = null;
int symbol = -1;
// check var decl with assign is ok
if (left instanceof ASTIdentifier) {
var = (ASTIdentifier) left;
symbol = var.getSymbol();
if (symbol >= 0 && options.isLexical()) {
if (var instanceof ASTVar) {
if (!defineVariable((ASTVar) var, block)) {
return redefinedVariable(var, var.getName());
}
} else if (options.isLexicalShade() && var.isShaded()) {
return undefinedVariable(var, var.getName());
}
}
}
boolean antish = options.isAntish();
// 0: determine initial object & property:
final int last = left.jjtGetNumChildren() - 1;
// right is the value expression to assign
Object right = node.jjtGetChild(1).jjtAccept(this, data);
// a (var?) v = ... expression
if (var != null) {
if (symbol >= 0) {
// check we are not assigning a symbol itself
if (last < 0) {
if (assignop != null) {
final Object self = getVariable(frame, block, var);
right = operators.tryAssignOverload(node, assignop, self, right);
if (right == JexlOperator.ASSIGN) {
return self;
}
}
frame.set(symbol, right);
// make the closure accessible to itself, ie capture the currently set variable after frame creation
if (right instanceof Closure) {
((Closure) right).setCaptured(symbol, right);
}
// 1
return right;
}
object = getVariable(frame, block, var);
// top level is a symbol, can not be an antish var
antish = false;
} else {
// check we are not assigning direct global
if (last < 0) {
if (assignop != null) {
final Object self = context.get(var.getName());
right = operators.tryAssignOverload(node, assignop, self, right);
if (right == JexlOperator.ASSIGN) {
return self;
}
}
setContextVariable(node, var.getName(), right);
// 2
return right;
}
object = context.get(var.getName());
// top level accesses object, can not be an antish var
if (object != null) {
antish = false;
}
}
} else if (!(left instanceof ASTReference)) {
throw new JexlException(left, "illegal assignment form 0");
}
// 1: follow children till penultimate, resolve dot/array
JexlNode objectNode = null;
StringBuilder ant = null;
int v = 1;
// start at 1 if symbol
main: for (int c = symbol >= 0 ? 1 : 0; c < last; ++c) {
objectNode = left.jjtGetChild(c);
object = objectNode.jjtAccept(this, object);
if (object != null) {
// disallow mixing antish variable & bean with same root; avoid ambiguity
antish = false;
} else if (antish) {
// initialize if first time
if (ant == null) {
final JexlNode first = left.jjtGetChild(0);
final ASTIdentifier firstId = first instanceof ASTIdentifier ? (ASTIdentifier) first : null;
if ((firstId == null) || (firstId.getSymbol() >= 0)) {
// ant remains null, object is null, stop solving
antish = false;
break main;
}
ant = new StringBuilder(firstId.getName());
}
// catch up to current child
for (; v <= c; ++v) {
final JexlNode child = left.jjtGetChild(v);
final ASTIdentifierAccess aid = child instanceof ASTIdentifierAccess ? (ASTIdentifierAccess) child : null;
// remain antish only if unsafe navigation
if ((aid == null) || aid.isSafe() || aid.isExpression()) {
antish = false;
break main;
}
ant.append('.');
ant.append(aid.getName());
}
// solve antish
object = context.get(ant.toString());
} else {
throw new JexlException(objectNode, "illegal assignment form");
}
}
// 2: last objectNode will perform assignement in all cases
JexlNode propertyNode = left.jjtGetChild(last);
final ASTIdentifierAccess propertyId = propertyNode instanceof ASTIdentifierAccess ? (ASTIdentifierAccess) propertyNode : null;
final Object property;
if (propertyId != null) {
// deal with creating/assignining antish variable
if (antish && ant != null && object == null && !propertyId.isSafe() && !propertyId.isExpression()) {
if (last > 0) {
ant.append('.');
}
ant.append(propertyId.getName());
if (assignop != null) {
final Object self = context.get(ant.toString());
right = operators.tryAssignOverload(node, assignop, self, right);
if (right == JexlOperator.ASSIGN) {
return self;
}
}
setContextVariable(propertyNode, ant.toString(), right);
// 3
return right;
}
// property of an object ?
property = evalIdentifier(propertyId);
} else if (propertyNode instanceof ASTArrayAccess) {
// can have multiple nodes - either an expression, integer literal or reference
final int numChildren = propertyNode.jjtGetNumChildren() - 1;
for (int i = 0; i < numChildren; i++) {
final JexlNode nindex = propertyNode.jjtGetChild(i);
final Object index = nindex.jjtAccept(this, null);
object = getAttribute(object, index, nindex);
}
propertyNode = propertyNode.jjtGetChild(numChildren);
property = propertyNode.jjtAccept(this, null);
} else {
throw new JexlException(objectNode, "illegal assignment form");
}
// we can not *have* a null object though.
if (object == null) {
// no object, we fail
return unsolvableProperty(objectNode, "<null>.<?>", true, null);
}
// 3: one before last, assign
if (assignop != null) {
final Object self = getAttribute(object, property, propertyNode);
right = operators.tryAssignOverload(node, assignop, self, right);
if (right == JexlOperator.ASSIGN) {
return self;
}
}
setAttribute(object, property, right, propertyNode);
// 4
return right;
}
Aggregations