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 }