diff --git a/src/java/org/apache/commons/lang/enum/Enum.java b/src/java/org/apache/commons/lang/enum/Enum.java index 7d55b2c75..6162b9021 100644 --- a/src/java/org/apache/commons/lang/enum/Enum.java +++ b/src/java/org/apache/commons/lang/enum/Enum.java @@ -214,13 +214,60 @@ * *

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

+ * + *

Nested class Enums

* + *

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:

+ * + *
+ * 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
+ *   }
+ * }
+ * 
+ * + *

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: + * + *

+ * 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
+ *   }
+ * }
+ * 
+ * + *

For more details, see the 'Nested' test cases. + * * @author Apache Avalon project * @author Stephen Colebourne * @author Chris Webb * @author Mike Bowler * @since 1.0 - * @version $Id: Enum.java,v 1.22 2003/09/07 14:32:34 psteitz Exp $ + * @version $Id: Enum.java,v 1.23 2003/11/29 15:03:54 scolebourne Exp $ */ public abstract class Enum implements Comparable, Serializable { diff --git a/src/test/org/apache/commons/lang/enum/EnumTest.java b/src/test/org/apache/commons/lang/enum/EnumTest.java index 226b60478..2dcdcb59b 100644 --- a/src/test/org/apache/commons/lang/enum/EnumTest.java +++ b/src/test/org/apache/commons/lang/enum/EnumTest.java @@ -53,6 +53,8 @@ */ package org.apache.commons.lang.enum; +import java.util.ArrayList; +import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -67,7 +69,7 @@ * Test cases for the {@link Enum} class. * * @author Stephen Colebourne - * @version $Id: EnumTest.java,v 1.11 2003/08/18 02:22:27 bayard Exp $ + * @version $Id: EnumTest.java,v 1.12 2003/11/29 15:03:54 scolebourne Exp $ */ public final class EnumTest extends TestCase { @@ -132,7 +134,7 @@ public void testIterator() { } public void testList() { - List list = ColorEnum.getEnumList(); + List list = new ArrayList(ColorEnum.getEnumList()); assertNotNull(list); @@ -146,19 +148,17 @@ public void testList() { } public void testMap() { - Map map = ColorEnum.getEnumMap(); + Map map = new HashMap(ColorEnum.getEnumMap()); assertNotNull(map); - - assertEquals( map.keySet().size(), - ColorEnum.getEnumList().size()); - assertTrue(map.containsValue(ColorEnum.RED)); assertTrue(map.containsValue(ColorEnum.GREEN)); assertTrue(map.containsValue(ColorEnum.BLUE)); assertSame(ColorEnum.RED, map.get("Red")); assertSame(ColorEnum.GREEN, map.get("Green")); assertSame(ColorEnum.BLUE, map.get("Blue")); + assertEquals( map.keySet().size(), + ColorEnum.getEnumList().size()); } public void testGet() { @@ -415,4 +415,54 @@ public void testExtended3Map() { assertSame(Extended3Enum.DELTA, map.get("Delta")); } + //----------------------------------------------------------------------- + public void testNested() { + List list = new ArrayList(Nest.ColorEnum.getEnumList()); + assertEquals(3, list.size()); // all is well + Iterator it = list.iterator(); + assertSame(Nest.ColorEnum.RED, it.next()); + assertSame(Nest.ColorEnum.GREEN, it.next()); + assertSame(Nest.ColorEnum.BLUE, it.next()); + // This nesting works because the enum constants are defined in the SAME + // class as the getEnumList(). It just acts as a normal enum. + } + + public void testNestedBroken() { + List list = new ArrayList(NestBroken.ColorEnum.getEnumList()); + assertEquals(0, list.size()); // no enums!!! + // this is BROKEN because the enum constants are defined in a DIFFERENT + // class from getEnumList(). Once NestBroken class is referenced, + // and thus class loaded with its enum constants, the getEnumList works: + new NestBroken(); + list = new ArrayList(NestBroken.ColorEnum.getEnumList()); + assertEquals(3, list.size()); // all is well!!! + Iterator it = list.iterator(); + assertSame(NestBroken.RED, it.next()); + assertSame(NestBroken.GREEN, it.next()); + assertSame(NestBroken.BLUE, it.next()); + } + + public void testNestedLinked() { + List list = new ArrayList(NestLinked.ColorEnum.getEnumList()); + assertEquals(3, list.size()); // all is well + Iterator it = list.iterator(); + assertSame(NestLinked.RED, it.next()); + assertSame(NestLinked.GREEN, it.next()); + assertSame(NestLinked.BLUE, it.next()); + // This nesting works because a static block in the enum class forces a + // class load of the outer class which defines the enum constants. + } + + public void testNestedReferenced() { + List list = new ArrayList(NestReferenced.ColorEnum.getEnumList()); + assertEquals(3, list.size()); // all is well + Iterator it = list.iterator(); + assertSame(NestReferenced.RED, it.next()); + assertSame(NestReferenced.GREEN, it.next()); + assertSame(NestReferenced.BLUE, it.next()); + // This nesting works because the enum constants are actually defined in + // the SAME class as the getEnumList(). The references in the outer class + // are just extra references. + } + } diff --git a/src/test/org/apache/commons/lang/enum/Nest.java b/src/test/org/apache/commons/lang/enum/Nest.java new file mode 100644 index 000000000..a8fbacc4f --- /dev/null +++ b/src/test/org/apache/commons/lang/enum/Nest.java @@ -0,0 +1,100 @@ +/* ==================================================================== + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2002-2003 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowledgement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgement may appear in the software itself, + * if and wherever such third-party acknowledgements normally appear. + * + * 4. The names "The Jakarta Project", "Commons", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Software Foundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ +package org.apache.commons.lang.enum; + +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * Color enumeration demonstrating a normal simple nesting case. + * All is well here as the nested enum class is really no different + * to any other class. + * + * @author Stephen Colebourne + * @version $Id: Nest.java,v 1.1 2003/11/29 15:03:54 scolebourne Exp $ + */ + +public final class Nest { + + public Nest() { + super(); + } + + public static 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); + } + } +} diff --git a/src/test/org/apache/commons/lang/enum/NestBroken.java b/src/test/org/apache/commons/lang/enum/NestBroken.java new file mode 100644 index 000000000..c943912df --- /dev/null +++ b/src/test/org/apache/commons/lang/enum/NestBroken.java @@ -0,0 +1,104 @@ +/* ==================================================================== + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2002-2003 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowledgement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgement may appear in the software itself, + * if and wherever such third-party acknowledgements normally appear. + * + * 4. The names "The Jakarta Project", "Commons", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Software Foundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ +package org.apache.commons.lang.enum; + +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * Color enumeration which demonstrates how to break the enum system. + *

+ * The class loader sees the two classes here as independent - the enum + * class is nested, not an inner class. Calling getEnumList() on ColorEnum + * will return an empty list, unless and until the NestBroken class is + * referenced. + * + * @author Stephen Colebourne + * @version $Id: NestBroken.java,v 1.1 2003/11/29 15:03:54 scolebourne Exp $ + */ + +public final class NestBroken { + + public static final ColorEnum RED = new ColorEnum("Red"); + public static final ColorEnum GREEN = new ColorEnum("Green"); + public static final ColorEnum BLUE = new ColorEnum("Blue"); + + public NestBroken() { + super(); + } + + public static final class ColorEnum extends Enum { + + 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); + } + } +} diff --git a/src/test/org/apache/commons/lang/enum/NestLinked.java b/src/test/org/apache/commons/lang/enum/NestLinked.java new file mode 100644 index 000000000..97ca17c17 --- /dev/null +++ b/src/test/org/apache/commons/lang/enum/NestLinked.java @@ -0,0 +1,111 @@ +/* ==================================================================== + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2002-2003 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowledgement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgement may appear in the software itself, + * if and wherever such third-party acknowledgements normally appear. + * + * 4. The names "The Jakarta Project", "Commons", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Software Foundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ +package org.apache.commons.lang.enum; + +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * Color enumeration which demonstrates how to define the constants in a + * different class to the Enum. The extra static{} block is + * needed to ensure that the enum constants are created before the + * static methods on the ColorEnum are used. + *

+ * The class loader sees the two classes here as independent - the enum + * class is nested, not an inner class. The static block thus forces the + * class load of the outer class, which is needed to initialise the enums. + * + * @author Stephen Colebourne + * @version $Id: NestLinked.java,v 1.1 2003/11/29 15:03:54 scolebourne Exp $ + */ + +public final class NestLinked { + + public static final ColorEnum RED = new ColorEnum("Red"); + public static final ColorEnum GREEN = new ColorEnum("Green"); + public static final ColorEnum BLUE = new ColorEnum("Blue"); + + public NestLinked() { + super(); + } + + public static final class ColorEnum extends Enum { + + static { + // Explicitly reference the class where the enums are defined + Object obj = NestLinked.RED; + } + + 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); + } + } +} diff --git a/src/test/org/apache/commons/lang/enum/NestReferenced.java b/src/test/org/apache/commons/lang/enum/NestReferenced.java new file mode 100644 index 000000000..df59d06a2 --- /dev/null +++ b/src/test/org/apache/commons/lang/enum/NestReferenced.java @@ -0,0 +1,107 @@ +/* ==================================================================== + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2002-2003 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowledgement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgement may appear in the software itself, + * if and wherever such third-party acknowledgements normally appear. + * + * 4. The names "The Jakarta Project", "Commons", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Software Foundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ +package org.apache.commons.lang.enum; + +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * Color enumeration which demonstrates how to provide a view of the constants + * in a different class to the Enum. This technique is the safest, however it + * is obviously inconvenient as it involves defining two sets of constants. + * See NestedLinked for an alternative. + * + * @author Stephen Colebourne + * @version $Id: NestReferenced.java,v 1.1 2003/11/29 15:03:54 scolebourne Exp $ + */ + +public final class NestReferenced { + + public static final ColorEnum RED = ColorEnum.RED; + public static final ColorEnum GREEN = ColorEnum.GREEN; + public static final ColorEnum BLUE = ColorEnum.BLUE; + + public NestReferenced() { + super(); + } + + public static final class ColorEnum extends Enum { + + // must be defined here, not just in outer class + private static final ColorEnum RED = new ColorEnum("Red"); + private static final ColorEnum GREEN = new ColorEnum("Green"); + private 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); + } + } +}