View Javadoc

1   /*
2    * $Id: TypeSafeEnumerator.java,v 1.1 2005/03/22 13:00:41 mch Exp $
3    */
4   
5   package org.astrogrid.utils;
6   
7   
8   import java.util.Vector;
9   import java.util.Hashtable;
10  import java.util.Iterator;
11  
12  
13  /***
14  * A superclass and set of helper methods for easily making, using
15   * and streaming type-safe enumerations, if you don't have the new generic
16   * java stuff.
17  * <P>
18  * Type-safe enumerators are a way of creating pre-defined lists of things,
19  * ensuring that only things of those correct instances are used as arguments.
20  * A good example is the Severity class of the event logs; it is simply not
21  * possible to pass anything except a Severity into the relevant MessageLog methods.
22  * <P>
23  * By subclassing from util.TypeSafeEnumerator, you get lots of advantages:
24  * string lookups, ability to iterate through the options, automatic BML
25  * stream handling and combo-box property editor that lists all the options.
26  * <P>
27  * Subclasses might override getList() and getFor() as described in those method
28  * docs below, to provide properly typed methods.
29  * <P>
30  * For use with a PropertyEditor, all you need to do is create the subclass, the appropriate get/set
31  * property methods and register the class with the TypeSafeEnumEditor at
32  * the end of the PropertyPane class (plenty of examples there) and bobs your uncle.
33  * <P>
34   * Example use of TypeSafeEnumerator:
35   * <pre>
36   * public class Severity extends TypeSafeEnumerator {
37   *     public final static Severity INFO = new Severity("Information only");
38   *     public final static Severity WARNING = new Severity("Warning");
39   *     public final static Severity ALARM = new Severity("Alarm");
40   *
41   *     public Severity(String msg) {
42   *         super(msg);
43   *     }
44   * }
45   * </pre>
46   * You can now use Severity as a parameter, and you will only ever get the
47   * ones defined above (or subclasses if not final!).
48   * <p>
49   * Extended from a javaworld article
50   * <p>
51   * @author M Hill
52   */
53  
54  
55  
56  public class TypeSafeEnumerator implements java.io.Serializable
57  {
58     /*** a hashtable of vectors...  each class look up corresponds to the
59      * subclass of the typesafeenumerator.  the vector consists of the list
60      * of that subclass's instance. */
61     protected static Hashtable classLookup = new Hashtable();
62  
63     //instance stuff
64     private String text; //all instances have an associated bit of text
65  
66     /***
67      * Standard Constructor
68      */
69     protected TypeSafeEnumerator(String aString)
70     {
71        text = aString;
72        addInstance();
73     }
74  
75     /***
76      * Adds instance to the classLookup table.  Might be called by
77      * subclass constructors.
78      */
79     protected void addInstance()
80     {
81        Vector instanceList = (Vector) classLookup.get(this.getClass().getName());
82  
83        if (instanceList == null)
84        {
85           //no entries yet for this type
86           instanceList = new Vector();
87           classLookup.put(this.getClass().getName(), instanceList);
88  
89           //register with property editor manager
90           //not sure about this as we start to make a simple class dependent on things like property editors,
91           //but still...  can always comment it out...
92           //       java.beans.PropertyEditorManager.registerEditor(this.getClass(), builder.property.editor.TypeSafeEnumEditor.class);
93        }
94  
95        instanceList.add(this);
96     }
97  
98     /***
99      * Returns a string representing the enumeration instance
100     */
101    public String toString()
102    {
103 //    return ""+this.getClass() + ":" + text;   //for testing
104       return text;
105    }
106 
107    /***
108     * There can be problems when deserialising objects because a new instance
109    * is created (regardless of whether the constructor is private) by the
110     * stream instantiator.
111     * <p>
112     * See java tip 122 on www.javaworld.com
113     * <p>
114     * Note that we can use .getClass() here because it's an instance method -
115     * we can't use it in the static (class) methods because in those cases
116     * the class will always be TypeSafeEnumerator rather than the appropriate
117     * subclass...
118     */
119     private Object readResolve () throws java.io.ObjectStreamException
120     {
121         return getFor(this.getClass(), text);   //get the instance already existing in the table
122     }
123 
124    /***
125     * Returns the text defining the enumeration instance
126     */
127    public String getText()
128    {
129       return text;
130    }
131 
132    /***
133     * Gets an iterator over the list of all those instances for the
134     * given class.  A nice subclass might implement:
135     * <pre>
136     * public static Iterator getIterator() { return getIterator(<SubClass>.class);
137     * </pre>
138     }
139     */
140    public static Iterator getIterator(Class aClass)
141    {
142       Vector instanceList = (Vector) classLookup.get(aClass.getName());
143 
144       if (instanceList == null)
145       {
146          //no reference has been made to the subclass yet, so the values have
147          //not been created.  Do so by making one instance here
148          try {
149             aClass.newInstance();
150             //try again
151             instanceList = (Vector) classLookup.get(aClass.getName());
152          } catch (Exception e) {
153             throw new NullPointerException("Could not create instances of typesafe enumerator "+aClass);
154          }
155       }
156 
157       return instanceList.iterator();
158    }
159 
160    /***
161     * Returns a list of the various instances for a particular class.  Suitable for combo boxes, etc.
162     * I don't really want to return the instance vector as it means people can
163     * fiddle with it...
164     * <P>
165    * A nice subclass might implement:
166     * <pre>
167     *
168     *     public static Object[] getAll() { return getAll(<SubClass>.class); }
169     *
170     * </pre>
171     * or use getList() to return a typed array
172     * @see getList()
173     */
174    public static Object[] getAll(Class aClass)
175    {
176       return getList(aClass).toArray();
177    }
178 
179    /***
180     * Provides a way for subclasses to do a typesafe getAll(), by implementing
181     * the following method:
182     * <pre>
183     *    public static <SubClass>[] getAll() {
184     *        return (<SubClass>[] getList(<SubClass>.class).toArray(new <SubClass>[])
185     *    }
186    */
187    public static Vector getList(Class aClass)
188    {
189       return (Vector) classLookup.get(aClass.getName());
190    }
191 
192    /***
193     * Return the instance of the subclass given the particular string
194     * A nice subclass might implement:
195     * <code>
196           public static <b>SubClass</b> getFor(String aString)
197           {
198              return (<b>SubClass</b>) getFor(<b>SubClass</b>.class, aString);
199           }
200     * </code>
201     */
202    public static TypeSafeEnumerator getFor(Class aClass, String aString)
203    {
204       TypeSafeEnumerator instance;
205 
206       for (Iterator iterator = getIterator(aClass); iterator.hasNext(); )
207       {
208          instance = (TypeSafeEnumerator) iterator.next();
209 
210          if (instance.getText().equalsIgnoreCase(aString))
211             return instance;
212       }
213       throw new IllegalArgumentException("No enumeration (instance) of "+aClass+" found for '"+aString+"'");
214    }
215 
216 
217 }
218 
219 /*
220  * $Log: TypeSafeEnumerator.java,v $
221  * Revision 1.1  2005/03/22 13:00:41  mch
222  * Seperated utils from common
223  *
224  * Revision 1.2  2003/10/02 13:10:46  mch
225  * Added documentation
226  *
227  */
228 
229 
230