
org.apache.commons.jexl3.internal.Operators Maven / Gradle / Ivy
/*
* 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.JexlArithmetic;
import org.apache.commons.jexl3.JexlEngine;
import org.apache.commons.jexl3.JexlException;
import org.apache.commons.jexl3.JexlOperator;
import org.apache.commons.jexl3.internal.introspection.MethodExecutor;
import org.apache.commons.jexl3.introspection.JexlMethod;
import org.apache.commons.jexl3.introspection.JexlUberspect;
import org.apache.commons.jexl3.parser.JexlNode;
import java.lang.reflect.Method;
import java.util.function.Consumer;
/**
* Helper class to deal with operator overloading and specifics.
* @since 3.0
*/
public class Operators {
/** The owner. */
protected final InterpreterBase interpreter;
/** The overloaded arithmetic operators. */
protected final JexlArithmetic.Uberspect operators;
/**
* Constructor.
* @param owner the owning interpreter
*/
protected Operators(final InterpreterBase owner) {
final JexlArithmetic arithmetic = owner.arithmetic;
final JexlUberspect uberspect = owner.uberspect;
this.interpreter = owner;
this.operators = uberspect.getArithmetic(arithmetic);
}
/**
* Checks whether a method returns a boolean or a Boolean.
* @param vm the JexlMethod (may be null)
* @return true of false
*/
private boolean returnsBoolean(final JexlMethod vm) {
if (vm !=null) {
final Class> rc = vm.getReturnType();
return Boolean.TYPE.equals(rc) || Boolean.class.equals(rc);
}
return false;
}
/**
* Checks whether a method returns an int or an Integer.
* @param vm the JexlMethod (may be null)
* @return true of false
*/
private boolean returnsInteger(final JexlMethod vm) {
if (vm !=null) {
final Class> rc = vm.getReturnType();
return Integer.TYPE.equals(rc) || Integer.class.equals(rc);
}
return false;
}
/**
* Checks whether a method is a JexlArithmetic method.
* @param vm the JexlMethod (may be null)
* @return true of false
*/
private boolean isArithmetic(final JexlMethod vm) {
if (vm instanceof MethodExecutor) {
final Method method = ((MethodExecutor) vm).getMethod();
return JexlArithmetic.class.equals(method.getDeclaringClass());
}
return false;
}
/**
* Throw a NPE if operator is strict and one of the arguments is null.
* @param arithmetic the JEXL arithmetic instance
* @param operator the operator to check
* @param args the operands
* @throws JexlArithmetic.NullOperand if operator is strict and an operand is null
*/
protected void controlNullOperands(final JexlArithmetic arithmetic, final JexlOperator operator, final Object...args) {
for (final Object arg : args) {
// only check operator if necessary
if (arg == null) {
// check operator only once if it is not strict
if (arithmetic.isStrict(operator)) {
throw new JexlArithmetic.NullOperand();
}
break;
}
}
}
/**
* Attempts to call an operator.
*
* This performs the null argument control against the strictness of the operator.
*
*
* 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) {
final JexlArithmetic arithmetic = interpreter.arithmetic;
controlNullOperands(arithmetic, operator, args);
if (operators != null && operators.overloads(operator)) {
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 && !vm.tryFailed(result)) {
node.jjtSetValue(vm);
}
return result;
}
} catch (final Exception xany) {
// ignore return if lenient, will return try_failed
interpreter.operatorError(node, operator, xany);
}
}
return JexlEngine.TRY_FAILED;
}
/**
* Helper for postfix assignment operators.
* @param operator the operator
* @return true if operator is a postfix operator (x++, y--)
*/
private static boolean isPostfix(final JexlOperator operator) {
return operator == JexlOperator.GET_AND_INCREMENT || operator == JexlOperator.GET_AND_DECREMENT;
}
/**
* Tidy arguments based on operator arity.
* The interpreter may add a null to the arguments of operator expecting only one parameter.
* @param operator the operator
* @param args the arguements (as seen by the interpreter)
* @return the tidied arguments
*/
private Object[] arguments(final JexlOperator operator, final Object...args) {
return operator.getArity() == 1 && args.length > 1 ? new Object[]{args[0]} : args;
}
/**
* Evaluates an assign operator.
*
* 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
*
* @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 Consumer
© 2015 - 2025 Weber Informatics LLC | Privacy Policy