Folks,
<edit purpose="bump" secondaryPurpose="explain stuff a bit better">
Sorry I didn't explain what this thing is... and zero bites in the veiw of several active silver backs means, I presume, that I did something magorly wrong... so...
This class class calls the
compare method passing the result of named "getter" method on the two objects to compare. So what? Well... Ummm... It means you can sort by any field of any List (or PriorityQueue, or what-have-you) without having to pre-write a Comparator.... Which is good.
</edit>
I was inspired by a blog entry by ?was it warnerja? which was referenced in a thread which was linked from an off-topic post in a thread I read last ?Thursday? night... and to cut a long storry short... I can't find the blog entry again :(
My implementation is based on [this one by Lone Deranger|http://www.planet-source-code.com/vb/scripts/ShowCode.asp?txtCodeId=3308&lngWId=2] ... which has a couple of problems
1. infinite recursion in the equals method == OutOfStackSpaceException
2. It gets the method defintion every time you call compare == sloooooooooooowwwwwwwwwww.
This version
* is a bit faster
* verifies as much as possible in constructor.
* uses generics to verify (as far as possible) the runtime "compared Class" type.
But I'm pretty sure someone who really knows there generics could improve upon the implementation (or quit possibly the fundamental design). Specifically I'm not in love passing the Class of the
compared objects as well as define the generic type. There's GOT to be a better way... I'm just to inexpert/stupid to recognise it... I've probably been staring straight at it for the last two hours.
So.... This thing works... but any suggestions and/or constructive criticism would be welcome ;-) Thanx.
UniversalReflectiveComparator.java
package forums;
import java.util.Comparator;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
public class UniversalReflectiveComparator<T> implements Comparator<T>
{
public static String DEFAULT_METHOD_NAME = "toString";
public static final int ASCENDING = 1;
public static final int DESCENDING = -1;
public static int DEFAULT_ORDER = ASCENDING;
private static final java.lang.Object[] NO_ARGS = null;
private final String methodName;
private final Method getterMethod;
private final int order;
public UniversalReflectiveComparator(Class classToCompare)
throws NoSuchMethodException
{
this(classToCompare, DEFAULT_METHOD_NAME, DEFAULT_ORDER);
}
public UniversalReflectiveComparator(Class classToCompare, String methodName)
throws NoSuchMethodException
{
this(classToCompare, methodName, DEFAULT_ORDER);
}
public UniversalReflectiveComparator(Class classToCompare, int order)
throws NoSuchMethodException
{
this(classToCompare, DEFAULT_METHOD_NAME, order);
}
@SuppressWarnings("unchecked")
public UniversalReflectiveComparator(Class classToCompare, String methodName, int order)
throws NoSuchMethodException
{
getterMethod = classToCompare.getMethod(methodName, (java.lang.Class<?>[])null);
Class returnType = getterMethod.getReturnType();
if ("void".equals(returnType.getName())) {
throw new IllegalArgumentException("Cannot compare on the '"+methodName+"' method"
+ " because its return type is void (ie: it does not return a value to compare).");
}
if ( !doesImplement(returnType, Comparable.class.getCanonicalName()) ) {
throw new IllegalArgumentException("Cannot compare on the '"+methodName+"' method"
+ " because its return type '"+returnType.getName()+"' does not implement Comparable.");
}
this.methodName = methodName;
this.order = order;
}
@Override
@SuppressWarnings("unchecked")
public int compare(T o1, T o2) {
try {
Comparable o1attribute = (Comparable) getterMethod.invoke(o1, NO_ARGS);
Comparable o2attribute = (Comparable) getterMethod.invoke(o2, NO_ARGS);
return o1attribute.compareTo(o2attribute) * order;
} catch (Exception e) {
throw new IllegalStateException("Failed to compare "+clazz(o1)+ " to "+clazz(o2), e);
}
}
/**
* Returns the type and string value of the given object.
* eg: java.lang.String 'Hello World!'
*/
private static String clazz(Object object) {
return object.getClass().getCanonicalName()+" '"+String.valueOf(object)+"'";
}
/**
* Returns: Does the given clazz implement the given interfaceName
* @param clazz the Class to be examined.
* @param canonicalInterfaceName the full name (with or without generics)
* of the interface sought: path.to.Interface[<path.to.GenericType>]
*/
private static boolean doesImplement(Class clazz, String canonicalInterfaceName) {
for ( Type intrface : clazz.getGenericInterfaces() ) {
if ( intrface.toString().startsWith(canonicalInterfaceName) ) {
return true;
}
}
return false;
}
}
UniversalComparatorTest.java
package forums;
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.io.FileReader;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
// A simple "Bean" class for testing only.
// Does NOT implement comparable!
class Word {
private final String word;
public Word(String word) { this.word=word; }
public String getWord() { return this.word; }
public void noop() { }
public String toString() { return this.word; }
public Word notComparable() { return new Word("notComparable"); }
}
class UniversalComparatorTest
{
@SuppressWarnings("unchecked")
public static void main(String[] args) {
try {
List<Word> words = readWords("WordFinder.txt");
System.err.println("---------------------------------------------------");
// SUCCESS CASES
//short for new UniversalReflectiveComparator<Word>(Word.class,"toString",ASCENDING);
try {
Collections.sort(words, new UniversalReflectiveComparator<Word>(Word.class));
System.out.println(words);
} catch (Exception e) {e.printStackTrace();}
System.err.println("---------------------------------------------------");
try {
Collections.sort(words, new UniversalReflectiveComparator<Word>(Word.class, "getWord", UniversalReflectiveComparator.DESCENDING));
} catch (Exception e) {e.printStackTrace();}
System.out.println(words);
System.err.println("---------------------------------------------------");
try {
Collections.sort(words, new UniversalReflectiveComparator<Word>(Word.class, UniversalReflectiveComparator.DESCENDING));
} catch (Exception e) {e.printStackTrace();}
System.out.println(words);
System.err.println("---------------------------------------------------");
// FAIL CASES
try {
Collections.sort(words, new UniversalReflectiveComparator<Word>(Word.class, "nonExistantMethodName"));
} catch (Exception e) {e.printStackTrace();}
System.err.println("---------------------------------------------------");
try {
Collections.sort(words, new UniversalReflectiveComparator<Word>(Word.class, "noop"));
} catch (Exception e) {e.printStackTrace();}
System.err.println("---------------------------------------------------");
try {
Collections.sort(words, new UniversalReflectiveComparator<Word>(Word.class, "notComparable"));
} catch (Exception e) {e.printStackTrace();}
System.err.println("---------------------------------------------------");
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* reads each line of the given file into a List of strings.
* @param String filename - the name of the file to read
* @return an List handle ArrayList of strings containing file contents.
*/
public static List<Word> readWords(String filename) throws FileNotFoundException, IOException {
List<Word> results = new ArrayList<Word>();
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(filename));
String line = null;
while ( (line=reader.readLine()) != null ) {
results.add(new Word(line));
}
} finally {
if(reader!=null)reader.close();
}
return results;
}
}
Cheers all. Keith.
PS: If you happen to know that blog entry... could you post a link... please.
Edited by: corlettk on 2/11/2008 15:52 - Ooops!
Edited by: corlettk on 2/11/2008 18:20 - You Snobs!
Edited by: corlettk on 2/11/2008 19:00 - Dodgems bump!