001/* 002 * Copyright 2008 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.ole.sys; 017 018import java.io.Serializable; 019import java.lang.reflect.InvocationTargetException; 020import java.util.Collections; 021import java.util.Comparator; 022import java.util.List; 023 024import org.apache.commons.beanutils.PropertyUtils; 025import org.apache.commons.lang.ObjectUtils; 026 027/** 028 * The comparator can dynamically implement java.util.Comparator and facilitate to sort a given colletion. This implementation is 029 * based on an article by York Davis, which was published in Java Developer's Journal (http://java.sys-con.com/read/45837.htm). 030 */ 031public class DynamicCollectionComparator<T> implements Comparator<T>, Serializable { 032 private List<T> list; 033 private String[] fieldNames; 034 private SortOrder sortOrder; 035 036 /** 037 * enumerate the valid values of sort order 038 */ 039 public enum SortOrder { 040 ASC, DESC 041 } 042 043 /** 044 * private constructs a DynamicCollectionComparator.java. 045 * 046 * @param list the given collection that needs to be sorted 047 * @param fieldName the field name ordered by 048 * @param sortOrder the given sort order, either ascending or descending 049 */ 050 private DynamicCollectionComparator(List<T> list, SortOrder sortOrder, String... fieldNames) { 051 super(); 052 053 if (fieldNames == null || fieldNames.length <= 0) { 054 throw new IllegalArgumentException("The input field names cannot be null or empty"); 055 } 056 057 this.list = list; 058 this.fieldNames = fieldNames; 059 this.sortOrder = sortOrder; 060 } 061 062 /** 063 * sort the given collection ordered by the given field name. Ascending order is used. 064 * 065 * @param list the given collection that needs to be sorted 066 * @param fieldName the field name ordered by 067 */ 068 public static <C> void sort(List<C> list, String... fieldNames) { 069 sort(list, SortOrder.ASC, fieldNames); 070 } 071 072 /** 073 * sort the given collection ordered by the given field name 074 * 075 * @param list the given collection that needs to be sorted 076 * @param fieldName the field name ordered by 077 * @param sortOrder the given sort order, either ascending or descending 078 */ 079 public static <C> void sort(List<C> list, SortOrder sortOrder, String... fieldNames) { 080 Comparator<C> comparator = new DynamicCollectionComparator<C>(list, sortOrder, fieldNames); 081 Collections.sort(list, comparator); 082 } 083 084 /** 085 * compare the two given objects for order. Returns a negative integer, zero, or a positive integer as this object is less than, 086 * equal to, or greater than the specified object. If the objects implement Comparable interface, the objects compare with each 087 * other based on the implementation; otherwise, the objects will be converted into Strings and compared as String. 088 */ 089 public int compare(T object0, T object1) { 090 int comparisonResult = 0; 091 092 for (String fieldName : fieldNames) { 093 comparisonResult = this.compare(object0, object1, fieldName); 094 095 if (comparisonResult != 0) { 096 break; 097 } 098 } 099 100 return comparisonResult; 101 } 102 103 /** 104 * compare the two given objects for order. Returns a negative integer, zero, or a positive integer as this object is less than, 105 * equal to, or greater than the specified object. If the objects implement Comparable interface, the objects compare with each 106 * other based on the implementation; otherwise, the objects will be converted into Strings and compared as String. 107 */ 108 public int compare(T object0, T object1, String fieldName) { 109 int comparisonResult = 0; 110 111 try { 112 Object propery0 = PropertyUtils.getProperty(object0, fieldName); 113 Object propery1 = PropertyUtils.getProperty(object1, fieldName); 114 115 if(propery0 == null && propery1 == null) { 116 comparisonResult = 0; 117 } 118 else if(propery0 == null) { 119 comparisonResult = -1; 120 } 121 else if(propery1 == null) { 122 comparisonResult = 1; 123 } 124 else if (propery0 instanceof Comparable) { 125 Comparable comparable0 = (Comparable) propery0; 126 Comparable comparable1 = (Comparable) propery1; 127 128 comparisonResult = comparable0.compareTo(comparable1); 129 } 130 else { 131 String property0AsString = ObjectUtils.toString(propery0); 132 String property1AsString = ObjectUtils.toString(propery1); 133 134 comparisonResult = property0AsString.compareTo(property1AsString); 135 } 136 } 137 catch (IllegalAccessException e) { 138 throw new RuntimeException("unable to compare property: " + fieldName, e); 139 } 140 catch (InvocationTargetException e) { 141 throw new RuntimeException("unable to compare property: " + fieldName, e); 142 } 143 catch (NoSuchMethodException e) { 144 throw new RuntimeException("unable to compare property: " + fieldName, e); 145 } 146 147 return comparisonResult * this.getSortOrderAsNumber(); 148 } 149 150 /** 151 * convert the sort order as an interger. If the sort order is "DESC" (descending order), reutrn -1; otherwise, return 1. 152 * 153 * @return -1 if the sort order is "DESC" (descending order); otherwise, return 1. 154 */ 155 public int getSortOrderAsNumber() { 156 return sortOrder.equals(SortOrder.ASC) ? 1 : -1; 157 } 158}