/* * Copyright 2009-2016 Tilmann Zaeschke. All rights reserved. * * This file is part of ZooDB. * * ZooDB is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * ZooDB is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with ZooDB. If not, see <http://www.gnu.org/licenses/>. * * See the README and COPYING files for further information. */ package org.zoodb.internal.query; import java.math.BigDecimal; import java.math.BigInteger; import java.util.Date; import org.zoodb.api.impl.ZooPC; import org.zoodb.internal.util.DBLogger; public class TypeConverterTools { public static enum COMPARISON_TYPE { BIG_DECIMAL(true), BIG_INT(true), DOUBLE(true), FLOAT(true), LONG(true), INT(true), SHORT(true), BYTE(true), CHAR(true), BOOLEAN(false), STRING(false), DATE(false), PC(false), SCO(true), //can be number, e.g. Long.class NULL(true), UNKNOWN(true); private boolean canBeNumber; private COMPARISON_TYPE(boolean canBeNumber) { this.canBeNumber = canBeNumber; } public boolean canBeNumber() { return canBeNumber; } public static COMPARISON_TYPE fromObject(Object v) { if (v == null || v == QueryTerm.NULL) { return NULL; } return fromClass(v.getClass()); } public static COMPARISON_TYPE fromClass(Class<?> type) { //TODO we can use perfect hashing here for fast lookup! if (type == null) { //can happen for implicit parameters return UNKNOWN; } else if (type == Long.class || type == Long.TYPE) { return LONG; } else if (type == Integer.class || type == Integer.TYPE) { return INT; } else if (type == Short.class || type == Short.TYPE) { return SHORT; } else if (type == Byte.class || type == Byte.TYPE) { return BYTE; } else if (type == Double.class || type == Double.TYPE) { return DOUBLE; } else if (type == Float.class || type == Float.TYPE) { return FLOAT; } else if (type == Character.class || type == Character.TYPE) { return CHAR; } else if (type == String.class) { return STRING; } else if (type == Boolean.class || type == Boolean.TYPE) { return BOOLEAN; } else if (ZooPC.class.isAssignableFrom(type)) { return PC; } else if (type == Date.class) { return DATE; } else if (type == BigInteger.class) { return BIG_INT; } else if (type == BigDecimal.class) { return BIG_DECIMAL; } return SCO; } public static COMPARISON_TYPE fromOperands(COMPARISON_TYPE lhsCt, COMPARISON_TYPE rhsCt) { //swap them (according to ordinal()) to eliminate some 'if'. (?) if (rhsCt.ordinal() < lhsCt.ordinal()) { COMPARISON_TYPE x = lhsCt; lhsCt = rhsCt; rhsCt = x; } if (lhsCt == SCO && rhsCt == SCO) { return SCO; } switch (lhsCt) { case BIG_DECIMAL : case BIG_INT: case DOUBLE: case FLOAT: case LONG: case INT: case SHORT: case BYTE: case CHAR: if (rhsCt.canBeNumber()) { return lhsCt; } failComp(lhsCt, rhsCt); case BOOLEAN: if (rhsCt == BOOLEAN) { return BOOLEAN; } if (rhsCt == SCO) { return SCO; } failComp(lhsCt, rhsCt); case PC: if (rhsCt == PC || rhsCt == NULL) { return PC; } if (rhsCt == SCO) { return SCO; } failComp(lhsCt, rhsCt); case STRING: if (rhsCt == STRING) { return STRING; } return UNKNOWN; default: return UNKNOWN; } } private static void failComp(COMPARISON_TYPE lhsCt, COMPARISON_TYPE rhsCt) { throw DBLogger.newUser("Cannot compare " + lhsCt + " with " + rhsCt); } } public static COMPARISON_TYPE fromTypes(Class<?> lhs, Class<?> rhs) { COMPARISON_TYPE lhsCt = COMPARISON_TYPE.fromClass(lhs); COMPARISON_TYPE rhsCt = COMPARISON_TYPE.fromClass(rhs); return COMPARISON_TYPE.fromOperands(lhsCt, rhsCt); } /** * This assumes that comparability implies assignability or convertability... * @param o object * @param type type */ public static void checkAssignability(Object o, Class<?> type) { COMPARISON_TYPE ctO = COMPARISON_TYPE.fromObject(o); COMPARISON_TYPE ctT = COMPARISON_TYPE.fromClass(type); try { COMPARISON_TYPE.fromOperands(ctO, ctT); } catch (Exception e) { throw DBLogger.newUser("Cannot assign " + o.getClass() + " to " + type, e); } } /** * This assumes that comparability implies assignability or convertability... * @param c1 type #1 * @param c2 type #2 */ public static void checkAssignability(Class<?> c1, Class<?> c2) { COMPARISON_TYPE ct1 = COMPARISON_TYPE.fromClass(c1); COMPARISON_TYPE ct2 = COMPARISON_TYPE.fromClass(c2); try { COMPARISON_TYPE.fromOperands(ct1, ct2); } catch (Exception e) { throw DBLogger.newUser("Cannot assign " + c2 + " to " + c1, e); } } public static double toDouble(Object o) { if (o instanceof Double) { return (double)o; } else if (o instanceof Float) { return (float)o; } return toLong(o); } public static long toLong(Object o) { if (o instanceof Long) { return (long)o; } return toInt(o); } public static int toInt(Object o) { //TODO use perfect hashing on types to create hashmap? if (o instanceof Integer) { return (int)(Integer)o; } else if (o instanceof Short) { return (Short)o; } else if (o instanceof Byte) { return (Byte)o; } else if (o instanceof Character) { return (Character)o; } throw DBLogger.newUser("Cannot cast type to number: " + o.getClass().getName()); } }