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<MyEnum> enumModel = new EnumComboBoxModel<MyEnum1>( 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 "Some Good Stuff"; 083 * case BadStuff: return "Some Bad Stuff"; 084 * } 085 * return "ERROR"; 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 }