| /*========================================================================= |
| * Copyright Copyright (c) 2000-2014 Pivotal Software, Inc. All Rights Reserved. |
| * This product is protected by U.S. and international copyright |
| * and intellectual property laws. Pivotal products are covered by |
| * more patents listed at http://www.pivotal.io/patents. |
| * $Id: TypeUtils.java,v 1.1 2005/01/27 06:26:33 vaibhav Exp $ |
| *========================================================================= |
| */ |
| package com.gemstone.gemfire.cache.query.internal.types; |
| |
| |
| import java.util.*; |
| |
| import com.gemstone.gemfire.InternalGemFireError; |
| import com.gemstone.gemfire.cache.*; |
| import com.gemstone.gemfire.cache.query.*; |
| import com.gemstone.gemfire.cache.query.internal.parse.*; |
| import com.gemstone.gemfire.cache.query.types.*; |
| import com.gemstone.gemfire.cache.query.internal.*; |
| import com.gemstone.gemfire.distributed.internal.InternalDistributedSystem; |
| import com.gemstone.gemfire.internal.i18n.LocalizedStrings; |
| import com.gemstone.gemfire.pdx.internal.PdxInstanceEnum; |
| |
| |
| /** |
| * Utilities for casting and comparing values of possibly differing types, |
| * testing and cloning query literals. |
| * |
| * @version $Revision: 1.1 $ |
| * @author ericz |
| */ |
| |
| public class TypeUtils implements OQLLexerTokenTypes |
| { |
| private static List _numericPrimitiveClasses = Arrays.asList(new Class[] |
| {Byte.TYPE, |
| Short.TYPE, |
| Integer.TYPE, |
| Long.TYPE, |
| Float.TYPE, |
| Double.TYPE}); |
| |
| private static List _numericWrapperClasses = Arrays.asList(new Class[] |
| {Byte.class, |
| Short.class, |
| Integer.class, |
| Long.class, |
| Float.class, |
| Double.class}); |
| |
| /* Common Types */ |
| /** ObjectType for Object.class */ |
| public static final ObjectType OBJECT_TYPE = new ObjectTypeImpl(Object.class); |
| |
| |
| /** prevent instantiation */ |
| private TypeUtils() {} |
| |
| |
| /** Verify that type-cast will work, or else throw an informative |
| * exception. |
| * @return the castTarget |
| * @throws InternalGemFireError if cast will fail |
| */ |
| public static Object checkCast(Object castTarget, Class castClass) { |
| if (castTarget == null) return null; // null can be cast to anything |
| if (!castClass.isInstance(castTarget)) { |
| throw new InternalGemFireError(LocalizedStrings.TypeUtils_EXPECTED_INSTANCE_OF_0_BUT_WAS_1.toLocalizedString(new Object[] {castClass.getName(), castTarget.getClass().getName()})); |
| } |
| return castTarget; |
| } |
| |
| |
| // implicit conversion of numeric types: |
| // floating point type to other floating point type, or |
| // any integral type to any other integral type if possible |
| // if an integral conversion cannot be done then a TypeMismatchException |
| // is thrown. |
| /* public static Object convert(Object obj, Class toType) |
| throws TypeMismatchException |
| { |
| // if obj is null, just leave it alone |
| if (obj == null) |
| return null; |
| |
| |
| if (toType.isInstance(obj)) |
| return obj; |
| |
| if (Float.class.isAssignableFrom(toType) && obj instanceof Double) |
| return new Float(((Double)obj).floatValue()); |
| |
| if (Double.class.isAssignableFrom(toType) && obj instanceof Float) |
| return Double.valueOf(((Float)obj).doubleValue()); |
| |
| if (Long.class.isAssignableFrom(toType) && |
| obj instanceof Byte || obj instanceof Short || obj instanceof Integer) |
| return Long.valueOf(((Number)obj).longValue()); |
| |
| if (Integer.class.isAssignableFrom(toType) && |
| obj instanceof Byte || obj instanceof Short || obj instanceof Long) |
| { |
| int newInt = ((Number)obj).intValue(); |
| if (!(obj instanceof Long) || (long)newInt == ((Long)obj).longValue()) |
| return Integer.valueOf(newInt); |
| } |
| |
| if (Short.class.isAssignableFrom(toType) && |
| obj instanceof Byte || obj instanceof Integer || obj instanceof Long) |
| { |
| short newShort = ((Number)obj).shortValue(); |
| if (obj instanceof Byte |
| || (obj instanceof Long && (long)newShort == ((Long)obj).longValue()) |
| || (obj instanceof Integer && (int)newShort == ((Integer)obj).intValue())) |
| return new Short(newShort); |
| } |
| |
| throw new TypeMismatchException(LocalizedStrings.TypeUtils_UNABLE_TO_CONVERT_0_TO_1.toLocalizedString(new Object[] {obj, toType})); |
| } |
| */ |
| /** |
| * Compares two objects using the operator |
| * |
| * @param obj1 |
| * @param obj2 |
| * @param compOp |
| * @return boolean;<br> |
| * {@link Undefined} if either of the operands is {@link Undefined} or |
| * if either of the operands is Null and operator is other than == or |
| * != |
| * @throws TypeMismatchException |
| */ |
| public static Object compare(Object obj1, Object obj2, int compOp) |
| throws TypeMismatchException { |
| if (obj1 == null || obj2 == null) { |
| Boolean result = nullCompare(obj1, obj2, compOp); |
| if (result == null) |
| return QueryService.UNDEFINED; |
| return result; |
| } |
| |
| // if either object is UNDEFINED, result is UNDEFINED |
| if (obj1 == QueryService.UNDEFINED || obj2 == QueryService.UNDEFINED) { |
| if (compOp == TOK_NE && !(obj1 == QueryService.UNDEFINED && obj2 == QueryService.UNDEFINED)) { |
| return true; |
| } |
| else if (compOp == TOK_EQ && obj1.equals(obj2)) { |
| return true; |
| } |
| else { |
| return QueryService.UNDEFINED; |
| } |
| } |
| |
| try { |
| int r; |
| |
| if (obj1 instanceof java.util.Date && obj2 instanceof java.util.Date) |
| r = getTemporalComparator().compare(obj1, obj2); |
| |
| else if (obj1.getClass() != obj2.getClass() |
| && (obj1 instanceof Number && obj2 instanceof Number)) { |
| /* @todo check for NaN, in which case we should not call compareTo |
| Must also handle this in the index lookup code to be consistent |
| See bug 37716 |
| NumericComparator cmprtr = getNumericComparator(); |
| boolean b; |
| if (obj1.equals(Float.NaN) || obj1.equals(Double.NaN)) { |
| return new Boolean(cmprtr.compareWithNaN(obj2)); |
| } |
| else if (obj2.equals(Float.NaN) || obj2.equals(Float.NaN)) { |
| return new Boolean(cmprtr.compareWithNaN(obj1)); |
| } |
| */ |
| r = getNumericComparator().compare(obj1, obj2); |
| } |
| |
| else if (obj1 instanceof Boolean || obj2 instanceof Boolean) |
| return Boolean.valueOf(booleanCompare(obj1, obj2, compOp)); |
| |
| |
| else if (obj1 instanceof Comparable && obj2 instanceof Comparable) |
| r = ((Comparable)obj1).compareTo(obj2); |
| // comparison of two arbitrary objects: use equals() |
| else if (compOp == TOK_EQ) |
| return Boolean.valueOf(obj1.equals(obj2)); |
| else if (compOp == TOK_NE) |
| return Boolean.valueOf(!obj1.equals(obj2)); |
| else |
| throw new TypeMismatchException(LocalizedStrings.TypeUtils_UNABLE_TO_USE_A_RELATIONAL_COMPARISON_OPERATOR_TO_COMPARE_AN_INSTANCE_OF_CLASS_0_WITH_AN_INSTANCE_OF_1.toLocalizedString(new Object[] {obj1.getClass().getName(), obj2.getClass().getName()})); |
| |
| |
| switch(compOp) { |
| case TOK_EQ: |
| return Boolean.valueOf(r == 0); |
| case TOK_LT: |
| return Boolean.valueOf(r < 0); |
| case TOK_LE: |
| return Boolean.valueOf(r <= 0); |
| case TOK_GT: |
| return Boolean.valueOf(r > 0); |
| case TOK_GE: |
| return Boolean.valueOf(r >= 0); |
| case TOK_NE: |
| return Boolean.valueOf(r != 0); |
| default: |
| throw new IllegalArgumentException(LocalizedStrings.TypeUtils_UNKNOWN_OPERATOR_0.toLocalizedString(Integer.valueOf(compOp))); |
| } |
| } catch (ClassCastException e) { |
| // if a ClassCastException was thrown and the operator is equals or not equals, |
| // then override and return true or false |
| if (compOp == TOK_EQ) |
| return Boolean.FALSE; |
| if (compOp == TOK_NE) |
| return Boolean.TRUE; |
| |
| throw new TypeMismatchException(LocalizedStrings.TypeUtils_UNABLE_TO_COMPARE_OBJECT_OF_TYPE_0_WITH_OBJECT_OF_TYPE_1.toLocalizedString(new Object[] {obj1.getClass().getName(), obj2.getClass().getName()}), e); |
| } catch (TypeMismatchException e) { |
| // same for TypeMismatchException |
| if (compOp == TOK_EQ) |
| return Boolean.FALSE; |
| if (compOp == TOK_NE) |
| return Boolean.TRUE; |
| throw e; |
| } |
| } |
| |
| public static Comparator getTemporalComparator() { |
| return new TemporalComparator(); |
| } |
| |
| public static Comparator getNumericComparator() { |
| return new NumericComparator(); |
| } |
| |
| public static Comparator getExtendedNumericComparator() { |
| return new ExtendedNumericComparator(); |
| } |
| |
| |
| public static Object indexKeyFor(Object obj) |
| throws TypeMismatchException { |
| if (obj == null) |
| return null; |
| if (obj instanceof Byte) |
| return Integer.valueOf(((Byte)obj).intValue()); |
| if (obj instanceof Short) |
| return Integer.valueOf(((Short)obj).intValue()); |
| // Ketan : Added later. Indexes should be created |
| // if the IndexedExpr implements Comparable interface. |
| if (obj instanceof Comparable) { |
| if (obj instanceof Enum) { |
| obj = new PdxInstanceEnum((Enum<?>)obj); |
| } |
| return obj; |
| } |
| throw new TypeMismatchException(LocalizedStrings.TypeUtils_INDEXES_ARE_NOT_SUPPORTED_FOR_TYPE_0.toLocalizedString(obj.getClass().getName())); |
| } |
| |
| // returns null if this type doesn't need a Comparator |
| public static Comparator comparatorFor(Class pathType) { |
| Iterator i = _numericWrapperClasses.iterator(); |
| while(i.hasNext()) |
| if (((Class)i.next()).isAssignableFrom(pathType)) |
| return getNumericComparator(); |
| i = _numericPrimitiveClasses.iterator(); |
| while(i.hasNext()) |
| if (((Class)i.next()).isAssignableFrom(pathType)) |
| return getNumericComparator(); |
| if (java.util.Date.class.isAssignableFrom(pathType)) |
| return getTemporalComparator(); |
| return null; |
| } |
| |
| |
| // indexes are allowed on primitive types except char, |
| // plus Strings, and temporals |
| /** |
| * Return the type of the keys for a given path type. |
| * @param pathType the Class of the last attribute in the path |
| * @return the Class of the index keys |
| * @throws TypeMismatchException if indexes are not allowed on this type |
| */ |
| |
| public static Class indexTypeFor(Class pathType) |
| throws TypeMismatchException { |
| if (Character.class.isAssignableFrom(pathType) || Character.TYPE == pathType) |
| return pathType; |
| |
| if (Byte.class.isAssignableFrom(pathType) || Short.class.isAssignableFrom(pathType) |
| || Byte.TYPE == pathType || Short.TYPE == pathType) |
| return Integer.class; |
| Iterator i = _numericWrapperClasses.iterator(); |
| while (i.hasNext()) { |
| Class cls = (Class)i.next(); |
| if (cls.isAssignableFrom(pathType)) |
| return pathType; |
| } |
| i = _numericPrimitiveClasses.iterator(); |
| while (i.hasNext()) { |
| Class cls = (Class)i.next(); |
| if (cls == pathType) |
| return pathType; |
| } |
| |
| if (java.util.Date.class.isAssignableFrom(pathType) || pathType == String.class) |
| return pathType; |
| throw new TypeMismatchException(LocalizedStrings.TypeUtils_INDEXES_ARE_NOT_SUPPORTED_ON_PATHS_OF_TYPE_0.toLocalizedString(pathType.getName())); |
| } |
| |
| |
| |
| public static boolean areTypesConvertible(Class[] srcTypes, Class[] destTypes) { |
| Support.assertArg(srcTypes.length == destTypes.length, |
| "Arguments 'srcTypes' and 'destTypes' must be of same length"); |
| |
| for (int i = 0; i < srcTypes.length; i++) |
| if (!isTypeConvertible(srcTypes[i], destTypes[i])) |
| return false; |
| return true; |
| } |
| |
| public static boolean isTypeConvertible(Class srcType, Class destType) { |
| // handle null: if srcType is null, then it represents |
| // a null runtime value, and for our purposes it is type |
| // convertible to any type that is assignable from Object.class |
| if (srcType == null) |
| return (Object.class.isAssignableFrom(destType)); |
| |
| // check to see if the classes are assignable |
| if (destType.isAssignableFrom(srcType)) |
| return true; |
| |
| // handle booleans: are we going from a wrapper Boolean |
| // to a primitive boolean or vice-versa? |
| if ((srcType == Boolean.TYPE || srcType == Boolean.class) |
| && |
| (destType == Boolean.TYPE || destType == Boolean.class)) |
| return true; |
| |
| |
| // a numeric primitive or wrapper can be converted to |
| // the same wrapper or a same or wider primitive. |
| // handle chars specially |
| int i = _numericPrimitiveClasses.indexOf(srcType); |
| if (i < 0) |
| i = _numericWrapperClasses.indexOf(srcType); |
| |
| int destP = _numericPrimitiveClasses.indexOf(destType); |
| int destW = -1; |
| if (destP < 0) |
| destW = _numericWrapperClasses.indexOf(destType); |
| |
| // same size wrapper |
| if (i >= 0 && destW == i) |
| return true; |
| |
| // same or wider primitive |
| if (i >= 0 && destP >= i) |
| return true; |
| |
| // chars |
| if (srcType == Character.class || srcType == Character.TYPE) { |
| // chars: same size wrapper/primitive |
| if (destType == Character.class || destType == Character.TYPE) |
| return true; |
| } |
| |
| // no other possibilities |
| return false; |
| } |
| |
| |
| private static boolean booleanCompare(Object obj1, Object obj2, int compOp) |
| throws TypeMismatchException { |
| if (!(obj1 instanceof Boolean) || !(obj2 instanceof Boolean)) |
| throw new TypeMismatchException(LocalizedStrings.TypeUtils_BOOLEANS_CAN_ONLY_BE_COMPARED_WITH_BOOLEANS.toLocalizedString()); |
| |
| if (compOp == TOK_EQ) |
| return obj1.equals(obj2); |
| else if (compOp == TOK_NE) |
| return !obj1.equals(obj2); |
| else |
| throw new TypeMismatchException(LocalizedStrings.TypeUtils_BOOLEAN_VALUES_CAN_ONLY_BE_COMPARED_WITH_OR.toLocalizedString()); |
| } |
| |
| |
| // returns a Boolean or null if UNDEFINED |
| private static Boolean nullCompare(Object obj1, Object obj2, int compOp) { |
| switch(compOp) { |
| case TOK_EQ: |
| if (obj1 == null) |
| return Boolean.valueOf(obj2 == null); |
| else // obj1 is not null obj2 must be |
| return Boolean.FALSE; |
| case TOK_NE: |
| if (obj1 == null) |
| return Boolean.valueOf(obj2 != null); |
| else // obj1 is not null so obj2 must be |
| return Boolean.TRUE; |
| default: |
| return null; |
| } |
| } |
| |
| // public static boolean isList(Class clazz) { |
| // if( clazz == java.util.ArrayList.class || |
| // clazz == java.util.LinkedList.class || |
| // clazz == java.util.Vector.class) |
| // return true; |
| // return false; |
| // } |
| |
| public static boolean isMap(ObjectType objType) { |
| return objType instanceof MapType; |
| } |
| |
| // public static boolean isSet(Class clazz){ |
| // if( clazz == java.util.HashSet.class || |
| // clazz == java.util.LinkedHashSet.class || |
| // clazz == java.util.TreeSet.class) |
| // return true; |
| // return false; |
| // } |
| |
| public static ObjectType getObjectType(Class cls) { |
| if (cls == Object.class) { |
| return OBJECT_TYPE; |
| } |
| if (Collection.class.isAssignableFrom(cls)) { |
| // we don't have element type info here |
| return new CollectionTypeImpl(cls, OBJECT_TYPE); |
| } |
| if (cls.isArray()) { |
| return new CollectionTypeImpl(cls, getObjectType(cls.getComponentType())); |
| } |
| if (Region.class.isAssignableFrom(cls)) { |
| // we don't have access to the region itself for element type |
| return new CollectionTypeImpl(cls, OBJECT_TYPE); |
| } |
| if (Map.class.isAssignableFrom(cls)) { |
| return new MapTypeImpl(cls, OBJECT_TYPE, OBJECT_TYPE); |
| } |
| // if it's a struct we have no field info, so just return an ObjectTypeImpl |
| return new ObjectTypeImpl(cls); |
| } |
| |
| public static ObjectType getRegionEntryType(Region rgn) { |
| // just use an ObjectType for now |
| return new ObjectTypeImpl(Region.Entry.class); |
| } |
| |
| } |
| |