KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > commons > collections > comparators > ComparatorChain


1 /*
2  * Copyright 1999-2004 The Apache Software Foundation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.apache.commons.collections.comparators;
17
18 import java.io.Serializable JavaDoc;
19 import java.util.ArrayList JavaDoc;
20 import java.util.BitSet JavaDoc;
21 import java.util.Comparator JavaDoc;
22 import java.util.Iterator JavaDoc;
23 import java.util.List JavaDoc;
24
25 /**
26  * <p>A ComparatorChain is a Comparator that wraps one or
27  * more Comparators in sequence. The ComparatorChain
28  * calls each Comparator in sequence until either 1)
29  * any single Comparator returns a non-zero result
30  * (and that result is then returned),
31  * or 2) the ComparatorChain is exhausted (and zero is
32  * returned). This type of sorting is very similar
33  * to multi-column sorting in SQL, and this class
34  * allows Java classes to emulate that kind of behaviour
35  * when sorting a List.</p>
36  *
37  * <p>To further facilitate SQL-like sorting, the order of
38  * any single Comparator in the list can be reversed.</p>
39  *
40  * <p>Calling a method that adds new Comparators or
41  * changes the ascend/descend sort <i>after compare(Object,
42  * Object) has been called</i> will result in an
43  * UnsupportedOperationException. However, <i>take care</i>
44  * to not alter the underlying List of Comparators
45  * or the BitSet that defines the sort order.</p>
46  *
47  * <p>Instances of ComparatorChain are not synchronized.
48  * The class is not thread-safe at construction time, but
49  * it <i>is</i> thread-safe to perform multiple comparisons
50  * after all the setup operations are complete.</p>
51  *
52  * @since Commons Collections 2.0
53  * @author Morgan Delagrange
54  * @version $Revision: 1.18 $ $Date: 2004/05/16 11:48:49 $
55  */

56 public class ComparatorChain implements Comparator JavaDoc, Serializable JavaDoc {
57
58     /** Serialization version from Collections 2.0. */
59     private static final long serialVersionUID = -721644942746081630L;
60     
61     /** The list of comparators in the chain. */
62     protected List JavaDoc comparatorChain = null;
63     /** Order - false (clear) = ascend; true (set) = descend. */
64     protected BitSet JavaDoc orderingBits = null;
65    /** Whether the chain has been "locked". */
66     protected boolean isLocked = false;
67
68     //-----------------------------------------------------------------------
69
/**
70      * Construct a ComparatorChain with no Comparators.
71      * You must add at least one Comparator before calling
72      * the compare(Object,Object) method, or an
73      * UnsupportedOperationException is thrown
74      */

75     public ComparatorChain() {
76         this(new ArrayList JavaDoc(),new BitSet JavaDoc());
77     }
78
79     /**
80      * Construct a ComparatorChain with a single Comparator,
81      * sorting in the forward order
82      *
83      * @param comparator First comparator in the Comparator chain
84      */

85     public ComparatorChain(Comparator JavaDoc comparator) {
86         this(comparator,false);
87     }
88
89     /**
90      * Construct a Comparator chain with a single Comparator,
91      * sorting in the given order
92      *
93      * @param comparator First Comparator in the ComparatorChain
94      * @param reverse false = forward sort; true = reverse sort
95      */

96     public ComparatorChain(Comparator JavaDoc comparator, boolean reverse) {
97         comparatorChain = new ArrayList JavaDoc();
98         comparatorChain.add(comparator);
99         orderingBits = new BitSet JavaDoc(1);
100         if (reverse == true) {
101             orderingBits.set(0);
102         }
103     }
104
105     /**
106      * Construct a ComparatorChain from the Comparators in the
107      * List. All Comparators will default to the forward
108      * sort order.
109      *
110      * @param list List of Comparators
111      * @see #ComparatorChain(List,BitSet)
112      */

113     public ComparatorChain(List JavaDoc list) {
114         this(list,new BitSet JavaDoc(list.size()));
115     }
116
117     /**
118      * Construct a ComparatorChain from the Comparators in the
119      * given List. The sort order of each column will be
120      * drawn from the given BitSet. When determining the sort
121      * order for Comparator at index <i>i</i> in the List,
122      * the ComparatorChain will call BitSet.get(<i>i</i>).
123      * If that method returns <i>false</i>, the forward
124      * sort order is used; a return value of <i>true</i>
125      * indicates reverse sort order.
126      *
127      * @param list List of Comparators. NOTE: This constructor does not perform a
128      * defensive copy of the list
129      * @param bits Sort order for each Comparator. Extra bits are ignored,
130      * unless extra Comparators are added by another method.
131      */

132     public ComparatorChain(List JavaDoc list, BitSet JavaDoc bits) {
133         comparatorChain = list;
134         orderingBits = bits;
135     }
136
137     //-----------------------------------------------------------------------
138
/**
139      * Add a Comparator to the end of the chain using the
140      * forward sort order
141      *
142      * @param comparator Comparator with the forward sort order
143      */

144     public void addComparator(Comparator JavaDoc comparator) {
145         addComparator(comparator,false);
146     }
147
148     /**
149      * Add a Comparator to the end of the chain using the
150      * given sort order
151      *
152      * @param comparator Comparator to add to the end of the chain
153      * @param reverse false = forward sort order; true = reverse sort order
154      */

155     public void addComparator(Comparator JavaDoc comparator, boolean reverse) {
156         checkLocked();
157         
158         comparatorChain.add(comparator);
159         if (reverse == true) {
160             orderingBits.set(comparatorChain.size() - 1);
161         }
162     }
163
164     /**
165      * Replace the Comparator at the given index, maintaining
166      * the existing sort order.
167      *
168      * @param index index of the Comparator to replace
169      * @param comparator Comparator to place at the given index
170      * @exception IndexOutOfBoundsException
171      * if index &lt; 0 or index &gt;= size()
172      */

173     public void setComparator(int index, Comparator JavaDoc comparator)
174     throws IndexOutOfBoundsException JavaDoc {
175         setComparator(index,comparator,false);
176     }
177
178     /**
179      * Replace the Comparator at the given index in the
180      * ComparatorChain, using the given sort order
181      *
182      * @param index index of the Comparator to replace
183      * @param comparator Comparator to set
184      * @param reverse false = forward sort order; true = reverse sort order
185      */

186     public void setComparator(int index, Comparator JavaDoc comparator, boolean reverse) {
187         checkLocked();
188
189         comparatorChain.set(index,comparator);
190         if (reverse == true) {
191             orderingBits.set(index);
192         } else {
193             orderingBits.clear(index);
194         }
195     }
196
197
198     /**
199      * Change the sort order at the given index in the
200      * ComparatorChain to a forward sort.
201      *
202      * @param index Index of the ComparatorChain
203      */

204     public void setForwardSort(int index) {
205         checkLocked();
206         orderingBits.clear(index);
207     }
208
209     /**
210      * Change the sort order at the given index in the
211      * ComparatorChain to a reverse sort.
212      *
213      * @param index Index of the ComparatorChain
214      */

215     public void setReverseSort(int index) {
216         checkLocked();
217         orderingBits.set(index);
218     }
219
220     /**
221      * Number of Comparators in the current ComparatorChain.
222      *
223      * @return Comparator count
224      */

225     public int size() {
226         return comparatorChain.size();
227     }
228
229     /**
230      * Determine if modifications can still be made to the
231      * ComparatorChain. ComparatorChains cannot be modified
232      * once they have performed a comparison.
233      *
234      * @return true = ComparatorChain cannot be modified; false =
235      * ComparatorChain can still be modified.
236      */

237     public boolean isLocked() {
238         return isLocked;
239     }
240
241     // throw an exception if the ComparatorChain is locked
242
private void checkLocked() {
243         if (isLocked == true) {
244             throw new UnsupportedOperationException JavaDoc("Comparator ordering cannot be changed after the first comparison is performed");
245         }
246     }
247
248     private void checkChainIntegrity() {
249         if (comparatorChain.size() == 0) {
250             throw new UnsupportedOperationException JavaDoc("ComparatorChains must contain at least one Comparator");
251         }
252     }
253
254     //-----------------------------------------------------------------------
255
/**
256      * Perform comparisons on the Objects as per
257      * Comparator.compare(o1,o2).
258      *
259      * @param o1 the first object to compare
260      * @param o2 the second object to compare
261      * @return -1, 0, or 1
262      * @exception UnsupportedOperationException
263      * if the ComparatorChain does not contain at least one
264      * Comparator
265      */

266     public int compare(Object JavaDoc o1, Object JavaDoc o2) throws UnsupportedOperationException JavaDoc {
267         if (isLocked == false) {
268             checkChainIntegrity();
269             isLocked = true;
270         }
271
272         // iterate over all comparators in the chain
273
Iterator JavaDoc comparators = comparatorChain.iterator();
274         for (int comparatorIndex = 0; comparators.hasNext(); ++comparatorIndex) {
275
276             Comparator JavaDoc comparator = (Comparator JavaDoc) comparators.next();
277             int retval = comparator.compare(o1,o2);
278             if (retval != 0) {
279                 // invert the order if it is a reverse sort
280
if (orderingBits.get(comparatorIndex) == true) {
281                     if(Integer.MIN_VALUE == retval) {
282                         retval = Integer.MAX_VALUE;
283                     } else {
284                         retval *= -1;
285                     }
286                 }
287
288                 return retval;
289             }
290
291         }
292
293         // if comparators are exhausted, return 0
294
return 0;
295     }
296
297     //-----------------------------------------------------------------------
298
/**
299      * Implement a hash code for this comparator that is consistent with
300      * {@link #equals(Object) equals}.
301      *
302      * @return a suitable hash code
303      * @since Commons Collections 3.0
304      */

305     public int hashCode() {
306         int hash = 0;
307         if(null != comparatorChain) {
308             hash ^= comparatorChain.hashCode();
309         }
310         if(null != orderingBits) {
311             hash ^= orderingBits.hashCode();
312         }
313         return hash;
314     }
315
316     /**
317      * Returns <code>true</code> iff <i>that</i> Object is
318      * is a {@link Comparator} whose ordering is known to be
319      * equivalent to mine.
320      * <p>
321      * This implementation returns <code>true</code>
322      * iff <code><i>object</i>.{@link Object#getClass() getClass()}</code>
323      * equals <code>this.getClass()</code>, and the underlying
324      * comparators and order bits are equal.
325      * Subclasses may want to override this behavior to remain consistent
326      * with the {@link Comparator#equals(Object)} contract.
327      *
328      * @param object the object to compare with
329      * @return true if equal
330      * @since Commons Collections 3.0
331      */

332     public boolean equals(Object JavaDoc object) {
333         if(this == object) {
334             return true;
335         } else if(null == object) {
336             return false;
337         } else if(object.getClass().equals(this.getClass())) {
338             ComparatorChain chain = (ComparatorChain)object;
339             return ( (null == orderingBits ? null == chain.orderingBits : orderingBits.equals(chain.orderingBits))
340                    && (null == comparatorChain ? null == chain.comparatorChain : comparatorChain.equals(chain.comparatorChain)) );
341         } else {
342             return false;
343         }
344     }
345
346 }
347
Popular Tags