KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > alfresco > web > data > Sort


1 /*
2  * Copyright (C) 2005 Alfresco, Inc.
3  *
4  * Licensed under the Mozilla Public License version 1.1
5  * with a permitted attribution clause. You may obtain a
6  * copy of the License at
7  *
8  * http://www.alfresco.org/legal/license.txt
9  *
10  * Unless required by applicable law or agreed to in writing,
11  * software distributed under the License is distributed on an
12  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
13  * either express or implied. See the License for the specific
14  * language governing permissions and limitations under the
15  * License.
16  */

17 package org.alfresco.web.data;
18
19 import java.lang.reflect.Method JavaDoc;
20 import java.text.CollationKey JavaDoc;
21 import java.text.Collator JavaDoc;
22 import java.util.ArrayList JavaDoc;
23 import java.util.Comparator JavaDoc;
24 import java.util.Date JavaDoc;
25 import java.util.List JavaDoc;
26 import java.util.Locale JavaDoc;
27 import java.util.Map JavaDoc;
28
29 import org.apache.log4j.Logger;
30
31 /**
32  * Sort
33  *
34  * Base sorting helper supports locale specific case sensitive, case in-sensitive and
35  * numeric data sorting.
36  *
37  * @author Kevin Roast
38  */

39 public abstract class Sort
40 {
41    // ------------------------------------------------------------------------------
42
// Construction
43

44    /**
45     * Constructor
46     *
47     * @param data a the List of String[] data to sort
48     * @param column the column getter method to use on the row to sort
49     * @param bForward true for a forward sort, false for a reverse sort
50     * @param mode sort mode to use (see IDataContainer constants)
51     */

52    public Sort(List JavaDoc data, String JavaDoc column, boolean bForward, String JavaDoc mode)
53    {
54       this.data = data;
55       this.column = column;
56       this.bForward = bForward;
57       this.sortMode = mode;
58       
59       if (this.data.size() != 0)
60       {
61          // setup the Collator for our Locale
62
Collator JavaDoc collator = Collator.getInstance(Locale.getDefault());
63          
64          // set the strength according to the sort mode
65
if (mode.equals(IDataContainer.SORT_CASEINSENSITIVE))
66          {
67             collator.setStrength(Collator.SECONDARY);
68          }
69          else
70          {
71             collator.setStrength(Collator.IDENTICAL);
72          }
73          
74          this.keys = buildCollationKeys(collator);
75       }
76    }
77    
78    
79    // ------------------------------------------------------------------------------
80
// Abstract Methods
81

82    /**
83     * Runs the Sort routine on the current dataset
84     */

85    public abstract void sort();
86    
87    
88    // ------------------------------------------------------------------------------
89
// Helper methods
90

91    /**
92     * Build a list of collation keys for comparing locale sensitive strings or build
93     * the appropriate objects for comparison for other standard data types.
94     *
95     * @param collator the Collator object to use to build String keys
96     */

97    protected List JavaDoc buildCollationKeys(Collator JavaDoc collator)
98    {
99       List JavaDoc data = this.data;
100       int iSize = data.size();
101       List JavaDoc keys = new ArrayList JavaDoc(iSize);
102       
103       try
104       {
105          // create the Bean getter method invoker to retrieve the value for a colunm
106
String JavaDoc methodName = getGetterMethodName(this.column);
107          Class JavaDoc returnType = null;;
108          Method JavaDoc getter = null;
109          // there will always be at least one item to sort if we get to this method
110
Object JavaDoc bean = this.data.get(0);
111          try
112          {
113             getter = bean.getClass().getMethod(methodName, (Class JavaDoc [])null);
114             returnType = getter.getReturnType();
115          }
116          catch (NoSuchMethodException JavaDoc nsmerr)
117          {
118             // no bean getter method found - try Map implementation
119
if (bean instanceof Map JavaDoc)
120             {
121                Object JavaDoc obj = ((Map JavaDoc)bean).get(this.column);
122                if (obj != null)
123                {
124                   returnType = obj.getClass();
125                }
126                else
127                {
128                   if (s_logger.isInfoEnabled())
129                   {
130                      s_logger.info("Unable to get return type class for RichList column: " + column +
131                            ". Suggest set java type directly in sort component tag.");
132                   }
133                   returnType = Object JavaDoc.class;
134                }
135             }
136             else
137             {
138                throw new IllegalStateException JavaDoc("Unable to find bean getter or Map impl for column name: " + this.column);
139             }
140          }
141          
142          // create appropriate comparator instance based on data type
143
// using the strategy pattern so sub-classes of Sort simply invoke the
144
// compare() method on the comparator interface - no type info required
145
boolean bknownType = true;
146          if (returnType.equals(String JavaDoc.class))
147          {
148             if (strongStringCompare == true)
149             {
150                this.comparator = new StringComparator();
151             }
152             else
153             {
154                this.comparator = new SimpleStringComparator();
155             }
156          }
157          else if (returnType.equals(Date JavaDoc.class))
158          {
159             this.comparator = new DateComparator();
160          }
161          else if (returnType.equals(boolean.class) || returnType.equals(Boolean JavaDoc.class))
162          {
163             this.comparator = new BooleanComparator();
164          }
165          else if (returnType.equals(int.class) || returnType.equals(Integer JavaDoc.class))
166          {
167             this.comparator = new IntegerComparator();
168          }
169          else if (returnType.equals(long.class) || returnType.equals(Long JavaDoc.class))
170          {
171             this.comparator = new LongComparator();
172          }
173          else if (returnType.equals(float.class) || returnType.equals(Float JavaDoc.class))
174          {
175             this.comparator = new FloatComparator();
176          }
177          else
178          {
179             s_logger.warn("Unsupported sort data type: " + returnType + " defaulting to .toString()");
180             this.comparator = new SimpleComparator();
181             bknownType = false;
182          }
183          
184          // create a collation key for each required column item in the dataset
185
for (int iIndex=0; iIndex<iSize; iIndex++)
186          {
187             Object JavaDoc obj;
188             if (getter != null)
189             {
190                // if we have a bean getter method impl use that
191
obj = getter.invoke(data.get(iIndex), (Object JavaDoc [])null);
192             }
193             else
194             {
195                // else we must have a bean Map impl
196
obj = ((Map JavaDoc)data.get(iIndex)).get(column);
197             }
198             
199             if (obj instanceof String JavaDoc)
200             {
201                String JavaDoc str = (String JavaDoc)obj;
202                if (strongStringCompare == true)
203                {
204                   if (str.indexOf(' ') != -1)
205                   {
206                      // quote white space characters or they will be ignored by the Collator!
207
int iLength = str.length();
208                      StringBuilder JavaDoc s = new StringBuilder JavaDoc(iLength + 4);
209                      char c;
210                      for (int i=0; i<iLength; i++)
211                      {
212                         c = str.charAt(i);
213                         if (c != ' ')
214                         {
215                            s.append(c);
216                         }
217                         else
218                         {
219                            s.append('\'').append(c).append('\'');
220                         }
221                      }
222                      str = s.toString();
223                   }
224                   keys.add(collator.getCollationKey(str));
225                }
226                else
227                {
228                   keys.add(str);
229                }
230             }
231             else if (bknownType == true)
232             {
233                // the appropriate wrapper object will be create by the reflection
234
// system to wrap primative types e.g. int and boolean.
235
// therefore the correct type will be ready for use by the comparator
236
keys.add(obj);
237             }
238             else
239             {
240                if (obj != null)
241                {
242                   keys.add(obj.toString());
243                }
244                else
245                {
246                   keys.add(null);
247                }
248             }
249          }
250       }
251       catch (Exception JavaDoc err)
252       {
253          throw new RuntimeException JavaDoc(err);
254       }
255       
256       return keys;
257    }
258    
259    /**
260     * Given the array and two indices, swap the two items in the
261     * array.
262     */

263    protected void swap(final List JavaDoc v, final int a, final int b)
264    {
265       Object JavaDoc temp = v.get(a);
266       v.set(a, v.get(b));
267       v.set(b, temp);
268    }
269    
270    /**
271     * Return the comparator to be used during column value comparison
272     *
273     * @return Comparator for the appropriate column data type
274     */

275    protected Comparator JavaDoc getComparator()
276    {
277       return this.comparator;
278    }
279    
280    /**
281     * Return the name of the Bean getter method for the specified getter name
282     *
283     * @param name of the field to build getter method name for e.g. "value"
284     *
285     * @return the name of the Bean getter method for the field name e.g. "getValue"
286     */

287    protected static String JavaDoc getGetterMethodName(String JavaDoc name)
288    {
289       return "get" + name.substring(0, 1).toUpperCase() +
290              name.substring(1, name.length());
291    }
292    
293    
294    // ------------------------------------------------------------------------------
295
// Inner classes for data type comparison
296

297    private class SimpleComparator implements Comparator JavaDoc
298    {
299       /**
300        * @see org.alfresco.web.data.IDataComparator#compare(java.lang.Object, java.lang.Object)
301        */

302       public int compare(final Object JavaDoc obj1, final Object JavaDoc obj2)
303       {
304          if (obj1 == null && obj2 == null) return 0;
305          if (obj1 == null) return -1;
306          if (obj2 == null) return 1;
307          return (obj1.toString()).compareTo(obj2.toString());
308       }
309    }
310    
311    private class SimpleStringComparator implements Comparator JavaDoc
312    {
313       /**
314        * @see org.alfresco.web.data.IDataComparator#compare(java.lang.Object, java.lang.Object)
315        */

316       public int compare(final Object JavaDoc obj1, final Object JavaDoc obj2)
317       {
318          if (obj1 == null && obj2 == null) return 0;
319          if (obj1 == null) return -1;
320          if (obj2 == null) return 1;
321          return ((String JavaDoc)obj1).compareToIgnoreCase((String JavaDoc)obj2);
322       }
323    }
324    
325    private class StringComparator implements Comparator JavaDoc
326    {
327       /**
328        * @see org.alfresco.web.data.IDataComparator#compare(java.lang.Object, java.lang.Object)
329        */

330       public int compare(final Object JavaDoc obj1, final Object JavaDoc obj2)
331       {
332          if (obj1 == null && obj2 == null) return 0;
333          if (obj1 == null) return -1;
334          if (obj2 == null) return 1;
335          return ((CollationKey JavaDoc)obj1).compareTo((CollationKey JavaDoc)obj2);
336       }
337    }
338    
339    private class IntegerComparator implements Comparator JavaDoc
340    {
341       /**
342        * @see org.alfresco.web.data.IDataComparator#compare(java.lang.Object, java.lang.Object)
343        */

344       public int compare(final Object JavaDoc obj1, final Object JavaDoc obj2)
345       {
346          if (obj1 == null && obj2 == null) return 0;
347          if (obj1 == null) return -1;
348          if (obj2 == null) return 1;
349          return ((Integer JavaDoc)obj1).compareTo((Integer JavaDoc)obj2);
350       }
351    }
352    
353    private class FloatComparator implements Comparator JavaDoc
354    {
355       /**
356        * @see org.alfresco.web.data.IDataComparator#compare(java.lang.Object, java.lang.Object)
357        */

358       public int compare(final Object JavaDoc obj1, final Object JavaDoc obj2)
359       {
360          if (obj1 == null && obj2 == null) return 0;
361          if (obj1 == null) return -1;
362          if (obj2 == null) return 1;
363          return ((Float JavaDoc)obj1).compareTo((Float JavaDoc)obj2);
364       }
365    }
366    
367    private class LongComparator implements Comparator JavaDoc
368    {
369       /**
370        * @see org.alfresco.web.data.IDataComparator#compare(java.lang.Object, java.lang.Object)
371        */

372       public int compare(final Object JavaDoc obj1, final Object JavaDoc obj2)
373       {
374          if (obj1 == null && obj2 == null) return 0;
375          if (obj1 == null) return -1;
376          if (obj2 == null) return 1;
377          return ((Long JavaDoc)obj1).compareTo((Long JavaDoc)obj2);
378       }
379    }
380    
381    private class BooleanComparator implements Comparator JavaDoc
382    {
383       /**
384        * @see org.alfresco.web.data.IDataComparator#compare(java.lang.Object, java.lang.Object)
385        */

386       public int compare(final Object JavaDoc obj1, final Object JavaDoc obj2)
387       {
388          if (obj1 == null && obj2 == null) return 0;
389          if (obj1 == null) return -1;
390          if (obj2 == null) return 1;
391          return ((Boolean JavaDoc)obj1).equals((Boolean JavaDoc)obj2) ? -1 : 1;
392       }
393    }
394    
395    private class DateComparator implements Comparator JavaDoc
396    {
397       /**
398        * @see org.alfresco.web.data.IDataComparator#compare(java.lang.Object, java.lang.Object)
399        */

400       public int compare(final Object JavaDoc obj1, final Object JavaDoc obj2)
401       {
402          if (obj1 == null && obj2 == null) return 0;
403          if (obj1 == null) return -1;
404          if (obj2 == null) return 1;
405          return ((Date JavaDoc)obj1).compareTo((Date JavaDoc)obj2);
406       }
407    }
408    
409    
410    // ------------------------------------------------------------------------------
411
// Private Data
412

413    /** list of Object[] data to sort */
414    protected List JavaDoc data;
415    
416    /** column name to sort against */
417    protected String JavaDoc column;
418    
419    /** sort direction */
420    protected boolean bForward;
421    
422    /** sort mode (see IDataContainer constants) */
423    protected String JavaDoc sortMode;
424    
425    /** locale sensitive collator */
426    protected Collator JavaDoc collator;
427    
428    /** collation keys for comparisons */
429    protected List JavaDoc keys = null;
430    
431    /** the comparator instance to use for comparing values when sorting */
432    private Comparator JavaDoc comparator = null;
433    
434    // TODO: make this configurable
435
/** config value whether to use strong collation Key string comparisons */
436    private boolean strongStringCompare = false;
437    
438    private static Logger s_logger = Logger.getLogger(IDataContainer.class);
439    
440 } // end class Sort
441
Popular Tags