All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.squeakysand.commons.lang.CompareToHelper Maven / Gradle / Ivy

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