cf.addInterface(Comparator.class);
cf.addInterface(Serializable.class);
// Define fields to hold usage comparator and collator.
TypeDesc comparatorType = TypeDesc.forClass(Comparator.class);
TypeDesc comparatorArrayType = comparatorType.toArrayType();
cf.addField(Modifiers.PRIVATE,
"mCollators", comparatorArrayType).markSynthetic();
cf.addField(Modifiers.PRIVATE,
"mUsingComparators", comparatorArrayType).markSynthetic();
// Create constructor to initialize fields.
TypeDesc[] paramTypes = {
comparatorArrayType, comparatorArrayType
};
MethodInfo ctor = cf.addConstructor(Modifiers.PUBLIC, paramTypes);
ctor.markSynthetic();
CodeBuilder builder = new CodeBuilder(ctor);
builder.loadThis();
builder.invokeSuperConstructor(null);
builder.loadThis();
builder.loadLocal(builder.getParameter(0));
builder.storeField("mCollators", comparatorArrayType);
builder.loadThis();
builder.loadLocal(builder.getParameter(1));
builder.storeField("mUsingComparators", comparatorArrayType);
builder.returnVoid();
// Create the all-important compare method.
Method compareMethod, compareToMethod;
try {
compareMethod = Comparator.class.getMethod
("compare", new Class[] {Object.class, Object.class});
compareToMethod = Comparable.class.getMethod
("compareTo", new Class[] {Object.class});
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString());
}
MethodInfo mi = cf.addMethod(compareMethod);
mi.markSynthetic();
builder = new CodeBuilder(mi);
Label endLabel = builder.createLabel();
LocalVariable obj1 = builder.getParameter(0);
LocalVariable obj2 = builder.getParameter(1);
// The first rule always applies to the beans directly. All others
// apply to properties.
BeanComparator[] ruleParts = rules.getRuleParts();
BeanComparator bc = ruleParts[0];
if ((bc.mFlags & 0x01) != 0) {
// Reverse beans.
LocalVariable temp = obj1;
obj1 = obj2;
obj2 = temp;
}
// Handle the case when obj1 and obj2 are the same (or both null)
builder.loadLocal(obj1);
builder.loadLocal(obj2);
builder.ifEqualBranch(endLabel, true);
// Do null order checks for beans.
boolean nullHigh = (bc.mFlags & 0x02) == 0;
Label label = builder.createLabel();
builder.loadLocal(obj1);
builder.ifNullBranch(label, false);
builder.loadConstant(nullHigh ? 1 : -1);
builder.returnValue(TypeDesc.INT);
label.setLocation();
label = builder.createLabel();
builder.loadLocal(obj2);
builder.ifNullBranch(label, false);
builder.loadConstant(nullHigh ? -1 : 1);
builder.returnValue(TypeDesc.INT);
label.setLocation();
// Call 'using' Comparator if one is provided.
LocalVariable result =
builder.createLocalVariable("result", TypeDesc.INT);
if (bc.mUsingComparator != null) {
builder.loadThis();
builder.loadField("mUsingComparators", comparatorArrayType);
builder.loadConstant(0);
builder.loadFromArray(TypeDesc.forClass(Comparator.class));
builder.loadLocal(obj1);
builder.loadLocal(obj2);
builder.invoke(compareMethod);
builder.storeLocal(result);
builder.loadLocal(result);
label = builder.createLabel();
builder.ifZeroComparisonBranch(label, "==");
builder.loadLocal(result);
builder.returnValue(TypeDesc.INT);
label.setLocation();
}
// Cast bean parameters to correct types so that properties may be
// accessed.
TypeDesc type = TypeDesc.forClass(bc.mBeanClass);
builder.loadLocal(obj1);
builder.checkCast(type);
builder.storeLocal(obj1);
builder.loadLocal(obj2);
builder.checkCast(type);
builder.storeLocal(obj2);
// Generate code to perform comparisons against each property.
for (int i=1; i<ruleParts.length; i++) {
bc = ruleParts[i];
BeanProperty prop =
(BeanProperty)bc.getProperties().get(bc.mOrderByName);
Class propertyClass = prop.getType();
TypeDesc propertyType = TypeDesc.forClass(propertyClass);
// Create local variable to hold property values.
LocalVariable p1 = builder.createLocalVariable("p1", propertyType);
LocalVariable p2 = builder.createLocalVariable("p2", propertyType);
// Access properties and store in local variables.
builder.loadLocal(obj1);
builder.invoke(prop.getReadMethod());
builder.storeLocal(p1);
builder.loadLocal(obj2);
builder.invoke(prop.getReadMethod());
builder.storeLocal(p2);
if ((bc.mFlags & 0x01) != 0) {
// Reverse properties.
LocalVariable temp = p1;
p1 = p2;
p2 = temp;
}
Label nextLabel = builder.createLabel();
// Handle the case when p1 and p2 are the same (or both null)
if (!propertyClass.isPrimitive()) {
builder.loadLocal(p1);
builder.loadLocal(p2);
builder.ifEqualBranch(nextLabel, true);
// Do null order checks for properties.
nullHigh = (bc.mFlags & 0x02) == 0;
label = builder.createLabel();
builder.loadLocal(p1);
builder.ifNullBranch(label, false);
builder.loadConstant(nullHigh ? 1 : -1);
builder.returnValue(TypeDesc.INT);
label.setLocation();
label = builder.createLabel();
builder.loadLocal(p2);
builder.ifNullBranch(label, false);
builder.loadConstant(nullHigh ? -1 : 1);
builder.returnValue(TypeDesc.INT);
label.setLocation();
}
// Call 'using' Comparator if one is provided, else assume
// Comparable.
if (bc.mUsingComparator != null) {
builder.loadThis();
builder.loadField("mUsingComparators", comparatorArrayType);
builder.loadConstant(i);
builder.loadFromArray(TypeDesc.forClass(Comparator.class));
builder.loadLocal(p1);
builder.convert(propertyType, propertyType.toObjectType());
builder.loadLocal(p2);
builder.convert(propertyType, propertyType.toObjectType());
builder.invoke(compareMethod);
} else {
// If case-sensitive is off and a collator is provided and
// property could be a String, apply collator.
if ((bc.mFlags & 0x04) == 0 && bc.mCollator != null &&
propertyClass.isAssignableFrom(String.class)) {
Label resultLabel = builder.createLabel();
if (!String.class.isAssignableFrom(propertyClass)) {
// Check if both property values are strings at
// runtime. If they aren't, cast to Comparable and call
// compareTo.
TypeDesc stringType = TypeDesc.STRING;
builder.loadLocal(p1);
builder.instanceOf(stringType);
Label notString = builder.createLabel();
builder.ifZeroComparisonBranch(notString, "==");