001    package edu.nrao.sss.sort;
002    
003    /**
004     * A single sorting key.
005     * <p>
006     * <b>Version Info:</b>
007     * <table style="margin-left:2em">
008     *   <tr><td>$Revision: 593 $</td></tr>
009     *   <tr><td>$Date: 2007-05-07 15:54:14 -0600 (Mon, 07 May 2007) $</td></tr>
010     *   <tr><td>$Author: dharland $</td></tr>
011     * </table></p>
012     * 
013     * @author David M. Harland
014     * @since 2007-05-03
015     */
016    public abstract class SortKey<T>
017      implements Orderable
018    {
019      private SortOrder order;
020      
021      /** Helps create a new key with a natural order. */
022      protected SortKey()
023      {
024        order = SortOrder.NATURAL;
025      }
026      
027      /* (non-Javadoc)
028       * @see Orderable#setOrder(SortOrder)
029       */
030      public void setOrder(SortOrder newOrder)
031      {
032        if (newOrder == SortOrder.CUSTOM)
033          throw new IllegalArgumentException("This sort key (" +
034                                             this.getClass().getName() + 
035                                             ") does not allow SortOrder.CUSTOM.");
036        
037        order = (newOrder == null) ? SortOrder.NATURAL : newOrder;
038      }
039      
040      /* (non-Javadoc)
041       * @see Orderable#getOrder()
042       */
043      public SortOrder getOrder()  { return order; }
044      
045      /**
046       * Returns a number less than zero, zero, or greater than zero
047       * as <tt>o1</tt> is before, coincident with, or after <tt>o2</tt>
048       * when sorted in this key's current order.
049       */
050      protected int compareObjects(T o1, T o2)
051      {
052        //Quick exit if objects are equal
053        if (objectsAreEqual(o1, o2))
054          return 0;
055        
056        //Quick exit if exactly one object is null
057        int nullOrder = nullOrdering(o1, o2);
058        if (nullOrder != 0)
059          return nullOrder;
060        
061        return compareObjects(o1, o2, order);
062      }
063      
064      /**
065       * Returns a number less than zero, zero, or greater than zero
066       * as <tt>o1</tt> is before, coincident with, or after <tt>o2</tt>
067       * when sorted in according to t he given sort order.
068       * <p/>
069       * This method is called by compareObjects(T o1, T o2).
070       */
071      protected int compareObjects(T o1, T o2, SortOrder ordering)
072      {
073        switch (ordering)
074        {
075          case AS_IS:      return 0;
076          
077          case NATURAL:    return compareNatural(o1, o2);
078          
079          case ASCENDING:  return compareAscending(o1, o2);
080          
081          case DESCENDING: return -compareAscending(o1, o2);
082          
083          default:
084            throw new IllegalStateException(
085              "Unrecognized or disallowed SortOrder "+
086              ordering + " found in comparObjects(...)");
087        }
088      }
089    
090      /**
091       * Returns a number less than zero, zero, or greater than zero
092       * as <tt>o1</tt> is before, coincident with, or after <tt>o2</tt>
093       * when sorted in natural order.
094       */
095      protected abstract int compareNatural(T o1, T o2);
096      
097      /**
098       * Returns a number less than zero, zero, or greater than zero
099       * as <tt>o1</tt> is before, coincident with, or after <tt>o2</tt>
100       * when sorted in ascending order.
101       */
102      protected abstract int compareAscending(T o1, T o2);
103    
104      /**
105       * Returns <i>true</i> if the two objects are equal.
106       * This method is used by {@link #compareObjects(Object, Object)}.
107       */
108      protected boolean objectsAreEqual(Object o1, Object o2)
109      {
110        //Quick return if same instance (incl both == null)
111        if (o1 == o2)
112          return true;
113    
114        //Quick return if equal
115        if ((o1 != null) && o1.equals(o2))
116          return true;
117    
118        return false;
119      }
120      
121      /**
122       * Handles comparisons when exactly one of the two objects is null.
123       * For ascending and natural sorts, null objects are placed at the
124       * end of the line.  For descending sorts they are placed at the beginning.
125       * This method is used by {@link #compareObjects(Object, Object)}.
126       */
127      protected int nullOrdering(T o1, T o2)
128      {
129        //TODO: There's an implicit assumption here that NATURAL == ASCENDING
130        //      Trouble?
131        if ((o1 == null) && (o2 != null))
132          return (order == SortOrder.DESCENDING) ? -1 : +1;
133        
134        if ((o1 != null) && (o2 == null))
135          return (order == SortOrder.DESCENDING) ? +1 : -1;
136        
137        return 0;
138      }
139    }