/*
* 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);
}
}
}