/* * Copyright 2011 Eric F. Savage, code@efsavage.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.ajah.util.compare; import java.math.BigDecimal; import java.util.Collection; import java.util.Date; import com.ajah.util.CollectionUtils; import com.ajah.util.Identifiable; import com.ajah.util.IdentifiableEnum; import com.ajah.util.StringUtils; import com.ajah.util.ToStringable; /** * Utility methods for comparisons. * * @author <a href="http://efsavage.com">Eric F. Savage</a>, <a * href="mailto:code@efsavage.com">code@efsavage.com</a>. */ public class CompareUtils { /** * Compares two {@link BigDecimal}s, allowing for nulls. * * @param first * The first number, may be null. * @param second * The second number, may be null. * @param nullsEqual * Should two null objects be treated as equal (true) or throw an * exception (false)? * @return The comparison of the two numbers or 0 if both are null and * nullsEqual is true. */ public static int compare(final BigDecimal first, final BigDecimal second, final boolean nullsEqual) { final int retVal = compareNulls(first, second, nullsEqual); if (retVal != 0 || (nullsEqual && first == null)) { return retVal; } return first.compareTo(second); } /** * Compares two booleans, with true being greater than false. * * @param first * First value. * @param second * Second value. * @return -1 if first is false and second is true, 1 if first is true and * second is false, otherwise 0. */ public static int compare(final boolean first, final boolean second) { if (first == second) { return 0; } else if (first) { return 1; } return -1; } /** * Compares two {@link Date}, checking for nulls first. * * @see Date#compareTo(Date) * @param first * The first date, may be null. * @param second * The second date, may be null. * @param nullsEqual * Should two null objects be treated as equal (true) or throw an * exception (false)? * @return The comparison of the two Dates. * @throws IllegalArgumentException * If Both values are null and nullsEqual is false, as this * means it cannot be passed along to a comparator even though * they are "equal". */ public static int compare(final Date first, final Date second, final boolean nullsEqual) { final int retVal = compareNulls(first, second, nullsEqual); if (nullsEqual && retVal == 0 && first == null) { return retVal; } if (retVal != 0) { return retVal; } return first.compareTo(second); } /** * Compares two doubles. * * @param first * First value. * @param second * Second value. * @return -1 if first < second, 1 if first > second, 0 if equal. */ public static int compare(final double first, final double second) { if (first < second) { return -1; } else if (first > second) { return 1; } return 0; } public static int compare(final IdentifiableEnum<String> first, final IdentifiableEnum<String> second, final boolean nullsEqual) { final int retVal = compareNulls(first, second, nullsEqual); if (nullsEqual && retVal == 0 && first == null) { return retVal; } if (retVal != 0) { return retVal; } return first.getId().compareTo(second.getId()); } /** * Compares two longs. * * @param first * First value. * @param second * Second value. * @return -1 if first < second, 1 if first > second, 0 if equal. */ public static int compare(final long first, final long second) { if (first < second) { return -1; } else if (first > second) { return 1; } return 0; } /** * Compares two strings via {@link #compare(String, String, boolean)}, with * nullsEqual set to false. * * @see String#compareTo(String) * @param first * The first string, may be null. * @param second * The second string, may be null. * @return The comparison of the two Strings. * @throws IllegalArgumentException * If Both values are null, as this means it cannot be passed * along to a comparator even though they are "equal". */ public static int compare(final String first, final String second) { return compare(first, second, false); } /** * Compares two strings, checking for nulls first. * * @see String#compareTo(String) * @param first * The first string, may be null. * @param second * The second string, may be null. * @param nullsEqual * Should two null objects be treated as equal (true) or throw an * exception (false)? * @return The comparison of the two Strings. * @throws IllegalArgumentException * If Both values are null and nullsEqual is false, as this * means it cannot be passed along to a comparator even though * they are "equal". */ public static int compare(final String first, final String second, final boolean nullsEqual) { final int retVal = compareNulls(first, second, nullsEqual); if (nullsEqual && retVal == 0 && first == null) { return retVal; } if (retVal != 0) { return retVal; } return first.compareTo(second); } /** * Compares two {@link ToStringable}s, checking for nulls first. * * @see String#compareTo(String) * @param first * The first ToStringable, may be null. * @param second * The second ToStringable, may be null. * @param nullsEqual * Should two null objects be treated as equal (true) or throw an * exception (false)? * @return The comparison of the two ToStringables. * @throws IllegalArgumentException * If Both values are null and nullsEqual is false, as this * means it cannot be passed along to a comparator even though * they are "equal". */ public static int compare(final ToStringable first, final ToStringable second, final boolean nullsEqual) { final int retVal = compareNulls(first, second, nullsEqual); if (nullsEqual && retVal == 0 && first == null) { return retVal; } if (retVal != 0) { return retVal; } return compare(first.toString(), second.toString()); } /** * Compares two identifiables, checking for nulls on the objects and IDs * first. * * @see Comparable#compareTo(Object) * @param first * The first object, may be null. * @param second * The second object, may be null. * @param nullsEqual * Should two null objects be treated as equal (true) or throw an * exception (false)? * @return The comparison of the two object. * @throws IllegalArgumentException * If Both values are null and nullsEqual is false, as this * means it cannot be passed along to a comparator even though * they are "equal". */ public static <K extends Comparable<K>, T extends Identifiable<K>> int compareIds(final T first, final T second, final boolean nullsEqual) { int retVal = compareNulls(first, second, nullsEqual); if (nullsEqual && retVal == 0 && first == null) { return retVal; } retVal = compareNulls(first.getId(), second.getId(), nullsEqual); if (nullsEqual && retVal == 0 && first.getId() == null) { return retVal; } if (retVal != 0) { return retVal; } return first.getId().compareTo(second.getId()); } /** * Compares two strings, checking for nulls first, and ignoring case. * * @see String#compareToIgnoreCase(String) * @param first * The first string, may be null. * @param second * The second string, may be null. * @return The comparison of the two Strings. * @throws IllegalArgumentException * If Both values are null, as this means it cannot be passed * along to a comparator even though they are "equal". */ public static int compareIgnoreCase(final String first, final String second) { final int retVal = compareNulls(first, second); if (retVal != 0) { return retVal; } return first.compareToIgnoreCase(second); } /** * Compares objects for null via * {@link #compareNulls(Object, Object, boolean)}, with nullsEqual set to * false. * * @param first * First value. * @param second * Second value. * @return -1 if first is null and second is not, 1 if first is not null and * second is, 0 if both are not-null. * @throws IllegalArgumentException * If Both values are null, as this means it cannot be passed * along to a comparator even though they are "equal". */ public static int compareNulls(final Object first, final Object second) { return compareNulls(first, second, false); } /** * Compares two objects, with null being considered lesser than not-null. * * @param first * First value. * @param second * Second value. * @param nullsEqual * @return -1 if first is null and second is not, 1 if first is not null and * second is, 0 if both are not-null. * @throws IllegalArgumentException * If Both values are null and nullsEqual is false, as this * means it cannot be passed along to a comparator even though * they are "equal". */ public static int compareNulls(final Object first, final Object second, final boolean nullsEqual) { if (first == null) { if (second == null) { if (nullsEqual) { return 0; } throw new IllegalArgumentException("Both values are null"); } return -1; } if (second == null) { return 1; } return 0; } /** * Compares the presence (not the contents) of two collections, checking for * nulls first. An empty or null collection will sort lower (-1) than a * populated one. * * @see String#compareTo(String) * @param first * The first string, may be null. * @param second * The second string, may be null. * @return The comparison of the two Strings. * @throws IllegalArgumentException * If Both values are null and nullsEqual is false, as this * means it cannot be passed along to a comparator even though * they are "equal". */ public static int comparePresence(final Collection<?> first, final Collection<?> second) { final int retVal = compareNulls(first, second, true); if (retVal != 0) { return retVal; } if (CollectionUtils.isEmpty(first)) { if (CollectionUtils.isEmpty(second)) { // both empty return 0; } // empty, not-empty return -1; } if (CollectionUtils.isEmpty(second)) { // not-empty, empty return 1; } // both not-empty return 0; } /** * Compares the presence (not the contents) of two strings, checking for * nulls first. An empty or null string will sort lower (-1) than a * non-blank string. Null values are considered equal to empty strings. * * @see String#compareTo(String) * @param first * The first string, may be null. * @param second * The second string, may be null. * @return The comparison of the two Strings. * @throws IllegalArgumentException * If Both values are null and nullsEqual is false, as this * means it cannot be passed along to a comparator even though * they are "equal". */ public static int comparePresence(final String first, final String second) { final int retVal = compareNulls(first, second, true); if (retVal != 0) { return retVal; } if (StringUtils.isBlank(first)) { if (StringUtils.isBlank(second)) { // both blank return 0; } // blank, not-blank return -1; } if (StringUtils.isBlank(second)) { // not-blank, blank return 1; } // not-blank, not-blank return 0; } /** * Compares two comparables, checking for nulls first. * * @see Comparable#compareTo(Object) * @param first * The first object, may be null. * @param second * The second object, may be null. * @param nullsEqual * Should two null objects be treated as equal (true) or throw an * exception (false)? * @return The comparison of the two object. * @throws IllegalArgumentException * If Both values are null and nullsEqual is false, as this * means it cannot be passed along to a comparator even though * they are "equal". */ public static <T2, T1 extends Comparable<T2>> int safeCompare(final T1 first, final T2 second, final boolean nullsEqual) { final int retVal = compareNulls(first, second, nullsEqual); if (nullsEqual && retVal == 0 && first == null) { return retVal; } if (retVal != 0) { return retVal; } return first.compareTo(second); } }