1 module hunt.Enum; 2 3 import hunt.Exceptions; 4 import hunt.util.Common; 5 import hunt.util.Comparator; 6 7 import std.traits; 8 9 /** 10 */ 11 interface Enum(E) : Comparable!E { 12 13 string name(); 14 15 int ordinal(); 16 17 string toString(); 18 } 19 20 /** 21 * This is the common base class of all enumeration types. 22 */ 23 abstract class AbstractEnum(E) : Enum!E { 24 25 /** 26 * Sole constructor. Programmers cannot invoke this constructor. 27 * It is for use by code emitted by the compiler in response to 28 * enum type declarations. 29 * 30 * @param name - The name of this enum constant, which is the identifier 31 * used to declare it. 32 * @param ordinal - The ordinal of this enumeration constant (its position 33 * in the enum declaration, where the initial constant is assigned 34 * an ordinal of zero). 35 */ 36 protected this(string name, int ordinal) { 37 this._name = name; 38 this._ordinal = ordinal; 39 } 40 41 /** 42 * The name of this enum constant, as declared in the enum declaration. 43 * Most programmers should use the {@link #toString} method rather than 44 * accessing this field. 45 */ 46 protected string _name; 47 48 /** 49 * Returns the name of this enum constant, exactly as declared in its 50 * enum declaration. 51 * 52 * <b>Most programmers should use the {@link #toString} method in 53 * preference to this one, as the toString method may return 54 * a more user-friendly name.</b> This method is designed primarily for 55 * use in specialized situations where correctness depends on getting the 56 * exact name, which will not vary from release to release. 57 * 58 * @return the name of this enum constant 59 */ 60 final string name() { 61 return _name; 62 } 63 64 /** 65 * The ordinal of this enumeration constant (its position 66 * in the enum declaration, where the initial constant is assigned 67 * an ordinal of zero). 68 * 69 * Most programmers will have no use for this field. It is designed 70 * for use by sophisticated enum-based data structures, such as 71 * {@link java.util.EnumSet} and {@link java.util.EnumMap}. 72 */ 73 protected int _ordinal; 74 75 /** 76 * Returns the ordinal of this enumeration constant (its position 77 * in its enum declaration, where the initial constant is assigned 78 * an ordinal of zero). 79 * 80 * Most programmers will have no use for this method. It is 81 * designed for use by sophisticated enum-based data structures, such 82 * as {@link java.util.EnumSet} and {@link java.util.EnumMap}. 83 * 84 * @return the ordinal of this enumeration constant 85 */ 86 final int ordinal() { 87 return _ordinal; 88 } 89 90 /** 91 * Returns the name of this enum constant, as contained in the 92 * declaration. This method may be overridden, though it typically 93 * isn't necessary or desirable. An enum type should override this 94 * method when a more "programmer-friendly" string form exists. 95 * 96 * @return the name of this enum constant 97 */ 98 override string toString() { 99 return _name; 100 } 101 102 /** 103 * Returns true if the specified object is equal to this 104 * enum constant. 105 * 106 * @param other the object to be compared for equality with this object. 107 * @return true if the specified object is equal to this 108 * enum constant. 109 */ 110 final override bool opEquals(Object other) { 111 return this is other; 112 } 113 114 /** 115 * Returns a hash code for this enum constant. 116 * 117 * @return a hash code for this enum constant. 118 */ 119 // final int hashCode() { 120 // return super.hashCode(); 121 // } 122 123 /** 124 * Throws CloneNotSupportedException. This guarantees that enums 125 * are never cloned, which is necessary to preserve their "singleton" 126 * status. 127 * 128 * @return (never returns) 129 */ 130 // protected final Object clone() { 131 // throw new CloneNotSupportedException(); 132 // } 133 134 /** 135 * Compares this enum with the specified object for order. Returns a 136 * negative integer, zero, or a positive integer as this object is less 137 * than, equal to, or greater than the specified object. 138 * 139 * Enum constants are only comparable to other enum constants of the 140 * same enum type. The natural order implemented by this 141 * method is the order in which the constants are declared. 142 */ 143 final int opCmp(E o) { 144 Enum!E other = cast(Enum!E) o; 145 Enum!E self = this; 146 if (other is null) 147 throw new NullPointerException(); 148 return compare(self.ordinal, other.ordinal); 149 } 150 } 151 152 153 154 155 156 /** 157 * Returns the enum constant of the specified enum type with the 158 * specified name. The name must match exactly an identifier used 159 * to declare an enum constant in this type. (Extraneous whitespace 160 * characters are not permitted.) 161 * 162 * <p>Note that for a particular enum type {@code T}, the 163 * implicitly declared {@code static T valueOf(string)} 164 * method on that enum may be used instead of this method to map 165 * from a name to the corresponding enum constant. All the 166 * constants of an enum type can be obtained by calling the 167 * implicit {@code static T[] values()} method of that 168 * type. 169 * 170 * @param <T> The enum type whose constant is to be returned 171 * @param enumType the {@code Class} object of the enum type from which 172 * to return a constant 173 * @param name the name of the constant to return 174 * @return the enum constant of the specified enum type with the 175 * specified name 176 * @throws IllegalArgumentException if the specified enum type has 177 * no constant with the specified name, or the specified 178 * class object does not represent an enum type 179 */ 180 T valueOf(T)(string name, T defaultValue = T.init) if(is(T : Enum!(T))) { 181 static if(hasStaticMember!(T, "values")) { 182 enum string code = generateLocator!(T, "values", name.stringof, defaultValue.stringof)(); 183 mixin(code); 184 } else { 185 static assert(false, "Can't find static member values in " ~ fullyQualifiedName!T ~ "."); 186 } 187 } 188 189 private string generateLocator(T, string memberName, string paramName, string defaultValue)() 190 if(is(T : Enum!(T))) { 191 import std.format; 192 string s; 193 194 s = format(` 195 foreach(T t; T.%1$s) { 196 if(t.name() == %2$s) return t; 197 } 198 199 import hunt.logging; 200 warning("Can't locate the member: " ~ %2$s ~ " in " ~ typeid(T).name ~ ".%1$s"); 201 return %3$s;`, memberName, paramName, defaultValue); 202 203 return s; 204 }