    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.io.Serializable;
    020    import java.lang.reflect.InvocationTargetException;
    021    import java.lang.reflect.Method;
    022    import java.util.ArrayList;
    023    import java.util.Collections;
    024    import java.util.HashMap;
    025    import java.util.Iterator;
    026    import java.util.List;
    027    import java.util.Map;
    028    import java.util.WeakHashMap;
    029
    030    import org.apache.commons.lang.ClassUtils;
    031    import org.apache.commons.lang.StringUtils;
    032
    033    /**
    034     * <p>Abstract superclass for type-safe enums.</p>
    035     *
    036     * <p>One feature of the C programming language lacking in Java is enumerations. The
    037     * C implementation based on ints was poor and open to abuse. The original Java
    038     * recommendation and most of the JDK also uses int constants. It has been recognised
    039     * however that a more robust type-safe class-based solution can be designed. This
    040     * class follows the basic Java type-safe enumeration pattern.</p>
    041     *
    042     * <p><em>NOTE:</em> Due to the way in which Java ClassLoaders work, comparing
    043     * Enum objects should always be done using <code>equals()</code>, not <code>==</code>.
    044     * The equals() method will try == first so in most cases the effect is the same.</p>
    045     *
    046     * <p>Of course, if you actually want (or don't mind) Enums in different class
    047     * loaders being non-equal, then you can use <code>==</code>.</p>
    048     *
    049     * <h4>Simple Enums</h4>
    050     *
    051     * <p>To use this class, it must be subclassed. For example:</p>
    052     *
    053     * <pre>
    054     * public final class ColorEnum extends Enum {
    055     *   public static final ColorEnum RED = new ColorEnum("Red");
    056     *   public static final ColorEnum GREEN = new ColorEnum("Green");
    057     *   public static final ColorEnum BLUE = new ColorEnum("Blue");
    058     *
    059     *   private ColorEnum(String color) {
    060     *     super(color);
    061     *   }
    062     *
    063     *   public static ColorEnum getEnum(String color) {
    064     *     return (ColorEnum) getEnum(ColorEnum.class, color);
    065     *   }
    066     *
    067     *   public static Map getEnumMap() {
    068     *     return getEnumMap(ColorEnum.class);
    069     *   }
    070     *
    071     *   public static List getEnumList() {
    072     *     return getEnumList(ColorEnum.class);
    073     *   }
    074     *
    075     *   public static Iterator iterator() {
    076     *     return iterator(ColorEnum.class);
    077     *   }
    078     * }
    079     * </pre>
    080     *
    081     * <p>As shown, each enum has a name. This can be accessed using <code>getName</code>.</p>
    082     *
    083     * <p>The <code>getEnum</code> and <code>iterator</code> methods are recommended.
    084     * Unfortunately, Java restrictions require these to be coded as shown in each subclass.
    085     * An alternative choice is to use the {@link EnumUtils} class.</p>
    086     *
    087     * <h4>Subclassed Enums</h4>
    088     * <p>A hierarchy of Enum classes can be built. In this case, the superclass is
    089     * unaffected by the addition of subclasses (as per normal Java). The subclasses
    090     * may add additional Enum constants <em>of the type of the superclass</em>. The
    091     * query methods on the subclass will return all of the Enum constants from the
    092     * superclass and subclass.</p>
    093     *
    094     * <pre>
    095     * public final class ExtraColorEnum extends ColorEnum {
    096     *   // NOTE: Color enum declared above is final, change that to get this
    097     *   // example to compile.
    098     *   public static final ColorEnum YELLOW = new ExtraColorEnum("Yellow");
    099     *
    100     *   private ExtraColorEnum(String color) {
    101     *     super(color);
    102     *   }
    103     *
    104     *   public static ColorEnum getEnum(String color) {
    105     *     return (ColorEnum) getEnum(ExtraColorEnum.class, color);
    106     *   }
    107     *
    108     *   public static Map getEnumMap() {
    109     *     return getEnumMap(ExtraColorEnum.class);
    110     *   }
    111     *
    112     *   public static List getEnumList() {
    113     *     return getEnumList(ExtraColorEnum.class);
    114     *   }
    115     *
    116     *   public static Iterator iterator() {
    117     *     return iterator(ExtraColorEnum.class);
    118     *   }
    119     * }
    120     * </pre>
    121     *
    122     * <p>This example will return RED, GREEN, BLUE, YELLOW from the List and iterator
    123     * methods in that order. The RED, GREEN and BLUE instances will be the same (==)
    124     * as those from the superclass ColorEnum. Note that YELLOW is declared as a
    125     * ColorEnum and not an ExtraColorEnum.</p>
    126     *
    127     * <h4>Functional Enums</h4>
    128     *
    129     * <p>The enums can have functionality by defining subclasses and
    130     * overriding the <code>getEnumClass()</code> method:</p>
    131     *
    132     * <pre>
    133     *   public static final OperationEnum PLUS = new PlusOperation();
    134     *   private static final class PlusOperation extends OperationEnum {
    135     *     private PlusOperation() {
    136     *       super("Plus");
    137     *     }
    138     *     public int eval(int a, int b) {
    139     *       return a + b;
    140     *     }
    141     *   }
    142     *   public static final OperationEnum MINUS = new MinusOperation();
    143     *   private static final class MinusOperation extends OperationEnum {
    144     *     private MinusOperation() {
    145     *       super("Minus");
    146     *     }
    147     *     public int eval(int a, int b) {
    148     *       return a - b;
    149     *     }
    150     *   }
    151     *
    152     *   private OperationEnum(String color) {
    153     *     super(color);
    154     *   }
    155     *
    156     *   public final Class getEnumClass() {     // NOTE: new method!
    157     *     return OperationEnum.class;
    158     *   }
    159     *
    160     *   public abstract double eval(double a, double b);
    161     *
    162     *   public static OperationEnum getEnum(String name) {
    163     *     return (OperationEnum) getEnum(OperationEnum.class, name);
    164     *   }
    165     *
    166     *   public static Map getEnumMap() {
    167     *     return getEnumMap(OperationEnum.class);
    168     *   }
    169     *
    170     *   public static List getEnumList() {
    171     *     return getEnumList(OperationEnum.class);
    172     *   }
    173     *
    174     *   public static Iterator iterator() {
    175     *     return iterator(OperationEnum.class);
    176     *   }
    177     * }
    178     * </pre>
    179     * <p>The code above will work on JDK 1.2. If JDK1.3 and later is used,
    180     * the subclasses may be defined as anonymous.</p>
    181     *
    182     * <h4>Nested class Enums</h4>
    183     *
    184     * <p>Care must be taken with class loading when defining a static nested class
    185     * for enums. The static nested class can be loaded without the surrounding outer
    186     * class being loaded. This can result in an empty list/map/iterator being returned.
    187     * One solution is to define a static block that references the outer class where
    188     * the constants are defined. For example:</p>
    189     *
    190     * <pre>
    191     * public final class Outer {
    192     *   public static final BWEnum BLACK = new BWEnum("Black");
    193     *   public static final BWEnum WHITE = new BWEnum("White");
    194     *
    195     *   // static nested enum class
    196     *   public static final class BWEnum extends Enum {
    197     *
    198     *     static {
    199     *       // explicitly reference BWEnum class to force constants to load
    200     *       Object obj = Outer.BLACK;
    201     *     }
    202     *
    203     *     // ... other methods omitted
    204     *   }
    205     * }
    206     * </pre>
    207     *
    208     * <p>Although the above solves the problem, it is not recommended. The best solution
    209     * is to define the constants in the enum class, and hold references in the outer class:
    210     *
    211     * <pre>
    212     * public final class Outer {
    213     *   public static final BWEnum BLACK = BWEnum.BLACK;
    214     *   public static final BWEnum WHITE = BWEnum.WHITE;
    215     *
    216     *   // static nested enum class
    217     *   public static final class BWEnum extends Enum {
    218     *     // only define constants in enum classes - private if desired
    219     *     private static final BWEnum BLACK = new BWEnum("Black");
    220     *     private static final BWEnum WHITE = new BWEnum("White");
    221     *
    222     *     // ... other methods omitted
    223     *   }
    224     * }
    225     * </pre>
    226     *
    227     * <p>For more details, see the 'Nested' test cases.
    228     *
    229     * <h4>Lang Enums and Java 5.0 Enums</h4>
    230     *
    231     * <p>Enums were added to Java in Java 5.0. The main differences between Lang's
    232     * implementation and the new official JDK implementation are: </p>
    233     * <ul>
    234     * <li>The standard Enum is a not just a superclass, but is a type baked into the
    235     * language. </li>
    236     * <li>The standard Enum does not support extension, so the standard methods that
    237     * are provided in the Lang enum are not available. </li>
    238     * <li>Lang mandates a String name, whereas the standard Enum uses the class
    239     * name as its name. getName() changes to name(). </li>
    240     * </ul>
    241     *
    242     * <p>Generally people should use the standard Enum. Migrating from the Lang
    243     * enum to the standard Enum is not as easy as it might be due to the lack of
    244     * class inheritence in standard Enums. This means that it's not possible
    245     * to provide a 'super-enum' which could provide the same utility methods
    246     * that the Lang enum does. The following utility class is a Java 5.0
    247     * version of our EnumUtils class and provides those utility methods. </p>
    248     *
    249     * <pre>
    250     * import java.util.*;
    251     *
    252     * public class EnumUtils {
    253     *
    254     *   public static Enum getEnum(Class enumClass, String token) {
    255     *     return Enum.valueOf(enumClass, token);
    256     *   }
    257     *
    258     *   public static Map getEnumMap(Class enumClass) {
    259     *     HashMap map = new HashMap();
    260     *     Iterator itr = EnumUtils.iterator(enumClass);
    261     *     while(itr.hasNext()) {
    262     *       Enum enm = (Enum) itr.next();
    263     *       map.put( enm.name(), enm );
    264     *     }
    265     *     return map;
    266     *   }
    267     *
    268     *   public static List getEnumList(Class enumClass) {
    269     *     return new ArrayList( EnumSet.allOf(enumClass) );
    270     *   }
    271     *
    272     *   public static Iterator iterator(Class enumClass) {
    273     *     return EnumUtils.getEnumList(enumClass).iterator();
    274     *   }
    275     * }
    276     * </pre>
    277     *
    278     * @author Apache Avalon project
    279     * @author Apache Software Foundation
    280     * @author Chris Webb
    281     * @author Mike Bowler
    282     * @author Matthias Eichel
    283     * @since 2.1 (class existed in enum package from v1.0)
    284     * @version $Id: Enum.java 912394 2010-02-21 20:16:22Z niallp $
    285     */
    286    public abstract class Enum implements Comparable, Serializable {
    287
    288        /**
    289         * Required for serialization support.
    290         *
    291         * @see java.io.Serializable
    292         */
    293        private static final long serialVersionUID = -487045951170455942L;
    294
    295        // After discussion, the default size for HashMaps is used, as the
    296        // sizing algorithm changes across the JDK versions
    297        /**
    298         * An empty <code>Map</code>, as JDK1.2 didn't have an empty map.
    299         */
    300        private static final Map EMPTY_MAP = Collections.unmodifiableMap(new HashMap(0));
    301
    302        /**
    303         * <code>Map</code>, key of class name, value of <code>Entry</code>.
    304         */
    305        private static Map cEnumClasses
    306            // LANG-334: To avoid exposing a mutating map,
    307            // we copy it each time we add to it. This is cheaper than
    308            // using a synchronized map since we are almost entirely reads
    309            = new WeakHashMap();
    310
    311        /**
    312         * The string representation of the Enum.
    313         */
    314        private final String iName;
    315
    316        /**
    317         * The hashcode representation of the Enum.
    318         */
    319        private transient final int iHashCode;
    320
    321        /**
    322         * The toString representation of the Enum.
    323         * @since 2.0
    324         */
    325        protected transient String iToString = null;
    326
    327        /**
    328         * <p>Enable the iterator to retain the source code order.</p>
    329         */
    330        private static class Entry {
    331            /**
    332             * Map of Enum name to Enum.
    333             */
    334            final Map map = new HashMap();
    335            /**
    336             * Map of Enum name to Enum.
    337             */
    338            final Map unmodifiableMap = Collections.unmodifiableMap(map);
    339            /**
    340             * List of Enums in source code order.
    341             */
    342            final List list = new ArrayList(25);
    343            /**
    344             * Map of Enum name to Enum.
    345             */
    346            final List unmodifiableList = Collections.unmodifiableList(list);
    347
    348            /**
    349             * <p>Restrictive constructor.</p>
    350             */
    351            protected Entry() {
    352                super();
    353            }
    354        }
    355
    356        /**
    357         * <p>Constructor to add a new named item to the enumeration.</p>
    358         *
    359         * @param name  the name of the enum object,
    360         *  must not be empty or <code>null</code>
    361         * @throws IllegalArgumentException if the name is <code>null</code>
    362         *  or an empty string
    363         * @throws IllegalArgumentException if the getEnumClass() method returns
    364         *  a null or invalid Class
    365         */
    366        protected Enum(String name) {
    367            super();
    368            init(name);
    369            iName = name;
    370            iHashCode = 7 + getEnumClass().hashCode() + 3 * name.hashCode();
    371            // cannot create toString here as subclasses may want to include other data
    372        }
    373
    374        /**
    375         * Initializes the enumeration.
    376         *
    377         * @param name  the enum name
    378         * @throws IllegalArgumentException if the name is null or empty or duplicate
    379         * @throws IllegalArgumentException if the enumClass is null or invalid
    380         */
    381        private void init(String name) {
    382            if (StringUtils.isEmpty(name)) {
    383                throw new IllegalArgumentException("The Enum name must not be empty or null");
    384            }
    385
    386            Class enumClass = getEnumClass();
    387            if (enumClass == null) {
    388                throw new IllegalArgumentException("getEnumClass() must not be null");
    389            }
    390            Class cls = getClass();
    391            boolean ok = false;
    392            while (cls != null && cls != Enum.class && cls != ValuedEnum.class) {
    393                if (cls == enumClass) {
    394                    ok = true;
    395                    break;
    396                }
    397                cls = cls.getSuperclass();
    398            }
    399            if (ok == false) {
    400                throw new IllegalArgumentException("getEnumClass() must return a superclass of this class");
    401            }
    402
    403            Entry entry;
    404            synchronized( Enum.class ) { // LANG-334
    405                // create entry
    406                entry = (Entry) cEnumClasses.get(enumClass);
    407                if (entry == null) {
    408                    entry = createEntry(enumClass);
    409                    Map myMap = new WeakHashMap( ); // we avoid the (Map) constructor to achieve JDK 1.2 support
    410                    myMap.putAll( cEnumClasses );
    411                    myMap.put(enumClass, entry);
    412                    cEnumClasses = myMap;
    413                }
    414            }
    415            if (entry.map.containsKey(name)) {
    416                throw new IllegalArgumentException("The Enum name must be unique, '" + name + "' has already been added");
    417            }
    418            entry.map.put(name, this);
    419            entry.list.add(this);
    420        }
    421
    422        /**
    423         * <p>Handle the deserialization of the class to ensure that multiple
    424         * copies are not wastefully created, or illegal enum types created.</p>
    425         *
    426         * @return the resolved object
    427         */
    428        protected Object readResolve() {
    429            Entry entry = (Entry) cEnumClasses.get(getEnumClass());
    430            if (entry == null) {
    431                return null;
    432            }
    433            return entry.map.get(getName());
    434        }
    435
    436        //--------------------------------------------------------------------------------
    437
    438        /**
    439         * <p>Gets an <code>Enum</code> object by class and name.</p>
    440         *
    441         * @param enumClass  the class of the Enum to get, must not
    442         *  be <code>null</code>
    443         * @param name  the name of the <code>Enum</code> to get,
    444         *  may be <code>null</code>
    445         * @return the enum object, or <code>null</code> if the enum does not exist
    446         * @throws IllegalArgumentException if the enum class
    447         *  is <code>null</code>
    448         */
    449        protected static Enum getEnum(Class enumClass, String name) {
    450            Entry entry = getEntry(enumClass);
    451            if (entry == null) {
    452                return null;
    453            }
    454            return (Enum) entry.map.get(name);
    455        }
    456
    457        /**
    458         * <p>Gets the <code>Map</code> of <code>Enum</code> objects by
    459         * name using the <code>Enum</code> class.</p>
    460         *
    461         * <p>If the requested class has no enum objects an empty
    462         * <code>Map</code> is returned.</p>
    463         *
    464         * @param enumClass  the class of the <code>Enum</code> to get,
    465         *  must not be <code>null</code>
    466         * @return the enum object Map
    467         * @throws IllegalArgumentException if the enum class is <code>null</code>
    468         * @throws IllegalArgumentException if the enum class is not a subclass of Enum
    469         */
    470        protected static Map getEnumMap(Class enumClass) {
    471            Entry entry = getEntry(enumClass);
    472            if (entry == null) {
    473                return EMPTY_MAP;
    474            }
    475            return entry.unmodifiableMap;
    476        }
    477
    478        /**
    479         * <p>Gets the <code>List</code> of <code>Enum</code> objects using the
    480         * <code>Enum</code> class.</p>
    481         *
    482         * <p>The list is in the order that the objects were created (source code order).
    483         * If the requested class has no enum objects an empty <code>List</code> is
    484         * returned.</p>
    485         *
    486         * @param enumClass  the class of the <code>Enum</code> to get,
    487         *  must not be <code>null</code>
    488         * @return the enum object Map
    489         * @throws IllegalArgumentException if the enum class is <code>null</code>
    490         * @throws IllegalArgumentException if the enum class is not a subclass of Enum
    491         */
    492        protected static List getEnumList(Class enumClass) {
    493            Entry entry = getEntry(enumClass);
    494            if (entry == null) {
    495                return Collections.EMPTY_LIST;
    496            }
    497            return entry.unmodifiableList;
    498        }
    499
    500        /**
    501         * <p>Gets an <code>Iterator</code> over the <code>Enum</code> objects in
    502         * an <code>Enum</code> class.</p>
    503         *
    504         * <p>The <code>Iterator</code> is in the order that the objects were
    505         * created (source code order). If the requested class has no enum
    506         * objects an empty <code>Iterator</code> is returned.</p>
    507         *
    508         * @param enumClass  the class of the <code>Enum</code> to get,
    509         *  must not be <code>null</code>
    510         * @return an iterator of the Enum objects
    511         * @throws IllegalArgumentException if the enum class is <code>null</code>
    512         * @throws IllegalArgumentException if the enum class is not a subclass of Enum
    513         */
    514        protected static Iterator iterator(Class enumClass) {
    515            return Enum.getEnumList(enumClass).iterator();
    516        }
    517
    518        //-----------------------------------------------------------------------
    519        /**
    520         * <p>Gets an <code>Entry</code> from the map of Enums.</p>
    521         *
    522         * @param enumClass  the class of the <code>Enum</code> to get
    523         * @return the enum entry
    524         */
    525        private static Entry getEntry(Class enumClass) {
    526            if (enumClass == null) {
    527                throw new IllegalArgumentException("The Enum Class must not be null");
    528            }
    529            if (Enum.class.isAssignableFrom(enumClass) == false) {
    530                throw new IllegalArgumentException("The Class must be a subclass of Enum");
    531            }
    532            Entry entry = (Entry) cEnumClasses.get(enumClass);
    533
    534            if (entry == null) {
    535                try {
    536                    // LANG-76 - try to force class initialization for JDK 1.5+
    537                    Class.forName(enumClass.getName(), true, enumClass.getClassLoader());
    538                    entry = (Entry) cEnumClasses.get(enumClass);
    539                } catch (Exception e) {
    540                    // Ignore
    541                }
    542            }
    543
    544            return entry;
    545        }
    546
    547        /**
    548         * <p>Creates an <code>Entry</code> for storing the Enums.</p>
    549         *
    550         * <p>This accounts for subclassed Enums.</p>
    551         *
    552         * @param enumClass  the class of the <code>Enum</code> to get
    553         * @return the enum entry
    554         */
    555        private static Entry createEntry(Class enumClass) {
    556            Entry entry = new Entry();
    557            Class cls = enumClass.getSuperclass();
    558            while (cls != null && cls != Enum.class && cls != ValuedEnum.class) {
    559                Entry loopEntry = (Entry) cEnumClasses.get(cls);
    560                if (loopEntry != null) {
    561                    entry.list.addAll(loopEntry.list);
    562                    entry.map.putAll(loopEntry.map);
    563                    break;  // stop here, as this will already have had superclasses added
    564                }
    565                cls = cls.getSuperclass();
    566            }
    567            return entry;
    568        }
    569
    570        //-----------------------------------------------------------------------
    571        /**
    572         * <p>Retrieve the name of this Enum item, set in the constructor.</p>
    573         *
    574         * @return the <code>String</code> name of this Enum item
    575         */
    576        public final String getName() {
    577            return iName;
    578        }
    579
    580        /**
    581         * <p>Retrieves the Class of this Enum item, set in the constructor.</p>
    582         *
    583         * <p>This is normally the same as <code>getClass()</code>, but for
    584         * advanced Enums may be different. If overridden, it must return a
    585         * constant value.</p>
    586         *
    587         * @return the <code>Class</code> of the enum
    588         * @since 2.0
    589         */
    590        public Class getEnumClass() {
    591            return getClass();
    592        }
    593
    594        /**
    595         * <p>Tests for equality.</p>
    596         *
    597         * <p>Two Enum objects are considered equal
    598         * if they have the same class names and the same names.
    599         * Identity is tested for first, so this method usually runs fast.</p>
    600         *
    601         * <p>If the parameter is in a different class loader than this instance,
    602         * reflection is used to compare the names.</p>
    603         *
    604         * @param other  the other object to compare for equality
    605         * @return <code>true</code> if the Enums are equal
    606         */
    607        public final boolean equals(Object other) {
    608            if (other == this) {
    609                return true;
    610            } else if (other == null) {
    611                return false;
    612            } else if (other.getClass() == this.getClass()) {
    613                // Ok to do a class cast to Enum here since the test above
    614                // guarantee both
    615                // classes are in the same class loader.
    616                return iName.equals(((Enum) other).iName);
    617            } else {
    618                // This and other are in different class loaders, we must check indirectly
    619                if (other.getClass().getName().equals(this.getClass().getName()) == false) {
    620                    return false;
    621                }
    622                return iName.equals( getNameInOtherClassLoader(other) );
    623            }
    624        }
    625
    626        /**
    627         * <p>Returns a suitable hashCode for the enumeration.</p>
    628         *
    629         * @return a hashcode based on the name
    630         */
    631        public final int hashCode() {
    632            return iHashCode;
    633        }
    634
    635        /**
    636         * <p>Tests for order.</p>
    637         *
    638         * <p>The default ordering is alphabetic by name, but this
    639         * can be overridden by subclasses.</p>
    640         *
    641         * <p>If the parameter is in a different class loader than this instance,
    642         * reflection is used to compare the names.</p>
    643         *
    644         * @see java.lang.Comparable#compareTo(Object)
    645         * @param other  the other object to compare to
    646         * @return -ve if this is less than the other object, +ve if greater
    647         *  than, <code>0</code> of equal
    648         * @throws ClassCastException if other is not an Enum
    649         * @throws NullPointerException if other is <code>null</code>
    650         */
    651        public int compareTo(Object other) {
    652            if (other == this) {
    653                return 0;
    654            }
    655            if (other.getClass() != this.getClass()) {
    656                if (other.getClass().getName().equals(this.getClass().getName())) {
    657                    return iName.compareTo( getNameInOtherClassLoader(other) );
    658                }
    659                throw new ClassCastException(
    660                        "Different enum class '" + ClassUtils.getShortClassName(other.getClass()) + "'");
    661            }
    662            return iName.compareTo(((Enum) other).iName);
    663        }
    664
    665        /**
    666         * <p>Use reflection to return an objects class name.</p>
    667         *
    668         * @param other The object to determine the class name for
    669         * @return The class name
    670         */
    671        private String getNameInOtherClassLoader(Object other) {
    672            try {
    673                Method mth = other.getClass().getMethod("getName", null);
    674                String name = (String) mth.invoke(other, null);
    675                return name;
    676            } catch (NoSuchMethodException e) {
    677                // ignore - should never happen
    678            } catch (IllegalAccessException e) {
    679                // ignore - should never happen
    680            } catch (InvocationTargetException e) {
    681                // ignore - should never happen
    682            }
    683            throw new IllegalStateException("This should not happen");
    684        }
    685
    686        /**
    687         * <p>Human readable description of this Enum item.</p>
    688         *
    689         * @return String in the form <code>type[name]</code>, for example:
    690         * <code>Color[Red]</code>. Note that the package name is stripped from
    691         * the type name.
    692         */
    693        public String toString() {
    694            if (iToString == null) {
    695                String shortName = ClassUtils.getShortClassName(getEnumClass());
    696                iToString = shortName + "[" + getName() + "]";
    697            }
    698            return iToString;
    699        }
    700
    701    }