001    /*
002     * $Id: EnumComboBoxModel.java 2794 2008-03-05 02:46:37Z kschaefe $
003     *
004     * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
005     * Santa Clara, California 95054, U.S.A. All rights reserved.
006     *
007     * This library is free software; you can redistribute it and/or
008     * modify it under the terms of the GNU Lesser General Public
009     * License as published by the Free Software Foundation; either
010     * version 2.1 of the License, or (at your option) any later version.
011     * 
012     * This library is distributed in the hope that it will be useful,
013     * but WITHOUT ANY WARRANTY; without even the implied warranty of
014     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015     * Lesser General Public License for more details.
016     * 
017     * You should have received a copy of the GNU Lesser General Public
018     * License along with this library; if not, write to the Free Software
019     * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
020     */
021    package org.jdesktop.swingx.combobox;
022    
023    import java.util.ArrayList;
024    import java.util.EnumSet;
025    import java.util.HashMap;
026    import java.util.Iterator;
027    import java.util.Map;
028    
029    /**
030     * <p>
031     * A ComboBoxModel implementation that safely wraps an Enum. It allows the
032     * developer to directly use an enum as their model for a combobox without any
033     * extra work, though the display can can be further customized.
034     * </p>
035     * 
036     * <h4>Simple Usage</h4>
037     * 
038     * <p>
039     * The simplest usage is to wrap an <code>enum</code> inside the
040     * <code>EnumComboBoxModel</code> and then set it as the model on the combo
041     * box. The combo box will then appear on screen with each value in the
042     * <code>enum</code> as a value in the combobox.
043     * </p>
044     * <p>
045     * ex:
046     * </p>
047     * 
048     * <pre><code>
049     *  enum MyEnum { GoodStuff, BadStuff };
050     *  ...
051     *  JComboBox combo = new JComboBox();
052     *  combo.setModel(new EnumComboBoxModel(MyEnum.class));
053     * </code></pre>
054     * 
055     * <h4>Type safe access</h4>
056     * <p>
057     * By using generics and co-variant types you can make accessing elements from
058     * the model be completely typesafe. ex:
059     * </p>
060     * 
061     * <pre><code>
062     * EnumComboBoxModel&lt;MyEnum&gt; enumModel = new EnumComboBoxModel&lt;MyEnum1&gt;(
063     *         MyEnum1.class);
064     * 
065     * MyEnum first = enumModel.getElement(0);
066     * 
067     * MyEnum selected = enumModel.getSelectedItem();
068     * </code></pre>
069     * 
070     * <h4>Advanced Usage</h4>
071     * <p>
072     * Since the exact <code>toString()</code> value of each enum constant may not
073     * be exactly what you want on screen (the values won't have spaces, for
074     * example) you can override to toString() method on the values when you declare
075     * your enum. Thus the display value is localized to the enum and not in your
076     * GUI code. ex:
077     * 
078     * <pre><code>
079     *    private enum MyEnum {GoodStuff, BadStuff;
080     *        public String toString() {
081     *           switch(this) {
082     *               case GoodStuff: return &quot;Some Good Stuff&quot;;
083     *               case BadStuff: return &quot;Some Bad Stuff&quot;;
084     *           }
085     *           return &quot;ERROR&quot;;
086     *        }
087     *    };
088     * </code></pre>
089     * 
090     * Note: if more than one enum constant returns the same {@code String} via
091     * {@code toString()}, this model will throw an exception on creation.
092     * 
093     * @author joshy
094     * @author Karl Schaefer
095     */
096    public class EnumComboBoxModel<E extends Enum<E>> extends ListComboBoxModel<E> {
097        private static final long serialVersionUID = 2176566393195371004L;
098        
099        private final Map<String, E> valueMap;
100        private final Class<E> enumClass;
101    
102        /**
103         * Creates an {@code EnumComboBoxModel} for the enum represent by the
104         * {@code Class} {@code en}.
105         * 
106         * @param en
107         *            the enum class type
108         * @throws IllegalArgumentException
109         *             if the {@code Enum.toString} returns the same value for more
110         *             than one constant
111         */
112        public EnumComboBoxModel(Class<E> en) {
113            super(new ArrayList<E>(EnumSet.allOf(en)));
114            
115            //we could size these, probably not worth it; enums are usually small 
116            valueMap = new HashMap<String, E>();
117            enumClass = en;
118            
119            Iterator<E> iter = data.iterator();
120            
121            while (iter.hasNext()) {
122                E element = iter.next();
123                String s = element.toString();
124                
125                if (valueMap.containsKey(s)) {
126                    throw new IllegalArgumentException(
127                            "multiple constants map to one string value");
128                }
129                
130                valueMap.put(s, element);
131            }
132        }
133    
134        /**
135         * {@inheritDoc}
136         */
137        @SuppressWarnings("unchecked")
138        public void setSelectedItem(Object anItem) {
139            E input = null;
140            
141            if (enumClass.isInstance(anItem)) {
142                input = (E) anItem;
143            } else {
144                input = valueMap.get(anItem);
145            }
146            
147            if (input != null || anItem == null) {
148                selected = input;
149            }
150            
151            this.fireContentsChanged(this, 0, getSize());
152        }
153        
154        /*
155        public static void main(String[] args) {
156            JFrame frame = new JFrame();
157            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
158            frame.setLayout(new BoxLayout(frame.getContentPane(),BoxLayout.Y_AXIS));
159            
160            
161            JComboBox combo1 = new JComboBox();
162            combo1.setModel(new EnumComboBoxModel(MyEnum1.class));
163            frame.add(combo1);
164            
165            JComboBox combo2 = new JComboBox();
166            combo2.setModel(new EnumComboBoxModel(MyEnum2.class));
167            frame.add(combo2);
168            
169            EnumComboBoxModel<MyEnum1> enumModel = new EnumComboBoxModel<MyEnum1>(MyEnum1.class);
170            JComboBox combo3 = new JComboBox();
171            combo3.setModel(enumModel);
172            frame.add(combo3);
173            
174            MyEnum1 selected = enumModel.getSelectedItem();
175            
176            //uncomment to see the ClassCastException
177    //        enumModel.setSelectedItem("Die clown");
178            
179            frame.pack();
180            frame.setVisible(true);
181        }
182        
183        private enum MyEnum1 {GoodStuff, BadStuff};
184        private enum MyEnum2 {GoodStuff, BadStuff;
185        public String toString() {
186            switch(this) {
187                case GoodStuff: return "Some Good Stuff";
188                case BadStuff: return "Some Bad Stuff";
189            }
190            return "ERROR";
191        }
192        };
193        */
194    
195    }