StringComparator.java
package net.zer0bandwidth.android.lib.util;
import java.util.Comparator;
/**
* There might be lots of ways to compare strings. There are at least two of
* them just in this library. Also, I get pretty crossed up as to how the value
* returned by a comparator works. So here's an interface with some handy
* semantic constants for derpy developers like myself.
* @since zer0bandwidth-net/android 0.2.1 (#56)
*/
public interface StringComparator
extends Comparator<String>
{
/**
* An abstract base class which provides logic that will be common to most,
* if not all, actual implementations of the interface. The implementation
* of {@link #compare} begins by comparing the strings as objects, examining
* their nullness/emptiness. If that algorithm is already enough to identify
* the "greater" string, then there is no need for the actual implementation
* class to do anything further. Otherwise, the implementation class's
* {@link #executeComparison} may proceed already knowing that the strings
* are non-null and non-empty, and that {@link String#equals} has already
* been tried and is not worth trying again.
* @since zer0bandwidth-net/android 0.2.1 (#56)
*/
abstract class Base implements StringComparator
{
/**
* Semantic constant not intended as an actual return value; instead,
* the internal {@link #compareAsObjects} method will return this when
* its naïve evaluation of the strings (comparing their nullness
* and emptiness) isn't enough to resolve the comparison.
*/
protected static final int EVALUATION_INDETERMINATE = Integer.MIN_VALUE;
/**
* Compares the strings as objects — that is, it bases its
* comparison on the simple question of whether one or both of the
* strings is null or empty.
*
* <ul>
* <li>Two references to the same object are trivially equal.</li>
* <li>Null and/or empty strings are equal.</li>
* <li>Non-null strings for which {@code s1.equals(s2)} are equal.</li>
* <li>
* Otherwise, a non-null, non-empty string is "greater than" a null
* or empty string.
* </li>
* </ul>
*
* @param s1 the first string to compare
* @param s2 the second string to compare
* @return an indication of order or equality
* <ul>
* <li>{@link #EQUAL} (0) if the strings are equal</li>
* <li>{@link #S1_IS_GREATER} (1) if the first is "greater"</li>
* <li>{@link #S2_IS_GREATER} (-1) if the second is "greater"</li>
* <li>
* {@link #EVALUATION_INDETERMINATE} if this method's
* naïve algorithm was not enough to figure out which is
* greater
* </li>
* </ul>
*/
protected static int compareAsObjects( String s1, String s2 )
{
if( s1 == null )
return( ( s2 == null || s2.isEmpty() ) ? EQUAL : S2_IS_GREATER ) ;
else if( s2 == null ) // and s1 is not
return( s1.isEmpty() ? EQUAL : S1_IS_GREATER ) ;
else if( s1.isEmpty() ) // and neither string is null
return( s2.isEmpty() ? EQUAL : S2_IS_GREATER ) ;
else if( s1.equals(s2) )
return EQUAL ;
else
return EVALUATION_INDETERMINATE ;
}
/**
* Executes the comparison that is specific to this class. If the base
* class's implementation of {@link #compare} is used as-is, then this
* method is guaranteed that the strings are non-null, non-empty, and
* that {@link String#equals} has returned {@code false}.
* @param s1 the first string to compare
* @param s2 the second string to compare
* @return an indication of order or equality
* <ul>
* <li>{@link #EQUAL} (0) if the strings are equal</li>
* <li>{@link #S1_IS_GREATER} (1) if the first is "greater"</li>
* <li>{@link #S2_IS_GREATER} (-1) if the second is "greater"</li>
* </ul>
*/
protected abstract int executeComparison( String s1, String s2 ) ;
/**
* Executes {@link #compareAsObjects} first to determine whether there
* is already a clear winner, and if not, calls the comparator's more
* specific {@link #executeComparison} method. Implementation classes
* must override this method if they don't want {@code compareAsObjects}
* to be executed first.
*
* <p>This method <i>does not</i> throw a {@link NullPointerException},
* as it is specifically designed to handle null values.</p>
*
* @param s1 the first string to compare
* @param s2 the second string to compare
* @return an indication of order or equality
* <ul>
* <li>{@link #EQUAL} (0) if the strings are equal</li>
* <li>{@link #S1_IS_GREATER} (1) if the first is "greater"</li>
* <li>{@link #S2_IS_GREATER} (-1) if the second is "greater"</li>
* <li>
* {@link #EVALUATION_INDETERMINATE} if this method's
* naïve algorithm was not enough to figure out which is
* greater
* </li>
* </ul>
*/
@Override
public int compare( String s1, String s2 )
{
int zAsObjects = compareAsObjects( s1, s2 ) ;
return( zAsObjects == EVALUATION_INDETERMINATE ?
this.executeComparison( s1, s2 ) : zAsObjects ) ;
}
}
/** Semantic constant indicating the <b>first</b> string is "greater". */
int S1_IS_GREATER = 1 ;
/** Semantic constant indicating the <b>second</b> string is "greater". */
int S2_IS_GREATER = -1 ;
/** Semantic constant indicating that the strings are equal. */
int EQUAL = 0 ;
}