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 }