I spent the last few days in an Oracle Database Security class. I had a little free time in between and decided to see how much work would be required to do this sort thing. Surprisingly, not as much as I thought.
For anyone interested, StringComparator allows you to sort on any public member variable or method. I made some choices based on my own preferences here that could easily be coded a different way. (See code below)
The requirements are simple.
StringComparator strComp = new StringComparator(Object sample, String sortbyme, String sorttype);
creates the Comparator object. "sample" is an object of the type that will be sorted. "sortbyme" is the case-sensitive name of the field or method that will be used to drive the sort. "sorttype" is either "string" or "method" indicating the obvious. The fields and/or methods used for sorting must evaluate as a String. (I have to add code to prevent other types.)
The code could use a few checks here and there, but works fairly well so far in limited testing. I would appreciate any comments, suggestions, etc.
Code:
import java.util.*;
import java.lang.reflect.*;
public class StringComparator implements Comparator{
Field[] publicFields;
Method[] theMethods;
Class[] parameterTypes;
Vector methvec;
Vector fieldvec;
String sortBy = "";
String sortType = "string";
boolean nameMatch = true;
int methodIndex = 0;
/** Creates a new instance of StringComparator */
public StringComparator() {
}
public StringComparator(Object sample, String sortbyme, String sorttype) {
sortBy = sortbyme;
sortType = sorttype;
Class c0 = sample.getClass(); //get sample class
methvec = new Vector();
fieldvec = new Vector();
String tempName = "";
theMethods = c0.getMethods();
for (int i = 0; i < theMethods.length; i++) {
tempName = theMethods[i].getName();
methvec.add(tempName);
if (tempName.equals(sortBy)) {
parameterTypes = theMethods[i].getParameterTypes();
methodIndex = i;
}
}
publicFields = c0.getFields();
for (int i = 0; i < publicFields.length; i++) {
fieldvec.add(publicFields[i].getName());
}
if (sortType.equals("string")) {
nameMatch = fieldvec.contains(sortBy);
}
if (sortType.equals("method")) {
nameMatch = methvec.contains(sortBy);
}
} // end of constructor
public boolean isValid() {
return nameMatch;
}
public void setSortBy(String sortbyme) {
sortBy = sortbyme;
}
public String getSortBy() {
return sortBy;
}
public int compare(Object obj1, Object obj2) {
Class c1 = obj1.getClass();
Class c2 = obj2.getClass();
String str1val = "";
String str2val = "";
if (sortType.equals("string")) {
try {
Field str1 = c1.getField(getSortBy());
Field str2 = c2.getField(getSortBy());
str1val = (String) str1.get(obj1);
str2val = (String) str2.get(obj2);
} catch (Exception e) {
System.out.println(e);
}
}
if (sortType.equals("method")) {
try {
str1val = (String) theMethods[methodIndex].invoke(obj1, parameterTypes);
str2val = (String) theMethods[methodIndex].invoke(obj2, parameterTypes);
} catch (IllegalAccessException e) {
System.out.println(e);
} catch (InvocationTargetException e) {
System.out.println(e);
}
}
return str1val.compareTo(str2val);
}
} I was concerned about overhead. So one of the choices I made was to pre-populate a lot of the metadata from the reflection api during construction of the object. This moved repetitive calls out of the actual compare() method. I will probably do some timing comparisons on say a 10,000 object array to see how it stacks up to a normal Comparator for the same task...when I get some more time anyway.