View Javadoc

1   /*
2   Wotonomy: OpenStep design patterns for pure Java applications.
3   Copyright (C) 2000 Michael Powers
4   
5   This library is free software; you can redistribute it and/or
6   modify it under the terms of the GNU Lesser General Public
7   License as published by the Free Software Foundation; either
8   version 2.1 of the License, or (at your option) any later version.
9   
10  This library is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  Lesser General Public License for more details.
14  
15  You should have received a copy of the GNU Lesser General Public
16  License along with this library; if not, see http://www.gnu.org
17  */
18  
19  package net.wotonomy.control;
20  
21  import java.io.Serializable;
22  import java.util.ArrayList;
23  import java.util.Collections;
24  import java.util.Comparator;
25  import java.util.Iterator;
26  import java.util.List;
27  
28  import net.wotonomy.foundation.NSArray;
29  import net.wotonomy.foundation.NSMutableArray;
30  import net.wotonomy.foundation.NSSelector;
31  
32  /***
33  * EOSortOrdering defines a sort key and operation.
34  * DisplayGroups use lists of EOSortOrdering to determine
35  * how to order their items.  
36  *
37  * @author michael@mpowers.net
38  * @author $Author: cgruber $
39  * @version $Revision: 894 $
40  */
41  public class EOSortOrdering implements Serializable, EOKeyValueArchiving
42  {
43      /***
44      * Sorts items in ascending order.
45      */
46      public static final 
47          NSSelector CompareAscending = new CompareAscendingComparator();
48          
49      /***
50      * Sorts items in descending order.
51      */
52      public static final 
53          NSSelector CompareDescending = new CompareDescendingComparator();
54          
55      /***
56      * Sorts items' string representations in ascending order
57      * in a case insensitive manner.
58      */
59      public static final 
60          NSSelector CompareCaseInsensitiveAscending = 
61              new CompareCaseInsensitiveAscendingComparator();
62          
63      /***
64      * Sorts items' string representations in descending order
65      * in a case insensitive manner.
66      */
67      public static final 
68          NSSelector CompareCaseInsensitiveDescending = 
69              new CompareCaseInsensitiveDescendingComparator();
70      
71      protected String key;
72      protected NSSelector selector;
73      
74      /***
75      * Factory-style constructor returns a new EOSortOrdering instance
76      * with the specified key and selector.  Neither may be null.
77      */
78      public static EOSortOrdering sortOrderingWithKey(String key, NSSelector selector)
79      {
80          return new EOSortOrdering( key, selector );
81      }
82      
83      /***
84      * Constructor creates an EOSortOrdering that uses the 
85      * specified key and selector.  Neither may be null.
86      */
87      public EOSortOrdering( String aKey, NSSelector aSelector )
88      {
89          key = aKey;
90          selector = aSelector;
91      }
92      
93      /***
94      * Constructor creates an EOSortOrdering that uses the 
95      * specified key and comparator.  Neither may be null.
96      * Not in the spec.
97      */
98      public EOSortOrdering( String aKey, Comparator aComparator )
99      {
100         key = aKey;
101         selector = new NSSelector( aKey, aComparator );
102     }
103     
104     /***
105     * Returns the property key.
106     */
107     public String key()
108     {
109         return key;   
110     }
111 
112     /***
113     * Returns the selector.
114     */
115     public NSSelector selector()
116     {
117         return selector;        
118     }
119     
120     public String toString()
121     {
122         return "[EOSortOrdering: key='"+key+"' selector='"+selector+"']";
123     }
124     
125     public boolean equals( Object anObject )
126     {
127         if ( anObject instanceof EOSortOrdering )
128         {
129             EOSortOrdering x = (EOSortOrdering) anObject;
130             if ( selector().equals( x.selector() ) )
131             {
132                 if ( key().equals( x.key() ) )
133                 {
134                     return true;
135                 }
136             }
137         }
138         return false;
139     }
140     
141     /***
142     * Sorts the specified list in place according to the specified
143     * list of EOSortOrderings.  The items will be sorted first by the
144     * first ordering, and items with equal values for that property 
145     * will be sorted by the next ordering, and so on.
146     */ 
147     public static void sortArrayUsingKeyOrderArray( 
148         List anObjectList, List aSortOrderingList )
149     {
150         List keys = new ArrayList( aSortOrderingList );
151         Collections.reverse( keys );
152         Iterator it = keys.iterator();
153         EOSortOrdering sortOrdering;
154         while ( it.hasNext() )
155         {
156             sortOrdering = (EOSortOrdering) it.next();
157             Collections.sort( anObjectList, 
158                 new DelegatingComparator( 
159                     sortOrdering.key(), sortOrdering.selector() ) );
160         }
161     }
162     
163     /***
164     * Sorts the specified list in place according to the specified
165     * list of EOSortOrderings.  The items will be sorted first by the
166     * first ordering, and items with equal values for that property 
167     * will be sorted by the next ordering, and so on.
168     */ 
169     public static NSArray sortedArrayUsingKeyOrderArray( 
170         List anObjectList, List aSortOrderingList )
171     {
172         NSArray result = new NSMutableArray();
173         result.addAll( anObjectList );
174         sortArrayUsingKeyOrderArray( result, aSortOrderingList );
175         return result;
176     }
177 
178 	public static Object decodeWithKeyValueUnarchiver(EOKeyValueUnarchiver arch) {
179 		String k = (String)arch.decodeObjectForKey("key");
180 		String sname = (String)arch.decodeObjectForKey("selectorName");
181 		NSSelector sel = null;
182 		if (sname.equals("compareAscending:"))
183 			sel = CompareAscending;
184 		else if (sname.equals("compareDescending:"))
185 			sel = CompareDescending;
186 		else if (sname.equals("compareCaseInsensitiveAscending:"))
187 			sel = CompareCaseInsensitiveAscending;
188 		else if (sname.equals("compareCaseInsensitiveDescending:"))
189 			sel = CompareCaseInsensitiveAscending;
190 		else {
191 			if (sname.endsWith(":"))
192 				sname = sname.substring(0, sname.length()-1);
193 			sel = new NSSelector(sname, new Class[]{ Object.class });
194 		}
195 		return new EOSortOrdering(k, sel);
196 	}
197 
198 	public void encodeWithKeyValueArchiver(EOKeyValueArchiver arch) {
199 		arch.encodeObject("EOSortOrdering", "class");
200 		arch.encodeObject(key(), "key");
201 		if (selector.equals(CompareAscending))
202 			arch.encodeObject("compareAscending:", "selectorName");
203 		else if (selector.equals(CompareDescending))
204 			arch.encodeObject("compareDescending:", "selectorName");
205 		else if (selector.equals(CompareCaseInsensitiveAscending))
206 			arch.encodeObject("compareCaseInsensitiveAscending:", "selectorName");
207 		else if (selector.equals(CompareCaseInsensitiveAscending))
208 			arch.encodeObject("compareCaseInsensitiveDescending:", "selectorName");
209 		else
210 			arch.encodeObject(selector.name() + ":", "selectorName");
211 	}
212 
213     private static class CompareAscendingComparator
214         extends NSSelector
215     {
216         public int compare(Object o1, Object o2)
217         {
218             if ( o1 instanceof Comparable ) 
219             {
220                 if ( o2 instanceof Comparable ) 
221                 {
222                     return ((Comparable)o1).compareTo( o2 );
223                 }
224             }
225             
226             // null handling: null is less than any object
227             
228             if ( o1 == null ) 
229             {
230                 if ( o2 == null )
231                 {
232                     return 0;
233                 }
234                 else
235                 {
236                    return -1;
237                 }
238             }
239             else // o1 != null
240             if ( o2 == null )
241             {
242                 return 1;
243             }
244             
245             // fall back on string representation comparison
246             
247             return o1.toString().compareTo( o2.toString() );
248         }
249                            
250         public boolean equals(Object obj)
251         {
252             return ( this == obj );   
253         }
254     }
255     
256     private static class CompareDescendingComparator 
257         extends CompareAscendingComparator
258     {
259         public int compare(Object o1, Object o2)
260         {
261             return -1 * super.compare( o1, o2 );             
262         }
263     }
264 
265     private static class CompareCaseInsensitiveAscendingComparator 
266         extends NSSelector
267     {
268         public int compare(Object o1, Object o2)
269         {
270             // null handling: null is less than any object
271             
272             if ( o1 == null ) 
273             {
274                 if ( o2 == null )
275                 {
276                     return 0;
277                 }
278                 else
279                 {
280                    return -1;
281                 }
282             }
283             else // o1 != null
284             if ( o2 == null )
285             {
286                 return 1;
287             }
288             
289             return o1.toString().toLowerCase().compareTo( 
290                 o2.toString().toLowerCase() );
291         }
292                            
293         public boolean equals(Object obj)
294         {
295             return ( this == obj );   
296         }
297     }
298     
299     private static class CompareCaseInsensitiveDescendingComparator 
300         extends CompareCaseInsensitiveAscendingComparator
301     {
302         public int compare(Object o1, Object o2)
303         {
304             return -1 * super.compare( o1, o2 );             
305         }
306     }
307     
308     private static class DelegatingComparator implements Comparator
309     {
310         private String key;
311         private Comparator comparator;
312         
313         public DelegatingComparator( String aKey, Comparator aComparator )
314         {
315             key = aKey;
316             comparator = aComparator;
317         }
318         
319         public int compare(Object o1, Object o2)
320         {
321             Object v1, v2;
322             if ( o1 instanceof EOKeyValueCoding )
323             {
324                 v1 = ((EOKeyValueCoding)o1).valueForKey( key );   
325             }
326             else
327             {
328                 v1 = EOKeyValueCodingSupport.valueForKey( o1, key );
329             }
330             if ( o2 instanceof EOKeyValueCoding )
331             {
332                 v2 = ((EOKeyValueCoding)o2).valueForKey( key );   
333             }
334             else
335             {
336                 v2 = EOKeyValueCodingSupport.valueForKey( o2, key );
337             }
338             return comparator.compare( v1, v2 );
339         }
340                            
341         public boolean equals(Object obj)
342         {
343             return ( this == obj );   
344         }
345     }
346 
347 }
348 
349 /*
350  * $Log$
351  * Revision 1.2  2006/02/16 16:47:14  cgruber
352  * Move some classes in to "internal" packages and re-work imports, etc.
353  *
354  * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
355  *
356  * Revision 1.1  2006/02/16 13:19:57  cgruber
357  * Check in all sources in eclipse-friendly maven-enabled packages.
358  *
359  * Revision 1.14  2003/08/11 19:39:52  chochos
360  * now encodes/decodes correctly.
361  *
362  * Revision 1.13  2003/08/09 01:29:56  chochos
363  * implements EOKeyValueArchiving
364  *
365  * Revision 1.12  2003/08/06 23:07:52  chochos
366  * general code cleanup (mostly, removing unused imports)
367  *
368  * Revision 1.11  2003/02/07 20:23:23  mpowers
369  * Provided backwards compatibility for comparators.
370  *
371  * Revision 1.10  2003/01/18 23:46:58  mpowers
372  * EOSortOrdering is now correctly using NSSelectors.
373  *
374  * Revision 1.9  2003/01/16 22:47:30  mpowers
375  * Compatibility changes to support compiling woextensions source.
376  * (34 out of 56 classes compile!)
377  *
378  * Revision 1.8  2002/03/01 20:23:27  mpowers
379  * Implemented equals.
380  *
381  * Revision 1.7  2002/02/06 21:15:04  mpowers
382  * Added null handling to CompareCaseInsensitiveAscendingComparator.
383  *
384  * Revision 1.6  2001/12/01 23:51:24  mpowers
385  * Made serializable.
386  *
387  * Revision 1.5  2001/03/29 03:29:49  mpowers
388  * Now using KeyValueCoding and Support instead of Introspector.
389  *
390  * Revision 1.4  2001/01/24 22:15:52  mpowers
391  * Fixed npe when comparing nulls.
392  *
393  * Revision 1.3  2001/01/12 19:11:56  mpowers
394  * Fixed table column click sorting.
395  *
396  * Revision 1.2  2001/01/12 17:23:46  mpowers
397  * Inner classes are now private.
398  *
399  * Revision 1.1  2001/01/11 20:34:26  mpowers
400  * Implemented EOSortOrdering and added support in framework.
401  * Added header-click to sort table columns.
402  *
403  *
404  */
405     
406