001    package edu.nrao.sss.sort;
002    
003    import java.util.ArrayList;
004    import java.util.Comparator;
005    import java.util.List;
006    
007    /**
008     * A sort key for {@link Enum enumerations}.
009     * <p/>
010     * The most common way to extend this class is to subclass it into a class
011     * that also implements {@link Comparator}.  The main job of the subclass
012     * is to provide the integers to this parent class.  Example:
013     * <pre>
014     *   &#x002F;**
015     *    * Sorts players according to the hand with which they bat.
016     *    *&#x002F;
017     *   public class PlayerBattingHandKey extends EnumSortKey&lt;Handedness&gt;
018     *     implements Comparator&lt;Player&gt;
019     *   {
020     *     public int compare(Player p1, Player p2)
021     *     {
022     *       return compareObjects(p1.getBattingHand()
023     *                             p2.getBattingHand());
024     *     }
025     *   }</pre>
026     * <p>
027     * Clients of the example <tt>PlayerBattingHandKey</tt> class are able to
028     * configure that comparator via the {@link #setOrder(SortOrder)} method,
029     * something they cannot do with the plain <tt>Comparator</tt> interface.
030     * They may then place instances of this class in a
031     * {@link CompoundComparator}.</p>
032     * <p>
033     * <b>Version Info:</b>
034     * <table style="margin-left:2em">
035     *   <tr><td>$Revision: 593 $</td></tr>
036     *   <tr><td>$Date: 2007-05-07 15:54:14 -0600 (Mon, 07 May 2007) $</td></tr>
037     *   <tr><td>$Author: dharland $</td></tr>
038     * </table></p>
039     * 
040     * @author David M. Harland
041     * @since 2007-05-03
042     */
043    
044    public abstract class EnumSortKey<E extends Enum<E>>
045      extends SortKey<E>
046    {
047      private List<E> customOrder;
048      
049      /** Helps create a new instance. */
050      protected EnumSortKey()
051      {
052        super();
053        
054        customOrder = new ArrayList<E>();
055      }
056      
057      /**
058       * Sets the ordering for this sort key.
059       * <p>
060       * The list of elements should contain no more than one occurrence of a
061       * given element.  Additional occurrences after the first are permitted
062       * but will be ignored.  The list need not have an occurrence of every
063       * element of the enumeration.  Any elements not present in the list will
064       * be deemed to be greater than an element that is in the list.
065       * A comparison between two unlisted elements will result in a tie.</p>
066       * <p>
067       * The custom ordering provided here will be used only when the 
068       * sort order is {@link #setOrder(SortOrder) set} to
069       * {@link SortOrder#CUSTOM}.</p>
070       * <p>
071       * This method will not save a reference to {@code ordering}, so any changes
072       * made to the list after this method is called will not be reflected in
073       * this object.</p>
074       * 
075       * @param ordering a list containing elements of an enumeration in the order
076       *                 in which this key should sort them.
077       */
078      public void setCustomOrder(List<E> ordering)
079      {
080        customOrder.clear();
081        customOrder.addAll(ordering);
082      }
083      
084      /**
085       * Returns a number less than zero, zero, or greater than zero
086       * as <tt>o1</tt> is before, coincident with, or after <tt>o2</tt>
087       * when sorted in according to t he given sort order.
088       * <p/>
089       * This method is called by compareObjects(T o1, T o2).
090       */
091      @Override
092      protected int compareObjects(E o1, E o2, SortOrder ordering)
093      {
094        switch (ordering)
095        {
096          case AS_IS:      return 0;
097          
098          case NATURAL:    return compareNatural(o1, o2);
099          
100          case ASCENDING:  return compareAscending(o1, o2);
101          
102          case DESCENDING: return -compareAscending(o1, o2);
103          
104          case CUSTOM:     return compareCustom(o1, o2);
105          
106          default:
107            throw new IllegalStateException(
108            "Unrecognized or disallowed SortOrder "+
109            ordering + " found in comparObjects(...)");
110        }
111      }
112    
113      /**
114       * Uses the natural ordering of {@code E}.
115       */
116      protected int compareNatural(E o1, E o2)
117      {
118        return o1.compareTo(o2);
119      }
120      
121      /**
122       * Uses the natural ordering of {@code E}.
123       */
124      protected int compareAscending(E o1, E o2)
125      {
126        return compareNatural(o1, o2);
127      }
128      
129      /**
130       * Uses a client-specified ordering
131       */
132      protected int compareCustom(E o1, E o2)
133      {
134        int comparison;
135        
136        int pos1 = customOrder.indexOf(o1);
137        int pos2 = customOrder.indexOf(o2);
138        
139        //If both are in custom list, return difference in positions
140        if (pos1 >= 0 && pos2 >= 0)
141        {
142          comparison = pos1 - pos2;
143        }
144        //If neither is in list, keep their current ordering
145        else if (pos1 < 0 && pos2 < 0)
146        {
147          comparison = 0;
148        }
149        //If exactly one is not in list, push unlisted element to end
150        else
151        {
152          if (pos1 < 0)
153            comparison = +1;
154          else //(pos2 < 0)
155            comparison = -1;
156        }
157        
158        return comparison;
159      }
160    }