1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements.  See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License.  You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package java.lang;
18
19import java.io.Serializable;
20import java.lang.reflect.Method;
21import java.security.AccessController;
22import java.security.PrivilegedExceptionAction;
23
24import org.apache.harmony.luni.util.Msg;
25
26/**
27 * The superclass of all enumerated types. Actual enumeration types inherit from
28 * this class, but extending this class does not make a class an enumeration
29 * type, since the compiler needs to generate special information for it.
30 */
31public abstract class Enum<E extends Enum<E>> implements Serializable,
32        Comparable<E> {
33
34    private static final long serialVersionUID = -4300926546619394005L;
35
36    private final String name;
37
38    private final int ordinal;
39
40    /**
41     * Constructor for constants of enum subtypes.
42     *
43     * @param name
44     *            the enum constant's declared name.
45     * @param ordinal
46     *            the enum constant's ordinal, which corresponds to its position
47     *            in the enum declaration, starting at zero.
48     */
49    protected Enum(String name, int ordinal) {
50        this.name = name;
51        this.ordinal = ordinal;
52    }
53
54    /**
55     * Returns the name of this enum constant. The name is the field as it
56     * appears in the {@code enum} declaration.
57     *
58     * @return the name of this enum constant.
59     * @see #toString()
60     */
61    public final String name() {
62        return name;
63    }
64
65    /**
66     * Returns the position of the enum constant in the declaration. The first
67     * constant has an ordinal value of zero.
68     *
69     * @return the ordinal value of this enum constant.
70     */
71    public final int ordinal() {
72        return ordinal;
73    }
74
75    /**
76     * Returns a string containing a concise, human-readable description of this
77     * object. In this case, the enum constant's name is returned.
78     *
79     * @return a printable representation of this object.
80     */
81    @Override
82    public String toString() {
83        return name;
84    }
85
86    /**
87     * Compares this object with the specified object and indicates if they are
88     * equal. In order to be equal, {@code object} must be identical to this
89     * enum constant.
90     *
91     * @param other
92     *            the object to compare this enum constant with.
93     * @return {@code true} if the specified object is equal to this
94     *         {@code Enum}; {@code false} otherwise.
95     */
96    @Override
97    public final boolean equals(Object other) {
98        return this == other;
99    }
100
101    @Override
102    public final int hashCode() {
103        return ordinal + (name == null ? 0 : name.hashCode());
104    }
105
106    /**
107     * {@code Enum} objects are singletons, they may not be cloned. This method
108     * always throws a {@code CloneNotSupportedException}.
109     *
110     * @return does not return.
111     * @throws CloneNotSupportedException
112     *             is always thrown.
113     */
114    @Override
115    protected final Object clone() throws CloneNotSupportedException {
116        // KA004=Enums may not be cloned
117        throw new CloneNotSupportedException(Msg.getString("KA004")); //$NON-NLS-1$
118    }
119
120    /**
121     * Compares this object to the specified enum object to determine their
122     * relative order. This method compares the object's ordinal values, that
123     * is, their position in the enum declaration.
124     *
125     * @param o
126     *            the enum object to compare this object to.
127     * @return a negative value if the ordinal value of this enum constant is
128     *         less than the ordinal value of {@code o}; 0 if the ordinal
129     *         values of this enum constant and {@code o} are equal; a positive
130     *         value if the ordinal value of this enum constant is greater than
131     *         the ordinal value of {@code o}.
132     * @see java.lang.Comparable
133     */
134    public final int compareTo(E o) {
135        return ordinal - o.ordinal;
136    }
137
138    /**
139     * Returns the enum constant's declaring class.
140     *
141     * @return the class object representing the constant's enum type.
142     */
143    @SuppressWarnings("unchecked")
144    public final Class<E> getDeclaringClass() {
145        Class<?> myClass = getClass();
146        Class<?> mySuperClass = myClass.getSuperclass();
147        if (Enum.class == mySuperClass) {
148            return (Class<E>)myClass;
149        }
150        return (Class<E>)mySuperClass;
151    }
152
153    /**
154     * Returns the constant with the specified name of the specified enum type.
155     *
156     * @param enumType
157     *            the class of the enumerated type to search for the constant
158     *            value.
159     * @param name
160     *            the name of the constant value to find.
161     * @return the enum constant.
162     * @throws NullPointerException
163     *             if either {@code enumType} or {@code name} are {@code null}.
164     * @throws IllegalArgumentException
165     *             if {@code enumType} is not an enumerated type or does not
166     *             have a constant value called {@code name}.
167     */
168    public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name) {
169        if ((enumType == null) || (name == null)) {
170            // KA001=Argument must not be null
171            throw new NullPointerException(Msg.getString("KA001")); //$NON-NLS-1$
172        }
173
174        // BEGIN android-changed
175        enumType.checkPublicMemberAccess();
176
177        T result = enumType.getClassCache().getEnumValue(name);
178
179        if (result == null) {
180            if (!enumType.isEnum()) {
181                // KA005={0} is not an enum type
182                throw new IllegalArgumentException(Msg.getString("KA005", enumType)); //$NON-NLS-1$
183            } else {
184                // KA006={0} is not a constant in the enum type {1}
185                throw new IllegalArgumentException(Msg.getString("KA006", name, //$NON-NLS-1$
186                                enumType));
187            }
188        }
189
190        return result;
191        // END android-changed
192    }
193
194    /*
195     * Helper to invoke the values() static method on T and answer the result.
196     * Returns null if there is a problem.
197     */
198    @SuppressWarnings("unchecked")
199    static <T extends Enum<T>> T[] getValues(final Class<T> enumType) {
200        try {
201            Method values = AccessController
202                    .doPrivileged(new PrivilegedExceptionAction<Method>() {
203                        public Method run() throws Exception {
204                            Method valsMethod = enumType.getMethod("values", //$NON-NLS-1$
205                                    (Class[]) null);
206                            valsMethod.setAccessible(true);
207                            return valsMethod;
208                        }
209                    });
210            return (T[]) values.invoke(enumType, (Object[])null);
211        } catch (Exception e) {
212            return null;
213        }
214    }
215}
216