From 8b1992ee2ffefeaf763477216241a02a80e0522e Mon Sep 17 00:00:00 2001 From: Stephen Colebourne Date: Mon, 4 Aug 2003 23:52:27 +0000 Subject: [PATCH] Rework Functional Enums to work on JDK1.2 bug 19030 git-svn-id: https://svn.apache.org/repos/asf/jakarta/commons/proper/lang/trunk@137570 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/commons/lang/enum/Enum.java | 137 +++++++++++------- .../apache/commons/lang/enum/ValuedEnum.java | 23 ++- .../commons/lang/enum/OperationEnum.java | 47 ++++-- 3 files changed, 136 insertions(+), 71 deletions(-) diff --git a/src/java/org/apache/commons/lang/enum/Enum.java b/src/java/org/apache/commons/lang/enum/Enum.java index 6bc6667ae..f3ae70de8 100644 --- a/src/java/org/apache/commons/lang/enum/Enum.java +++ b/src/java/org/apache/commons/lang/enum/Enum.java @@ -75,9 +75,12 @@ import org.apache.commons.lang.StringUtils; * however that a more robust type-safe class-based solution can be designed. This * class follows the basic Java type-safe enumeration pattern.

* - *

NOTE: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.

+ *

NOTE:Due to the way in which Java ClassLoaders work, comparing + * Enum objects should always be done using equals(), not ==. + * The equals() method will try == first so in most cases the effect is the same.

+ * + *

Of course, if you actually want (or don't mind) Enums in different class + * loaders being non-equal, then you can use ==.

* *

Simple Enums

* @@ -125,7 +128,7 @@ import org.apache.commons.lang.StringUtils; * superclass and subclass.

* *
- * 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 @@ import org.apache.commons.lang.StringUtils;
  * 
  * 

Functional Enums

* - *

The enums can have functionality by using anonymous inner classes - * [Effective Java, Bloch01]:

+ *

The enums can have functionality by defining subclasses and + * changing the super() call:

* *
- * 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,18 +208,20 @@ import org.apache.commons.lang.StringUtils;
  *   }
  * }
  * 
+ *

The code above will work on JDK 1.2. If JDK1.3 and later is used, + * the subclasses may be defined as anonymous.

* * @author Apache Avalon project * @author Stephen Colebourne * @author Chris Webb * @author Mike Bowler * @since 1.0 - * @version $Id: Enum.java,v 1.17 2003/07/30 23:17:23 scolebourne Exp $ + * @version $Id: Enum.java,v 1.18 2003/08/04 23:52:27 scolebourne Exp $ */ 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 @@ -225,6 +237,10 @@ public abstract class Enum implements Comparable, Serializable { * The string representation of the Enum. */ private final String iName; + /** + * The Enum class. + */ + private final Class iEnumClass; /** * The hashcode representation of the Enum. */ @@ -264,15 +280,53 @@ public abstract class Enum implements Comparable, Serializable { */ 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 + } + /** + *

Constructor to add a new named item to the enumeration.

+ * + *

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.

+ * + * @param name the name of the enum object, + * must not be empty or null + * @param enumClass the enum class, + * must not be null and must be this class or a superclass + * @throws IllegalArgumentException if the name is null + * or an empty string + * @throws IllegalArgumentException if the enumClass is null + * 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 @@ public abstract class Enum implements Comparable, Serializable { } 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 @@ public abstract class Enum implements Comparable, Serializable { * @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,29 +473,6 @@ public abstract class Enum implements Comparable, Serializable { return entry; } - /** - *

Convert a class to the actual common enum class.

- * - *

This accounts for anonymous inner classes.

- * - * @param cls the class to get the name for - * @return the class name - */ - 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; - } - //----------------------------------------------------------------------- /** *

Retrieve the name of this Enum item, set in the constructor.

@@ -455,6 +483,18 @@ public abstract class Enum implements Comparable, Serializable { return iName; } + /** + *

Retrieves the Class of this Enum item, set in the constructor.

+ * + *

This is normally the same as getClass(), but for + * advanced Enums may be different.

+ * + * @return the String name of this Enum item + */ + public final Class getEnumClass() { + return iEnumClass; + } + /** *

Tests for equality.

* @@ -473,7 +513,7 @@ public abstract class Enum implements Comparable, Serializable { } 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 abstract class Enum implements Comparable, Serializable { */ 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; diff --git a/src/java/org/apache/commons/lang/enum/ValuedEnum.java b/src/java/org/apache/commons/lang/enum/ValuedEnum.java index de64f5d4d..b90d1107c 100644 --- a/src/java/org/apache/commons/lang/enum/ValuedEnum.java +++ b/src/java/org/apache/commons/lang/enum/ValuedEnum.java @@ -133,12 +133,12 @@ import org.apache.commons.lang.ClassUtils; * @author Apache Avalon project * @author Stephen Colebourne * @since 1.0 - * @version $Id: ValuedEnum.java,v 1.10 2003/07/30 23:17:23 scolebourne Exp $ + * @version $Id: ValuedEnum.java,v 1.11 2003/08/04 23:52:27 scolebourne Exp $ */ 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,14 +148,26 @@ public abstract class ValuedEnum extends Enum { /** * 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); iValue = value; } + /** + * 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; + } + /** *

Gets an Enum object by class and value.

* @@ -217,8 +229,7 @@ public abstract class ValuedEnum extends Enum { */ 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; diff --git a/src/test/org/apache/commons/lang/enum/OperationEnum.java b/src/test/org/apache/commons/lang/enum/OperationEnum.java index 253989a01..6a3189349 100644 --- a/src/test/org/apache/commons/lang/enum/OperationEnum.java +++ b/src/test/org/apache/commons/lang/enum/OperationEnum.java @@ -61,27 +61,42 @@ import java.util.Map; * Operator enumeration. * * @author Stephen Colebourne - * @version $Id: OperationEnum.java,v 1.3 2003/08/02 18:38:36 scolebourne Exp $ + * @version $Id: OperationEnum.java,v 1.4 2003/08/04 23:52:27 scolebourne Exp $ */ 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);