All Downloads are FREE. Search and download functionalities are using the official Maven repository.

leap.lang.el.spel.ast.Ops Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2014 the original author or authors.
 *
 * Licensed 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 leap.lang.el.spel.ast;

import static leap.lang.el.ElTypes.*;

import java.math.BigDecimal;
import java.math.BigInteger;

import leap.lang.el.ElEvalContext;
import leap.lang.el.ElException;
import leap.lang.el.ElTypes;


class Ops {
	
	/**
	 * + operator : jsr341 spec
	 */
	static Object add(ElEvalContext ctx, int ltype,Object lval,int rtype,Object rval) {
		//■ If A and B are null, return (Long)0
		if(null == lval && null == rval){
			return Coerce.LG_ZERO;
		}
		
		if(ltype == -1){
			ltype = resolveTypeByVal(lval);
		}
		if(rtype == -1){
			rtype = resolveTypeByVal(rval);
		}
		
		//numbers
		if(ltype > 29 || rtype > 29){
			//If A or B is a BigDecimal, coerce both to BigDecimal and then return A.add(B)
			if(ltype == BIG_DECIMAL || rtype == BIG_DECIMAL){
				return Coerce.toBigDecimal(ctx, ltype, lval).add(Coerce.toBigDecimal(ctx, rtype, rval));
			}
			
			//If A or B is a Float, Double, or String containing ., e, or E:
			//If A or B is BigInteger, coerce both A and B to BigDecimal and apply operator.
			//Otherwise, coerce both A and B to Double and apply operator
			if(isFloatOrDoubleOrDotEe(ltype,lval) || isFloatOrDoubleOrDotEe(rtype, rval)){
				if(ltype == BIG_INTEGER || rtype == BIG_INTEGER){
					return Coerce.toBigDecimal(ctx, ltype, lval).add(Coerce.toBigDecimal(ctx, rtype, rval));
				}else{
					return Coerce.toDouble(ctx, ltype, lval) + (Coerce.toDouble(ctx, rtype, rval));
				}
			}
			
			//If A or B is BigInteger, coerce both to BigInteger and then return A.add(B)
			if(ltype == BIG_INTEGER || rtype == BIG_INTEGER){
				return Coerce.toBigInteger(ctx, ltype, lval).add(Coerce.toBigInteger(ctx, rtype, rval));
			}
			
			return Coerce.toLong(ctx, ltype, lval) + Coerce.toLong(ctx, rtype, rval);
		}
		
		//String add (not supported by jsr341)
		return ctx.toString(lval) + ctx.toString(rval); 
	}
	
	/**
	 * - operator : jsr341 spec
	 */
	static Object sub(ElEvalContext ctx, int ltype,Object lval,int rtype,Object rval) {
		//■ If A and B are null, return (Long)0
		if(null == lval && null == rval){
			return Coerce.LG_ZERO;
		}
		
		if(ltype == -1){
			ltype = resolveTypeByVal(lval);
		}
		if(rtype == -1){
			rtype = resolveTypeByVal(rval);
		}
		
		//If A or B is a BigDecimal, coerce both to BigDecimal and then return A.subtract(B)
		if(ltype == BIG_DECIMAL || rtype == BIG_DECIMAL){
			return Coerce.toBigDecimal(ctx, ltype, lval).subtract(Coerce.toBigDecimal(ctx, rtype, rval));
		}
		
		//If A or B is a Float, Double, or String containing ., e, or E:
		//If A or B is BigInteger, coerce both A and B to BigDecimal and apply operator.
		//Otherwise, coerce both A and B to Double and apply operator
		if(isFloatOrDoubleOrDotEe(ltype,lval) || isFloatOrDoubleOrDotEe(rtype, rval)){
			if(ltype == BIG_INTEGER || rtype == BIG_INTEGER){
				return Coerce.toBigDecimal(ctx, ltype, lval).subtract(Coerce.toBigDecimal(ctx, rtype, rval));
			}else{
				return Coerce.toDouble(ctx, ltype, lval) - (Coerce.toDouble(ctx, rtype, rval));
			}
		}
		
		//If A or B is BigInteger, coerce both to BigInteger and then return A.subtract(B)
		if(ltype == BIG_INTEGER || rtype == BIG_INTEGER){
			return Coerce.toBigInteger(ctx, ltype, lval).subtract(Coerce.toBigInteger(ctx, rtype, rval));
		}
		
		//Otherwise coerce both A and B to Long and apply operator
		return Coerce.toLong(ctx, ltype, lval) - Coerce.toLong(ctx, rtype, rval);
	}
	
	/**
	 * * operator : jsr341 spec
	 */
	static Object mul(ElEvalContext ctx, int ltype,Object lval,int rtype,Object rval) {
		//■ If A and B are null, return (Long)0
		if(null == lval && null == rval){
			return Coerce.LG_ZERO;
		}
		
		if(ltype == -1){
			ltype = resolveTypeByVal(lval);
		}
		if(rtype == -1){
			rtype = resolveTypeByVal(rval);
		}
		
		//If A or B is a BigDecimal, coerce both to BigDecimal and then return A.multiply(B)
		if(ltype == BIG_DECIMAL || rtype == BIG_DECIMAL){
			return Coerce.toBigDecimal(ctx, ltype, lval).multiply(Coerce.toBigDecimal(ctx, rtype, rval));
		}
		
		//If A or B is a Float, Double, or String containing ., e, or E:
		//If A or B is BigInteger, coerce both A and B to BigDecimal and apply operator.
		//Otherwise, coerce both A and B to Double and apply operator
		if(isFloatOrDoubleOrDotEe(ltype,lval) || isFloatOrDoubleOrDotEe(rtype, rval)){
			if(ltype == BIG_INTEGER || rtype == BIG_INTEGER){
				return Coerce.toBigDecimal(ctx, ltype, lval).multiply(Coerce.toBigDecimal(ctx, rtype, rval));
			}else{
				return Coerce.toDouble(ctx, ltype, lval) * (Coerce.toDouble(ctx, rtype, rval));
			}
		}
		
		//If A or B is BigInteger, coerce both to BigInteger and then return A.multiply(B)
		if(ltype == BIG_INTEGER || rtype == BIG_INTEGER){
			return Coerce.toBigInteger(ctx, ltype, lval).multiply(Coerce.toBigInteger(ctx, rtype, rval));
		}
		
		//Otherwise coerce both A and B to Long and apply operator
		return Coerce.toLong(ctx, ltype, lval) * Coerce.toLong(ctx, rtype, rval);
	}
	
	/**
	 * / operator : jsr341 spec
	 */
	static Object div(ElEvalContext ctx, int ltype,Object lval,int rtype,Object rval) {
		//■ If A and B are null, return (Long)0
		if(null == lval && null == rval){
			return Coerce.LG_ZERO;
		}
		
		if(ltype == -1){
			ltype = resolveTypeByVal(lval);
		}
		if(rtype == -1){
			rtype = resolveTypeByVal(rval);
		}
		
		//If A or B is a BigDecimal or a BigInteger, coerce both to BigDecimal and return A.divide(B, BigDecimal.ROUND_HALF_UP)
		if(ltype > 69 || rtype > 69 ){
			return Coerce.toBigDecimal(ctx, ltype, lval).divide(Coerce.toBigDecimal(ctx, rtype, rval),BigDecimal.ROUND_HALF_UP);
		}
		
		//Otherwise, coerce both A and B to Double and apply operator
		return Coerce.toDouble(ctx, ltype, lval) / Coerce.toDouble(ctx, rtype, rval);
	}
	
	/**
	 * % operator : jsr341 spec
	 */
	static Object mod(ElEvalContext ctx, int ltype,Object lval,int rtype,Object rval) {
		//■ If A and B are null, return (Long)0
		if(null == lval && null == rval){
			return Coerce.LG_ZERO;
		}
		
		if(ltype == -1){
			ltype = resolveTypeByVal(lval);
		}
		if(rtype == -1){
			rtype = resolveTypeByVal(rval);
		}
		
		//If A or B is a BigDecimal, Float, Double, or String containing ., e, or E, 
		//coerce both A and B to Double and apply operator
		if((ltype == BIG_DECIMAL || isFloatOrDoubleOrDotEe(ltype, lval)) ||  
		   (rtype == BIG_DECIMAL || isFloatOrDoubleOrDotEe(rtype, rval))) {
			return Coerce.toDouble(ctx, ltype, lval) % Coerce.toDouble(ctx, rtype, rval);
		}
		
		//If A or B is a BigInteger, coerce both to BigInteger and return A.remainder(B).
		if(ltype == BIG_INTEGER || rtype == BIG_INTEGER){
			return Coerce.toBigInteger(ctx, ltype, lval).remainder(Coerce.toBigInteger(ctx, rtype, rval));
		}
		
		//Otherwise coerce both A and B to Long and apply operator
		return Coerce.toLong(ctx, ltype, lval) % Coerce.toLong(ctx, rtype, rval);
	}
	
	/**
	 * == operator : jsr341 spec
	 */
	static Object eq(ElEvalContext ctx, int ltype,Object lval,int rtype,Object rval) {
		if(ltype == -1){
			ltype = resolveTypeByVal(lval);
		}
		if(rtype == -1){
			rtype = resolveTypeByVal(rval);
		}
		
		//If A or B is Boolean coerce both A and B to Boolean, apply operator
		if(isBoolean(ltype) || isBoolean(rtype)){
			return Coerce.toBoolean(ctx, ltype, lval) == Coerce.toBoolean(ctx, rtype, rval);
		}
		
		//If A or B is an enum, coerce both A and B to enum, apply operator
		if(ltype == ENUM){
			if(rtype == ENUM){
				return lval.equals(rval);
			}else{
				return lval.equals(ctx.convert(rval, lval.getClass()));
			}
		}else if(rtype == ENUM){
			if(ltype == ENUM){
				return rval.equals(lval);
			}else{
				return rval.equals(ctx.convert(lval, rval.getClass()));
			}
		}
		
		//If A or B is String coerce both A and B to String, compare lexically
		if(ltype == STRING || rtype == STRING){
			return Coerce.toString(ctx, ltype, lval).equals(Coerce.toString(ctx, rtype, rval));
		}

		//If A or B is Byte, Short, Character, Integer, or Long coerce both A and B to Long, apply operator
		if(ltype > 29 || rtype > 29){
			//if A or B is BigDecimal, coerce both A and B to BigDecimal and then: return A.equals(B)
			if(ltype == BIG_DECIMAL || rtype == BIG_DECIMAL){
				return Coerce.toBigDecimal(ctx, ltype, lval).equals(Coerce.toBigDecimal(ctx, rtype, rval));
			}
			
			//If A or B is Float or Double coerce both A and B to Double, apply operator
			if(ElTypes.isFloatOrDouble(ltype) || ElTypes.isFloatOrDouble(rtype)){
				return Coerce.toDouble(ctx, ltype, lval) == Coerce.toDouble(ctx, rtype, rval);
			}
			
			//If A or B is BigInteger, coerce both A and B to BigInteger and then: return A.equals(B)
			if(ltype == BIG_INTEGER || rtype == BIG_INTEGER){
				return Coerce.toBigInteger(ctx, ltype, lval).equals(Coerce.toBigInteger(ctx, rtype, rval));
			}
			
			return Coerce.toLong(ctx, ltype, lval) == Coerce.toLong(ctx, rtype, rval);
		}
		
		//Otherwise, apply operator to result of A.equals(B)	
		return lval.equals(rval);
	}
	
	/**
	 * >= operator : jsr341 spec
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
    static Object ge(ElEvalContext ctx, int ltype,Object lval,int rtype,Object rval) {
		if(ltype == -1){
			ltype = resolveTypeByVal(lval);
		}
		if(rtype == -1){
			rtype = resolveTypeByVal(rval);
		}

		//If A or B is BigDecimal, coerce both A and B to BigDecimal and use the return value of A.compareTo(B)
		if(ltype == BIG_DECIMAL || rtype == BIG_DECIMAL){
			return Coerce.toBigDecimal(ctx, ltype, lval).compareTo(Coerce.toBigDecimal(ctx, rtype, rval)) >= 0;
		}
		
		//If A or B is Float or Double coerce both A and B to Double, apply operator
		if(ElTypes.isFloatOrDouble(ltype) || ElTypes.isFloatOrDouble(rtype)){
			return Coerce.toDouble(ctx, ltype, lval) >= Coerce.toDouble(ctx, rtype, rval);
		}
		
		//If A or B is BigInteger, coerce both A and B to BigInteger and use the return value of A.compareTo(B).
		if(ltype == BIG_INTEGER || rtype == BIG_INTEGER){
			return Coerce.toBigInteger(ctx, ltype, lval).compareTo(Coerce.toBigInteger(ctx, rtype, rval)) >= 0;
		}
		
		//If A or B is Byte, Short, Character, Integer, or Long coerce both A and B to Long, apply operator
		if(ltype > 29 || rtype > 29){
			return Coerce.toLong(ctx, ltype, lval) >= Coerce.toLong(ctx, rtype, rval);
		}
		
		//If A or B is String coerce both A and B to String, compare lexically
		if(ltype == STRING || rtype == STRING){
			return Coerce.toString(ctx, ltype, lval).compareTo(Coerce.toString(ctx, rtype, rval)) >= 0;
		}
		
		//If A is Comparable, then: use result of A.compareTo(B)
		if(lval instanceof Comparable){
			return ((Comparable) lval).compareTo(rval) >= 0;
		}
		
		//If B is Comparable, then: use result of B.compareTo(A)
		if(rval instanceof Comparable){
			return ((Comparable) rval).compareTo(lval) < 0;
		}
		
		throw new ElException(ctx.getMessage("el.errors.valuesNotComparable", lval, rval));
	}
	
	/**
	 * > operator : jsr341 spec
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
    static Object gt(ElEvalContext ctx, int ltype,Object lval,int rtype,Object rval) {
		if(ltype == -1){
			ltype = resolveTypeByVal(lval);
		}
		if(rtype == -1){
			rtype = resolveTypeByVal(rval);
		}

		//If A or B is BigDecimal, coerce both A and B to BigDecimal and use the return value of A.compareTo(B)
		if(ltype == BIG_DECIMAL || rtype == BIG_DECIMAL){
			return Coerce.toBigDecimal(ctx, ltype, lval).compareTo(Coerce.toBigDecimal(ctx, rtype, rval)) > 0;
		}
		
		//If A or B is Float or Double coerce both A and B to Double, apply operator
		if(ElTypes.isFloatOrDouble(ltype) || ElTypes.isFloatOrDouble(rtype)){
			return Coerce.toDouble(ctx, ltype, lval) > Coerce.toDouble(ctx, rtype, rval);
		}
		
		//If A or B is BigInteger, coerce both A and B to BigInteger and use the return value of A.compareTo(B).
		if(ltype == BIG_INTEGER || rtype == BIG_INTEGER){
			return Coerce.toBigInteger(ctx, ltype, lval).compareTo(Coerce.toBigInteger(ctx, rtype, rval)) > 0;
		}
		
		//If A or B is Byte, Short, Character, Integer, or Long coerce both A and B to Long, apply operator
		if(ltype > 29 || rtype > 29){
			return Coerce.toLong(ctx, ltype, lval) > Coerce.toLong(ctx, rtype, rval);
		}
		
		//If A or B is String coerce both A and B to String, compare lexically
		if(ltype == STRING || rtype == STRING){
			return Coerce.toString(ctx, ltype, lval).compareTo(Coerce.toString(ctx, rtype, rval)) > 0;
		}
		
		//If A is Comparable, then: use result of A.compareTo(B)
		if(lval instanceof Comparable){
			return ((Comparable) lval).compareTo(rval) > 0;
		}
		
		//If B is Comparable, then: use result of B.compareTo(A)
		if(rval instanceof Comparable){
			return ((Comparable) rval).compareTo(lval) <= 0;
		}
		
		throw new ElException(ctx.getMessage("el.errors.valuesNotComparable", lval, rval));
	}
	
	/**
	 * < operator : jsr341 spec
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
    static Object lt(ElEvalContext ctx, int ltype,Object lval,int rtype,Object rval) {
		if(ltype == -1){
			ltype = resolveTypeByVal(lval);
		}
		if(rtype == -1){
			rtype = resolveTypeByVal(rval);
		}

		//If A or B is BigDecimal, coerce both A and B to BigDecimal and use the return value of A.compareTo(B)
		if(ltype == BIG_DECIMAL || rtype == BIG_DECIMAL){
			return Coerce.toBigDecimal(ctx, ltype, lval).compareTo(Coerce.toBigDecimal(ctx, rtype, rval)) < 0;
		}
		
		//If A or B is Float or Double coerce both A and B to Double, apply operator
		if(ElTypes.isFloatOrDouble(ltype) || ElTypes.isFloatOrDouble(rtype)){
			return Coerce.toDouble(ctx, ltype, lval) < Coerce.toDouble(ctx, rtype, rval);
		}
		
		//If A or B is BigInteger, coerce both A and B to BigInteger and use the return value of A.compareTo(B).
		if(ltype == BIG_INTEGER || rtype == BIG_INTEGER){
			return Coerce.toBigInteger(ctx, ltype, lval).compareTo(Coerce.toBigInteger(ctx, rtype, rval)) < 0;
		}
		
		//If A or B is Byte, Short, Character, Integer, or Long coerce both A and B to Long, apply operator
		if(ltype > 29 || rtype > 29){
			return Coerce.toLong(ctx, ltype, lval) < Coerce.toLong(ctx, rtype, rval);
		}
		
		//If A or B is String coerce both A and B to String, compare lexically
		if(ltype == STRING || rtype == STRING){
			return Coerce.toString(ctx, ltype, lval).compareTo(Coerce.toString(ctx, rtype, rval)) < 0;
		}
		
		//If A is Comparable, then: use result of A.compareTo(B)
		if(lval instanceof Comparable){
			return ((Comparable) lval).compareTo(rval) < 0;
		}
		
		//If B is Comparable, then: use result of B.compareTo(A)
		if(rval instanceof Comparable){
			return ((Comparable) rval).compareTo(lval) >= 0;
		}
		
		throw new ElException(ctx.getMessage("el.errors.valuesNotComparable", lval, rval));
	}
	
	/**
	 * <= operator : jsr341 spec
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
    static Object le(ElEvalContext ctx, int ltype,Object lval,int rtype,Object rval) {
		if(ltype == -1){
			ltype = resolveTypeByVal(lval);
		}
		if(rtype == -1){
			rtype = resolveTypeByVal(rval);
		}

		//If A or B is BigDecimal, coerce both A and B to BigDecimal and use the return value of A.compareTo(B)
		if(ltype == BIG_DECIMAL || rtype == BIG_DECIMAL){
			return Coerce.toBigDecimal(ctx, ltype, lval).compareTo(Coerce.toBigDecimal(ctx, rtype, rval)) <= 0;
		}
		
		//If A or B is Float or Double coerce both A and B to Double, apply operator
		if(ElTypes.isFloatOrDouble(ltype) || ElTypes.isFloatOrDouble(rtype)){
			return Coerce.toDouble(ctx, ltype, lval) <= Coerce.toDouble(ctx, rtype, rval);
		}
		
		//If A or B is BigInteger, coerce both A and B to BigInteger and use the return value of A.compareTo(B).
		if(ltype == BIG_INTEGER || rtype == BIG_INTEGER){
			return Coerce.toBigInteger(ctx, ltype, lval).compareTo(Coerce.toBigInteger(ctx, rtype, rval)) <= 0;
		}
		
		//If A or B is Byte, Short, Character, Integer, or Long coerce both A and B to Long, apply operator
		if(ltype > 29 || rtype > 29){
			return Coerce.toLong(ctx, ltype, lval) <= Coerce.toLong(ctx, rtype, rval);
		}
		
		//If A or B is String coerce both A and B to String, compare lexically
		if(ltype == STRING || rtype == STRING){
			return Coerce.toString(ctx, ltype, lval).compareTo(Coerce.toString(ctx, rtype, rval)) < 0;
		}
		
		//If A is Comparable, then: use result of A.compareTo(B)
		if(lval instanceof Comparable){
			return ((Comparable) lval).compareTo(rval) <= 0;
		}
		
		//If B is Comparable, then: use result of B.compareTo(A)
		if(rval instanceof Comparable){
			return ((Comparable) rval).compareTo(lval) > 0;
		}
		
		throw new ElException(ctx.getMessage("el.errors.valuesNotComparable", lval, rval));
	}
	
	/**
	 *  - operator , jsr341 spec
	 */
	static Object minus(ElEvalContext ctx, int type,Object val) {
		//If A is null, return (Long)0
		if(val == null){
			return Coerce.LG_ZERO;
		}
		
		if(type == -1){
			type = resolveTypeByVal(val);
		}
		
		//If A is a BigDecimal or BigInteger, return A.negate().
		if(type == BIG_DECIMAL){
			return ((BigDecimal)val).negate();
		}
		if(type == BIG_INTEGER){
			return ((BigInteger)val).negate();
		}
		
		/*
			If A is a String:
			■ If A contains ., e, or E, coerce to a Double and apply operator
			■ Otherwise, coerce to a Long and apply operator
			■ If operator results in exception, error
		 */
		if(type == STRING){
			if(isDotEe((String)val)){
				return - Coerce.toDouble(ctx, type, val);
			}else{
				return - Coerce.toLong(ctx, type, val);
			}
		}
		
		/*
			If A is Byte, Short, Integer, Long, Float, Double
			■ Retain type, apply operator
			■ If operator results in exception, error
		 */
		if(type > 39){
			if(type == W_INTEGER || type == INTEGER) {
				return - (Integer)val;
			}
			
			if(type == W_LONG || type == LONG){
				return - (Long)val;
			}
			
			if(type == W_DOUBLE || type == DOUBLE){
				return - (Double)val; 
			}
			
			if(type == W_FLOAT || type == FLOAT){
				return - (Float)val;
			}
			
			if(type == W_SHORT || type == SHORT){
				return - (Short)val;
			}
			
			if(type == W_BYTE || type == BYTE){
				return -(Byte)val;
			}
		}
		
		//Otherwise, error
		throw new ElException(ctx.getMessage("el.errors.cannotApplyMinusOperatorTo", val));
	}
	
	private static final boolean isFloatOrDoubleOrDotEe(int type,Object val) {
		return ElTypes.isFloatOrDouble(type) || (type == STRING && isDotEe((String)val));
	}
	
	private static final boolean isDotEe(String value) {
		if(!(value instanceof String)){
			return false;
		}
		
		String s = (String)value;
		
		int length = s.length();
		for (int i = 0; i < length; i++) {
			switch (s.charAt(i)) {
				case '.':
				case 'E':
				case 'e': return true;
			}
		}
		return false;
	}
	
	protected Ops() {
		
	}
	
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy