View Javadoc

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