content: /*
   * Copyright 1999-2012 Alibaba Group.
   *  
   * 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.
   */
  /**
   * (created at 2011-7-19)
   */
  package com.alibaba.cobar.parser.util;
  
  import java.math.BigDecimal;
  import java.math.BigInteger;
  import java.sql.SQLSyntaxErrorException;
  import java.util.HashMap;
  import java.util.Map;
  
  import com.alibaba.cobar.parser.ast.expression.primary.literal.LiteralBoolean;
  import com.alibaba.cobar.parser.recognizer.mysql.lexer.MySQLLexer;
  
  /**
   * adapt Java's expression rule into MySQL's
   * 
   * @author <a href="mailto:shuo.qius@alibaba-inc.com">QIU Shuo</a>
   */
  public class ExprEvalUtils {
      private static final int CLASS_MAP_DOUBLE = 1;
      private static final int CLASS_MAP_FLOAT = 2;
      private static final int CLASS_MAP_BIG_ING = 3;
      private static final int CLASS_MAP_BIG_DECIMAL = 4;
      private static final int CLASS_MAP_LONG = 5;
      private static final Map<Class<? extends Number>, Integer> classMap = new HashMap<Class<? extends Number>, Integer>(
              5);
      static {
          classMap.put(Double.class, CLASS_MAP_DOUBLE);
          classMap.put(Float.class, CLASS_MAP_FLOAT);
          classMap.put(BigInteger.class, CLASS_MAP_BIG_ING);
          classMap.put(BigDecimal.class, CLASS_MAP_BIG_DECIMAL);
          classMap.put(Long.class, CLASS_MAP_LONG);
      }
  
      public static boolean obj2bool(Object obj) {
          if (obj == LiteralBoolean.TRUE)
              return true;
          if (obj == LiteralBoolean.FALSE)
              return false;
  
          if (obj instanceof Boolean) {
              return (Boolean) obj;
          }
          Number num;
          if (obj instanceof String) {
              num = ExprEvalUtils.string2Number((String) obj);
          } else {
              num = (Number) obj;
          }
          Integer classType = classMap.get(num.getClass());
          if (classType == null) {
              return num.intValue() != 0;
          }
          switch (classType) {
          case CLASS_MAP_BIG_DECIMAL:
              return BigDecimal.ZERO.compareTo((BigDecimal) num) != 0;
          case CLASS_MAP_BIG_ING:
              return BigInteger.ZERO.compareTo((BigInteger) num) != 0;
          case CLASS_MAP_DOUBLE:
              return ((Double) num).doubleValue() != 0d;
          case CLASS_MAP_FLOAT:
              return ((Float) num).floatValue() != 0f;
          case CLASS_MAP_LONG:
              return ((Long) num).longValue() != 0L;
          default:
              throw new IllegalArgumentException("unsupported number type: " + num.getClass());
          }
      }
  
      private static final int NUM_INT = 1;
      private static final int NUM_LONG = 2;
      private static final int NUM_BIG_INTEGER = 3;
      private static final int NUM_BIG_DECIMAL = 4;
  
      public static Number calculate(UnaryOperandCalculator cal, Number num) {
          if (num == null)
              return null;
          if (num instanceof Integer)
              return cal.calculate((Integer) num);
          if (num instanceof Long)
              return cal.calculate((Long) num);
          if (num instanceof BigDecimal)
              return cal.calculate((BigDecimal) num);
          if (num instanceof BigInteger)
              return cal.calculate((BigInteger) num);
          throw new IllegalArgumentException("unsupported add calculate: " + num.getClass());
      }
  
      public static Number calculate(BinaryOperandCalculator cal, Number n1, Number n2) {
          if (n1 == null || n2 == null)
              return null;
          if (n1 instanceof Integer)
              return cal.calculate((Integer) n1, (Integer) n2);
          if (n1 instanceof Long)
              return cal.calculate((Long) n1, (Long) n2);
          if (n1 instanceof BigDecimal)
              return cal.calculate((BigDecimal) n1, (BigDecimal) n2);
          if (n1 instanceof BigInteger)
              return cal.calculate((BigInteger) n1, (BigInteger) n2);
          throw new IllegalArgumentException("unsupported add calculate: " + n1.getClass());
      }
  
      /**
       * @param obj1 class of String or Number
       */
      public static Pair<Number, Number> convertNum2SameLevel(Object obj1, Object obj2) {
          Number n1, n2;
          if (obj1 instanceof String) {
              n1 = string2Number((String) obj1);
          } else {
              n1 = (Number) obj1;
          }
          if (obj2 instanceof String) {
              n2 = string2Number((String) obj2);
          } else {
              n2 = (Number) obj2;
          }
          if (n1 == null || n2 == null) {
              return new Pair<Number, Number>(n1, n2);
          }
          int l1 = getNumberLevel(n1.getClass());
          int l2 = getNumberLevel(n2.getClass());
          if (l1 > l2) {
              n2 = upTolevel(n2, l1);
          } else if (l1 < l2) {
              n1 = upTolevel(n1, l2);
          }
          return new Pair<Number, Number>(n1, n2);
      }
  
      private static Number upTolevel(Number num, int level) {
          switch (level) {
          case NUM_INT:
              if (num instanceof Integer)
                  return num;
              return num.intValue();
          case NUM_LONG:
              if (num instanceof Long)
                  return num;
              return num.longValue();
          case NUM_BIG_INTEGER:
              if (num instanceof BigInteger)
                  return num;
              return new BigInteger(num.toString());
          case NUM_BIG_DECIMAL:
              if (num instanceof BigDecimal)
                  return num;
              return new BigDecimal(num.toString());
          default:
              throw new IllegalArgumentException("unsupported number level: " + level);
          }
      }
  
      private static int getNumberLevel(Class<?> clz) {
          if (Integer.class.isAssignableFrom(clz)) {
              return NUM_INT;
          }
          if (Long.class.isAssignableFrom(clz)) {
              return NUM_LONG;
          }
          if (BigInteger.class.isAssignableFrom(clz)) {
              return NUM_BIG_INTEGER;
          }
          if (BigDecimal.class.isAssignableFrom(clz)) {
              return NUM_BIG_DECIMAL;
          }
          throw new IllegalArgumentException("unsupported number class: " + clz);
      }
  
      public static Number string2Number(String str) {
          if (str == null)
              return null;
          try {
              return new Integer(str);
          } catch (Exception e) {
          }
          try {
              return new Long(str);
          } catch (Exception e) {
          }
          try {
              MySQLLexer lexer = new MySQLLexer(str);
              switch (lexer.token()) {
              case LITERAL_NUM_PURE_DIGIT:
                  return lexer.integerValue();
              case LITERAL_NUM_MIX_DIGIT:
                  return lexer.decimalValue();
              default:
                  throw new IllegalArgumentException("unrecognized number: " + str);
              }
          } catch (SQLSyntaxErrorException e) {
              throw new IllegalArgumentException(e);
          }
      }
  
  }
  