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

org.mvel2.ast.BinaryOperation Maven / Gradle / Ivy

Go to download

A fork of MVEL by Axibase. Original version is hosted here: https://github.com/mvel/mvel. MVEL is a powerful expression language for Java-based applications. It provides a plethora of features and is suited for everything from the smallest property binding and extraction, to full blown scripts.

The newest version!
/**
 * MVEL 2.0
 * Copyright (C) 2007 The Codehaus
 * Mike Brock, Dhanji Prasanna, John Graham, Mark Proctor
 *
 * 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 org.mvel2.ast;

import org.mvel2.CompileException;
import org.mvel2.Operator;
import org.mvel2.ParserContext;
import org.mvel2.ScriptRuntimeException;
import org.mvel2.integration.VariableResolverFactory;
import org.mvel2.util.CompatibilityStrategy;
import org.mvel2.util.NullType;
import org.mvel2.util.ParseTools;

import static org.mvel2.DataConversion.canConvert;
import static org.mvel2.DataConversion.convert;
import static org.mvel2.Operator.PTABLE;
import static org.mvel2.debug.DebugTools.getOperatorSymbol;
import static org.mvel2.math.MathProcessor.doOperations;
import static org.mvel2.util.CompilerTools.getReturnTypeFromOp;
import static org.mvel2.util.ParseTools.boxPrimitive;

public class BinaryOperation extends BooleanNode {
  private final int operation;
  private int lType = -1;
  private int rType = -1;

  public BinaryOperation(int operation, ParserContext ctx) {
    super(ctx);
    this.operation = operation;
  }

  public BinaryOperation(int operation, ASTNode left, ASTNode right, ParserContext ctx) {
    super(ctx);
    this.operation = operation;
    if ((this.left = left) == null) {
      throw new ScriptRuntimeException("not a statement");
    }
    if ((this.right = right) == null) {
      throw new ScriptRuntimeException("not a statement");
    }

    //    if (ctx.isStrongTyping()) {
    switch (operation) {
      case Operator.ADD:
        /**
         * In the special case of Strings, the return type may leftward propagate.
         */
        if (left.getEgressType() == String.class || right.getEgressType() == String.class) {
          egressType = String.class;
          lType = ParseTools.__resolveType(left.egressType);
          rType = ParseTools.__resolveType(right.egressType);

          return;
        }

      default:
        egressType = getReturnTypeFromOp(operation, this.left.egressType, this.right.egressType);
        if (!ctx.isStrongTyping()) break;

        final boolean leftIsAssignableFromRight = left.getEgressType().isAssignableFrom(right.getEgressType());
        final boolean rightIsAssignableFromLeft = right.getEgressType().isAssignableFrom(left.getEgressType());

        if (!leftIsAssignableFromRight && !rightIsAssignableFromLeft) {

          // Convert literals only when passing from String to Character or from Float to Double
          final boolean requiresConversion = right.getEgressType() == String.class || (
                  right.getEgressType() == Double.class && ( left.getEgressType() == Float.class || left.getEgressType() == float.class ) );

          if (right.isLiteral() && requiresConversion && canConvert(left.getEgressType(), right.getEgressType())) {
            Class targetType = isArithmeticOperation(operation) ? egressType : left.getEgressType();
            this.right = new LiteralNode(convert(right.getReducedValueAccelerated(null, null, null), targetType), pCtx);
          }
          else if ( !(areCompatible(left.getEgressType(), right.getEgressType()) ||
                      isValidEqualityCheck(operation, left.getEgressType(), right.getEgressType()) ||
                      isValidComparison(operation, left.getEgressType(), right.getEgressType()))
          ) {
            throw new CompileException("incompatible types in statement: " + right.getEgressType()
                    + " (compared from: " + left.getEgressType() + ")",
                    left.getExpr() != null ? left.getExpr() : right.getExpr(),
                    left.getExpr() != null ? left.getStart() : right.getStart());
          }
        }
    }


    // }

    if (this.left.isLiteral() && this.right.isLiteral()) {
      if (this.left.egressType == this.right.egressType) {
        lType = rType = ParseTools.__resolveType(left.egressType);
      } else {
        lType = ParseTools.__resolveType(this.left.egressType);
        rType = ParseTools.__resolveType(this.right.egressType);
      }
    }
  }

  private boolean isArithmeticOperation(int operation) {
    return operation <= Operator.POWER;
  }

  private boolean isValidComparison(int operation, Class leftClass, Class rightClass) {
    return (operation >= Operator.LTHAN && operation <= Operator.GETHAN) &&
            (CompatibilityStrategy.areComparisonCompatible(leftClass, rightClass) ||
             CompatibilityStrategy.areComparisonCompatible(rightClass, leftClass));
  }

  private boolean isValidEqualityCheck(int operation,Class leftClass, Class rightClass) {
    return (operation == Operator.EQUAL || operation == Operator.NEQUAL) &&
            (CompatibilityStrategy.areEqualityCompatible(leftClass, rightClass) ||
             CompatibilityStrategy.areEqualityCompatible(rightClass, leftClass));
  }

  private boolean areCompatible(Class leftClass, Class rightClass) {
    if (leftClass.equals(NullType.class) || rightClass.equals(NullType.class)) {
      return true;
    }
    Class boxedLeftClass = boxPrimitive(leftClass);
    Class boxedRightClass = boxPrimitive(rightClass);
    boolean leftIsNumber = Number.class.isAssignableFrom(boxedLeftClass);
    boolean rightIsNumber = Number.class.isAssignableFrom(boxedRightClass);
    return
            (leftIsNumber && rightIsNumber) ||
                    ((leftIsNumber || leftClass.isPrimitive() || rightIsNumber || rightClass.isPrimitive()) && canConvert(boxedLeftClass, boxedRightClass));
  }

  public Object getReducedValueAccelerated(Object ctx, Object thisValue, VariableResolverFactory factory) {
    return doOperations(lType, left.getReducedValueAccelerated(ctx, thisValue, factory), left.getName(), operation,
        rType, right.getReducedValueAccelerated(ctx, thisValue, factory), right.getName());
  }


  public Object getReducedValue(Object ctx, Object thisValue, VariableResolverFactory factory) {
    throw new RuntimeException("unsupported AST operation");
  }

  public int getOperation() {
    return operation;
  }

  public BinaryOperation getRightBinary() {
    return right != null && right instanceof BinaryOperation ? (BinaryOperation) right : null;
  }

  public void setRightMost(ASTNode right) {
    BinaryOperation n = this;
    while (n.right != null && n.right instanceof BinaryOperation) {
      n = (BinaryOperation) n.right;
    }
    n.right = right;

    if (n == this) {
      if ((rType = ParseTools.__resolveType(n.right.getEgressType())) == 0) rType = -1;
    }
  }

  public ASTNode getRightMost() {
    BinaryOperation n = this;
    while (n.right != null && n.right instanceof BinaryOperation) {
      n = (BinaryOperation) n.right;
    }
    return n.right;
  }

  public int getPrecedence() {
    return PTABLE[operation];
  }

  public boolean isGreaterPrecedence(BinaryOperation o) {
    return o.getPrecedence() > PTABLE[operation];
  }

  @Override
  public boolean isLiteral() {
    return false;
  }

  public String toString() {
    return "(" + left + " " + getOperatorSymbol(operation) + " " + right + ")";
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy