You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by ja...@apache.org on 2018/02/07 21:53:38 UTC
[geode] branch develop updated: GEODE-1758: Refactor,
test and clean up TypeUtils (#1367)
This is an automated email from the ASF dual-hosted git repository.
jasonhuynh pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/geode.git
The following commit(s) were added to refs/heads/develop by this push:
new 952db5f GEODE-1758: Refactor, test and clean up TypeUtils (#1367)
952db5f is described below
commit 952db5f1c46a293fe3d5bf01894a5be50effce1d
Author: Juan José Ramos <ju...@users.noreply.github.com>
AuthorDate: Wed Feb 7 21:53:36 2018 +0000
GEODE-1758: Refactor, test and clean up TypeUtils (#1367)
* GEODE-4436: JUnit tests for TypeUtils class.
* GEODE-4437: Improvements for TypeUtils class.
---
.../cache/query/internal/types/TypeUtils.java | 662 ++++++-----
.../query/internal/types/ObjectTypeAssert.java | 67 ++
.../cache/query/internal/types/TypeUtilTest.java | 44 -
.../query/internal/types/TypeUtilsJUnitTest.java | 1197 ++++++++++++++++++++
.../apache/geode/codeAnalysis/excludedClasses.txt | 6 +
5 files changed, 1646 insertions(+), 330 deletions(-)
diff --git a/geode-core/src/main/java/org/apache/geode/cache/query/internal/types/TypeUtils.java b/geode-core/src/main/java/org/apache/geode/cache/query/internal/types/TypeUtils.java
index 87beda4..133d4b5 100644
--- a/geode-core/src/main/java/org/apache/geode/cache/query/internal/types/TypeUtils.java
+++ b/geode-core/src/main/java/org/apache/geode/cache/query/internal/types/TypeUtils.java
@@ -14,11 +14,10 @@
*/
package org.apache.geode.cache.query.internal.types;
-
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
-import java.util.Iterator;
+import java.util.Date;
import java.util.List;
import java.util.Map;
@@ -29,7 +28,6 @@ import org.apache.geode.cache.query.TypeMismatchException;
import org.apache.geode.cache.query.internal.Support;
import org.apache.geode.cache.query.internal.Undefined;
import org.apache.geode.cache.query.internal.parse.OQLLexerTokenTypes;
-import org.apache.geode.cache.query.types.MapType;
import org.apache.geode.cache.query.types.ObjectType;
import org.apache.geode.internal.i18n.LocalizedStrings;
import org.apache.geode.pdx.internal.EnumInfo.PdxInstanceEnumInfo;
@@ -45,121 +43,96 @@ import org.apache.geode.pdx.internal.PdxString;
*/
public class TypeUtils implements OQLLexerTokenTypes {
- private static List _numericPrimitiveClasses = Arrays.asList(
+ protected static List<Class> _numericPrimitiveClasses = Arrays.asList(
new Class[] {Byte.TYPE, Short.TYPE, Integer.TYPE, Long.TYPE, Float.TYPE, Double.TYPE});
- private static List _numericWrapperClasses = Arrays.asList(
+ protected static List<Class> _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
+ * Enum to execute comparisons based on types.
*/
- 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;
- }
+ private enum ComparisonStrategy {
+ TEMPORAL {
+ public Boolean execute(Object object1, Object object2, int comparator)
+ throws ClassCastException {
+ return applyComparator(getTemporalComparator().compare(object1, object2), comparator);
+ }
+ },
- // 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;
+ NUMERIC {
+ public Boolean execute(Object object1, Object object2, int comparator)
+ throws ClassCastException {
+ return applyComparator(getNumericComparator().compare(object1, object2), comparator);
}
- }
+ },
- if (obj1 instanceof PdxInstanceEnumInfo && obj2 instanceof Enum) {
- obj2 = new PdxInstanceEnum((Enum<?>) obj2);
- } else if (obj1 instanceof Enum && obj2 instanceof PdxInstanceEnumInfo) {
- obj1 = new PdxInstanceEnum((Enum<?>) obj1);
- }
+ BOOLEAN {
+ public Boolean execute(Object object1, Object object2, int comparator)
+ throws TypeMismatchException {
+ return booleanCompare(object1, object2, comparator);
+ }
+ },
- if (obj1 instanceof PdxString && obj2 instanceof String) {
- obj2 = new PdxString((String) obj2);
- } else if (obj1 instanceof String && obj2 instanceof PdxString) {
- obj1 = new PdxString((String) obj1);
+ COMPARABLE {
+ public Boolean execute(Object object1, Object object2, int comparator)
+ throws ClassCastException {
+ return applyComparator(((Comparable) object1).compareTo(object2), comparator);
+ }
+ },
+
+ ARBITRARY {
+ public Boolean execute(Object object1, Object object2, int comparator) {
+ if (comparator == TOK_EQ) {
+ return object1.equals(object2);
+ } else {
+ return !object1.equals(object2);
+ }
+ }
+ };
+
+ /**
+ *
+ * @param temporalResult
+ * @param comparator
+ * @return
+ * @throws IllegalArgumentException
+ */
+ protected Boolean applyComparator(int temporalResult, int comparator)
+ throws IllegalArgumentException {
+ switch (comparator) {
+ case TOK_EQ:
+ return temporalResult == 0;
+ case TOK_LT:
+ return temporalResult < 0;
+ case TOK_LE:
+ return temporalResult <= 0;
+ case TOK_GT:
+ return temporalResult > 0;
+ case TOK_GE:
+ return temporalResult >= 0;
+ case TOK_NE:
+ return temporalResult != 0;
+ default:
+ throw new IllegalArgumentException(LocalizedStrings.TypeUtils_UNKNOWN_OPERATOR_0
+ .toLocalizedString(Integer.valueOf(comparator)));
+ }
}
- 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)) {
+ /**
+ *
+ * @param object1Class
+ * @param object2Class
+ * @param comparator
+ * @return
+ */
+ public static ComparisonStrategy get(Class object1Class, Class object2Class, int comparator)
+ throws TypeMismatchException {
+ if (isAssignableFrom(object1Class, Date.class)
+ && isAssignableFrom(object1Class, Date.class)) {
+ return TEMPORAL;
+ } else if (object1Class != object2Class && (isAssignableFrom(object1Class, Number.class)
+ && isAssignableFrom(object2Class, Number.class))) {
/*
* @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 =
@@ -167,295 +140,412 @@ public class TypeUtils implements OQLLexerTokenTypes {
* { 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
+ return NUMERIC;
+ } else if (isAssignableFrom(object1Class, Boolean.class)
+ || isAssignableFrom(object2Class, Boolean.class)) {
+ return BOOLEAN;
+ } else if (isAssignableFrom(object1Class, Comparable.class)
+ && isAssignableFrom(object2Class, Comparable.class)) {
+ return COMPARABLE;
+ } else if ((comparator == TOK_EQ) || (comparator == TOK_NE)) {
+ return ARBITRARY;
+ } 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)));
+ .toLocalizedString(new Object[] {object1Class.getName(), object2Class.getName()}));
}
- } 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;
}
+
+ /**
+ * Executes the comparison strategy.
+ *
+ * @param object1
+ * @param object2
+ * @param comparator
+ * @return
+ * @throws TypeMismatchException
+ * @throws ClassCastException
+ */
+ public abstract Boolean execute(Object object1, Object object2, int comparator)
+ throws TypeMismatchException, ClassCastException;
}
+ /* Common Types */
+ /** ObjectType for Object.class */
+ public static final ObjectType OBJECT_TYPE = new ObjectTypeImpl(Object.class);
+
+ /** prevent instantiation */
+ private TypeUtils() {}
+
+ /**
+ * Creates an instance of {@link org.apache.geode.cache.query.internal.types.TemporalComparator}.
+ *
+ * @return Comparator for mixed comparisons between instances of java.util.Date, java.sql.Date,
+ * java.sql.Time, and java.sql.Timestamp.
+ * @see org.apache.geode.cache.query.internal.types.TemporalComparator
+ */
public static Comparator getTemporalComparator() {
return new TemporalComparator();
}
+ /**
+ * Creates an instance of {@link org.apache.geode.cache.query.internal.types.NumericComparator}.
+ *
+ * @return Comparator for mixed comparisons between numbers.
+ * @see org.apache.geode.cache.query.internal.types.NumericComparator
+ */
public static Comparator getNumericComparator() {
return new NumericComparator();
}
+ /**
+ * Creates an instance of
+ * {@link org.apache.geode.cache.query.internal.types.ExtendedNumericComparator}.
+ *
+ * @return A general comparator that compares different numeric types for equality.
+ * @see org.apache.geode.cache.query.internal.types.ExtendedNumericComparator
+ */
public static Comparator getExtendedNumericComparator() {
return new ExtendedNumericComparator();
}
+ /**
+ * 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;
+ }
+
+ /**
+ * Given an arbitrary object, return the value that should be used as the key for a particular
+ * index.
+ *
+ * @param obj the object to evaluate.
+ * @return the value that should be used as the key for an index.
+ * @throws TypeMismatchException if the object type is not supported to be used as an index.
+ */
public static Object indexKeyFor(Object obj) throws TypeMismatchException {
- if (obj == null)
+ if (obj == null) {
return null;
- if (obj instanceof Byte)
+ }
+
+ if (obj instanceof Byte) {
return Integer.valueOf(((Byte) obj).intValue());
- if (obj instanceof Short)
+ }
+
+ 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.
+ * Determines if the Class represented by the first parameter is either the same as, or is a
+ * superclass or superinterface of, the class or interface represented by the second parameter.
*
- * @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
+ * @param srcType the class to be checked.
+ * @param destType the class to be checked against.
+ * @return the {@code boolean} value indicating whether objects of the type {@code srcType} can be
+ * assigned to objects of type {@code destType} .
*/
-
- 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;
+ protected static boolean isAssignableFrom(Class srcType, Class destType) {
+ return destType.isAssignableFrom(srcType);
}
+ /**
+ * Determines if the Class represented by the first parameter can be safely converted to the Class
+ * represented by the second parameter.
+ *
+ * @param srcType the source class to be converted.
+ * @param destType the target class to be converted to.
+ * @return the {@code boolean} value indicating whether objects of the type {@code srcType} can be
+ * converted to objects of type {@code destType} .
+ */
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));
+ if (srcType == null) {
+ // TODO: This should always be true as Object is assignable from everything, is the root type
+ // of all Java classes. Object.class is always assignable from every other Java class.
+ return isAssignableFrom(destType, Object.class);
+ }
// check to see if the classes are assignable
- if (destType.isAssignableFrom(srcType))
+ if (isAssignableFrom(srcType, destType)) {
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))
+ && (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)
+ if (i < 0) {
i = _numericWrapperClasses.indexOf(srcType);
+ }
int destP = _numericPrimitiveClasses.indexOf(destType);
int destW = -1;
- if (destP < 0)
+ if (destP < 0) {
destW = _numericWrapperClasses.indexOf(destType);
+ }
// same size wrapper
- if (i >= 0 && destW == i)
+ if (i >= 0 && destW == i) {
return true;
+ }
// same or wider primitive
- if (i >= 0 && destP >= i)
+ 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)
+ if (destType == Character.class || destType == Character.TYPE) {
return true;
+ }
}
// no other possibilities
return false;
}
+ /**
+ * Determines if the all classes represented by the first parameter can be safely converted to the
+ * classes represented by the second parameter.
+ *
+ * @param srcTypes the source classes to be converted.
+ * @param destTypes the target classes to be converted to.
+ * @return the {@code boolean} value indicating whether all objects of the first array can be
+ * converted to objects of the second array.
+ */
+ public static boolean areTypesConvertible(Class[] srcTypes, Class[] destTypes) {
+ Support.assertArg(srcTypes.length == destTypes.length,
+ "Arguments 'srcTypes' and 'destTypes' must be of same length");
- 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;
+ for (int i = 0; i < srcTypes.length; i++) {
+ if (!isTypeConvertible(srcTypes[i], destTypes[i])) {
+ return false;
+ }
}
- }
- // 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;
+ return true;
}
- // 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;
- // }
-
+ /**
+ * Return the {@link org.apache.geode.cache.query.types.ObjectType} corresponding to the specified
+ * Class.
+ *
+ * @param cls
+ * @return the internal ObjectType implementation that represents the specified class.
+ */
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);
}
+ /**
+ * Return a fixed {@link org.apache.geode.cache.query.types.ObjectType} representing a Region
+ * Entry.
+ *
+ * @param rgn
+ * @return the internal ObjectType implementation that represents a the region entry class.
+ */
public static ObjectType getRegionEntryType(Region rgn) {
// just use an ObjectType for now
return new ObjectTypeImpl(Region.Entry.class);
}
+ /**
+ * Compare two booleans.
+ *
+ * @param obj1
+ * @param obj2
+ * @param compOp
+ * @return a boolean indicating the result of applying the comparison operator on the arguments.
+ * @throws TypeMismatchException When either one of the arguments is not a Boolean, or the
+ * comparison operator is not supported.
+ */
+ protected 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());
+ }
+ }
+
+ /**
+ *
+ * @param obj1
+ * @param obj2
+ * @param compOp
+ * @return a Boolean, or null if UNDEFINED.
+ */
+ private static Boolean nullCompare(Object obj1, Object obj2, int compOp) {
+ Boolean result = null;
+
+ switch (compOp) {
+ case TOK_EQ: {
+ if (obj1 == null) {
+ result = Boolean.valueOf(obj2 == null);
+ } else { // obj1 is not null obj2 must be
+ result = Boolean.FALSE;
+ }
+
+ break;
+ }
+
+ case TOK_NE: {
+ if (obj1 == null) {
+ result = Boolean.valueOf(obj2 != null);
+ } else { // obj1 is not null so obj2 must be
+ result = Boolean.TRUE;
+ }
+
+ break;
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * 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 {
+ // Check for nulls first.
+ 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;
+ }
+ }
+
+ // Prepare pdx instances if needed.
+ if (obj1 instanceof PdxInstanceEnumInfo && obj2 instanceof Enum) {
+ obj2 = new PdxInstanceEnum((Enum<?>) obj2);
+ } else if (obj1 instanceof Enum && obj2 instanceof PdxInstanceEnumInfo) {
+ obj1 = new PdxInstanceEnum((Enum<?>) obj1);
+ }
+
+ if (obj1 instanceof PdxString && obj2 instanceof String) {
+ obj2 = new PdxString((String) obj2);
+ } else if (obj1 instanceof String && obj2 instanceof PdxString) {
+ obj1 = new PdxString((String) obj1);
+ }
+
+ try {
+ ComparisonStrategy strategy =
+ ComparisonStrategy.get(obj1.getClass(), obj2.getClass(), compOp);
+ return strategy.execute(obj1, obj2, compOp);
+ } catch (ClassCastException | TypeMismatchException e) {
+ // if a ClassCastException or TypeMismatchException 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;
+ }
+
+ if (isAssignableFrom(e.getClass(), ClassCastException.class)) {
+ 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);
+ } else {
+ throw e;
+ }
+ }
+ }
}
diff --git a/geode-core/src/test/java/org/apache/geode/cache/query/internal/types/ObjectTypeAssert.java b/geode-core/src/test/java/org/apache/geode/cache/query/internal/types/ObjectTypeAssert.java
new file mode 100644
index 0000000..227bc51
--- /dev/null
+++ b/geode-core/src/test/java/org/apache/geode/cache/query/internal/types/ObjectTypeAssert.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You 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.apache.geode.cache.query.internal.types;
+
+import org.assertj.core.api.AbstractAssert;
+
+import org.apache.geode.cache.query.types.ObjectType;
+
+/**
+ * Custom Assertion to validate {@link ObjectType} instances.
+ */
+public class ObjectTypeAssert extends AbstractAssert<ObjectTypeAssert, ObjectType> {
+
+ public ObjectTypeAssert(ObjectType actual) {
+ super(actual, ObjectTypeAssert.class);
+ }
+
+ public static ObjectTypeAssert assertThat(ObjectType actual) {
+ return new ObjectTypeAssert(actual);
+ }
+
+ public ObjectTypeAssert resolves(Class clazz) {
+ isNotNull();
+ org.assertj.core.api.Assertions.assertThat(actual.resolveClass()).isEqualTo(clazz);
+
+ return this;
+ }
+
+ public ObjectTypeAssert isObject() {
+ isNotNull();
+ isExactlyInstanceOf(ObjectTypeImpl.class);
+
+ return this;
+ }
+
+ public ObjectTypeAssert isCollectionOf(Class componentType) {
+ isNotNull();
+ isExactlyInstanceOf(CollectionTypeImpl.class);
+ org.assertj.core.api.Assertions.assertThat(((CollectionTypeImpl) actual).getElementType())
+ .isExactlyInstanceOf(componentType);
+
+ return this;
+ }
+
+ public ObjectTypeAssert isMapOf(Class keyType, Class valueType) {
+ isNotNull();
+ isExactlyInstanceOf(MapTypeImpl.class);
+ org.assertj.core.api.Assertions.assertThat(((MapTypeImpl) actual).getKeyType())
+ .isExactlyInstanceOf(keyType);
+ org.assertj.core.api.Assertions.assertThat(((MapTypeImpl) actual).getElementType())
+ .isExactlyInstanceOf(valueType);
+
+ return this;
+ }
+}
diff --git a/geode-core/src/test/java/org/apache/geode/cache/query/internal/types/TypeUtilTest.java b/geode-core/src/test/java/org/apache/geode/cache/query/internal/types/TypeUtilTest.java
deleted file mode 100644
index 7dcdf74..0000000
--- a/geode-core/src/test/java/org/apache/geode/cache/query/internal/types/TypeUtilTest.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
- * agreements. See the NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The ASF licenses this file to You 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.apache.geode.cache.query.internal.types;
-
-import static org.junit.Assert.*;
-
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-
-import org.apache.geode.cache.query.internal.parse.OQLLexerTokenTypes;
-import org.apache.geode.pdx.internal.PdxString;
-import org.apache.geode.test.junit.categories.UnitTest;
-
-@Category(UnitTest.class)
-public class TypeUtilTest {
-
- @Test
- public void comparingEquivalentPdxStringToStringShouldMatchCorrectly() throws Exception {
- String theString = "MyString";
- PdxString pdxString = new PdxString(theString);
- assertTrue((Boolean) TypeUtils.compare(pdxString, theString, OQLLexerTokenTypes.TOK_EQ));
- assertTrue((Boolean) TypeUtils.compare(theString, pdxString, OQLLexerTokenTypes.TOK_EQ));
- }
-
- @Test
- public void comparingUnequivalentPdxStringToStringShouldNotMatch() throws Exception {
- String theString = "MyString";
- PdxString pdxString = new PdxString("AnotherString");
- assertFalse((Boolean) TypeUtils.compare(pdxString, theString, OQLLexerTokenTypes.TOK_EQ));
- assertFalse((Boolean) TypeUtils.compare(theString, pdxString, OQLLexerTokenTypes.TOK_EQ));
- }
-}
diff --git a/geode-core/src/test/java/org/apache/geode/cache/query/internal/types/TypeUtilsJUnitTest.java b/geode-core/src/test/java/org/apache/geode/cache/query/internal/types/TypeUtilsJUnitTest.java
new file mode 100644
index 0000000..eaa27df
--- /dev/null
+++ b/geode-core/src/test/java/org/apache/geode/cache/query/internal/types/TypeUtilsJUnitTest.java
@@ -0,0 +1,1197 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You 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.apache.geode.cache.query.internal.types;
+
+import static org.apache.geode.cache.query.internal.parse.OQLLexerTokenTypes.*;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.io.Serializable;
+import java.lang.reflect.Field;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.PriorityQueue;
+import java.util.Random;
+import java.util.Vector;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.stream.Collectors;
+
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.powermock.api.mockito.PowerMockito;
+import org.powermock.core.classloader.annotations.PowerMockIgnore;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+import org.apache.geode.InternalGemFireError;
+import org.apache.geode.cache.Region;
+import org.apache.geode.cache.query.QueryService;
+import org.apache.geode.cache.query.TypeMismatchException;
+import org.apache.geode.cache.query.internal.CompiledValue;
+import org.apache.geode.cache.query.internal.parse.OQLLexerTokenTypes;
+import org.apache.geode.internal.cache.AbstractRegion;
+import org.apache.geode.internal.cache.BucketRegion;
+import org.apache.geode.internal.cache.LocalRegion;
+import org.apache.geode.internal.cache.PartitionedRegion;
+import org.apache.geode.pdx.internal.PdxInstanceEnum;
+import org.apache.geode.pdx.internal.PdxString;
+import org.apache.geode.test.junit.categories.UnitTest;
+
+@Category(UnitTest.class)
+@PowerMockIgnore("*.UnitTest")
+@RunWith(PowerMockRunner.class)
+@PrepareForTest(TypeUtils.class)
+public class TypeUtilsJUnitTest {
+ private List<Integer> equalityOperators =
+ Arrays.stream(new int[] {TOK_EQ, TOK_NE}).boxed().collect(Collectors.toList());
+ private List<Integer> comparisonOperators =
+ Arrays.stream(new int[] {TOK_EQ, TOK_LT, TOK_LE, TOK_GT, TOK_GE, TOK_NE}).boxed()
+ .collect(Collectors.toList());
+
+ @Test
+ public void getTemporalComparatorShouldAlwaysReturnAnInstanceOfTemporalComparator() {
+ Comparator comparator = TypeUtils.getTemporalComparator();
+ assertThat(comparator).isNotNull();
+ assertThat(comparator).isExactlyInstanceOf(TemporalComparator.class);
+ }
+
+ @Test
+ public void getNumericComparatorShouldAlwaysReturnAnInstanceOfNumericComparator() {
+ Comparator comparator = TypeUtils.getNumericComparator();
+ assertThat(comparator).isNotNull();
+ assertThat(comparator).isExactlyInstanceOf(NumericComparator.class);
+ }
+
+ @Test
+ public void getExtendedNumericComparatorShouldAlwaysReturnAnInstanceOfExtendedNumericComparator() {
+ Comparator comparator = TypeUtils.getExtendedNumericComparator();
+ assertThat(comparator).isNotNull();
+ assertThat(comparator).isExactlyInstanceOf(ExtendedNumericComparator.class);
+ }
+
+ @Test
+ public void checkCastShouldReturnNullWhenTargetObjectIsNull() {
+ Object result = TypeUtils.checkCast(null, CompiledValue.class);
+ assertThat(result).isNull();
+ }
+
+ @Test
+ public void checkCastShouldThrowExceptionWhenTargetObjectCanNotBeTypeCasted() {
+ assertThatThrownBy(() -> TypeUtils.checkCast(new String("SomeCharacters"), Integer.class))
+ .isInstanceOf(InternalGemFireError.class)
+ .hasMessageMatching("^expected instance of (.*) but was (.*)$");
+ }
+
+ @Test
+ /**
+ * Can't test every possible combination, so try a few ones.
+ */
+ public void checkCastShouldReturnCorrectlyWhenTargetObjectIsNotNullAndCanBeTypeCasted() {
+ Object stringCastTarget = new String("SomeCharacters");
+ Object stringCastResult = TypeUtils.checkCast(stringCastTarget, String.class);
+ assertThat(stringCastResult).isNotNull();
+ assertThat(stringCastResult).isInstanceOf(String.class);
+ assertThat(stringCastResult).isSameAs(stringCastTarget);
+
+ Object integerCastTarget = new Integer(20);
+ Object integerCastResult = TypeUtils.checkCast(integerCastTarget, Integer.class);
+ assertThat(integerCastResult).isNotNull();
+ assertThat(integerCastResult).isInstanceOf(Integer.class);
+ assertThat(integerCastResult).isSameAs(integerCastResult);
+
+ Object numberCastResult = TypeUtils.checkCast(integerCastTarget, Number.class);
+ assertThat(numberCastResult).isNotNull();
+ assertThat(numberCastResult).isInstanceOf(Integer.class);
+ assertThat(numberCastResult).isSameAs(numberCastResult);
+ }
+
+ @Test
+ public void indexKeyForShouldReturnNullWhenTheKeyIsNull() throws TypeMismatchException {
+ Object key = TypeUtils.indexKeyFor(null);
+ assertThat(key).isNull();
+ }
+
+ @Test
+ public void indexKeyForShouldThrowExceptionWhenTheKeyTypeCanNotBeUsedAsIndex() {
+ assertThatThrownBy(() -> TypeUtils.indexKeyFor(new AtomicInteger(0)))
+ .isInstanceOf(TypeMismatchException.class)
+ .hasMessageMatching("^Indexes are not supported for type ' (.*) '$");
+ }
+
+ @Test
+ public void indexKeyForShouldReturnIntegerWhenObjectIsInstanceOfByte()
+ throws TypeMismatchException {
+ Object keyByte = new Byte("5");
+ Object keyByteResult = TypeUtils.indexKeyFor(keyByte);
+ assertThat(keyByteResult).isNotNull();
+ assertThat(keyByteResult).isInstanceOf(Integer.class);
+ assertThat(keyByteResult).isEqualTo(new Integer("5"));
+ }
+
+ @Test
+ public void indexKeyForShouldReturnIntegerWhenObjectIsInstanceOfShort()
+ throws TypeMismatchException {
+ Object keyShort = new Short("10");
+ Object keyShortResult = TypeUtils.indexKeyFor(keyShort);
+ assertThat(keyShortResult).isNotNull();
+ assertThat(keyShortResult).isInstanceOf(Integer.class);
+ assertThat(keyShortResult).isEqualTo(new Integer("10"));
+ }
+
+ @Test
+ public void indexKeyForShouldReturnPdxInstanceEnumWhenObjectIsInstanceOfEnum()
+ throws TypeMismatchException {
+ Object keyEnum = TimeUnit.SECONDS;
+ Object keyEnumResult = TypeUtils.indexKeyFor(keyEnum);
+ assertThat(keyEnumResult).isNotNull();
+ assertThat(keyEnumResult).isInstanceOf(PdxInstanceEnum.class);
+ assertThat(((PdxInstanceEnum) keyEnumResult).getName()).isEqualTo(TimeUnit.SECONDS.name());
+ assertThat(((PdxInstanceEnum) keyEnumResult).getOrdinal())
+ .isEqualTo(TimeUnit.SECONDS.ordinal());
+ assertThat(((PdxInstanceEnum) keyEnumResult).getClassName())
+ .isEqualTo(TimeUnit.SECONDS.getDeclaringClass().getName());
+ }
+
+ @Test
+ public void indexKeyForShouldReturnIdentityWhenObjectIsInstanceOfComparable()
+ throws TypeMismatchException {
+ Object keyComparable = new String("myKey");
+ Object keyComparableResult = TypeUtils.indexKeyFor(keyComparable);
+ assertThat(keyComparableResult).isNotNull();
+ assertThat(keyComparableResult).isSameAs(keyComparable);
+
+ Object customComparableKey = new Comparable() {
+ @Override
+ public int compareTo(Object o) {
+ return 0;
+ }
+ };
+
+ Object customComparableKeyResult = TypeUtils.indexKeyFor(customComparableKey);
+ assertThat(customComparableKeyResult).isNotNull();
+ assertThat(customComparableKeyResult).isSameAs(customComparableKey);
+ }
+
+ /**
+ * Can't test every possible combination so try the known, relevant ones.
+ */
+ @Test
+ public void isAssignableFromShouldWorkProperlyForKnownTypes() {
+ // Booleans
+ assertThat(TypeUtils.isAssignableFrom(new Boolean(true).getClass(), Comparable.class)).isTrue();
+ assertThat(TypeUtils.isAssignableFrom(new Boolean(false).getClass(), Comparable.class))
+ .isTrue();
+ assertThat(TypeUtils.isAssignableFrom(new AtomicBoolean(false).getClass(), Comparable.class))
+ .isFalse();
+
+ // Dates supported by the TemporalComparator
+ long currentTime = Calendar.getInstance().getTimeInMillis();
+ assertThat(TypeUtils.isAssignableFrom(new Date(currentTime).getClass(), Date.class)).isTrue();
+ assertThat(TypeUtils.isAssignableFrom(new Long(currentTime).getClass(), Date.class)).isFalse();
+ assertThat(TypeUtils.isAssignableFrom(new java.sql.Date(currentTime).getClass(), Date.class))
+ .isTrue();
+ assertThat(TypeUtils.isAssignableFrom(new java.sql.Time(currentTime).getClass(), Date.class))
+ .isTrue();
+ assertThat(
+ TypeUtils.isAssignableFrom(new java.sql.Timestamp(currentTime).getClass(), Date.class))
+ .isTrue();
+
+ // Numbers supported by the NumericComparator
+ Random random = new Random();
+ assertThat(TypeUtils.isAssignableFrom(new Short("0").getClass(), Number.class)).isTrue();
+ assertThat(TypeUtils.isAssignableFrom(new Long(random.nextLong()).getClass(), Number.class))
+ .isTrue();
+ assertThat(TypeUtils.isAssignableFrom(new Float(random.nextLong()).getClass(), Number.class))
+ .isTrue();
+ assertThat(TypeUtils.isAssignableFrom(new Double(random.nextLong()).getClass(), Number.class))
+ .isTrue();
+ assertThat(
+ TypeUtils.isAssignableFrom(new BigDecimal(random.nextLong()).getClass(), Number.class))
+ .isTrue();
+ assertThat(TypeUtils.isAssignableFrom(new Integer(random.nextInt()).getClass(), Number.class))
+ .isTrue();
+ assertThat(TypeUtils.isAssignableFrom(new BigInteger("00").getClass(), Number.class)).isTrue();
+ assertThat(
+ TypeUtils.isAssignableFrom(new AtomicInteger(random.nextInt()).getClass(), Number.class))
+ .isTrue();
+
+ // Comparable Interface
+ assertThat(TypeUtils.isAssignableFrom(new PdxString("").getClass(), Comparable.class)).isTrue();
+ assertThat(TypeUtils.isAssignableFrom(new Long(random.nextLong()).getClass(), Comparable.class))
+ .isTrue();
+ assertThat(
+ TypeUtils.isAssignableFrom(new Integer(random.nextInt()).getClass(), Comparable.class))
+ .isTrue();
+ }
+
+ @Test
+ public void isTypeConvertibleShouldDelegateToIsAssignableFromMethodForNonWrappedTypesAndNullSourceType() {
+ PowerMockito.spy(TypeUtils.class);
+
+ // Special classes (Enum, Object, Class, Interface) and srcType as null.
+ assertThat(TypeUtils.isTypeConvertible(null, Enum.class)).isTrue();
+ PowerMockito.verifyStatic(TypeUtils.class, times(1));
+ TypeUtils.isAssignableFrom(Enum.class, Object.class);
+
+ assertThat(TypeUtils.isTypeConvertible(null, Object.class)).isTrue();
+ PowerMockito.verifyStatic(TypeUtils.class, times(1));
+ TypeUtils.isAssignableFrom(Object.class, Object.class);
+
+ assertThat(TypeUtils.isTypeConvertible(null, Class.class)).isTrue();
+ PowerMockito.verifyStatic(TypeUtils.class, times(1));
+ TypeUtils.isAssignableFrom(Class.class, Object.class);
+
+ assertThat(TypeUtils.isTypeConvertible(null, TimeUnit.class)).isTrue();
+ PowerMockito.verifyStatic(TypeUtils.class, times(1));
+ TypeUtils.isAssignableFrom(TimeUnit.class, Object.class);
+
+ assertThat(TypeUtils.isTypeConvertible(null, Serializable.class)).isTrue();
+ PowerMockito.verifyStatic(TypeUtils.class, times(1));
+ TypeUtils.isAssignableFrom(Serializable.class, Object.class);
+
+ // Regular, non java wrapped classes.
+ assertThat(TypeUtils.isTypeConvertible(AtomicInteger.class, Number.class)).isTrue();
+ PowerMockito.verifyStatic(TypeUtils.class, times(1));
+ TypeUtils.isAssignableFrom(AtomicInteger.class, Number.class);
+
+ assertThat(TypeUtils.isTypeConvertible(NumericComparator.class, Comparator.class)).isTrue();
+ PowerMockito.verifyStatic(TypeUtils.class, times(1));
+ TypeUtils.isAssignableFrom(NumericComparator.class, Comparator.class);
+
+ assertThat(TypeUtils.isTypeConvertible(PartitionedRegion.class, LocalRegion.class)).isTrue();
+ PowerMockito.verifyStatic(TypeUtils.class, times(1));
+ TypeUtils.isAssignableFrom(PartitionedRegion.class, LocalRegion.class);
+ }
+
+ @Test
+ public void isTypeConvertibleShouldReturnTrueForBooleanPrimitivesAndWrappers() {
+ assertThat(TypeUtils.isTypeConvertible(null, Boolean.class)).isTrue();
+ assertThat(TypeUtils.isTypeConvertible(Boolean.TYPE, Boolean.TYPE)).isTrue();
+ assertThat(TypeUtils.isTypeConvertible(Boolean.TYPE, Boolean.class)).isTrue();
+ assertThat(TypeUtils.isTypeConvertible(Boolean.class, Boolean.TYPE)).isTrue();
+ assertThat(TypeUtils.isTypeConvertible(Boolean.class, Boolean.class)).isTrue();
+ }
+
+ @Test
+ public void isTypeConvertibleShouldReturnTrueForNumericPrimitivesAndWrappers() {
+ for (int i = 0; i < TypeUtils._numericPrimitiveClasses.size(); i++) {
+ Class sourceType = TypeUtils._numericPrimitiveClasses.get(i);
+
+ TypeUtils._numericPrimitiveClasses.stream().skip(i).forEachOrdered(
+ destType -> assertThat(TypeUtils.isTypeConvertible(sourceType, destType)).isTrue());
+ TypeUtils._numericWrapperClasses.stream().skip(i).limit(1).forEachOrdered(
+ destType -> assertThat(TypeUtils.isTypeConvertible(sourceType, destType)).isTrue());
+ }
+
+ for (int i = 0; i < TypeUtils._numericWrapperClasses.size(); i++) {
+ Class sourceType = TypeUtils._numericWrapperClasses.get(i);
+
+ TypeUtils._numericPrimitiveClasses.stream().skip(i).forEachOrdered(
+ destType -> assertThat(TypeUtils.isTypeConvertible(sourceType, destType)).isTrue());
+ TypeUtils._numericWrapperClasses.stream().skip(i).limit(1).forEachOrdered(
+ destType -> assertThat(TypeUtils.isTypeConvertible(sourceType, destType)).isTrue());
+ }
+ }
+
+ @Test
+ public void isTypeConvertibleShouldReturnTrueForCharacterPrimitivesAndWrappers() {
+ assertThat(TypeUtils.isTypeConvertible(null, Character.class)).isTrue();
+ assertThat(TypeUtils.isTypeConvertible(Character.TYPE, Character.TYPE)).isTrue();
+ assertThat(TypeUtils.isTypeConvertible(Character.TYPE, Character.class)).isTrue();
+ assertThat(TypeUtils.isTypeConvertible(Character.class, Character.TYPE)).isTrue();
+ assertThat(TypeUtils.isTypeConvertible(Character.class, Character.class)).isTrue();
+ }
+
+ @Test
+ public void areTypesConvertibleShouldThrowExceptionWhenTheCollectionSizesAreDifferent() {
+ Class[] srcTypes = new Class[] {Byte.class};
+ Class[] destTypes = new Class[] {Integer.class, Long.class};
+ assertThatThrownBy(() -> TypeUtils.areTypesConvertible(srcTypes, destTypes))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("Arguments 'srcTypes' and 'destTypes' must be of same length");
+ }
+
+ @Test
+ public void areTypesConvertibleShouldReturnTrueIfAllTypesWithinTheCollectionsAreConvertible() {
+ Class[] srcTypes =
+ new Class[] {null, Byte.TYPE, Character.TYPE, Boolean.TYPE, NumericComparator.class};
+ Class[] destTypes =
+ new Class[] {Object.class, Integer.TYPE, Character.class, Boolean.class, Comparator.class};
+
+ assertThat(TypeUtils.areTypesConvertible(srcTypes, destTypes)).isTrue();
+ }
+
+ @Test
+ public void areTypesConvertibleShouldReturnFalseIfAtLeastOneTypeWithinTheCollectionsIsNotConvertible() {
+ Class[] srcTypes =
+ new Class[] {null, Byte.TYPE, Character.TYPE, Boolean.TYPE, NumericComparator.class};
+ Class[] destTypes =
+ new Class[] {Object.class, Integer.TYPE, Character.class, Object.class, Comparator.class};
+
+ assertThat(TypeUtils.areTypesConvertible(srcTypes, destTypes)).isFalse();
+ }
+
+ @Test
+ public void getObjectTypeShouldReturnTheProperTypeImplementation() {
+ // Object
+ ObjectTypeAssert.assertThat(TypeUtils.getObjectType(Object.class)).resolves(Object.class)
+ .isObject();
+
+ // Collections
+ ObjectTypeAssert.assertThat(TypeUtils.getObjectType(Vector.class)).resolves(Vector.class)
+ .isCollectionOf(ObjectTypeImpl.class);
+ ObjectTypeAssert.assertThat(TypeUtils.getObjectType(HashSet.class)).resolves(HashSet.class)
+ .isCollectionOf(ObjectTypeImpl.class);
+ ObjectTypeAssert.assertThat(TypeUtils.getObjectType(ArrayList.class)).resolves(ArrayList.class)
+ .isCollectionOf(ObjectTypeImpl.class);
+ ObjectTypeAssert.assertThat(TypeUtils.getObjectType(PriorityQueue.class))
+ .resolves(PriorityQueue.class).isCollectionOf(ObjectTypeImpl.class);
+ ObjectTypeAssert.assertThat(TypeUtils.getObjectType(LinkedBlockingQueue.class))
+ .resolves(LinkedBlockingQueue.class).isCollectionOf(ObjectTypeImpl.class);
+
+ // Typed Collections
+ Integer[] integers = new Integer[] {};
+ ObjectTypeAssert.assertThat(TypeUtils.getObjectType(integers.getClass()))
+ .resolves(Integer[].class).isCollectionOf(ObjectTypeImpl.class);
+
+ Vector[] vectors = new Vector[] {};
+ ObjectTypeAssert.assertThat(TypeUtils.getObjectType(vectors.getClass()))
+ .resolves(Vector[].class).isCollectionOf(CollectionTypeImpl.class);
+
+ // Regions
+ ObjectTypeAssert.assertThat(TypeUtils.getObjectType(Region.class)).resolves(Region.class)
+ .isCollectionOf(ObjectTypeImpl.class);
+ ObjectTypeAssert.assertThat(TypeUtils.getObjectType(BucketRegion.class))
+ .resolves(BucketRegion.class).isCollectionOf(ObjectTypeImpl.class);
+ ObjectTypeAssert.assertThat(TypeUtils.getObjectType(AbstractRegion.class))
+ .resolves(AbstractRegion.class).isCollectionOf(ObjectTypeImpl.class);
+ ObjectTypeAssert.assertThat(TypeUtils.getObjectType(PartitionedRegion.class))
+ .resolves(PartitionedRegion.class).isCollectionOf(ObjectTypeImpl.class);
+
+ // Regular Maps
+ ObjectTypeAssert.assertThat(TypeUtils.getObjectType(Map.class)).resolves(Map.class)
+ .isMapOf(ObjectTypeImpl.class, ObjectTypeImpl.class);
+ ObjectTypeAssert.assertThat(TypeUtils.getObjectType(HashMap.class)).resolves(HashMap.class)
+ .isMapOf(ObjectTypeImpl.class, ObjectTypeImpl.class);
+ ObjectTypeAssert.assertThat(TypeUtils.getObjectType(Hashtable.class)).resolves(Hashtable.class)
+ .isMapOf(ObjectTypeImpl.class, ObjectTypeImpl.class);
+ ObjectTypeAssert.assertThat(TypeUtils.getObjectType(ConcurrentHashMap.class))
+ .resolves(ConcurrentHashMap.class).isMapOf(ObjectTypeImpl.class, ObjectTypeImpl.class);
+
+ // Other Classes
+ ObjectTypeAssert.assertThat(TypeUtils.getObjectType(String.class)).resolves(String.class)
+ .isObject();
+ ObjectTypeAssert.assertThat(TypeUtils.getObjectType(Integer.class)).resolves(Integer.class)
+ .isObject();
+ ObjectTypeAssert.assertThat(TypeUtils.getObjectType(BigDecimal.class))
+ .resolves(BigDecimal.class).isObject();
+ }
+
+ @Test
+ public void getRegionEntryTypeShouldReturnTheProperTypeImplementation() {
+ ObjectTypeAssert.assertThat(TypeUtils.getRegionEntryType(mock(Region.class)))
+ .resolves(Region.Entry.class).isObject();
+ ObjectTypeAssert.assertThat(TypeUtils.getRegionEntryType(mock(BucketRegion.class)))
+ .resolves(Region.Entry.class).isObject();
+ ObjectTypeAssert.assertThat(TypeUtils.getRegionEntryType(mock(AbstractRegion.class)))
+ .resolves(Region.Entry.class).isObject();
+ ObjectTypeAssert.assertThat(TypeUtils.getRegionEntryType(mock(PartitionedRegion.class)))
+ .resolves(Region.Entry.class).isObject();
+ }
+
+ @Test
+ public void booleanCompareShouldThrowExceptionIfValuesAreNotInstancesOfBoolean() {
+ assertThatThrownBy(() -> TypeUtils.booleanCompare(true, new Object(), anyInt()))
+ .isInstanceOf(TypeMismatchException.class)
+ .hasMessageMatching("^Booleans can only be compared with booleans$");
+ assertThatThrownBy(() -> TypeUtils.booleanCompare(new Object(), false, anyInt()))
+ .isInstanceOf(TypeMismatchException.class)
+ .hasMessageMatching("^Booleans can only be compared with booleans$");
+ assertThatThrownBy(() -> TypeUtils.booleanCompare(new Object(), new Object(), anyInt()))
+ .isInstanceOf(TypeMismatchException.class)
+ .hasMessageMatching("^Booleans can only be compared with booleans$");
+ }
+
+ @Test
+ public void booleanCompareShouldThrowExceptionForNonEqualityComparisonOperators() {
+ OQLLexerTokenTypes tempInstance = new OQLLexerTokenTypes() {};
+ Field[] fields = OQLLexerTokenTypes.class.getDeclaredFields();
+
+ Arrays.stream(fields).forEach(field -> {
+ try {
+ int token = field.getInt(tempInstance);
+ if (!equalityOperators.contains(token)) {
+ assertThatThrownBy(() -> TypeUtils.booleanCompare(true, false, token))
+ .isInstanceOf(TypeMismatchException.class)
+ .hasMessageMatching("^Boolean values can only be compared with = or <>$");
+ }
+ } catch (IllegalAccessException exception) {
+ throw new RuntimeException(exception);
+ }
+ });
+ }
+
+ @Test
+ public void booleanCompareShouldReturnCorrectlyForEqualityComparisonOperators()
+ throws TypeMismatchException {
+ assertThat(TypeUtils.booleanCompare(true, true, TOK_EQ)).isTrue();
+ assertThat(TypeUtils.booleanCompare(false, false, TOK_EQ)).isTrue();
+ assertThat(TypeUtils.booleanCompare(true, false, TOK_EQ)).isFalse();
+ assertThat(TypeUtils.booleanCompare(false, true, TOK_EQ)).isFalse();
+
+ assertThat(TypeUtils.booleanCompare(Boolean.TRUE, Boolean.TRUE, TOK_EQ)).isTrue();
+ assertThat(TypeUtils.booleanCompare(Boolean.FALSE, Boolean.FALSE, TOK_EQ)).isTrue();
+ assertThat(TypeUtils.booleanCompare(Boolean.TRUE, Boolean.FALSE, TOK_EQ)).isFalse();
+ assertThat(TypeUtils.booleanCompare(Boolean.FALSE, Boolean.TRUE, TOK_EQ)).isFalse();
+ }
+
+ @Test
+ public void comparingNullValuesShouldReturnBooleanOrUndefined() throws TypeMismatchException {
+ assertThat(TypeUtils.compare(null, null, OQLLexerTokenTypes.TOK_EQ)).isNotNull()
+ .isEqualTo(Boolean.TRUE);
+ assertThat(TypeUtils.compare(null, new Object(), OQLLexerTokenTypes.TOK_EQ)).isNotNull()
+ .isEqualTo(Boolean.FALSE);
+ assertThat(TypeUtils.compare(new Object(), null, OQLLexerTokenTypes.TOK_EQ)).isNotNull()
+ .isEqualTo(Boolean.FALSE);
+
+ assertThat(TypeUtils.compare(null, null, OQLLexerTokenTypes.TOK_NE)).isNotNull()
+ .isEqualTo(Boolean.FALSE);
+ assertThat(TypeUtils.compare(null, new Object(), OQLLexerTokenTypes.TOK_NE)).isNotNull()
+ .isEqualTo(Boolean.TRUE);
+ assertThat(TypeUtils.compare(new Object(), null, OQLLexerTokenTypes.TOK_NE)).isNotNull()
+ .isEqualTo(Boolean.TRUE);
+
+ OQLLexerTokenTypes tempInstance = new OQLLexerTokenTypes() {};
+ Field[] fields = OQLLexerTokenTypes.class.getDeclaredFields();
+
+ Arrays.stream(fields).forEach(field -> {
+ try {
+ int token = field.getInt(tempInstance);
+ if (!equalityOperators.contains(token)) {
+ assertThat(TypeUtils.compare(new Object(), null, token))
+ .isEqualTo(QueryService.UNDEFINED);
+ }
+ } catch (IllegalAccessException | TypeMismatchException exception) {
+ throw new RuntimeException(exception);
+ }
+ });
+ }
+
+ @Test
+ public void comparingUndefinedValuesShouldReturnBooleanOrUndefined()
+ throws TypeMismatchException {
+ assertThat(TypeUtils.compare(QueryService.UNDEFINED, new Object(), OQLLexerTokenTypes.TOK_NE))
+ .isNotNull().isEqualTo(Boolean.TRUE);
+ assertThat(TypeUtils.compare(new Object(), QueryService.UNDEFINED, OQLLexerTokenTypes.TOK_NE))
+ .isNotNull().isEqualTo(Boolean.TRUE);
+ assertThat(TypeUtils.compare(QueryService.UNDEFINED, QueryService.UNDEFINED,
+ OQLLexerTokenTypes.TOK_EQ)).isNotNull().isEqualTo(Boolean.TRUE);
+
+ assertThat(TypeUtils.compare(QueryService.UNDEFINED, new Object(), OQLLexerTokenTypes.TOK_EQ))
+ .isNotNull().isEqualTo(QueryService.UNDEFINED);
+ assertThat(TypeUtils.compare(new Object(), QueryService.UNDEFINED, OQLLexerTokenTypes.TOK_EQ))
+ .isNotNull().isEqualTo(QueryService.UNDEFINED);
+ assertThat(TypeUtils.compare(QueryService.UNDEFINED, QueryService.UNDEFINED,
+ OQLLexerTokenTypes.TOK_NE)).isNotNull().isEqualTo(QueryService.UNDEFINED);
+ }
+
+ @Test
+ public void comparingEquivalentPdxStringToStringShouldMatchCorrectly() throws Exception {
+ String theString = "MyString";
+ PdxString pdxString = new PdxString(theString);
+
+ assertThat(TypeUtils.compare(pdxString, theString, OQLLexerTokenTypes.TOK_EQ))
+ .isInstanceOf(Boolean.class);
+ assertThat((Boolean) TypeUtils.compare(pdxString, theString, OQLLexerTokenTypes.TOK_EQ))
+ .isTrue();
+
+ assertThat(TypeUtils.compare(theString, pdxString, OQLLexerTokenTypes.TOK_EQ))
+ .isInstanceOf(Boolean.class);
+ assertThat((Boolean) TypeUtils.compare(pdxString, theString, OQLLexerTokenTypes.TOK_EQ))
+ .isTrue();
+ }
+
+ @Test
+ public void comparingUnequivalentPdxStringToStringShouldNotMatch() throws Exception {
+ String theString = "MyString";
+ PdxString pdxString = new PdxString("AnotherString");
+
+ assertThat(TypeUtils.compare(pdxString, theString, OQLLexerTokenTypes.TOK_EQ))
+ .isInstanceOf(Boolean.class);
+ assertThat((Boolean) TypeUtils.compare(pdxString, theString, OQLLexerTokenTypes.TOK_EQ))
+ .isFalse();
+
+ assertThat(TypeUtils.compare(pdxString, theString, OQLLexerTokenTypes.TOK_EQ))
+ .isInstanceOf(Boolean.class);
+ assertThat((Boolean) TypeUtils.compare(theString, pdxString, OQLLexerTokenTypes.TOK_EQ))
+ .isFalse();
+ }
+
+ @Test
+ public void comparingTemporalValuesShouldDelegateToTemporalComparator() {
+ // We use spies to track the execution of wanted and unwanted methods.
+ PowerMockito.spy(TypeUtils.class);
+ NumericComparator numericComparator = spy(NumericComparator.class);
+ TemporalComparator temporalComparator = spy(TemporalComparator.class);
+ PowerMockito.when(TypeUtils.getNumericComparator()).thenReturn(numericComparator);
+ PowerMockito.when(TypeUtils.getTemporalComparator()).thenReturn(temporalComparator);
+
+ // Spies to make sure that other comparison methods are not executed.
+ Date beginningOfTimeAsDate = spy(new Date(0L));
+ Date currentCalendarTimeAsDate = spy(Calendar.getInstance().getTime());
+ java.sql.Date beginningOfTimeAsSqlDate =
+ spy(new java.sql.Date(beginningOfTimeAsDate.getTime()));
+ java.sql.Date currentCalendarTimeAsSqlDate =
+ spy(new java.sql.Date(currentCalendarTimeAsDate.getTime()));
+ java.sql.Time beginningOfTimeAsSqlTime =
+ spy(new java.sql.Time(beginningOfTimeAsDate.getTime()));
+ java.sql.Time currentCalendarTimeAsSqlTime =
+ spy(new java.sql.Time(currentCalendarTimeAsDate.getTime()));
+ java.sql.Timestamp beginningOfTimeAsSqlTimestamp =
+ spy(new java.sql.Timestamp(beginningOfTimeAsDate.getTime()));
+ java.sql.Timestamp currentCalendarTimeAsSqlTimestamp =
+ spy(new java.sql.Timestamp(currentCalendarTimeAsDate.getTime()));
+
+ List<Object> originDates = Arrays.asList(new Object[] {beginningOfTimeAsDate,
+ beginningOfTimeAsSqlDate, beginningOfTimeAsSqlTime, beginningOfTimeAsSqlTimestamp});
+ List<Object> currentDates =
+ Arrays.asList(new Object[] {currentCalendarTimeAsDate, currentCalendarTimeAsSqlDate,
+ currentCalendarTimeAsSqlTime, currentCalendarTimeAsSqlTimestamp});
+
+ originDates.forEach(originDate -> {
+ originDates.forEach(originDate2 -> {
+ try {
+ assertThat(TypeUtils.compare(originDate, originDate2, OQLLexerTokenTypes.TOK_EQ))
+ .isEqualTo(Boolean.TRUE);
+ assertThat(TypeUtils.compare(originDate, originDate2, OQLLexerTokenTypes.TOK_LT))
+ .isEqualTo(Boolean.FALSE);
+ assertThat(TypeUtils.compare(originDate, originDate2, OQLLexerTokenTypes.TOK_LE))
+ .isEqualTo(Boolean.TRUE);
+ assertThat(TypeUtils.compare(originDate, originDate2, OQLLexerTokenTypes.TOK_GT))
+ .isEqualTo(Boolean.FALSE);
+ assertThat(TypeUtils.compare(originDate, originDate2, OQLLexerTokenTypes.TOK_GE))
+ .isEqualTo(Boolean.TRUE);
+ assertThat(TypeUtils.compare(originDate, originDate2, OQLLexerTokenTypes.TOK_NE))
+ .isEqualTo(Boolean.FALSE);
+ verify((Comparable) originDate, times(0)).compareTo(any());
+ verify((Comparable) originDate2, times(0)).compareTo(any());
+ verify(temporalComparator, times(6)).compare(any(), any());
+ reset(temporalComparator);
+ } catch (TypeMismatchException typeMismatchException) {
+ throw new RuntimeException(typeMismatchException);
+ }
+ });
+ });
+
+ originDates.forEach(originDate -> {
+ currentDates.forEach(currentTime -> {
+ try {
+ assertThat(TypeUtils.compare(originDate, currentTime, OQLLexerTokenTypes.TOK_EQ))
+ .isEqualTo(Boolean.FALSE);
+ assertThat(TypeUtils.compare(originDate, currentTime, OQLLexerTokenTypes.TOK_LT))
+ .isEqualTo(Boolean.TRUE);
+ assertThat(TypeUtils.compare(originDate, currentTime, OQLLexerTokenTypes.TOK_LE))
+ .isEqualTo(Boolean.TRUE);
+ assertThat(TypeUtils.compare(originDate, currentTime, OQLLexerTokenTypes.TOK_GT))
+ .isEqualTo(Boolean.FALSE);
+ assertThat(TypeUtils.compare(originDate, currentTime, OQLLexerTokenTypes.TOK_GE))
+ .isEqualTo(Boolean.FALSE);
+ assertThat(TypeUtils.compare(originDate, currentTime, OQLLexerTokenTypes.TOK_NE))
+ .isEqualTo(Boolean.TRUE);
+ verify((Comparable) originDate, times(0)).compareTo(any());
+ verify((Comparable) currentTime, times(0)).compareTo(any());
+ verify(temporalComparator, times(6)).compare(any(), any());
+ reset(temporalComparator);
+ } catch (TypeMismatchException typeMismatchException) {
+ throw new RuntimeException(typeMismatchException);
+ }
+ });
+ });
+
+ currentDates.forEach(currentTime -> {
+ originDates.forEach(originDate -> {
+ try {
+ assertThat(TypeUtils.compare(currentTime, originDate, OQLLexerTokenTypes.TOK_EQ))
+ .isEqualTo(Boolean.FALSE);
+ assertThat(TypeUtils.compare(currentTime, originDate, OQLLexerTokenTypes.TOK_LT))
+ .isEqualTo(Boolean.FALSE);
+ assertThat(TypeUtils.compare(currentTime, originDate, OQLLexerTokenTypes.TOK_LE))
+ .isEqualTo(Boolean.FALSE);
+ assertThat(TypeUtils.compare(currentTime, originDate, OQLLexerTokenTypes.TOK_GT))
+ .isEqualTo(Boolean.TRUE);
+ assertThat(TypeUtils.compare(currentTime, originDate, OQLLexerTokenTypes.TOK_GE))
+ .isEqualTo(Boolean.TRUE);
+ assertThat(TypeUtils.compare(currentTime, originDate, OQLLexerTokenTypes.TOK_NE))
+ .isEqualTo(Boolean.TRUE);
+ verify((Comparable) originDate, times(0)).compareTo(any());
+ verify((Comparable) currentTime, times(0)).compareTo(any());
+ verify(temporalComparator, times(6)).compare(any(), any());
+ reset(temporalComparator);
+ } catch (TypeMismatchException typeMismatchException) {
+ throw new RuntimeException(typeMismatchException);
+ }
+ });
+ });
+
+ // Extra check to verify that no other comparison methods were called.
+ verify(numericComparator, times(0)).compare(any(), any());
+ }
+
+ @Test
+ public void comparingTemporalValuesShouldThrowExceptionWhenTheComparisonOperatorIsNotSupported()
+ throws TypeMismatchException {
+ // We use spies to track the execution of wanted and unwanted methods.
+ PowerMockito.spy(TypeUtils.class);
+ TemporalComparator temporalComparator = spy(TemporalComparator.class);
+ PowerMockito.when(TypeUtils.getTemporalComparator()).thenReturn(temporalComparator);
+
+ OQLLexerTokenTypes tempInstance = new OQLLexerTokenTypes() {};
+ Field[] fields = OQLLexerTokenTypes.class.getDeclaredFields();
+
+ Arrays.stream(fields).forEach(field -> {
+ try {
+ int token = field.getInt(tempInstance);
+ if (!comparisonOperators.contains(token)) {
+ assertThatThrownBy(() -> TypeUtils.compare(new Date(), new Date(), token))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessageMatching("^Unknown operator: (.*)$");
+ }
+ } catch (IllegalAccessException exception) {
+ throw new RuntimeException(exception);
+ }
+ });
+
+ verify(temporalComparator, times(fields.length - comparisonOperators.size())).compare(any(),
+ any());
+ }
+
+ @Test
+ public void comparingTemporalValuesForWhichTheComparatorThrowsClassCastExceptionShouldReturnBooleanWhenTheComparisonOperatorIsSupported()
+ throws TypeMismatchException {
+ // We use spies to track the execution of wanted and unwanted methods.
+ PowerMockito.spy(TypeUtils.class);
+ TemporalComparator temporalComparator = mock(TemporalComparator.class);
+ PowerMockito.when(TypeUtils.getTemporalComparator()).thenReturn(temporalComparator);
+ when(temporalComparator.compare(any(), any())).thenThrow(new ClassCastException(""));
+
+ assertThat(TypeUtils.compare(new Date(), new Date(), OQLLexerTokenTypes.TOK_NE))
+ .isEqualTo(Boolean.TRUE);
+ assertThat(TypeUtils.compare(new Date(), new Date(), OQLLexerTokenTypes.TOK_EQ))
+ .isEqualTo(Boolean.FALSE);
+
+ OQLLexerTokenTypes tempInstance = new OQLLexerTokenTypes() {};
+ Field[] fields = OQLLexerTokenTypes.class.getDeclaredFields();
+
+ Arrays.stream(fields).forEach(field -> {
+ try {
+ int token = field.getInt(tempInstance);
+ if (!equalityOperators.contains(token)) {
+ assertThatThrownBy(() -> TypeUtils.compare(new Date(), new Date(), token))
+ .isInstanceOf(TypeMismatchException.class).hasMessageMatching(
+ "^Unable to compare object of type ' (.*) ' with object of type ' (.*) '$");
+ }
+ } catch (IllegalAccessException exception) {
+ throw new RuntimeException(exception);
+ }
+ });
+ }
+
+ @Test
+ public void comparingNumericValuesShouldDelegateToNumericComparator() {
+ // We use spies to track the execution of wanted and unwanted methods.
+ PowerMockito.spy(TypeUtils.class);
+ NumericComparator numericComparator = spy(NumericComparator.class);
+ TemporalComparator temporalComparator = spy(TemporalComparator.class);
+ PowerMockito.when(TypeUtils.getNumericComparator()).thenReturn(numericComparator);
+ PowerMockito.when(TypeUtils.getTemporalComparator()).thenReturn(temporalComparator);
+
+ // Can't spy final classes, nor primitives.
+ long lLong = Short.MIN_VALUE;
+ long hLong = Short.MAX_VALUE;
+ int lInteger = Short.MIN_VALUE;
+ int hInteger = Short.MAX_VALUE;
+ float lFloat = Short.MIN_VALUE;
+ float hFloat = Short.MAX_VALUE;
+ short lShort = Short.MIN_VALUE;
+ short hShort = Short.MAX_VALUE;
+ double lDouble = Short.MIN_VALUE;
+ double hDouble = Short.MAX_VALUE;
+ Long lowestLong = new Long(Short.MIN_VALUE);
+ Long highestLong = new Long(Short.MAX_VALUE);
+ Float lowestFloat = new Float(Short.MIN_VALUE);
+ Float highestFloat = new Float(Short.MAX_VALUE);
+ Short lowestShort = new Short(Short.MIN_VALUE);
+ Short highestShort = new Short(Short.MAX_VALUE);
+ Double lowestDouble = new Double(Short.MIN_VALUE);
+ Double highestDouble = new Double(Short.MAX_VALUE);
+ Integer lowestInteger = new Integer(Short.MIN_VALUE);
+ Integer highestInteger = new Integer(Short.MAX_VALUE);
+ AtomicLong lowestAtomicLong = new AtomicLong(Short.MIN_VALUE);
+ AtomicLong highestAtomicLong = new AtomicLong(Short.MAX_VALUE);
+ BigDecimal lowestBigDecimal = BigDecimal.valueOf(Short.MIN_VALUE);
+ BigDecimal highestBigDecimal = BigDecimal.valueOf(Short.MAX_VALUE);
+ BigInteger lowestBigInteger = BigInteger.valueOf(Short.MIN_VALUE);
+ BigInteger highestBigInteger = BigInteger.valueOf(Short.MAX_VALUE);
+ AtomicInteger lowestAtomicInteger = new AtomicInteger(Short.MIN_VALUE);
+ AtomicInteger highestAtomicInteger = new AtomicInteger(Short.MAX_VALUE);
+
+ List<Number> lowestNumbers = Arrays.asList(new Number[] {lInteger, lLong, lFloat, lShort,
+ lDouble, lowestLong, lowestFloat, lowestShort, lowestDouble, lowestInteger,
+ lowestAtomicLong, lowestBigDecimal, lowestBigInteger, lowestAtomicInteger});
+ List<Number> highestNumbers = Arrays.asList(new Number[] {hInteger, hLong, hFloat, hShort,
+ hDouble, highestLong, highestFloat, highestShort, highestDouble, highestInteger,
+ highestAtomicLong, highestBigDecimal, highestBigInteger, highestAtomicInteger});
+
+ lowestNumbers.forEach(lowest -> {
+ lowestNumbers.stream().filter(number -> number.getClass() != lowest.getClass())
+ .forEach((lowest2) -> {
+ try {
+ assertThat(TypeUtils.compare(lowest, lowest2, OQLLexerTokenTypes.TOK_EQ))
+ .isEqualTo(Boolean.TRUE);
+ assertThat(TypeUtils.compare(lowest, lowest2, OQLLexerTokenTypes.TOK_LT))
+ .isEqualTo(Boolean.FALSE);
+ assertThat(TypeUtils.compare(lowest, lowest2, OQLLexerTokenTypes.TOK_NE))
+ .isEqualTo(Boolean.FALSE);
+ assertThat(TypeUtils.compare(lowest, lowest2, OQLLexerTokenTypes.TOK_LE))
+ .isEqualTo(Boolean.TRUE);
+ assertThat(TypeUtils.compare(lowest, lowest2, OQLLexerTokenTypes.TOK_GT))
+ .isEqualTo(Boolean.FALSE);
+ assertThat(TypeUtils.compare(lowest, lowest2, OQLLexerTokenTypes.TOK_GE))
+ .isEqualTo(Boolean.TRUE);
+ verify(numericComparator, times(6)).compare(any(), any());
+ reset(numericComparator);
+ } catch (TypeMismatchException typeMismatchException) {
+ throw new RuntimeException(typeMismatchException);
+ }
+ });
+ });
+
+ lowestNumbers.forEach(lowestNumber -> {
+ highestNumbers.stream().filter(number -> number.getClass() != lowestNumber.getClass())
+ .forEach((highestNumber) -> {
+ try {
+ assertThat(TypeUtils.compare(lowestNumber, highestNumber, OQLLexerTokenTypes.TOK_EQ))
+ .isEqualTo(Boolean.FALSE);
+ assertThat(TypeUtils.compare(lowestNumber, highestNumber, OQLLexerTokenTypes.TOK_LT))
+ .isEqualTo(Boolean.TRUE);
+ assertThat(TypeUtils.compare(lowestNumber, highestNumber, OQLLexerTokenTypes.TOK_GT))
+ .isEqualTo(Boolean.FALSE);
+ assertThat(TypeUtils.compare(lowestNumber, highestNumber, OQLLexerTokenTypes.TOK_LE))
+ .isEqualTo(Boolean.TRUE);
+ assertThat(TypeUtils.compare(lowestNumber, highestNumber, OQLLexerTokenTypes.TOK_GE))
+ .isEqualTo(Boolean.FALSE);
+ assertThat(TypeUtils.compare(lowestNumber, highestNumber, OQLLexerTokenTypes.TOK_NE))
+ .isEqualTo(Boolean.TRUE);
+ verify(numericComparator, times(6)).compare(any(), any());
+ reset(numericComparator);
+ } catch (TypeMismatchException typeMismatchException) {
+ throw new RuntimeException(typeMismatchException);
+ }
+ });
+ });
+
+ highestNumbers.forEach(highestNumer -> {
+ lowestNumbers.stream().filter(number -> number.getClass() != highestNumer.getClass())
+ .forEach((lowestNumber) -> {
+ try {
+ assertThat(TypeUtils.compare(highestNumer, lowestNumber, OQLLexerTokenTypes.TOK_EQ))
+ .isEqualTo(Boolean.FALSE);
+ assertThat(TypeUtils.compare(highestNumer, lowestNumber, OQLLexerTokenTypes.TOK_LT))
+ .isEqualTo(Boolean.FALSE);
+ assertThat(TypeUtils.compare(highestNumer, lowestNumber, OQLLexerTokenTypes.TOK_LE))
+ .isEqualTo(Boolean.FALSE);
+ assertThat(TypeUtils.compare(highestNumer, lowestNumber, OQLLexerTokenTypes.TOK_NE))
+ .isEqualTo(Boolean.TRUE);
+ assertThat(TypeUtils.compare(highestNumer, lowestNumber, OQLLexerTokenTypes.TOK_GT))
+ .isEqualTo(Boolean.TRUE);
+ assertThat(TypeUtils.compare(highestNumer, lowestNumber, OQLLexerTokenTypes.TOK_GE))
+ .isEqualTo(Boolean.TRUE);
+ verify(numericComparator, times(6)).compare(any(), any());
+ reset(numericComparator);
+ } catch (TypeMismatchException typeMismatchException) {
+ throw new RuntimeException(typeMismatchException);
+ }
+ });
+ });
+
+ // Extra check to verify that no other comparison methods were called.
+ verify(temporalComparator, times(0)).compare(any(), any());
+ }
+
+ @Test
+ public void comparingNumericValuesShouldThrowExceptionWhenTheComparisonOperatorIsNotSupported()
+ throws TypeMismatchException {
+ // We use spies to track the execution of wanted and unwanted methods.
+ PowerMockito.spy(TypeUtils.class);
+ NumericComparator numericComparator = spy(NumericComparator.class);
+ PowerMockito.when(TypeUtils.getNumericComparator()).thenReturn(numericComparator);
+
+ OQLLexerTokenTypes tempInstance = new OQLLexerTokenTypes() {};
+ Field[] fields = OQLLexerTokenTypes.class.getDeclaredFields();
+
+ Arrays.stream(fields).forEach(field -> {
+ try {
+ int token = field.getInt(tempInstance);
+ if (!comparisonOperators.contains(token)) {
+ assertThatThrownBy(() -> TypeUtils.compare(new Integer("20"), new Double("20.12"), token))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessageMatching("^Unknown operator: (.*)$");
+ }
+ } catch (IllegalAccessException exception) {
+ throw new RuntimeException(exception);
+ }
+ });
+
+ verify(numericComparator, times(fields.length - comparisonOperators.size())).compare(any(),
+ any());
+ }
+
+ @Test
+ public void comparingNumericValuesForWhichTheComparatorThrowsClassCastExceptionShouldReturnBooleanWhenTheComparisonOperatorIsSupported()
+ throws TypeMismatchException {
+ // We use spies to track the execution of wanted and unwanted methods.
+ PowerMockito.spy(TypeUtils.class);
+ NumericComparator numericComparator = mock(NumericComparator.class);
+ PowerMockito.when(TypeUtils.getNumericComparator()).thenReturn(numericComparator);
+ when(numericComparator.compare(any(), any())).thenThrow(new ClassCastException(""));
+
+ assertThat(
+ TypeUtils.compare(new Integer("20"), new BigDecimal("100"), OQLLexerTokenTypes.TOK_NE))
+ .isEqualTo(Boolean.TRUE);
+ assertThat(
+ TypeUtils.compare(new Integer("20"), new BigDecimal("100"), OQLLexerTokenTypes.TOK_EQ))
+ .isEqualTo(Boolean.FALSE);
+
+ OQLLexerTokenTypes tempInstance = new OQLLexerTokenTypes() {};
+ Field[] fields = OQLLexerTokenTypes.class.getDeclaredFields();
+
+ Arrays.stream(fields).forEach(field -> {
+ try {
+ int token = field.getInt(tempInstance);
+ if (!equalityOperators.contains(token)) {
+ assertThatThrownBy(
+ () -> TypeUtils.compare(new Integer("20"), new BigDecimal("100"), token))
+ .isInstanceOf(TypeMismatchException.class).hasMessageMatching(
+ "^Unable to compare object of type ' (.*) ' with object of type ' (.*) '$");
+ }
+ } catch (IllegalAccessException exception) {
+ throw new RuntimeException(exception);
+ }
+ });
+ }
+
+ @Test
+ public void comparingBooleanValuesShouldDelegateToBooleanCompareImplementation()
+ throws TypeMismatchException {
+ // We use spies to track the execution of wanted and unwanted methods.
+ PowerMockito.spy(TypeUtils.class);
+ NumericComparator numericComparator = spy(NumericComparator.class);
+ TemporalComparator temporalComparator = spy(TemporalComparator.class);
+ PowerMockito.when(TypeUtils.getNumericComparator()).thenReturn(numericComparator);
+ PowerMockito.when(TypeUtils.getTemporalComparator()).thenReturn(temporalComparator);
+
+ assertThat(TypeUtils.compare(true, Boolean.TRUE, OQLLexerTokenTypes.TOK_EQ))
+ .isEqualTo(Boolean.TRUE);
+ PowerMockito.verifyStatic(TypeUtils.class, times(1));
+ TypeUtils.booleanCompare(true, Boolean.TRUE, OQLLexerTokenTypes.TOK_EQ);
+
+ assertThat(TypeUtils.compare(Boolean.TRUE, true, OQLLexerTokenTypes.TOK_NE))
+ .isEqualTo(Boolean.FALSE);
+ PowerMockito.verifyStatic(TypeUtils.class, times(1));
+ TypeUtils.booleanCompare(true, Boolean.TRUE, OQLLexerTokenTypes.TOK_NE);
+
+ assertThat(TypeUtils.compare(true, Boolean.FALSE, OQLLexerTokenTypes.TOK_EQ))
+ .isEqualTo(Boolean.FALSE);
+ PowerMockito.verifyStatic(TypeUtils.class, times(1));
+ TypeUtils.booleanCompare(true, Boolean.FALSE, OQLLexerTokenTypes.TOK_EQ);
+
+ assertThat(TypeUtils.compare(Boolean.FALSE, true, OQLLexerTokenTypes.TOK_NE))
+ .isEqualTo(Boolean.TRUE);
+ PowerMockito.verifyStatic(TypeUtils.class, times(1));
+ TypeUtils.booleanCompare(Boolean.FALSE, true, OQLLexerTokenTypes.TOK_NE);
+
+ // Extra check to verify that no other comparison methods were called.
+ verify(numericComparator, times(0)).compare(any(), any());
+ verify(temporalComparator, times(0)).compare(any(), any());
+ }
+
+ /**
+ * Test class that implements the Comparable interface.
+ */
+ class ComparableObject implements Comparable {
+ Integer id;
+
+ public ComparableObject(Integer id) {
+ this.id = id;
+ }
+
+ @Override
+ public int compareTo(Object o) {
+ return this.id.compareTo(((ComparableObject) o).id);
+ }
+ }
+
+ @Test
+ public void comparingComparableInstancesShouldDelegateToDefaultCompareToMethod()
+ throws TypeMismatchException {
+ PowerMockito.spy(TypeUtils.class);
+ Comparable startValue = spy(new ComparableObject(0));
+ Comparable finishValue = spy(new ComparableObject(10));
+ NumericComparator numericComparator = spy(NumericComparator.class);
+ TemporalComparator temporalComparator = spy(TemporalComparator.class);
+ PowerMockito.when(TypeUtils.getNumericComparator()).thenReturn(numericComparator);
+ PowerMockito.when(TypeUtils.getTemporalComparator()).thenReturn(temporalComparator);
+
+ assertThat(TypeUtils.compare(startValue, startValue, OQLLexerTokenTypes.TOK_EQ))
+ .isEqualTo(Boolean.TRUE);
+ assertThat(TypeUtils.compare(startValue, startValue, OQLLexerTokenTypes.TOK_LT))
+ .isEqualTo(Boolean.FALSE);
+ assertThat(TypeUtils.compare(startValue, startValue, OQLLexerTokenTypes.TOK_LE))
+ .isEqualTo(Boolean.TRUE);
+ assertThat(TypeUtils.compare(startValue, startValue, OQLLexerTokenTypes.TOK_GT))
+ .isEqualTo(Boolean.FALSE);
+ assertThat(TypeUtils.compare(startValue, startValue, OQLLexerTokenTypes.TOK_GE))
+ .isEqualTo(Boolean.TRUE);
+ assertThat(TypeUtils.compare(startValue, startValue, OQLLexerTokenTypes.TOK_NE))
+ .isEqualTo(Boolean.FALSE);
+ verify(startValue, times(6)).compareTo(startValue);
+ reset(startValue);
+
+ assertThat(TypeUtils.compare(finishValue, finishValue, OQLLexerTokenTypes.TOK_EQ))
+ .isEqualTo(Boolean.TRUE);
+ assertThat(TypeUtils.compare(finishValue, finishValue, OQLLexerTokenTypes.TOK_LT))
+ .isEqualTo(Boolean.FALSE);
+ assertThat(TypeUtils.compare(finishValue, finishValue, OQLLexerTokenTypes.TOK_LE))
+ .isEqualTo(Boolean.TRUE);
+ assertThat(TypeUtils.compare(finishValue, finishValue, OQLLexerTokenTypes.TOK_GT))
+ .isEqualTo(Boolean.FALSE);
+ assertThat(TypeUtils.compare(finishValue, finishValue, OQLLexerTokenTypes.TOK_GE))
+ .isEqualTo(Boolean.TRUE);
+ assertThat(TypeUtils.compare(finishValue, finishValue, OQLLexerTokenTypes.TOK_NE))
+ .isEqualTo(Boolean.FALSE);
+ verify(finishValue, times(6)).compareTo(finishValue);
+ reset(finishValue);
+
+ assertThat(TypeUtils.compare(startValue, finishValue, OQLLexerTokenTypes.TOK_EQ))
+ .isEqualTo(Boolean.FALSE);
+ assertThat(TypeUtils.compare(startValue, finishValue, OQLLexerTokenTypes.TOK_LT))
+ .isEqualTo(Boolean.TRUE);
+ assertThat(TypeUtils.compare(startValue, finishValue, OQLLexerTokenTypes.TOK_LE))
+ .isEqualTo(Boolean.TRUE);
+ assertThat(TypeUtils.compare(startValue, finishValue, OQLLexerTokenTypes.TOK_GT))
+ .isEqualTo(Boolean.FALSE);
+ assertThat(TypeUtils.compare(startValue, finishValue, OQLLexerTokenTypes.TOK_GE))
+ .isEqualTo(Boolean.FALSE);
+ assertThat(TypeUtils.compare(startValue, finishValue, OQLLexerTokenTypes.TOK_NE))
+ .isEqualTo(Boolean.TRUE);
+ verify(startValue, times(6)).compareTo(finishValue);
+ reset(startValue);
+
+ assertThat(TypeUtils.compare(finishValue, startValue, OQLLexerTokenTypes.TOK_EQ))
+ .isEqualTo(Boolean.FALSE);
+ assertThat(TypeUtils.compare(finishValue, startValue, OQLLexerTokenTypes.TOK_LT))
+ .isEqualTo(Boolean.FALSE);
+ assertThat(TypeUtils.compare(finishValue, startValue, OQLLexerTokenTypes.TOK_LE))
+ .isEqualTo(Boolean.FALSE);
+ assertThat(TypeUtils.compare(finishValue, startValue, OQLLexerTokenTypes.TOK_GT))
+ .isEqualTo(Boolean.TRUE);
+ assertThat(TypeUtils.compare(finishValue, startValue, OQLLexerTokenTypes.TOK_GE))
+ .isEqualTo(Boolean.TRUE);
+ assertThat(TypeUtils.compare(finishValue, startValue, OQLLexerTokenTypes.TOK_NE))
+ .isEqualTo(Boolean.TRUE);
+ verify(finishValue, times(6)).compareTo(startValue);
+ reset(finishValue);
+
+ // Extra check to verify that no other comparison methods were called.
+ verify(numericComparator, times(0)).compare(any(), any());
+ verify(temporalComparator, times(0)).compare(any(), any());
+ }
+
+ @Test
+ public void comparingComparableValuesShouldThrowExceptionWhenTheComparisonOperatorIsNotSupported()
+ throws TypeMismatchException {
+ OQLLexerTokenTypes tempInstance = new OQLLexerTokenTypes() {};
+ Field[] fields = OQLLexerTokenTypes.class.getDeclaredFields();
+
+ Arrays.stream(fields).forEach(field -> {
+ try {
+ int token = field.getInt(tempInstance);
+ if (!comparisonOperators.contains(token)) {
+ assertThatThrownBy(
+ () -> TypeUtils.compare(mock(Comparable.class), mock(Comparable.class), token))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessageMatching("^Unknown operator: (.*)$");
+ }
+ } catch (Exception exception) {
+ throw new RuntimeException(exception);
+ }
+ });
+ }
+
+ @Test
+ public void comparingComparableValuesForWhichTheCompareMethodThrowsClassCastExceptionShouldReturnBooleanWhenTheComparisonOperatorIsSupported()
+ throws TypeMismatchException {
+ ComparableObject comparableValue = mock(ComparableObject.class);
+ when(comparableValue.compareTo(any())).thenThrow(new ClassCastException(""));
+
+ assertThat(
+ TypeUtils.compare(comparableValue, mock(Comparable.class), OQLLexerTokenTypes.TOK_NE))
+ .isEqualTo(Boolean.TRUE);
+ assertThat(
+ TypeUtils.compare(comparableValue, mock(Comparable.class), OQLLexerTokenTypes.TOK_EQ))
+ .isEqualTo(Boolean.FALSE);
+
+ OQLLexerTokenTypes tempInstance = new OQLLexerTokenTypes() {};
+ Field[] fields = OQLLexerTokenTypes.class.getDeclaredFields();
+
+ Arrays.stream(fields).forEach(field -> {
+ try {
+ int token = field.getInt(tempInstance);
+ if (!equalityOperators.contains(token)) {
+ assertThatThrownBy(
+ () -> TypeUtils.compare(comparableValue, mock(Comparable.class), token))
+ .isInstanceOf(TypeMismatchException.class).hasMessageMatching(
+ "^Unable to compare object of type ' (.*) ' with object of type ' (.*) '$");
+ }
+ } catch (Exception exception) {
+ throw new RuntimeException(exception);
+ }
+ });
+ }
+
+ /**
+ * Arbitrary Test class (equals method can not be mocked).
+ */
+ public class ArbitraryObject {
+ String id;
+ AtomicInteger equalsInvocations;
+
+ public ArbitraryObject(String id) {
+ this.id = id;
+ this.equalsInvocations = new AtomicInteger(0);
+ }
+
+ public Integer getInvocationsAmount() {
+ return this.equalsInvocations.get();
+ }
+
+ public void resetInvocationsAmount() {
+ this.equalsInvocations.set(0);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ this.equalsInvocations.incrementAndGet();
+
+ if (this == o) {
+ return true;
+ }
+
+ if (!(o instanceof ArbitraryObject)) {
+ return false;
+ }
+
+ ArbitraryObject that = (ArbitraryObject) o;
+
+ return Objects.equals(id, that.id);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id);
+ }
+ }
+
+ @Test
+ public void comparingArbitraryObjectsShouldDelegateToDefaultEqualsMethod()
+ throws TypeMismatchException {
+ PowerMockito.spy(TypeUtils.class);
+ ArbitraryObject aValue = new ArbitraryObject("0");
+ ArbitraryObject anotherValue = new ArbitraryObject("1");
+ NumericComparator numericComparator = spy(NumericComparator.class);
+ TemporalComparator temporalComparator = spy(TemporalComparator.class);
+ PowerMockito.when(TypeUtils.getNumericComparator()).thenReturn(numericComparator);
+ PowerMockito.when(TypeUtils.getTemporalComparator()).thenReturn(temporalComparator);
+
+ assertThat(TypeUtils.compare(aValue, aValue, OQLLexerTokenTypes.TOK_EQ))
+ .isEqualTo(Boolean.TRUE);
+ assertThat(TypeUtils.compare(aValue, aValue, OQLLexerTokenTypes.TOK_NE))
+ .isEqualTo(Boolean.FALSE);
+ assertThat(aValue.getInvocationsAmount()).isEqualTo(2);
+ aValue.resetInvocationsAmount();
+
+ assertThat(TypeUtils.compare(aValue, anotherValue, OQLLexerTokenTypes.TOK_NE))
+ .isEqualTo(Boolean.TRUE);
+ assertThat(TypeUtils.compare(anotherValue, aValue, OQLLexerTokenTypes.TOK_NE))
+ .isEqualTo(Boolean.TRUE);
+ assertThat(TypeUtils.compare(aValue, anotherValue, OQLLexerTokenTypes.TOK_EQ))
+ .isEqualTo(Boolean.FALSE);
+ assertThat(TypeUtils.compare(anotherValue, aValue, OQLLexerTokenTypes.TOK_EQ))
+ .isEqualTo(Boolean.FALSE);
+ assertThat(aValue.getInvocationsAmount()).isEqualTo(2);
+ assertThat(anotherValue.getInvocationsAmount()).isEqualTo(2);
+ aValue.resetInvocationsAmount();
+ anotherValue.resetInvocationsAmount();
+
+ // Extra check to verify that no other comparison methods were called.
+ verify(numericComparator, times(0)).compare(any(), any());
+ verify(temporalComparator, times(0)).compare(any(), any());
+ }
+
+ @Test
+ public void comparingArbitraryObjectsUsingAnUnsupportedComparisonOperatorShouldThrowException() {
+ OQLLexerTokenTypes tempInstance = new OQLLexerTokenTypes() {};
+ Field[] fields = OQLLexerTokenTypes.class.getDeclaredFields();
+
+ Arrays.stream(fields).forEach(field -> {
+ try {
+ int token = field.getInt(tempInstance);
+ if (!comparisonOperators.contains(token)) {
+ assertThatThrownBy(() -> TypeUtils.compare(new ArbitraryObject("0"),
+ new ArbitraryObject("0"),
+ token)).isInstanceOf(TypeMismatchException.class).hasMessageMatching(
+ "^Unable to use a relational comparison operator to compare an instance of class ' (.*) ' with an instance of ' (.*) '$");
+ }
+ } catch (IllegalAccessException exception) {
+ throw new RuntimeException(exception);
+ }
+ });
+ }
+}
diff --git a/geode-core/src/test/resources/org/apache/geode/codeAnalysis/excludedClasses.txt b/geode-core/src/test/resources/org/apache/geode/codeAnalysis/excludedClasses.txt
index da96ce7..1a99416 100644
--- a/geode-core/src/test/resources/org/apache/geode/codeAnalysis/excludedClasses.txt
+++ b/geode-core/src/test/resources/org/apache/geode/codeAnalysis/excludedClasses.txt
@@ -81,3 +81,9 @@ org/apache/geode/distributed/internal/RuntimeDistributionConfigImpl
org/apache/geode/pdx/internal/PdxInstanceEnum
org/apache/geode/pdx/internal/PdxInstanceImpl
org/apache/geode/pdx/internal/WritablePdxInstanceImpl
+org/apache/geode/cache/query/internal/types/TypeUtils$ComparisonStrategy
+org/apache/geode/cache/query/internal/types/TypeUtils$ComparisonStrategy$1
+org/apache/geode/cache/query/internal/types/TypeUtils$ComparisonStrategy$2
+org/apache/geode/cache/query/internal/types/TypeUtils$ComparisonStrategy$3
+org/apache/geode/cache/query/internal/types/TypeUtils$ComparisonStrategy$4
+org/apache/geode/cache/query/internal/types/TypeUtils$ComparisonStrategy$5
--
To stop receiving notification emails like this one, please contact
jasonhuynh@apache.org.