Moving the enums package over to the backcompat branch. Won't be in 3.0 as people should use Java enums nowadays

git-svn-id: https://svn.apache.org/repos/asf/commons/proper/lang/trunk@751349 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Henri Yandell 2009-03-08 01:56:00 +00:00
parent bcf6e9d662
commit 784a817fef
4 changed files with 0 additions and 1118 deletions

View File

@ -1,690 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.lang.enums;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import org.apache.commons.lang.ClassUtils;
import org.apache.commons.lang.StringUtils;
/**
* <p>Abstract superclass for type-safe enums.</p>
*
* <p>One feature of the C programming language lacking in Java is enumerations. The
* C implementation based on ints was poor and open to abuse. The original Java
* recommendation and most of the JDK also uses int constants. It has been recognised
* however that a more robust type-safe class-based solution can be designed. This
* class follows the basic Java type-safe enumeration pattern.</p>
*
* <p><em>NOTE:</em> Due to the way in which Java ClassLoaders work, comparing
* Enum objects should always be done using <code>equals()</code>, not <code>==</code>.
* The equals() method will try == first so in most cases the effect is the same.</p>
*
* <p>Of course, if you actually want (or don't mind) Enums in different class
* loaders being non-equal, then you can use <code>==</code>.</p>
*
* <h4>Simple Enums</h4>
*
* <p>To use this class, it must be subclassed. For example:</p>
*
* <pre>
* public final class ColorEnum extends Enum {
* public static final ColorEnum RED = new ColorEnum("Red");
* public static final ColorEnum GREEN = new ColorEnum("Green");
* public static final ColorEnum BLUE = new ColorEnum("Blue");
*
* private ColorEnum(String color) {
* super(color);
* }
*
* public static ColorEnum getEnum(String color) {
* return (ColorEnum) getEnum(ColorEnum.class, color);
* }
*
* public static Map getEnumMap() {
* return getEnumMap(ColorEnum.class);
* }
*
* public static List getEnumList() {
* return getEnumList(ColorEnum.class);
* }
*
* public static Iterator iterator() {
* return iterator(ColorEnum.class);
* }
* }
* </pre>
*
* <p>As shown, each enum has a name. This can be accessed using <code>getName</code>.</p>
*
* <p>The <code>getEnum</code> and <code>iterator</code> methods are recommended.
* Unfortunately, Java restrictions require these to be coded as shown in each subclass.
* An alternative choice is to use the {@link EnumUtils} class.</p>
*
* <h4>Subclassed Enums</h4>
* <p>A hierarchy of Enum classes can be built. In this case, the superclass is
* unaffected by the addition of subclasses (as per normal Java). The subclasses
* may add additional Enum constants <em>of the type of the superclass</em>. The
* query methods on the subclass will return all of the Enum constants from the
* superclass and subclass.</p>
*
* <pre>
* public final class ExtraColorEnum extends ColorEnum {
* // NOTE: Color enum declared above is final, change that to get this
* // example to compile.
* public static final ColorEnum YELLOW = new ExtraColorEnum("Yellow");
*
* private ExtraColorEnum(String color) {
* super(color);
* }
*
* public static ColorEnum getEnum(String color) {
* return (ColorEnum) getEnum(ExtraColorEnum.class, color);
* }
*
* public static Map getEnumMap() {
* return getEnumMap(ExtraColorEnum.class);
* }
*
* public static List getEnumList() {
* return getEnumList(ExtraColorEnum.class);
* }
*
* public static Iterator iterator() {
* return iterator(ExtraColorEnum.class);
* }
* }
* </pre>
*
* <p>This example will return RED, GREEN, BLUE, YELLOW from the List and iterator
* methods in that order. The RED, GREEN and BLUE instances will be the same (==)
* as those from the superclass ColorEnum. Note that YELLOW is declared as a
* ColorEnum and not an ExtraColorEnum.</p>
*
* <h4>Functional Enums</h4>
*
* <p>The enums can have functionality by defining subclasses and
* overriding the <code>getEnumClass()</code> method:</p>
*
* <pre>
* public static final OperationEnum PLUS = new PlusOperation();
* private static final class PlusOperation extends OperationEnum {
* private PlusOperation() {
* super("Plus");
* }
* public int eval(int a, int b) {
* return a + b;
* }
* }
* public static final OperationEnum MINUS = new MinusOperation();
* private static final class MinusOperation extends OperationEnum {
* private MinusOperation() {
* super("Minus");
* }
* public int eval(int a, int b) {
* return a - b;
* }
* }
*
* private OperationEnum(String color) {
* super(color);
* }
*
* public final Class getEnumClass() { // NOTE: new method!
* return OperationEnum.class;
* }
*
* public abstract double eval(double a, double b);
*
* public static OperationEnum getEnum(String name) {
* return (OperationEnum) getEnum(OperationEnum.class, name);
* }
*
* public static Map getEnumMap() {
* return getEnumMap(OperationEnum.class);
* }
*
* public static List getEnumList() {
* return getEnumList(OperationEnum.class);
* }
*
* public static Iterator iterator() {
* return iterator(OperationEnum.class);
* }
* }
* </pre>
* <p>The code above will work on JDK 1.2. If JDK1.3 and later is used,
* the subclasses may be defined as anonymous.</p>
*
* <h4>Nested class Enums</h4>
*
* <p>Care must be taken with class loading when defining a static nested class
* for enums. The static nested class can be loaded without the surrounding outer
* class being loaded. This can result in an empty list/map/iterator being returned.
* One solution is to define a static block that references the outer class where
* the constants are defined. For example:</p>
*
* <pre>
* public final class Outer {
* public static final BWEnum BLACK = new BWEnum("Black");
* public static final BWEnum WHITE = new BWEnum("White");
*
* // static nested enum class
* public static final class BWEnum extends Enum {
*
* static {
* // explicitly reference BWEnum class to force constants to load
* Object obj = Outer.BLACK;
* }
*
* // ... other methods omitted
* }
* }
* </pre>
*
* <p>Although the above solves the problem, it is not recommended. The best solution
* is to define the constants in the enum class, and hold references in the outer class:
*
* <pre>
* public final class Outer {
* public static final BWEnum BLACK = BWEnum.BLACK;
* public static final BWEnum WHITE = BWEnum.WHITE;
*
* // static nested enum class
* public static final class BWEnum extends Enum {
* // only define constants in enum classes - private if desired
* private static final BWEnum BLACK = new BWEnum("Black");
* private static final BWEnum WHITE = new BWEnum("White");
*
* // ... other methods omitted
* }
* }
* </pre>
*
* <p>For more details, see the 'Nested' test cases.
*
* <h4>Lang Enums and Java 5.0 Enums</h4>
*
* <p>Enums were added to Java in Java 5.0. The main differences between Lang's
* implementation and the new official JDK implementation are: </p>
* <ul>
* <li>The standard Enum is a not just a superclass, but is a type baked into the
* language. </li>
* <li>The standard Enum does not support extension, so the standard methods that
* are provided in the Lang enum are not available. </li>
* <li>Lang mandates a String name, whereas the standard Enum uses the class
* name as its name. getName() changes to name(). </li>
* </ul>
*
* <p>Generally people should use the standard Enum. Migrating from the Lang
* enum to the standard Enum is not as easy as it might be due to the lack of
* class inheritence in standard Enums. This means that it's not possible
* to provide a 'super-enum' which could provide the same utility methods
* that the Lang enum does. The following utility class is a Java 5.0
* version of our EnumUtils class and provides those utility methods. </p>
*
* <pre>
* import java.util.*;
*
* public class EnumUtils {
*
* public static Enum getEnum(Class enumClass, String token) {
* return Enum.valueOf(enumClass, token);
* }
*
* public static Map getEnumMap(Class enumClass) {
* HashMap map = new HashMap();
* Iterator itr = EnumUtils.iterator(enumClass);
* while(itr.hasNext()) {
* Enum enm = (Enum) itr.next();
* map.put( enm.name(), enm );
* }
* return map;
* }
*
* public static List getEnumList(Class enumClass) {
* return new ArrayList( EnumSet.allOf(enumClass) );
* }
*
* public static Iterator iterator(Class enumClass) {
* return EnumUtils.getEnumList(enumClass).iterator();
* }
* }
* </pre>
*
* @author Apache Avalon project
* @author Stephen Colebourne
* @author Chris Webb
* @author Mike Bowler
* @author Matthias Eichel
* @since 2.1 (class existed in enum package from v1.0)
* @version $Id$
*/
public abstract class Enum implements Comparable, Serializable {
/**
* Required for serialization support.
*
* @see java.io.Serializable
*/
private static final long serialVersionUID = -487045951170455942L;
// After discussion, the default size for HashMaps is used, as the
// sizing algorithm changes across the JDK versions
/**
* An empty <code>Map</code>, as JDK1.2 didn't have an empty map.
*/
private static final Map EMPTY_MAP = Collections.unmodifiableMap(new HashMap(0));
/**
* <code>Map</code>, key of class name, value of <code>Entry</code>.
*/
private static Map cEnumClasses
// LANG-334: To avoid exposing a mutating map,
// we copy it each time we add to it. This is cheaper than
// using a synchronized map since we are almost entirely reads
= new WeakHashMap();
/**
* The string representation of the Enum.
*/
private final String iName;
/**
* The hashcode representation of the Enum.
*/
private transient final int iHashCode;
/**
* The toString representation of the Enum.
* @since 2.0
*/
protected transient String iToString = null;
/**
* <p>Enable the iterator to retain the source code order.</p>
*/
private static class Entry {
/**
* Map of Enum name to Enum.
*/
final Map map = new HashMap();
/**
* Map of Enum name to Enum.
*/
final Map unmodifiableMap = Collections.unmodifiableMap(map);
/**
* List of Enums in source code order.
*/
final List list = new ArrayList(25);
/**
* Map of Enum name to Enum.
*/
final List unmodifiableList = Collections.unmodifiableList(list);
/**
* <p>Restrictive constructor.</p>
*/
protected Entry() {
super();
}
}
/**
* <p>Constructor to add a new named item to the enumeration.</p>
*
* @param name the name of the enum object,
* must not be empty or <code>null</code>
* @throws IllegalArgumentException if the name is <code>null</code>
* or an empty string
* @throws IllegalArgumentException if the getEnumClass() method returns
* a null or invalid Class
*/
protected Enum(String name) {
super();
init(name);
iName = name;
iHashCode = 7 + getEnumClass().hashCode() + 3 * name.hashCode();
// cannot create toString here as subclasses may want to include other data
}
/**
* Initializes the enumeration.
*
* @param name the enum name
* @throws IllegalArgumentException if the name is null or empty or duplicate
* @throws IllegalArgumentException if the enumClass is null or invalid
*/
private void init(String name) {
if (StringUtils.isEmpty(name)) {
throw new IllegalArgumentException("The Enum name must not be empty or null");
}
Class enumClass = getEnumClass();
if (enumClass == null) {
throw new IllegalArgumentException("getEnumClass() must not be null");
}
Class cls = getClass();
boolean ok = false;
while (cls != null && cls != Enum.class && cls != ValuedEnum.class) {
if (cls == enumClass) {
ok = true;
break;
}
cls = cls.getSuperclass();
}
if (ok == false) {
throw new IllegalArgumentException("getEnumClass() must return a superclass of this class");
}
Entry entry;
synchronized( Enum.class ) { // LANG-334
// create entry
entry = (Entry) cEnumClasses.get(enumClass);
if (entry == null) {
entry = createEntry(enumClass);
Map myMap = new WeakHashMap( ); // we avoid the (Map) constructor to achieve JDK 1.2 support
myMap.putAll( cEnumClasses );
myMap.put(enumClass, entry);
cEnumClasses = myMap;
}
}
if (entry.map.containsKey(name)) {
throw new IllegalArgumentException("The Enum name must be unique, '" + name + "' has already been added");
}
entry.map.put(name, this);
entry.list.add(this);
}
/**
* <p>Handle the deserialization of the class to ensure that multiple
* copies are not wastefully created, or illegal enum types created.</p>
*
* @return the resolved object
*/
protected Object readResolve() {
Entry entry = (Entry) cEnumClasses.get(getEnumClass());
if (entry == null) {
return null;
}
return entry.map.get(getName());
}
//--------------------------------------------------------------------------------
/**
* <p>Gets an <code>Enum</code> object by class and name.</p>
*
* @param enumClass the class of the Enum to get, must not
* be <code>null</code>
* @param name the name of the <code>Enum</code> to get,
* may be <code>null</code>
* @return the enum object, or <code>null</code> if the enum does not exist
* @throws IllegalArgumentException if the enum class
* is <code>null</code>
*/
protected static Enum getEnum(Class enumClass, String name) {
Entry entry = getEntry(enumClass);
if (entry == null) {
return null;
}
return (Enum) entry.map.get(name);
}
/**
* <p>Gets the <code>Map</code> of <code>Enum</code> objects by
* name using the <code>Enum</code> class.</p>
*
* <p>If the requested class has no enum objects an empty
* <code>Map</code> is returned.</p>
*
* @param enumClass the class of the <code>Enum</code> to get,
* must not be <code>null</code>
* @return the enum object Map
* @throws IllegalArgumentException if the enum class is <code>null</code>
* @throws IllegalArgumentException if the enum class is not a subclass of Enum
*/
protected static Map getEnumMap(Class enumClass) {
Entry entry = getEntry(enumClass);
if (entry == null) {
return EMPTY_MAP;
}
return entry.unmodifiableMap;
}
/**
* <p>Gets the <code>List</code> of <code>Enum</code> objects using the
* <code>Enum</code> class.</p>
*
* <p>The list is in the order that the objects were created (source code order).
* If the requested class has no enum objects an empty <code>List</code> is
* returned.</p>
*
* @param enumClass the class of the <code>Enum</code> to get,
* must not be <code>null</code>
* @return the enum object Map
* @throws IllegalArgumentException if the enum class is <code>null</code>
* @throws IllegalArgumentException if the enum class is not a subclass of Enum
*/
protected static List getEnumList(Class enumClass) {
Entry entry = getEntry(enumClass);
if (entry == null) {
return Collections.EMPTY_LIST;
}
return entry.unmodifiableList;
}
/**
* <p>Gets an <code>Iterator</code> over the <code>Enum</code> objects in
* an <code>Enum</code> class.</p>
*
* <p>The <code>Iterator</code> is in the order that the objects were
* created (source code order). If the requested class has no enum
* objects an empty <code>Iterator</code> is returned.</p>
*
* @param enumClass the class of the <code>Enum</code> to get,
* must not be <code>null</code>
* @return an iterator of the Enum objects
* @throws IllegalArgumentException if the enum class is <code>null</code>
* @throws IllegalArgumentException if the enum class is not a subclass of Enum
*/
protected static Iterator iterator(Class enumClass) {
return Enum.getEnumList(enumClass).iterator();
}
//-----------------------------------------------------------------------
/**
* <p>Gets an <code>Entry</code> from the map of Enums.</p>
*
* @param enumClass the class of the <code>Enum</code> to get
* @return the enum entry
*/
private static Entry getEntry(Class enumClass) {
if (enumClass == null) {
throw new IllegalArgumentException("The Enum Class must not be null");
}
if (Enum.class.isAssignableFrom(enumClass) == false) {
throw new IllegalArgumentException("The Class must be a subclass of Enum");
}
Entry entry = (Entry) cEnumClasses.get(enumClass);
return entry;
}
/**
* <p>Creates an <code>Entry</code> for storing the Enums.</p>
*
* <p>This accounts for subclassed Enums.</p>
*
* @param enumClass the class of the <code>Enum</code> to get
* @return the enum entry
*/
private static Entry createEntry(Class enumClass) {
Entry entry = new Entry();
Class cls = enumClass.getSuperclass();
while (cls != null && cls != Enum.class && cls != ValuedEnum.class) {
Entry loopEntry = (Entry) cEnumClasses.get(cls);
if (loopEntry != null) {
entry.list.addAll(loopEntry.list);
entry.map.putAll(loopEntry.map);
break; // stop here, as this will already have had superclasses added
}
cls = cls.getSuperclass();
}
return entry;
}
//-----------------------------------------------------------------------
/**
* <p>Retrieve the name of this Enum item, set in the constructor.</p>
*
* @return the <code>String</code> name of this Enum item
*/
public final String getName() {
return iName;
}
/**
* <p>Retrieves the Class of this Enum item, set in the constructor.</p>
*
* <p>This is normally the same as <code>getClass()</code>, but for
* advanced Enums may be different. If overridden, it must return a
* constant value.</p>
*
* @return the <code>Class</code> of the enum
* @since 2.0
*/
public Class getEnumClass() {
return getClass();
}
/**
* <p>Tests for equality.</p>
*
* <p>Two Enum objects are considered equal
* if they have the same class names and the same names.
* Identity is tested for first, so this method usually runs fast.</p>
*
* <p>If the parameter is in a different class loader than this instance,
* reflection is used to compare the names.</p>
*
* @param other the other object to compare for equality
* @return <code>true</code> if the Enums are equal
*/
public final boolean equals(Object other) {
if (other == this) {
return true;
} else if (other == null) {
return false;
} else if (other.getClass() == this.getClass()) {
// Ok to do a class cast to Enum here since the test above
// guarantee both
// classes are in the same class loader.
return iName.equals(((Enum) other).iName);
} else {
// This and other are in different class loaders, we must check indirectly
if (other.getClass().getName().equals(this.getClass().getName()) == false) {
return false;
}
return iName.equals( getNameInOtherClassLoader(other) );
}
}
/**
* <p>Returns a suitable hashCode for the enumeration.</p>
*
* @return a hashcode based on the name
*/
public final int hashCode() {
return iHashCode;
}
/**
* <p>Tests for order.</p>
*
* <p>The default ordering is alphabetic by name, but this
* can be overridden by subclasses.</p>
*
* <p>If the parameter is in a different class loader than this instance,
* reflection is used to compare the names.</p>
*
* @see java.lang.Comparable#compareTo(Object)
* @param other the other object to compare to
* @return -ve if this is less than the other object, +ve if greater
* than, <code>0</code> of equal
* @throws ClassCastException if other is not an Enum
* @throws NullPointerException if other is <code>null</code>
*/
public int compareTo(Object other) {
if (other == this) {
return 0;
}
if (other.getClass() != this.getClass()) {
if (other.getClass().getName().equals(this.getClass().getName())) {
return iName.compareTo( getNameInOtherClassLoader(other) );
}
throw new ClassCastException(
"Different enum class '" + ClassUtils.getShortClassName(other.getClass()) + "'");
}
return iName.compareTo(((Enum) other).iName);
}
/**
* <p>Use reflection to return an objects class name.</p>
*
* @param other The object to determine the class name for
* @return The class name
*/
private String getNameInOtherClassLoader(Object other) {
try {
Method mth = other.getClass().getMethod("getName", null);
String name = (String) mth.invoke(other, null);
return name;
} catch (NoSuchMethodException e) {
// ignore - should never happen
} catch (IllegalAccessException e) {
// ignore - should never happen
} catch (InvocationTargetException e) {
// ignore - should never happen
}
throw new IllegalStateException("This should not happen");
}
/**
* <p>Human readable description of this Enum item.</p>
*
* @return String in the form <code>type[name]</code>, for example:
* <code>Color[Red]</code>. Note that the package name is stripped from
* the type name.
*/
public String toString() {
if (iToString == null) {
String shortName = ClassUtils.getShortClassName(getEnumClass());
iToString = shortName + "[" + getName() + "]";
}
return iToString;
}
}

View File

@ -1,124 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.lang.enums;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* <p>Utility class for accessing and manipulating {@link Enum}s.</p>
*
* @see Enum
* @see ValuedEnum
* @author Stephen Colebourne
* @author Gary Gregory
* @since 2.1 (class existed in enum package from v1.0)
* @version $Id$
*/
public class EnumUtils {
/**
* Public constructor. This class should not normally be instantiated.
* @since 2.0
*/
public EnumUtils() {
super();
}
/**
* <p>Gets an <code>Enum</code> object by class and name.</p>
*
* @param enumClass the class of the <code>Enum</code> to get
* @param name the name of the Enum to get, may be <code>null</code>
* @return the enum object
* @throws IllegalArgumentException if the enum class is <code>null</code>
*/
public static Enum getEnum(Class enumClass, String name) {
return Enum.getEnum(enumClass, name);
}
/**
* <p>Gets a <code>ValuedEnum</code> object by class and value.</p>
*
* @param enumClass the class of the <code>Enum</code> to get
* @param value the value of the <code>Enum</code> to get
* @return the enum object, or null if the enum does not exist
* @throws IllegalArgumentException if the enum class is <code>null</code>
*/
public static ValuedEnum getEnum(Class enumClass, int value) {
return (ValuedEnum) ValuedEnum.getEnum(enumClass, value);
}
/**
* <p>Gets the <code>Map</code> of <code>Enum</code> objects by
* name using the <code>Enum</code> class.</p>
*
* <p>If the requested class has no enum objects an empty
* <code>Map</code> is returned. The <code>Map</code> is unmodifiable.</p>
*
* @param enumClass the class of the <code>Enum</code> to get
* @return the enum object Map
* @throws IllegalArgumentException if the enum class is <code>null</code>
* @throws IllegalArgumentException if the enum class is not a subclass
* of <code>Enum</code>
*/
public static Map getEnumMap(Class enumClass) {
return Enum.getEnumMap(enumClass);
}
/**
* <p>Gets the <code>List</code> of <code>Enum</code> objects using
* the <code>Enum</code> class.</p>
*
* <p>The list is in the order that the objects were created
* (source code order).</p>
*
* <p>If the requested class has no enum objects an empty
* <code>List</code> is returned. The <code>List</code> is unmodifiable.</p>
*
* @param enumClass the class of the Enum to get
* @return the enum object Map
* @throws IllegalArgumentException if the enum class is <code>null</code>
* @throws IllegalArgumentException if the enum class is not a subclass
* of <code>Enum</code>
*/
public static List getEnumList(Class enumClass) {
return Enum.getEnumList(enumClass);
}
/**
* <p>Gets an <code>Iterator</code> over the <code>Enum</code> objects
* in an <code>Enum</code> class.</p>
*
* <p>The iterator is in the order that the objects were created
* (source code order).</p>
*
* <p>If the requested class has no enum objects an empty
* <code>Iterator</code> is returned. The <code>Iterator</code>
* is unmodifiable.</p>
*
* @param enumClass the class of the <code>Enum</code> to get
* @return an <code>Iterator</code> of the <code>Enum</code> objects
* @throws IllegalArgumentException if the enum class is <code>null</code>
* @throws IllegalArgumentException if the enum class is not a subclass of <code>Enum</code>
*/
public static Iterator iterator(Class enumClass) {
return Enum.getEnumList(enumClass).iterator();
}
}

View File

@ -1,236 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.lang.enums;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.lang.ClassUtils;
/**
* <p>Abstract superclass for type-safe enums with integer values suitable
* for use in <code>switch</code> statements.</p>
*
* <p><em>NOTE:</em>Due to the way in which Java ClassLoaders work, comparing
* <code>Enum</code> objects should always be done using the equals() method,
* not <code>==</code>. The equals() method will try <code>==</code> first so
* in most cases the effect is the same.</p>
*
* <p>To use this class, it must be subclassed. For example:</p>
*
* <pre>
* public final class JavaVersionEnum extends ValuedEnum {
* //standard enums for version of JVM
* public static final int JAVA1_0_VALUE = 100;
* public static final int JAVA1_1_VALUE = 110;
* public static final int JAVA1_2_VALUE = 120;
* public static final int JAVA1_3_VALUE = 130;
* public static final JavaVersionEnum JAVA1_0 = new JavaVersionEnum( "Java 1.0", JAVA1_0_VALUE );
* public static final JavaVersionEnum JAVA1_1 = new JavaVersionEnum( "Java 1.1", JAVA1_1_VALUE );
* public static final JavaVersionEnum JAVA1_2 = new JavaVersionEnum( "Java 1.2", JAVA1_2_VALUE );
* public static final JavaVersionEnum JAVA1_3 = new JavaVersionEnum( "Java 1.3", JAVA1_3_VALUE );
*
* private JavaVersionEnum(String name, int value) {
* super( name, value );
* }
*
* public static JavaVersionEnum getEnum(String javaVersion) {
* return (JavaVersionEnum) getEnum(JavaVersionEnum.class, javaVersion);
* }
*
* public static JavaVersionEnum getEnum(int javaVersion) {
* return (JavaVersionEnum) getEnum(JavaVersionEnum.class, javaVersion);
* }
*
* public static Map getEnumMap() {
* return getEnumMap(JavaVersionEnum.class);
* }
*
* public static List getEnumList() {
* return getEnumList(JavaVersionEnum.class);
* }
*
* public static Iterator iterator() {
* return iterator(JavaVersionEnum.class);
* }
* }
* </pre>
*
* <p><em>NOTE:</em>These are declared <code>final</code>, so compilers may
* inline the code. Ensure you recompile everything when using final. </p>
*
* <p>The above class could then be used as follows:</p>
*
* <pre>
* public void doSomething(JavaVersionEnum ver) {
* switch (ver.getValue()) {
* case JAVA1_0_VALUE:
* // ...
* break;
* case JAVA1_1_VALUE:
* // ...
* break;
* //...
* }
* }
* </pre>
*
* <p>As shown, each enum has a name and a value. These can be accessed using
* <code>getName</code> and <code>getValue</code>.</p>
*
* <p><em>NOTE:</em> Because the switch is ultimately sitting on top of an
* int, the example above is not type-safe. That is, there is nothing that
* checks that JAVA1_0_VALUE is a legal constant for JavaVersionEnum. </p>
*
* <p>The <code>getEnum</code> and <code>iterator</code> methods are recommended.
* Unfortunately, Java restrictions require these to be coded as shown in each subclass.
* An alternative choice is to use the {@link EnumUtils} class.</p>
*
* @author Apache Avalon project
* @author Stephen Colebourne
* @since 2.1 (class existed in enum package from v1.0)
* @version $Id$
*/
public abstract class ValuedEnum extends Enum {
/**
* Required for serialization support.
*
* @see java.io.Serializable
*/
private static final long serialVersionUID = -7129650521543789085L;
/**
* The value contained in enum.
*/
private final int iValue;
/**
* Constructor for enum item.
*
* @param name the name of enum item
* @param value the value of enum item
*/
protected ValuedEnum(String name, int value) {
super(name);
iValue = value;
}
/**
* <p>Gets an <code>Enum</code> object by class and value.</p>
*
* <p>This method loops through the list of <code>Enum</code>,
* thus if there are many <code>Enum</code>s this will be
* slow.</p>
*
* @param enumClass the class of the <code>Enum</code> to get
* @param value the value of the <code>Enum</code> to get
* @return the enum object, or null if the enum does not exist
* @throws IllegalArgumentException if the enum class is <code>null</code>
*/
protected static Enum getEnum(Class enumClass, int value) {
if (enumClass == null) {
throw new IllegalArgumentException("The Enum Class must not be null");
}
List list = Enum.getEnumList(enumClass);
for (Iterator it = list.iterator(); it.hasNext();) {
ValuedEnum enumeration = (ValuedEnum) it.next();
if (enumeration.getValue() == value) {
return enumeration;
}
}
return null;
}
/**
* <p>Get value of enum item.</p>
*
* @return the enum item's value.
*/
public final int getValue() {
return iValue;
}
/**
* <p>Tests for order.</p>
*
* <p>The default ordering is numeric by value, but this
* can be overridden by subclasses.</p>
*
* <p>NOTE: From v2.2 the enums must be of the same type.
* If the parameter is in a different class loader than this instance,
* reflection is used to compare the values.</p>
*
* @see java.lang.Comparable#compareTo(Object)
* @param other the other object to compare to
* @return -ve if this is less than the other object, +ve if greater than,
* <code>0</code> of equal
* @throws ClassCastException if other is not an <code>Enum</code>
* @throws NullPointerException if other is <code>null</code>
*/
public int compareTo(Object other) {
if (other == this) {
return 0;
}
if (other.getClass() != this.getClass()) {
if (other.getClass().getName().equals(this.getClass().getName())) {
return iValue - getValueInOtherClassLoader(other);
}
throw new ClassCastException(
"Different enum class '" + ClassUtils.getShortClassName(other.getClass()) + "'");
}
return iValue - ((ValuedEnum) other).iValue;
}
/**
* <p>Use reflection to return an objects value.</p>
*
* @param other the object to determine the value for
* @return the value
*/
private int getValueInOtherClassLoader(Object other) {
try {
Method mth = other.getClass().getMethod("getValue", null);
Integer value = (Integer) mth.invoke(other, null);
return value.intValue();
} catch (NoSuchMethodException e) {
// ignore - should never happen
} catch (IllegalAccessException e) {
// ignore - should never happen
} catch (InvocationTargetException e) {
// ignore - should never happen
}
throw new IllegalStateException("This should not happen");
}
/**
* <p>Human readable description of this <code>Enum</code> item.</p>
*
* @return String in the form <code>type[name=value]</code>, for example:
* <code>JavaVersion[Java 1.0=100]</code>. Note that the package name is
* stripped from the type name.
*/
public String toString() {
if (iToString == null) {
String shortName = ClassUtils.getShortClassName(getEnumClass());
iToString = shortName + "[" + getName() + "=" + getValue() + "]";
}
return iToString;
}
}

View File

@ -1,68 +0,0 @@
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<html>
<body>
<p>
Provides an implementation of the C style <code>enum</code> in the Java world.
</p>
<p>
The classic example being an RGB color enumeration.
</p>
<pre>
public final class ColorEnum extends Enum {
public static final ColorEnum RED = new ColorEnum("Red");
public static final ColorEnum GREEN = new ColorEnum("Green");
public static final ColorEnum BLUE = new ColorEnum("Blue");
private ColorEnum(String color) {
super(color);
}
public static ColorEnum getEnum(String color) {
return (ColorEnum) getEnum(ColorEnum.class, color);
}
public static Map getEnumMap() {
return getEnumMap(ColorEnum.class);
}
public static List getEnumList() {
return getEnumList(ColorEnum.class);
}
public static Iterator iterator() {
return iterator(ColorEnum.class);
}
}
</pre>
@since 2.1
<h2>Migration to Java5</h2>
<p>Java now provides Enums and the existing code can go away, with the following code changes. </p>
<p>Firstly rewrite your enum as a Java enum. See the
<a href="http://java.sun.com/docs/books/tutorial/java/javaOO/enum.html">Java tutorial</a> for more. </p>
<p>Then change the following: </p>
<pre>
getEnum -> valueOf
getEnumList -> new ArrayList( EnumSet.allOf(enumClass) ) // or just use an EnumSet
iterator -> EnumSet.allOf(...).iterator()
getEnumMap -> org.apache.commons.lang.EnumUtils.asMap(enumClass)
</pre>
</body>
</html>