View Javadoc

1   package com.sharkysoft.util;
2   
3   import java.util.Map;
4   import java.util.NoSuchElementException;
5   import java.util.TreeMap;
6   
7   /***
8    * Type-safe enumeration.
9    * 
10   * <p><b>Details:</b> An <code>EnumeratedType</code> defines a finite set of 
11   * values for a property.</p>
12   *
13   * <p>In C, enumerate types are typically represented using the
14   * <code>enum</code> construct.  This offers many of the benefits of
15   * type-<wbr>safety without sacrificing the efficiency of using
16   * <code>int</code>s to represent the values.  Unfortunately, Java has no such
17   * construct, and it has become common and acceptable to directly represent
18   * enumerated types using <code>final int</code>s.  Unfortunately, this
19   * adaptation lacks type safety.</p>
20   *
21   * <p><code>EnumeratedType</code> facilitates the declaration of type-<wbr>safe
22   * enumerated types.  Each element in the type's value set is represented using
23   * both <code>int</code>s and typed objects.  Methods that accept or return
24   * enumerated types can do so using type-<wbr>safe object values under most
25   * circumstances, and convert them to <code>int</code>s when necessary, such as
26   * in a <code>switch</code> statement.  Using enumerated types derived from this
27   * base class not only encourages healthy code, but it also makes debugging
28   * easier, since each type-<wbr>safe value can also be converted into a
29   * meaningful string value using <code>toString</code>.</p>
30   *
31   * @author Sharky
32   */
33  public abstract class EnumeratedType implements Comparable
34  {
35  
36  	/***
37  	 * Magic int.
38  	 *
39  	 * <p><b>Details:</b> <code>mnValue</code> is the magic <code>int</code>
40  	 * constant associated with this value.</p>
41  	 */
42  	private final int mnValue;
43  
44  	/***
45  	 * String form.
46  	 *
47  	 * <p><b>Details:</b> <code>msString</code> is the string representation this
48  	 * value, returned by <code>toString</code>.</p>
49  	 */
50  	private final String msString;
51  
52  	/***
53  	 * Maps integer values to object values.
54  	 *
55  	 * <p><b>Details:</b> <code>gpValues</code> maps integer values to object values.
56  	 * This map is populated by the constructor and supports
57  	 * <code>toObject</code>.</p>
58  	 */
59  	private static final Map gpValues = new TreeMap();
60  
61  	/***
62  	 * Initializes type-safe object value.
63  	 *
64  	 * <p><b>Details:</b> This constructor initializes type-safe object values.
65  	 * <code>inValue</code> is the magic <code>int</code> value associated with
66  	 * the enumerated value (usually declared as <code>public static final
67  	 * int</code> in the subclass).  <code>isString</code> is the string
68  	 * representation of the same value, which is usually just the
69  	 * <code>int</code> identifier in string form.</p>
70  	 *
71  	 * @param inValue int representation of value
72  	 * @param isString string representation of value
73  	 */
74  	protected EnumeratedType(final int inValue, final String isString)
75  	{
76  		mnValue = inValue;
77  		msString = isString;
78  		gpValues.put(new Integer(inValue), isString);
79  	}
80  
81  	/***
82  	 * Converts object value to integer value.
83  	 *
84  	 * <p><b>Details:</b> <code>toInt</code> returns the unique "magic
85  	 * integer" associated with this type-safe value.  This is useful in
86  	 * <code>switch</code> statements and other situations where <code>int</code>
87  	 * represention of the value might be more efficient.</p>
88  	 *
89  	 * @return integer value
90  	 */
91  	public int toInt()
92  	{
93  		return mnValue;
94  	}
95  
96  	/***
97  	 * Converts object value to string form.
98  	 *
99  	 * <p><b>Details:</b> <code>toString</code> converts this type-<wbr>safe
100 	 * object value to a string.  This may be useful for debugging.</p>
101 	 *
102 	 * @return string form
103 	 */
104 	public String toString()
105 	{
106 		return msString;
107 	}
108 
109 	/***
110 	 * Converts int value to object value.
111 	 *
112 	 * <p><b>Details:</b> <code>toEnumeratedType</code> returns the type-<wbr>safe
113 	 * object value corresponding to the given <code>int</code> value.</p>
114 	 *
115 	 * <p>Because this is a generic implementation that returns an
116 	 * <code>EnumeratedType</code>, rather than the specific object type related
117 	 * defined by the subclass, it is a good idea to define a <code>static</code>
118 	 * helper method in the subclass that returns a more specifically typed
119 	 * value.</p>
120 	 *
121 	 * @param inValue integer value
122 	 * @return object value
123 	 */
124 	public static EnumeratedType toEnumeratedType(final int inValue)
125 	{
126 		final EnumeratedType vpValue = (EnumeratedType) gpValues.get(new Integer(inValue));
127 		if (vpValue == null)
128 			throw new NoSuchElementException("inValue=" + inValue);
129 		return vpValue;
130 	}
131 
132 	/***
133 	 * Converts integer value to string form.
134 	 *
135 	 * <p><b>Details:</b> <code>toString</code> converts the given integer value
136 	 * to a string.  This may be useful for debugging.</p>
137 	 *
138 	 * @param inValue integer value
139 	 * @return string form
140 	 */
141 	public static String toString(final int inValue)
142 	{
143 		try
144 		{
145 			return toEnumeratedType(inValue).toString();
146 		}
147 		catch (NoSuchElementException ve)
148 		{
149 			return Integer.toString(inValue);
150 		}
151 	}
152 
153 	/***
154 	 * Orders enumerated values.
155 	 *
156 	 * <p><b>Details:</b> <code>compareTo</code> satisfies the contract of
157 	 * <code>compareTo</code> in interface <code>java.lang.Comparable</code>.
158 	 * Enumerated values are ordered according to their magic integer.</p>
159 	 *
160 	 * <p>This implementation throws a <code>ClassCastException</code> if one
161 	 * object's class does not descend from the other object's class.</p>
162 	 *
163 	 * @param ipValue value to commpare
164 	 * @return order of values
165 	 */
166 	public int compareTo(final Object ipValue)
167 	{
168 		final Class vpThisClass = getClass();
169 		final Class vpThatClass = ipValue.getClass();
170 		if (!(vpThisClass.isAssignableFrom(vpThatClass) || vpThatClass.isAssignableFrom(vpThisClass)))
171 			throw new ClassCastException("this=" + vpThisClass.getName() + ", that=" + vpThatClass.getName());
172 		final int vnThatValue = ((EnumeratedType) ipValue).mnValue;
173 		if (mnValue < vnThatValue)
174 			return -1;
175 		if (mnValue > vnThatValue)
176 			return +1;
177 		return 0;
178 	}
179 
180 }