com.squeakysand.commons.lang.CompareToHelper Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of squeakysand-commons Show documentation
Show all versions of squeakysand-commons Show documentation
Classes, interfaces and enums that assist with everyday Java development tasks.
The newest version!
/*
* Copyright 2010-2012 Craig S. Dickson (http://craigsdickson.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.squeakysand.commons.lang;
import com.squeakysand.commons.beans.BeanHelper;
import com.squeakysand.commons.beans.BeanHelperException;
import java.util.Arrays;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Helper class for writing implementations of the {@link java.lang.Comparable#compareTo(Object)} method from the
* {@link java.lang.Comparable} interface. All methods that accept an array as a parameter and methods that accept
* single Objects as parameters safely handle a null value being passed as arguments. Methods that accept
* an array also safely handle a null element in the array. The following rules are followed by all
* compareTo methods:
*
*
* - Two
null values are considered equal.
* - A
null object reference is considered less than a valid object reference. This applies to arrays
* elements as well as regular objects.
* - An array with a shorter length is considered smaller than one with a longer length, regardless of the contents of
* the elements.
*
*/
public final class CompareToHelper {
private static final Logger LOG = LoggerFactory.getLogger(CompareToHelper.class);
/**
* Generates compareTo value for two boolean values. The value true is considered to be greater than
* the value false.
*
* @param b1
* left side of compareTo operation.
* @param b2
* right side of compareTo operation.
* @return the resulting compareTo value.
*/
public static int compareTo(boolean b1, boolean b2) {
LOG.trace("invoked with: {}, {}", b1, b2);
int result = 0;
if (b1 != b2) {
if (b1) {
result = 1;
} else {
result = -1;
}
}
LOG.trace("result: {}", result);
return result;
}
/**
* Generates a compareTo value for two arrays of boolean values.
*
* @param b1
* left side of compareTo operation.
* @param b2
* right side of compareTo operation.
* @return the result of comparing each element and combinging the results.
*/
public static int compareTo(boolean[] b1, boolean[] b2) {
LOG.trace("invoked with: {}, {}", b1, b2);
int result = 0;
if (!Arrays.equals(b1, b2)) {
if ((b1 == null) && (b2 != null)) {
result = -1;
} else if ((b1 != null) && (b2 == null)) {
result = 1;
} else if (b1.length != b2.length) {
result = b1.length - b2.length;
} else {
// at this point we know that the arrays are the same length but at least one
// element must be different
int i = 0;
while (result == 0) {
result = compareTo(b1[i], b2[i]);
i++;
}
}
}
LOG.trace("result: {}", result);
return result;
}
/**
* Generates a compareTo value for two arrays of byte values.
*
* @param b1
* left side of compareTo operation.
* @param b2
* right side of compareTo operation.
* @return the result of comparing each element and combining the results.
*/
public static int compareTo(byte[] b1, byte[] b2) {
LOG.trace("invoked with: {}, {}", b1, b2);
int result = 0;
if (!Arrays.equals(b1, b2)) {
if ((b1 == null) && (b2 != null)) {
result = -1;
} else if ((b1 != null) && (b2 == null)) {
result = 1;
} else if (b1.length != b2.length) {
result = b1.length - b2.length;
} else {
// at this point we know that the arrays are the same length but at least one
// element must be different
int i = 0;
while (result == 0) {
result = compareTo(b1[i], b2[i]);
i++;
}
}
}
LOG.trace("result: {}", result);
return result;
}
/**
* Generates a compareTo value for two arrays of char values.
*
* @param c1
* left side of compareTo operation.
* @param c2
* right side of compareTo operation.
* @return the result of comparing each element and combining the results.
*/
public static int compareTo(char[] c1, char[] c2) {
LOG.trace("invoked with: {}, {}", c1, c2);
int result = 0;
if (!Arrays.equals(c1, c2)) {
if ((c1 == null) && (c2 != null)) {
result = -1;
} else if ((c1 != null) && (c2 == null)) {
result = 1;
} else if (c1.length != c2.length) {
result = c1.length - c2.length;
} else {
// at this point we know that the arrays are the same length but at least one
// element must be different
int i = 0;
while (result == 0) {
result = compareTo(c1[i], c2[i]);
i++;
}
}
}
LOG.trace("result: {}", result);
return result;
}
/**
* Generates compareTo value for two double values.
*
* @param d1
* left side of compareTo operation.
* @param d2
* right side of compareTo operation.
* @return the resulting compareTo value.
*/
public static int compareTo(double d1, double d2) {
LOG.trace("invoked with: {}, {}", d1, d2);
int result = 0;
double diff = d1 - d2;
if (diff != 0) {
if (diff > 0) {
result = 1;
} else {
result = -1;
}
}
LOG.trace("result: {}", result);
return result;
}
/**
* Generates a compareTo value for two arrays of double values.
*
* @param d1
* left side of compareTo operation.
* @param d2
* right side of compareTo operation.
* @return the result of comparing each element and combinging the results.
*/
public static int compareTo(double[] d1, double[] d2) {
LOG.trace("invoked with: {}, {}", d1, d2);
int result = 0;
if (!Arrays.equals(d1, d2)) {
if ((d1 == null) && (d2 != null)) {
result = -1;
} else if ((d1 != null) && (d2 == null)) {
result = 1;
} else if (d1.length != d2.length) {
result = d1.length - d2.length;
} else {
// at this point we know that the arrays are the same length but at least one
// element must be different
int i = 0;
while (result == 0) {
result = compareTo(d1[i], d2[i]);
i++;
}
}
}
LOG.trace("result: {}", result);
return result;
}
/**
* Generates compareTo value for two float values.
*
* @param f1
* left side of compareTo operation.
* @param f2
* right side of compareTo operation.
* @return the resulting compareTo value.
*/
public static int compareTo(float f1, float f2) {
LOG.trace("invoked with: {}, {}", f1, f2);
int result = 0;
float diff = f1 - f2;
if (diff != 0) {
if (diff > 0) {
result = 1;
} else {
result = -1;
}
}
LOG.trace("result: {}", result);
return result;
}
/**
* Generates a compareTo value for two arrays of float values.
*
* @param f1
* left side of compareTo operation.
* @param f2
* right side of compareTo operation.
* @return the result of comparing each element and combinging the results.
*/
public static int compareTo(float[] f1, float[] f2) {
LOG.trace("invoked with: {}, {}", f1, f2);
int result = 0;
if (!Arrays.equals(f1, f2)) {
if ((f1 == null) && (f2 != null)) {
result = -1;
} else if ((f1 != null) && (f2 == null)) {
result = 1;
} else if (f1.length != f2.length) {
result = f1.length - f2.length;
} else {
// at this point we know that the arrays are the same length but at least one
// element must be different
int i = 0;
while (result == 0) {
result = compareTo(f1[i], f2[i]);
i++;
}
}
}
LOG.trace("result: {}", result);
return result;
}
/**
* Generates compareTo value for two int values.
*
* @param i1
* left side of compareTo operation.
* @param i2
* right side of compareTo operation.
* @return the resulting compareTo value.
*/
public static int compareTo(int i1, int i2) {
LOG.trace("invoked with: {}, {}", i1, i2);
int result = i1 - i2;
LOG.trace("result: {}", result);
return result;
}
/**
* Generates a compareTo value for two arrays of int values.
*
* @param i1
* left side of compareTo operation.
* @param i2
* right side of compareTo operation.
* @return the result of comparing each element and combinging the results.
*/
public static int compareTo(int[] i1, int[] i2) {
LOG.trace("invoked with: {}, {}", i1, i2);
int result = 0;
if (!Arrays.equals(i1, i2)) {
if ((i1 == null) && (i2 != null)) {
result = -1;
} else if ((i1 != null) && (i2 == null)) {
result = 1;
} else if (i1.length != i2.length) {
result = i1.length - i2.length;
} else {
// at this point we know that the arrays are the same length but at least one
// element must be different
int i = 0;
while (result == 0) {
result = compareTo(i1[i], i2[i]);
i++;
}
}
}
LOG.trace("result: {}", result);
return result;
}
/**
* Generates compareTo value for two long values.
*
* @param l1
* left side of compareTo operation.
* @param l2
* right side of compareTo operation.
* @return the resulting compareTo value.
*/
public static int compareTo(long l1, long l2) {
LOG.trace("invoked with: {}, {}", l1, l2);
int result = 0;
long diff = l1 - l2;
if (diff != 0) {
if (diff > 0) {
result = 1;
} else {
result = -1;
}
}
LOG.trace("result: {}", result);
return result;
}
/**
* Generates a compareTo value for two arrays of long values.
*
* @param l1
* left side of compareTo operation.
* @param l2
* right side of compareTo operation.
* @return the result of comparing each element and combinging the results.
*/
public static int compareTo(long[] l1, long[] l2) {
LOG.trace("invoked with: {}, {}", l1, l2);
int result = 0;
if (!Arrays.equals(l1, l2)) {
if ((l1 == null) && (l2 != null)) {
result = -1;
} else if ((l1 != null) && (l2 == null)) {
result = 1;
} else if (l1.length != l2.length) {
result = l1.length - l2.length;
} else {
// at this point we know that the arrays are the same length but at least one
// element must be different
int i = 0;
while (result == 0) {
result = compareTo(l1[i], l2[i]);
i++;
}
}
}
LOG.trace("result: {}", result);
return result;
}
/**
* Generates a compareTo value for two Objects. This method is only fully reliable when one of the following
* conditions is true:
*
*
* - both arguments are null
* - one argument is null
* - the Class of the arguments overrides the default implementation of the hashCode method in such a way that
* naturally greater objects have a larger hashCode value than smaller objects and vice versa
*
*
*
* If none of these conditions are met, then this method relies on the equals and hashCode methods of the passed in
* Classes and so the return value is not guaranteed to adhere to the general contract of the Comparable interface.
* The {@link #compareTo(Comparable, Comparable)} method should be used instead where possible.
*
*
* @param o1
* left side of compareTo operation.
* @param o2
* right side of compareTo operation.
* @return the resulting compareTo value.
* @throws java.lang.ClassCastException
* if both arguments are not null and the following condition is not true
* o1.getClass().isAssignableFrom(o2.getClass())
*/
@SuppressWarnings({"rawtypes", "unchecked"})
public static int compareTo(Object o1, Object o2) {
LOG.trace("invoked with: {}, {}", o1, o2);
int result = checkForUnmatchedNulls(o1, o2);
if (result == 0) {
if ((o1 != null) && (o2 != null)) {
// first check that the objects are not the exact same object instance, if they are, return 0
if (o1 != o2) {
// now check if a general call to equals returns true, is so, return 0
if (!o1.equals(o2)) {
// now try to call one of the type specific compareTo methods
Class> o1class = o1.getClass();
Class> o2class = o2.getClass();
if (ClassHelper.classesImplementCommonInterface(Comparable.class, o1class, o2class)) {
// we know the clases implement Comparable, but we dont know if they are compatible
// which means this call may throw a ClassCastException, but this is in line with the
// specification for the Comparable interface
result = ((Comparable) o1).compareTo(o2);
} else {
// if we didnt find a type specific compareTo method, then default to comaparing hashcodes
LOG.warn("using hashcodes to compare objects of type {} and {}", o1class, o2class);
result = o1.hashCode() - o2.hashCode();
}
} else {
LOG.trace("args failed equality test");
}
} else {
LOG.trace("both args referred to same object instance");
}
} else {
LOG.trace("both args were null");
}
}
LOG.trace("result: {}", result);
return result;
}
/**
* Generates a compareTo value for an array of Objects.
*
* @param o1
* left side of compareTo operation.
* @param o2
* right side of compareTo operation.
* @return the result of comparing each element and combining the results.
* @see #compareTo(Object, Object)
*/
public static int compareTo(Object[] o1, Object[] o2) {
LOG.trace("invoked with: {}, {}", o1, o2);
int result = 0;
if (!Arrays.equals(o1, o2)) {
if ((o1 == null) && (o2 != null)) {
result = -1;
} else if ((o1 != null) && (o2 == null)) {
result = 1;
} else if (o1.length != o2.length) {
result = o1.length - o2.length;
} else {
// at this point we know that the arrays are the same length but at least one
// element must be different
int i = 0;
while (result == 0) {
result = compareTo(o1[i], o2[i]);
i++;
}
}
}
LOG.trace("result: {}", result);
return result;
}
/**
* Generates a compareTo value for two arrays of short values.
*
* @param s1
* left side of compareTo operation.
* @param s2
* right side of compareTo operation.
* @return the result of comparing each element and combining the results.
*/
public static int compareTo(short[] s1, short[] s2) {
LOG.trace("invoked with: {}, {}", s1, s2);
int result = 0;
if (!Arrays.equals(s1, s2)) {
if ((s1 == null) && (s2 != null)) {
result = -1;
} else if ((s1 != null) && (s2 == null)) {
result = 1;
} else if (s1.length != s2.length) {
result = s1.length - s2.length;
} else {
// at this point we know that the arrays are the same length but at least one
// element must be different
int i = 0;
while (result == 0) {
result = compareTo(s1[i], s2[i]);
i++;
}
}
}
LOG.trace("result: {}", result);
return result;
}
/**
* Generates compareTo value for two Comparable values.
*
* @param c1
* left side of compareTo operation.
* @param c2
* right side of compareTo operation.
* @return the resulting compareTo value.
* @param
* a T object.
*/
public static > int compareTo(T c1, T c2) {
LOG.trace("invoked with: {}, {}", c1, c2);
int result = checkForUnmatchedNulls(c1, c2);
if (result == 0) {
if ((c1 != null) && (c2 != null)) {
result = c1.compareTo(c2);
} else {
LOG.trace("args were both null");
}
} else {
LOG.trace("args were unmatched nulls");
}
LOG.trace("result: {}", result);
return result;
}
public static int compareTo(T t1, T t2, String... propertyNames) {
if (LOG.isTraceEnabled()) {
LOG.trace("invoked with: {}, {}, {}", new Object[] {t1, t2, ToStringHelper.toString(propertyNames)});
}
if (propertyNames == null) {
throw new NullParameterException("propertyNames");
}
if (propertyNames.length == 0) {
throw new IllegalArgumentException("propertyNames length cannot be zero");
}
int result = checkForUnmatchedNulls(t1, t2);
if (result == 0) {
if ((t1 != null) && (t2 != null)) {
for (int i = 0; i < propertyNames.length; i++) {
Object v1 = null;
try {
v1 = BeanHelper.getPropertyValue(t1, propertyNames[i]);
} catch (BeanHelperException e) {
LOG.error(e.getMessage(), e);
}
Object v2 = null;
try {
v2 = BeanHelper.getPropertyValue(t2, propertyNames[i]);
} catch (BeanHelperException e) {
LOG.error(e.getMessage(), e);
}
LOG.debug("comparing values: {} {}", v1, v2);
result = compareTo(v1, v2);
if (result != 0) {
break;
}
}
} else {
LOG.trace("both args were null");
}
} else {
LOG.trace("args were unmatched nulls");
}
LOG.trace("result: {}", result);
return result;
}
// private static void printClasInfo(Class> klass) {
// LOG.trace("printing info for class: {}", ToStringHelper.toString(klass));
// Type[] genericInterfaces = klass.getGenericInterfaces();
// LOG.trace("getGenericInterfaces: {}", ToStringHelper.toString(klass.getGenericInterfaces()));
// for (Type type : genericInterfaces) {
// if (type instanceof ParameterizedType) {
// ParameterizedType pType = (ParameterizedType) type;
// LOG.trace("### rawType = {}, ownerType = {}, actualTypeArguments = {}", new Object[] {pType.getRawType(),
// pType.getOwnerType(), ToStringHelper.toString(pType.getActualTypeArguments())});
// }
// }
// LOG.trace("getInterfaces: {}", ToStringHelper.toString(klass.getInterfaces()));
// TypeVariable[] typeParameters = klass.getTypeParameters();
// LOG.trace("getTypeParameters: {}", ToStringHelper.toString(typeParameters));
// for (TypeVariable typeVariable : typeParameters) {
// LOG.trace("#### name = {}, genericDeclaration = {}, bounds = {}", new Object [] {typeVariable.getName(),
// typeVariable.getGenericDeclaration(), ToStringHelper.toString(typeVariable.getBounds())});
// }
// }
/**
* Generates a compareTo value for an array of Comparable objects.
*
* @param c1
* left side of compareTo operation.
* @param c2
* right side of compareTo operation.
* @return the result of comparing each element and combining the results.
* @param
* a T object.
*/
public static > int compareTo(T[] c1, T[] c2) {
LOG.trace("invoked with: {}, {}", c1, c2);
int result = 0;
if (!Arrays.equals(c1, c2)) {
if ((c1 == null) && (c2 != null)) {
result = -1;
} else if ((c1 != null) && (c2 == null)) {
result = 1;
} else if (c1.length != c2.length) {
result = c1.length - c2.length;
} else {
// at this point we know that the arrays are the same length but at least one
// element must be different
int i = 0;
while (result == 0) {
result = compareTo(c1[i], c2[i]);
i++;
}
}
}
LOG.trace("result: {}", result);
return result;
}
private static int checkForUnmatchedNulls(T t1, T t2) {
LOG.trace("invoked with: {}, {}", t1, t2);
int result = 0;
if ((t1 != null) && (t2 == null)) {
result = Integer.MAX_VALUE;
} else if ((t1 == null) && (t2 != null)) {
result = Integer.MIN_VALUE;
}
LOG.trace("result: {}", result);
return result;
}
private CompareToHelper() {
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy