Thursday, January 11, 2007

import java.lang.reflect.Method;
import java.util.Comparator;
import java.util.Date;
import java.util.Map;

public class ObjectComparator implements Comparator
{

/** Constant to specify a descending sort for the attribute */
public static final int SORT_DESCENDING = 0;

/** Constant to specify an ascending sort for the attribute */
public static final int SORT_ASCENDING = 1;

/** The fully-qualified class name of the object to be sorted */
private String sortObjectClassName;

/** The actual Class object of the object to be sorted */
private Class sortObjectClass;

/** An array of Method objects that correspond to each sort attribute */
private Method sortFieldGetterMethods[];

/** An array of Strings that represent the argument(s) to pass to the getter method (really only necessary for Map objects) */
private String sortFieldMethodArgs[];

/** An integer array that corresponds to each sort attribute's sort order */
private int sortFieldOrder[];

/** If the classes to sort are instances of map, this is set to true */
private boolean isMap;

/**
* Private Constructor - not used
* @exception ObjectComparatorException
**/
private ObjectComparator() throws ObjectComparatorException
{
}

/**
* Constructor for ObjectComparator when passing in the actual object that will be sorted
* by later on. This is an alternative to passing in the fully-qualified class name for the
* objects that will be sorted.
*
* @param sortObject An instance of the object that will be sorted on
* @param sortFields An array of all the field names to sort by
* @param sortFieldOrder An integer array representing the sort order for the fields that will be sorted.
* Should either contain ObjectComparator.SORT_DESCENDING or ObjectComparator.SORT_ASCENDING
*
* @exception ObjectComparatorException if any errors occurred during creation
**/
public ObjectComparator(
Object sortObject,
String[] sortFields,
int[] sortFieldOrder)
throws ObjectComparatorException
{
this.sortObjectClass = sortObject.getClass();
this.sortObjectClassName = sortObjectClass.getName();
this.checkIsMap();
loadMethods(sortFields, sortFieldOrder);
}

/**
* Constructor for ObjectComparator when passing in the fully-qualified class name of the objects that will be sorted
* by later on. This is an alternative to passing in an instance of the actual object.
*
* @param sortObjectClassName The fully-qualified class name for the objects that will be sorted
* @param sortFields An array of all the field names to sort by
* @param sortFieldOrder An integer array representing the sort order for the fields that will be sorted.
* Should either contain ObjectComparator.SORT_DESCENDING or ObjectComparator.SORT_ASCENDING
*
* @exception ObjectComparatorException if any errors occurred during creation
**/
public ObjectComparator(
String sortObjectClassName,
String[] sortFields,
int[] sortFieldOrder)
throws ObjectComparatorException
{
try
{
this.sortObjectClass = Class.forName(sortObjectClassName);
this.sortObjectClassName = sortObjectClassName;
this.checkIsMap();
}
catch (ClassNotFoundException e)
{
throw new ObjectComparatorException(
"Error creating ObjectComparator: " + e);
}
loadMethods(sortFields, sortFieldOrder);
}

/**
* Constructor for ObjectComparator when passing in the actual object that will be sorted
* by later on. This is an alternative to passing in the fully-qualified class name for the
* objects that will be sorted. This constructor is also used when you want to sort by a maximum
* of only 3 fields, as opposed to passing in an array of fields to sort by.
*
* @param sortObject An instance of the object that will be sorted on
* @param sortField1 First field to sort by
* @param sortField1SortOrder Sort order for first field, either ObjectComparator.SORT_DESCENDING or ObjectComparator.SORT_ASCENDING
* @param sortField2 Second field to sort by
* @param sortField2SortOrder Sort order for second field, either ObjectComparator.SORT_DESCENDING or ObjectComparator.SORT_ASCENDING
* @param sortField3 Third field to sort by
* @param sortField3SortOrder Sort order for third field, either ObjectComparator.SORT_DESCENDING or ObjectComparator.SORT_ASCENDING
*
* @exception ObjectComparatorException if any errors occurred during creation
**/
public ObjectComparator(
Object sortObject,
String sortField1,
int sortField1SortOrder,
String sortField2,
int sortField2SortOrder,
String sortField3,
int sortField3SortOrder)
throws ObjectComparatorException
{
this.sortObjectClass = sortObject.getClass();
this.sortObjectClassName = sortObjectClass.getName();
this.checkIsMap();

loadMethods(
sortField1,
sortField1SortOrder,
sortField2,
sortField2SortOrder,
sortField3,
sortField3SortOrder);
}

/**
* Constructor for ObjectComparator when passing in the fully-qualified class name of the objects that will be sorted
* by later on. This is an alternative to passing in an instance of the actual object.
* This constructor is also used when you want to sort by a maximum
* of only 3 fields, as opposed to passing in an array of fields to sort by.
*
* @param sortObjectClassName An instance of the object that will be sorted on
* @param sortField1 First field to sort by
* @param sortField1SortOrder Sort order for first field, either ObjectComparator.SORT_DESCENDING or ObjectComparator.SORT_ASCENDING
* @param sortField2 Second field to sort by
* @param sortField2SortOrder Sort order for second field, either ObjectComparator.SORT_DESCENDING or ObjectComparator.SORT_ASCENDING
* @param sortField3 Third field to sort by
* @param sortField3SortOrder Sort order for third field, either ObjectComparator.SORT_DESCENDING or ObjectComparator.SORT_ASCENDING
*
* @exception ObjectComparatorException if any errors occurred during creation
**/
public ObjectComparator(
String sortObjectClassName,
String sortField1,
int sortField1SortOrder,
String sortField2,
int sortField2SortOrder,
String sortField3,
int sortField3SortOrder)
throws ObjectComparatorException
{

try
{
this.sortObjectClass = Class.forName(sortObjectClassName);
this.sortObjectClassName = sortObjectClassName;
}
catch (ClassNotFoundException e)
{
throw new ObjectComparatorException(
"Error creating ObjectComparator: " + e);
}
loadMethods(
sortField1,
sortField1SortOrder,
sortField2,
sortField2SortOrder,
sortField3,
sortField3SortOrder);
}

/**
* Loads the proper getter method for the field
*
* @param sortFields An array of all the field names to sort by
* @param sortFieldOrder An integer array representing the sort order for the fields that will be sorted.
* Should either contain ObjectComparator.SORT_DESCENDING or ObjectComparator.SORT_ASCENDING
*
* @exception ObjectComparatorException if any errors occurred during creation
**/
private void loadMethods(String[] sortFields, int[] sortFieldOrder)
throws ObjectComparatorException
{
if (sortFields.length != sortFieldOrder.length)
{
throw new ObjectComparatorException(
"Error creating ObjectComparator. Sort Fields array count "
+ "doesn't match Sort Field Order array count.");
}

//Determine the appropriate Method object for the field to be sorted by
String sortField = "";
int sortOrder = 0;
int length = sortFields.length;
this.sortFieldGetterMethods = new Method[length];
this.sortFieldOrder = new int[length];
this.sortFieldMethodArgs = new String[length];
int getterCount = 0;
for (int i = 0; i < length; i++)
{
sortField = sortFields[i];
sortOrder = sortFieldOrder[i];
if ((sortField != null && sortField != ""))
{
this.sortFieldGetterMethods[getterCount] =
getMethodName(sortField);
if (this.isMap) this.sortFieldMethodArgs[getterCount]=sortField; // Map objects use method "get" and pass the field name as an arg
this.sortFieldOrder[getterCount] = sortOrder;
getterCount++;
}
}
}

/**
* Loads the proper getter method for the field
*
* @param sortField1 First field to sort by
* @param sortField1SortOrder Sort order for first field, either ObjectComparator.SORT_DESCENDING or ObjectComparator.SORT_ASCENDING
* @param sortField2 Second field to sort by
* @param sortField2SortOrder Sort order for second field, either ObjectComparator.SORT_DESCENDING or ObjectComparator.SORT_ASCENDING
* @param sortField3 Third field to sort by
* @param sortField3SortOrder Sort order for third field, either ObjectComparator.SORT_DESCENDING or ObjectComparator.SORT_ASCENDING
*
* @exception ObjectComparatorException if any errors occurred during creation
**/
private void loadMethods(
String sortField1,
int sortField1SortOrder,
String sortField2,
int sortField2SortOrder,
String sortField3,
int sortField3SortOrder)
throws ObjectComparatorException
{
this.sortFieldGetterMethods = new Method[3];
this.sortFieldOrder = new int[3];

if (sortField1 != null && !sortField1.equals(""))
{
this.sortFieldGetterMethods[0] = getMethodName(sortField1);
if (this.isMap) this.sortFieldMethodArgs[0]=sortField1; // Map objects use method "get" and pass the field name as an arg
this.sortFieldOrder[0] = sortField1SortOrder;
}
if (sortField2 != null && !sortField2.equals(""))
{
this.sortFieldGetterMethods[1] = getMethodName(sortField2);
if (this.isMap) this.sortFieldMethodArgs[1]=sortField2; // Map objects use method "get" and pass the field name as an arg
this.sortFieldOrder[1] = sortField2SortOrder;
}
if (sortField3 != null && !sortField3.equals(""))
{
this.sortFieldGetterMethods[2] = getMethodName(sortField3);
if (this.isMap) this.sortFieldMethodArgs[2]=sortField3; // Map objects use method "get" and pass the field name as an arg
this.sortFieldOrder[2] = sortField3SortOrder;
}
}

/**
* Checks if the Class object currently set and associated with this ObjectComparator
* implements interface Map
*
*/
private void checkIsMap() {

/*
if (this.sortObjectClass.isInstance( aMapObject ) ) {
this.isMap = true;
}
*/
/*
* The method Class.isInstance(Object) above doesn't seem to work. Even when I created
* an explicit instance of the object to sort (like a 'Hashtable'), it never
* detected the correct object type. Instead, I am trying to get a hard instance
* of the object so I can check instanceof, which *does* seem to work. If I can't
* create an instance of the object using the empty constructor, then I'm assuming
* it's not a Map!
*/
try {
Object o = this.sortObjectClass.newInstance();
if (o instanceof Map) {
this.isMap = true;
}
}
catch (InstantiationException e)
{
// Do nothing, just assume it's not a map
/*
throw new ObjectComparatorException(
"could not check for map '"
+ " (map=" + this.isMap + ") findmap: " + findmap.toString()
+ "'. Error is: "
+ e); */
}
catch (IllegalAccessException e)
{
// Do nothing, just assume it's not a map
}

}


/**
* The implementation of the Comparator interface's compare method
*
* @param obj1 The first object to compare
* @param obj2 The second object to compare
* @return int
* @see java.util.Comparator#compare(Object, Object)
**/
public int compare(Object obj1, Object obj2)
{
int returnVal = 0;
try
{
Object obj1FieldValue = null;
Object obj2FieldValue = null;

int length = sortFieldGetterMethods.length;
Method getterMethod = null;
String[] methodArg = null;

for (int i = 0; i < length; i++)
{
getterMethod = sortFieldGetterMethods[i];
methodArg = new String[] { sortFieldMethodArgs[i] };

if (getterMethod != null)
{

if (this.isMap) {
// If we're a Map, we need to invoke get() method with the key as an argument
obj1FieldValue = getterMethod.invoke(obj1, methodArg);
obj2FieldValue = getterMethod.invoke(obj2, methodArg);
} else {
// If we're not a Map, we need to invoke the getter method
obj1FieldValue = getterMethod.invoke(obj1, null);
obj2FieldValue = getterMethod.invoke(obj2, null);
}

if(obj1FieldValue == null){
if (obj2FieldValue instanceof String )
obj1FieldValue = new String();
else if (obj2FieldValue instanceof Integer )
obj1FieldValue = new Integer(0);
else if (obj2FieldValue instanceof Long)
obj1FieldValue = new Long(0);
else if (obj2FieldValue instanceof Float)
obj1FieldValue = new Float(0);
else if (obj2FieldValue instanceof Double)
obj1FieldValue = new Double(0);
else if (obj2FieldValue instanceof Date)
obj1FieldValue = new Date(0);
else obj1FieldValue = new Object();
}

if (obj1FieldValue instanceof String )
{
if ( obj2FieldValue == null )
{
obj2FieldValue = new String();
}
returnVal =
obj1FieldValue.toString().toUpperCase().compareTo(
obj2FieldValue.toString().toUpperCase());
}
else if (obj1FieldValue instanceof Integer)
{
if ( obj2FieldValue == null )
{
obj2FieldValue = new Integer(0);
}
Integer obj1Integer = (Integer) obj1FieldValue;
Integer obj2Integer = (Integer) obj2FieldValue;
returnVal = obj1Integer.compareTo(obj2Integer);
}
else if (obj1FieldValue instanceof Long)
{
if ( obj2FieldValue == null )
{
obj2FieldValue = new Long(0);
}
Long obj1Long = (Long) obj1FieldValue;
Long obj2Long = (Long) obj2FieldValue;
returnVal = obj1Long.compareTo(obj2Long);
}
else if (obj1FieldValue instanceof Float)
{
if ( obj2FieldValue == null )
{
obj2FieldValue = new Float(0);
}
Float obj1Float = (Float) obj1FieldValue;
Float obj2Float = (Float) obj2FieldValue;
returnVal = obj1Float.compareTo(obj2Float);
}
else if (obj1FieldValue instanceof Double)
{
if ( obj2FieldValue == null )
{
obj2FieldValue = new Double(0);
}
Double obj1Double = (Double) obj1FieldValue;
Double obj2Double = (Double) obj2FieldValue;
returnVal = obj1Double.compareTo(obj2Double);
}
else if (obj1FieldValue instanceof Date)
{
if ( obj2FieldValue == null )
{
obj2FieldValue = new Date(0);
}

Date obj1Date = (Date) obj1FieldValue;
Date obj2Date = (Date) obj2FieldValue;
returnVal = obj1Date.compareTo(obj2Date);
}
else
{
if ( obj2FieldValue == null )
{
obj2FieldValue = new Object();
}
returnVal =
obj1FieldValue.toString().compareTo(
obj2FieldValue.toString());
}
if (returnVal != 0)
{
if (sortFieldOrder[i] == SORT_DESCENDING)
{
returnVal = returnVal * -1;
}
break;
}
}
}
}
catch (Exception e)
{
//Can't throw an exception since the inherited compare method doesn't define it
// That means any exception when comparing the objects goes into a black hole, and
// you won't be able to know why your compare didn't work.
returnVal = 0;
}
return returnVal;
}

/**
* Used to retrieve the 'getter' method, as a Method object, for the passed in field name
*
* @param fieldName The field name
* @return The getter method
* @throws ObjectComparatorException
**/
private Method getMethodName(String fieldName)
throws ObjectComparatorException
{
Method theMethod = null;
String methodName = null;
Class[] parameterTypes = null;
String firstChar = "";
if (fieldName != null && !fieldName.equals(""))
{
if (this.isMap) {
// If we're a Map, we'll always check for method "get()"
methodName="get";
// Object o = new Object();
Object o = new Object();
parameterTypes = new Class[] { o.getClass() };

} else {
// If we're not a Map, check for bean-style get method (i.e. "getMyProperty()")
firstChar = fieldName.substring(0, 1);
if (fieldName.length() >= 2)
{
methodName =
"get" + firstChar.toUpperCase() + fieldName.substring(1);
}
else
{
methodName = "get" + firstChar.toUpperCase();
}

}
}
try
{
theMethod = sortObjectClass.getMethod(methodName, parameterTypes);
}
catch (NoSuchMethodException e)
{
throw new ObjectComparatorException(
"No getter method matches the field name passed in '"
+ fieldName + " (map=" + this.isMap + ") "
+ "'. Error is: "
+ e);
}
return theMethod;
}
}

No comments: