001: /* ====================================================================
002: * The QueryForm License, Version 1.1
003: *
004: * Copyright (c) 1998 - 2003 David F. Glasser. All rights
005: * reserved.
006: *
007: * Redistribution and use in source and binary forms, with or without
008: * modification, are permitted provided that the following conditions
009: * are met:
010: *
011: * 1. Redistributions of source code must retain the above copyright
012: * notice, this list of conditions and the following disclaimer.
013: *
014: * 2. Redistributions in binary form must reproduce the above copyright
015: * notice, this list of conditions and the following disclaimer in
016: * the documentation and/or other materials provided with the
017: * distribution.
018: *
019: * 3. The end-user documentation included with the redistribution,
020: * if any, must include the following acknowledgment:
021: * "This product includes software developed by
022: * David F. Glasser."
023: * Alternately, this acknowledgment may appear in the software itself,
024: * if and wherever such third-party acknowledgments normally appear.
025: *
026: * 4. The names "QueryForm" and "David F. Glasser" must
027: * not be used to endorse or promote products derived from this
028: * software without prior written permission. For written
029: * permission, please contact dglasser@pobox.com.
030: *
031: * 5. Products derived from this software may not be called "QueryForm",
032: * nor may "QueryForm" appear in their name, without prior written
033: * permission of David F. Glasser.
034: *
035: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
036: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
037: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
038: * DISCLAIMED. IN NO EVENT SHALL DAVID F. GLASSER, THE APACHE SOFTWARE
039: * FOUNDATION OR ITS CONTRIBUTORS, OR ANY AUTHORS OR DISTRIBUTORS
040: * OF THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
041: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
042: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
043: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
044: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
045: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
046: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
047: * SUCH DAMAGE.
048: * ====================================================================
049: *
050: * This product includes software developed by the
051: * Apache Software Foundation (http://www.apache.org/).
052: *
053: * ====================================================================
054: *
055: * $Source: /cvsroot/qform/qform/src/org/glasser/util/comparators/BaseComparator.java,v $
056: * $Revision: 1.2 $
057: * $Author: dglasser $
058: * $Date: 2003/01/25 18:11:14 $
059: *
060: * --------------------------------------------------------------------
061: */
062: package org.glasser.util.comparators;
063:
064: import java.util.*;
065:
066: /**
067: * This is a base class for Comparators which provides commonly-needed functionality.
068: * <p>
069: * Its three fields are:
070: *
071: * <ul>
072: * <li> nullIsGreater (boolean)</li> which determines whether a null object
073: * passed to compare() is considered greater than or less than a non-null object.
074: * <li> sortDescending (boolean) </li> which determines whether the normal ordering
075: * of two objects will be reversed, resulting in a descending sort.
076: * <li> nestedComparator (Comparator)</li> when no order can be established for two objects
077: * (in other words the compare() method would normally return 0) then the two objects
078: * will be passed to the nestedComparator, in an attempt to determine an order. An example might
079: * be a subclass that compared the value of the lastName fields of two Person objects. It could
080: * have a similar comparator that compared the value of the firstName fields as its nestedComparator,
081: * so Person objects with the same lastName would be sorted according to their firstName fields.
082: * </ul>
083: *
084: * The value of these three fields can only be set in the constructor, or by subclasses through
085: * protected setter methods, which allows subclasses to be immutable.
086: * <p>
087: * A subclass only needs to implement the template method doCompare(Object, Object), which is
088: * called from within compare(). The compare() method will first handle the cases where
089: * one or both objects are null, and if both objects are non-null, it will pass them to the doCompare()
090: * method. The doCompare() method does not need to concern itself with the possibility of
091: * null arguments or reverse-sorts. If the doCompare() method returns 0 and a nestedComparator
092: * is available, the arguments will be passed to it. If the doCompare() method returns non-zero and
093: * the sortDescending flag is true, the sign of the return value will be flipped before it's
094: * returned to the caller.
095: *
096: */
097: public abstract class BaseComparator implements Comparator,
098: java.io.Serializable {
099:
100: protected boolean nullIsGreater = false;
101:
102: protected boolean sortDescending = false;
103:
104: protected Comparator nestedComparator = null;
105:
106: /**
107: * Constructs a BaseComparator instance with default field values. The resulting
108: * instance is immutable and thread-safe, unless its fields have been exposed
109: * by subclasses.
110: */
111: protected BaseComparator() {
112: }
113:
114: /**
115: * Constructs a BaseComparator instance with the given field values. The resulting
116: * instance is immutable and thread-safe, unless its fields have been exposed
117: * by subclasses.
118: */
119: protected BaseComparator(boolean nullIsGreater,
120: boolean sortDescending, Comparator nestedComparator) {
121: this .nullIsGreater = nullIsGreater;
122: this .sortDescending = sortDescending;
123: this .nestedComparator = nestedComparator;
124: }
125:
126: /**
127: * Sets the value of the nullIsGreater field, which determines whether
128: * null objects are considered "greater than" or "less than" non-null objects
129: * for sorting purposes. The default is false.
130: */
131: protected void setNullIsGreater(boolean nullIsGreater) {
132: this .nullIsGreater = nullIsGreater;
133: }
134:
135: /**
136: * Sets the value of the sortDescending field. If true, the sign of the
137: * value of the compare() method is "flipped" before it's returned to
138: * the caller, which will cause collections of objects to be sorted
139: * in reverse order.
140: */
141: protected void setSortDescending(boolean sortDescending) {
142: this .sortDescending = sortDescending;
143: }
144:
145: /**
146: * Sets a comparator that will be used as a "backup" comparator if this one
147: * is unable to establish an order for two objects (i.e., the compare() method
148: * would return 0.)
149: */
150: protected void setNestedComparator(Comparator nestedComparator) {
151: this .nestedComparator = nestedComparator;
152: }
153:
154: /**
155: * Returns the value of the nullIsGreater flag, which by default
156: * is false.
157: */
158: public boolean getNullIsGreater() {
159: return nullIsGreater;
160: }
161:
162: /**
163: * Returns the value of the sortDescending flag, which by default
164: * is false.
165: */
166: public boolean isSortDescending() {
167: return sortDescending;
168: }
169:
170: /**
171: * Returns the Comparator being used as the nested, or backup comparator,
172: * or null if one hasn't been set. Subclasses
173: * may choose to expose this method publicly, but if the nestedComparator is
174: * mutable, they may be compromising their immutability (and hence their thread safety.)
175: */
176: protected Comparator getNestedComparator() {
177: return nestedComparator;
178: }
179:
180: public final int compare(Object o1, Object o2) {
181:
182: // handle the trivial case where both objects are null
183: if (o1 == null && o2 == null)
184: return 0;
185:
186: // if one object is null, this will return non-zero
187: int retVal = compareForNulls(o1, o2);
188:
189: // if we got an ordering for these two objects, (which means
190: // one was null and the other non-null) we're done.
191: if (retVal != 0)
192: return adjust(retVal);
193:
194: // if we still don't have an ordering, let the subclass try
195: retVal = doCompare(o1, o2);
196: if (retVal != 0)
197: return adjust(retVal);
198:
199: // if the subclass couldn't establish an order, see if there's
200: // a nested comparator. Note that the result from the
201: // nested comparator is not affected by the value of
202: // the sortDescending field.
203: if (nestedComparator != null)
204: return nestedComparator.compare(o1, o2);
205:
206: // no ordering could be established for these two objects, so
207: // return 0.
208: return retVal;
209:
210: }
211:
212: /**
213: * This method will establish the ordering for two objects when
214: * one is null and one is non-null, based on the value of this Comparator's
215: * nullIsGreater flag.
216: *
217: * @return 0 if both objects are null or both are non-null; if only one object
218: * is null, it is considered "greater than" the non-null object if the
219: * nullIsGreater flag is true, and "less than" the non-null object if the
220: * nullIsGreater flag is false (the default.)
221: */
222: protected int compareForNulls(Object o1, Object o2) {
223: if (o1 == null && o2 == null)
224: return 0;
225: if (o1 == null) {
226: if (nullIsGreater)
227: return 1;
228: else
229: return -1;
230: } else if (o2 == null) {
231: if (nullIsGreater)
232: return -1;
233: else
234: return 1;
235: } else {
236: return 0; // both are non-null;
237: }
238: }
239:
240: /**
241: * This method is used to "flip" the sign of the compare() method's return value
242: * whenever the sortDescending flag is true.
243: */
244: private int adjust(int val) {
245: if (sortDescending == false)
246: return val;
247: return 0 - val;
248: }
249:
250: /**
251: * This method is implemented by subclasses, which should know about the types
252: * of the two objects being passed in and how to order them. This is a template
253: * method which is called from within compare(), and both arguments are guaranteed
254: * to be non-null. Implementations of this method should not be concerned with flipping
255: * the sign of the return value for descending sorts; that task will be handled within this
256: * (the base) class.
257: */
258: protected abstract int doCompare(Object o1, Object o2);
259:
260: }
|