001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.commons.lang.enums;
018:
019: import java.lang.reflect.InvocationTargetException;
020: import java.lang.reflect.Method;
021: import java.util.Iterator;
022: import java.util.List;
023:
024: import org.apache.commons.lang.ClassUtils;
025:
026: /**
027: * <p>Abstract superclass for type-safe enums with integer values suitable
028: * for use in <code>switch</code> statements.</p>
029: *
030: * <p><em>NOTE:</em>Due to the way in which Java ClassLoaders work, comparing
031: * <code>Enum</code> objects should always be done using the equals() method,
032: * not <code>==</code>. The equals() method will try <code>==</code> first so
033: * in most cases the effect is the same.</p>
034: *
035: * <p>To use this class, it must be subclassed. For example:</p>
036: *
037: * <pre>
038: * public final class JavaVersionEnum extends ValuedEnum {
039: * //standard enums for version of JVM
040: * public static final int JAVA1_0_VALUE = 100;
041: * public static final int JAVA1_1_VALUE = 110;
042: * public static final int JAVA1_2_VALUE = 120;
043: * public static final int JAVA1_3_VALUE = 130;
044: * public static final JavaVersionEnum JAVA1_0 = new JavaVersionEnum( "Java 1.0", JAVA1_0_VALUE );
045: * public static final JavaVersionEnum JAVA1_1 = new JavaVersionEnum( "Java 1.1", JAVA1_1_VALUE );
046: * public static final JavaVersionEnum JAVA1_2 = new JavaVersionEnum( "Java 1.2", JAVA1_2_VALUE );
047: * public static final JavaVersionEnum JAVA1_3 = new JavaVersionEnum( "Java 1.3", JAVA1_3_VALUE );
048: *
049: * private JavaVersionEnum(String name, int value) {
050: * super( name, value );
051: * }
052: *
053: * public static JavaVersionEnum getEnum(String javaVersion) {
054: * return (JavaVersionEnum) getEnum(JavaVersionEnum.class, javaVersion);
055: * }
056: *
057: * public static JavaVersionEnum getEnum(int javaVersion) {
058: * return (JavaVersionEnum) getEnum(JavaVersionEnum.class, javaVersion);
059: * }
060: *
061: * public static Map getEnumMap() {
062: * return getEnumMap(JavaVersionEnum.class);
063: * }
064: *
065: * public static List getEnumList() {
066: * return getEnumList(JavaVersionEnum.class);
067: * }
068: *
069: * public static Iterator iterator() {
070: * return iterator(JavaVersionEnum.class);
071: * }
072: * }
073: * </pre>
074: *
075: * <p><em>NOTE:</em>These are declared <code>final</code>, so compilers may
076: * inline the code. Ensure you recompile everything when using final. </p>
077: *
078: * <p>The above class could then be used as follows:</p>
079: *
080: * <pre>
081: * public void doSomething(JavaVersionEnum ver) {
082: * switch (ver.getValue()) {
083: * case JAVA1_0_VALUE:
084: * // ...
085: * break;
086: * case JAVA1_1_VALUE:
087: * // ...
088: * break;
089: * //...
090: * }
091: * }
092: * </pre>
093: *
094: * <p>As shown, each enum has a name and a value. These can be accessed using
095: * <code>getName</code> and <code>getValue</code>.</p>
096: *
097: * <p><em>NOTE:</em> Because the switch is ultimately sitting on top of an
098: * int, the example above is not type-safe. That is, there is nothing that
099: * checks that JAVA1_0_VALUE is a legal constant for JavaVersionEnum. </p>
100: *
101: * <p>The <code>getEnum</code> and <code>iterator</code> methods are recommended.
102: * Unfortunately, Java restrictions require these to be coded as shown in each subclass.
103: * An alternative choice is to use the {@link EnumUtils} class.</p>
104: *
105: * @author Apache Avalon project
106: * @author Stephen Colebourne
107: * @since 2.1 (class existed in enum package from v1.0)
108: * @version $Id: ValuedEnum.java 466275 2006-10-20 22:02:34Z bayard $
109: */
110: public abstract class ValuedEnum extends Enum {
111:
112: /**
113: * Required for serialization support.
114: *
115: * @see java.io.Serializable
116: */
117: private static final long serialVersionUID = -7129650521543789085L;
118:
119: /**
120: * The value contained in enum.
121: */
122: private final int iValue;
123:
124: /**
125: * Constructor for enum item.
126: *
127: * @param name the name of enum item
128: * @param value the value of enum item
129: */
130: protected ValuedEnum(String name, int value) {
131: super (name);
132: iValue = value;
133: }
134:
135: /**
136: * <p>Gets an <code>Enum</code> object by class and value.</p>
137: *
138: * <p>This method loops through the list of <code>Enum</code>,
139: * thus if there are many <code>Enum</code>s this will be
140: * slow.</p>
141: *
142: * @param enumClass the class of the <code>Enum</code> to get
143: * @param value the value of the <code>Enum</code> to get
144: * @return the enum object, or null if the enum does not exist
145: * @throws IllegalArgumentException if the enum class is <code>null</code>
146: */
147: protected static Enum getEnum(Class enumClass, int value) {
148: if (enumClass == null) {
149: throw new IllegalArgumentException(
150: "The Enum Class must not be null");
151: }
152: List list = Enum.getEnumList(enumClass);
153: for (Iterator it = list.iterator(); it.hasNext();) {
154: ValuedEnum enumeration = (ValuedEnum) it.next();
155: if (enumeration.getValue() == value) {
156: return enumeration;
157: }
158: }
159: return null;
160: }
161:
162: /**
163: * <p>Get value of enum item.</p>
164: *
165: * @return the enum item's value.
166: */
167: public final int getValue() {
168: return iValue;
169: }
170:
171: /**
172: * <p>Tests for order.</p>
173: *
174: * <p>The default ordering is numeric by value, but this
175: * can be overridden by subclasses.</p>
176: *
177: * <p>NOTE: From v2.2 the enums must be of the same type.
178: * If the parameter is in a different class loader than this instance,
179: * reflection is used to compare the values.</p>
180: *
181: * @see java.lang.Comparable#compareTo(Object)
182: * @param other the other object to compare to
183: * @return -ve if this is less than the other object, +ve if greater than,
184: * <code>0</code> of equal
185: * @throws ClassCastException if other is not an <code>Enum</code>
186: * @throws NullPointerException if other is <code>null</code>
187: */
188: public int compareTo(Object other) {
189: if (other == this ) {
190: return 0;
191: }
192: if (other.getClass() != this .getClass()) {
193: if (other.getClass().getName().equals(
194: this .getClass().getName())) {
195: return iValue - getValueInOtherClassLoader(other);
196: }
197: throw new ClassCastException("Different enum class '"
198: + ClassUtils.getShortClassName(other.getClass())
199: + "'");
200: }
201: return iValue - ((ValuedEnum) other).iValue;
202: }
203:
204: /**
205: * <p>Use reflection to return an objects value.</p>
206: *
207: * @param other the object to determine the value for
208: * @return the value
209: */
210: private int getValueInOtherClassLoader(Object other) {
211: try {
212: Method mth = other.getClass().getMethod("getValue", null);
213: Integer value = (Integer) mth.invoke(other, null);
214: return value.intValue();
215: } catch (NoSuchMethodException e) {
216: // ignore - should never happen
217: } catch (IllegalAccessException e) {
218: // ignore - should never happen
219: } catch (InvocationTargetException e) {
220: // ignore - should never happen
221: }
222: throw new IllegalStateException("This should not happen");
223: }
224:
225: /**
226: * <p>Human readable description of this <code>Enum</code> item.</p>
227: *
228: * @return String in the form <code>type[name=value]</code>, for example:
229: * <code>JavaVersion[Java 1.0=100]</code>. Note that the package name is
230: * stripped from the type name.
231: */
232: public String toString() {
233: if (iToString == null) {
234: String shortName = ClassUtils
235: .getShortClassName(getEnumClass());
236: iToString = shortName + "[" + getName() + "=" + getValue()
237: + "]";
238: }
239: return iToString;
240: }
241: }
|