scolebourne 2003/08/04 16:52:28 Modified: lang/src/java/org/apache/commons/lang/enum Enum.java ValuedEnum.java lang/src/test/org/apache/commons/lang/enum OperationEnum.java Log: Rework Functional Enums to work on JDK1.2 bug 19030 Revision Changes Path 1.18 +87 -48 jakarta-commons/lang/src/java/org/apache/commons/lang/enum/Enum.java Index: Enum.java =================================================================== RCS file: /home/cvs/jakarta-commons/lang/src/java/org/apache/commons/lang/enum/Enum.java,v retrieving revision 1.17 retrieving revision 1.18 diff -u -r1.17 -r1.18 --- Enum.java 30 Jul 2003 23:17:23 -0000 1.17 +++ Enum.java 4 Aug 2003 23:52:27 -0000 1.18 @@ -75,9 +75,12 @@ * 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 the equals() method, not ==. The equals() method will - * try == first so in most cases the effect is the same.</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> * @@ -125,7 +128,7 @@ * superclass and subclass.</p> * * <pre> - * public class ExtraColorEnum extends ColorEnum { + * 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"); @@ -159,24 +162,31 @@ * * <h4>Functional Enums</h4> * - * <p>The enums can have functionality by using anonymous inner classes - * [Effective Java, Bloch01]:</p> + * <p>The enums can have functionality by defining subclasses and + * changing the <code>super()</code> call:</p> * * <pre> - * public abstract class OperationEnum extends Enum { - * public static final OperationEnum PLUS = new OperationEnum("Plus") { - * public double eval(double a, double b) { + * 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 OperationEnum("Minus") { - * public double eval(double a, double 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); + * super(color, OperationEnum.class); // NOTE: super() changed! * } * * public abstract double eval(double a, double b); @@ -198,6 +208,8 @@ * } * } * </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> * * @author Apache Avalon project * @author Stephen Colebourne @@ -209,7 +221,7 @@ public abstract class Enum implements Comparable, Serializable { /** Lang version 1.0.1 serial compatability */ - static final long serialVersionUID = -487045951170455942L; + private static final long serialVersionUID = -487045951170455942L; // After discussion, the default size for HashMaps is used, as the // sizing algorithm changes across the JDK versions @@ -226,6 +238,10 @@ */ private final String iName; /** + * The Enum class. + */ + private final Class iEnumClass; + /** * The hashcode representation of the Enum. */ private transient final int iHashCode; @@ -264,15 +280,53 @@ */ protected Enum(String name) { super(); + init(name, getClass()); + iName = name; + iEnumClass = getClass(); + iHashCode = 7 + iEnumClass.hashCode() + 3 * name.hashCode(); + // cannot create toString here as subclasses may want to include other data + } + /** + * <p>Constructor to add a new named item to the enumeration.</p> + * + * <p>This constructor is used when a subclass wants to allow further + * subclasses to add values to the enumeration. The class specifies + * which class they are all to be tied to.</p> + * + * @param name the name of the enum object, + * must not be empty or <code>null</code> + * @param enumClass the enum class, + * must not be null and must be this class or a superclass + * @throws IllegalArgumentException if the name is <code>null</code> + * or an empty string + * @throws IllegalArgumentException if the enumClass is <code>null</code> + * or invalid + */ + protected Enum(String name, Class enumClass) { + super(); + init(name, enumClass); + iName = name; + iEnumClass = enumClass; + iHashCode = 7 + enumClass.hashCode() + 3 * name.hashCode(); + // cannot create toString here as subclasses may want to include other data + } + + /** + * Initializes the enumeration. + * + * @param name the enum name + * @param enumClass the enum class + * @throws IllegalArgumentException if the name is null or empty + * @throws IllegalArgumentException if the enumClass is null or invalid + */ + private void init(String name, Class enumClass) { if (StringUtils.isEmpty(name)) { throw new IllegalArgumentException("The Enum name must not be empty or null"); } - iName = name; - Class enumClass = Enum.getEnumClass(getClass()); Entry entry = (Entry) cEnumClasses.get(enumClass); if (entry == null) { - entry = createEntry(getClass()); + entry = createEntry(enumClass); cEnumClasses.put(enumClass, entry); } if (entry.map.containsKey(name)) { @@ -280,9 +334,6 @@ } entry.map.put(name, this); entry.list.add(this); - - iHashCode = 7 + enumClass.hashCode() + 3 * name.hashCode(); - // cannot create toString here as subclasses may want to include other data } /** @@ -292,7 +343,7 @@ * @return the resolved object */ protected Object readResolve() { - Entry entry = (Entry) cEnumClasses.get(Enum.getEnumClass(getClass())); + Entry entry = (Entry) cEnumClasses.get(iEnumClass); if (entry == null) { return null; } @@ -422,37 +473,26 @@ return entry; } + //----------------------------------------------------------------------- /** - * <p>Convert a class to the actual common enum class.</p> - * - * <p>This accounts for anonymous inner classes.</p> + * <p>Retrieve the name of this Enum item, set in the constructor.</p> * - * @param cls the class to get the name for - * @return the class name + * @return the <code>String</code> name of this Enum item */ - protected static Class getEnumClass(Class cls) { - String className = cls.getName(); - int index = className.lastIndexOf('$'); - if (index > -1) { - // is it an anonymous inner class? - String inner = className.substring(index + 1); - if (inner.length() > 0 && - inner.charAt(0) >= '0' && - inner.charAt(0) < '9') { - return cls.getSuperclass(); - } - } - return cls; + public final String getName() { + return iName; } - //----------------------------------------------------------------------- /** - * <p>Retrieve the name of this Enum item, set in the constructor.</p> + * <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.</p> * * @return the <code>String</code> name of this Enum item */ - public final String getName() { - return iName; + public final Class getEnumClass() { + return iEnumClass; } /** @@ -473,7 +513,7 @@ } else if (other.getClass() == this.getClass()) { // shouldn't happen, but... return iName.equals(((Enum) other).iName); - } else if (getEnumClass(other.getClass()).getName().equals(getEnumClass(this.getClass()).getName())) { + } else if (((Enum) other).iEnumClass.getName().equals(iEnumClass.getName())) { // different classloaders try { // try to avoid reflection @@ -537,8 +577,7 @@ */ public String toString() { if (iToString == null) { - Class cls = Enum.getEnumClass(getClass()); - String shortName = ClassUtils.getShortClassName(cls); + String shortName = ClassUtils.getShortClassName(iEnumClass); iToString = shortName + "[" + getName() + "]"; } return iToString; 1.11 +17 -6 jakarta-commons/lang/src/java/org/apache/commons/lang/enum/ValuedEnum.java Index: ValuedEnum.java =================================================================== RCS file: /home/cvs/jakarta-commons/lang/src/java/org/apache/commons/lang/enum/ValuedEnum.java,v retrieving revision 1.10 retrieving revision 1.11 diff -u -r1.10 -r1.11 --- ValuedEnum.java 30 Jul 2003 23:17:23 -0000 1.10 +++ ValuedEnum.java 4 Aug 2003 23:52:27 -0000 1.11 @@ -138,7 +138,7 @@ public abstract class ValuedEnum extends Enum { /** Lang version 1.0.1 serial compatability */ - static final long serialVersionUID = -7129650521543789085L; + private static final long serialVersionUID = -7129650521543789085L; /** * The value contained in enum. @@ -148,8 +148,8 @@ /** * Constructor for enum item. * - * @param name the name of enum item. - * @param value the value of enum item. + * @param name the name of enum item + * @param value the value of enum item */ protected ValuedEnum(String name, int value) { super(name); @@ -157,6 +157,18 @@ } /** + * Constructor for enum item. + * + * @param name the name of enum item + * @param enumClass the enum class + * @param value the value of enum item + */ + protected ValuedEnum(String name, Class enumClass, int value) { + super(name, enumClass); + 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>, @@ -217,8 +229,7 @@ */ public String toString() { if (iToString == null) { - Class cls = Enum.getEnumClass(getClass()); - String shortName = ClassUtils.getShortClassName(cls); + String shortName = ClassUtils.getShortClassName(getEnumClass()); iToString = shortName + "[" + getName() + "=" + getValue() + "]"; } return iToString; 1.4 +31 -16 jakarta-commons/lang/src/test/org/apache/commons/lang/enum/OperationEnum.java Index: OperationEnum.java =================================================================== RCS file: /home/cvs/jakarta-commons/lang/src/test/org/apache/commons/lang/enum/OperationEnum.java,v retrieving revision 1.3 retrieving revision 1.4 diff -u -r1.3 -r1.4 --- OperationEnum.java 2 Aug 2003 18:38:36 -0000 1.3 +++ OperationEnum.java 4 Aug 2003 23:52:27 -0000 1.4 @@ -64,24 +64,39 @@ * @version $Id$ */ public abstract class OperationEnum extends Enum { - public static final OperationEnum PLUS; - public static final OperationEnum MINUS; - static { - // Get around JDK Linux bug - PLUS = new OperationEnum("Plus") { - public int eval(int a, int b) { - return (a + b); - } - }; - MINUS = new OperationEnum("Minus") { - public int eval(int a, int b) { - return (a - b); - } - }; + // This syntax works for JDK 1.3 and upwards: +// public static final OperationEnum PLUS = new OperationEnum("Plus") { +// public int eval(int a, int b) { +// return (a + b); +// } +// }; +// public static final OperationEnum MINUS = new OperationEnum("Minus") { +// public int eval(int a, int b) { +// return (a - b); +// } +// }; + // This syntax works for JDK 1.2 and upwards: + public static final OperationEnum PLUS = new PlusOperation(); + private static 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 class MinusOperation extends OperationEnum { + private MinusOperation() { + super("Minus"); + } + public int eval(int a, int b) { + return (a - b); + } } private OperationEnum(String name) { - super(name); + super(name, OperationEnum.class); } public abstract int eval(int a, int b);
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]