1/*
2 * Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package javax.print.attribute;
27
28import java.io.InvalidObjectException;
29import java.io.ObjectStreamException;
30import java.io.Serializable;
31
32/**
33 * Class {@code EnumSyntax} is an abstract base class providing the common
34 * implementation of all "type safe enumeration" objects. An enumeration class
35 * (which extends class {@code EnumSyntax}) provides a group of enumeration
36 * values (objects) that are singleton instances of the enumeration class; for
37 * example:
38 *
39 * <pre>
40 *     public class Bach extends EnumSyntax {
41 *         public static final Bach JOHANN_SEBASTIAN     = new Bach(0);
42 *         public static final Bach WILHELM_FRIEDEMANN   = new Bach(1);
43 *         public static final Bach CARL_PHILIP_EMMANUEL = new Bach(2);
44 *         public static final Bach JOHANN_CHRISTIAN     = new Bach(3);
45 *         public static final Bach P_D_Q                = new Bach(4);
46 *
47 *         private static final String[] stringTable = {
48 *             "Johann Sebastian Bach",
49 *              "Wilhelm Friedemann Bach",
50 *              "Carl Philip Emmanuel Bach",
51 *              "Johann Christian Bach",
52 *              "P.D.Q. Bach"
53 *         };
54 *
55 *         protected String[] getStringTable() {
56 *             return stringTable;
57 *         }
58 *
59 *         private static final Bach[] enumValueTable = {
60 *             JOHANN_SEBASTIAN,
61 *              WILHELM_FRIEDEMANN,
62 *              CARL_PHILIP_EMMANUEL,
63 *              JOHANN_CHRISTIAN,
64 *              P_D_Q
65 *         };
66 *
67 *         protected EnumSyntax[] getEnumValueTable() {
68 *             return enumValueTable;
69 *         }
70 *     }
71 * </pre>
72 * You can then write code that uses the {@code ==} and {@code !=} operators to
73 * test enumeration values; for example:
74 * <pre>
75 *     Bach theComposer;
76 *     . . .
77 *     if (theComposer == Bach.JOHANN_SEBASTIAN) {
78 *         System.out.println ("The greatest composer of all time!");
79 *     }
80 * </pre>
81 * The {@code equals()} method for an enumeration class just does a test for
82 * identical objects ({@code ==}).
83 * <p>
84 * You can convert an enumeration value to a string by calling
85 * {@link #toString() toString()}. The string is obtained from a table supplied
86 * by the enumeration class.
87 * <p>
88 * Under the hood, an enumeration value is just an integer, a different integer
89 * for each enumeration value within an enumeration class. You can get an
90 * enumeration value's integer value by calling {@link #getValue() getValue()}.
91 * An enumeration value's integer value is established when it is constructed
92 * (see {@link #EnumSyntax(int) EnumSyntax(int)}). Since the constructor is
93 * protected, the only possible enumeration values are the singleton objects
94 * declared in the enumeration class; additional enumeration values cannot be
95 * created at run time.
96 * <p>
97 * You can define a subclass of an enumeration class that extends it with
98 * additional enumeration values. The subclass's enumeration values' integer
99 * values need not be distinct from the superclass's enumeration values' integer
100 * values; the {@code ==}, {@code !=}, {@code equals()}, and {@code toString()}
101 * methods will still work properly even if the subclass uses some of the same
102 * integer values as the superclass. However, the application in which the
103 * enumeration class and subclass are used may need to have distinct integer
104 * values in the superclass and subclass.
105 *
106 * @author David Mendenhall
107 * @author Alan Kaminsky
108 */
109public abstract class EnumSyntax implements Serializable, Cloneable {
110
111    /**
112     * Use serialVersionUID from JDK 1.4 for interoperability.
113     */
114    private static final long serialVersionUID = -2739521845085831642L;
115
116    /**
117     * This enumeration value's integer value.
118     *
119     * @serial
120     */
121    private int value;
122
123    /**
124     * Construct a new enumeration value with the given integer value.
125     *
126     * @param  value Integer value
127     */
128    protected EnumSyntax(int value) {
129        this.value = value;
130    }
131
132    /**
133     * Returns this enumeration value's integer value.
134     *
135     * @return the value
136     */
137    public int getValue() {
138        return value;
139    }
140
141    /**
142     * Returns a clone of this enumeration value, which to preserve the
143     * semantics of enumeration values is the same object as this enumeration
144     * value.
145     */
146    public Object clone() {
147        return this;
148    }
149
150    /**
151     * Returns a hash code value for this enumeration value. The hash code is
152     * just this enumeration value's integer value.
153     */
154    public int hashCode() {
155        return value;
156    }
157
158    /**
159     * Returns a string value corresponding to this enumeration value.
160     */
161    public String toString() {
162
163        String[] theTable = getStringTable();
164        int theIndex = value - getOffset();
165        return
166            theTable != null && theIndex >= 0 && theIndex < theTable.length ?
167            theTable[theIndex] :
168            Integer.toString (value);
169    }
170
171    /**
172     * During object input, convert this deserialized enumeration instance to
173     * the proper enumeration value defined in the enumeration attribute class.
174     *
175     * @return The enumeration singleton value stored at index <i>i</i>-<i>L</i>
176     *         in the enumeration value table returned by
177     *         {@link #getEnumValueTable() getEnumValueTable()}, where <i>i</i>
178     *         is this enumeration value's integer value and <i>L</i> is the
179     *         value returned by {@link #getOffset() getOffset()}
180     * @throws ObjectStreamException if the stream can't be deserialised
181     * @throws InvalidObjectException if the enumeration value table is
182     *         {@code null}, this enumeration value's integer value does not
183     *         correspond to an element in the enumeration value table, or the
184     *         corresponding element in the enumeration value table is
185     *         {@code null}. (Note:
186     *         {@link InvalidObjectException InvalidObjectException} is a
187     *         subclass of {@link ObjectStreamException ObjectStreamException},
188     *         which {@code readResolve()} is declared to throw.)
189     */
190    protected Object readResolve() throws ObjectStreamException {
191
192        EnumSyntax[] theTable = getEnumValueTable();
193
194        if (theTable == null) {
195            throw new InvalidObjectException(
196                                "Null enumeration value table for class " +
197                                getClass());
198        }
199
200        int theOffset = getOffset();
201        int theIndex = value - theOffset;
202
203        if (0 > theIndex || theIndex >= theTable.length) {
204            throw new InvalidObjectException
205                ("Integer value = " +  value + " not in valid range " +
206                 theOffset + ".." + (theOffset + theTable.length - 1) +
207                 "for class " + getClass());
208        }
209
210        EnumSyntax result = theTable[theIndex];
211        if (result == null) {
212            throw new InvalidObjectException
213                ("No enumeration value for integer value = " +
214                 value + "for class " + getClass());
215        }
216        return result;
217    }
218
219    // Hidden operations to be implemented in a subclass.
220
221    /**
222     * Returns the string table for this enumeration value's enumeration class.
223     * The enumeration class's integer values are assumed to lie in the range
224     * <i>L</i>..<i>L</i>+<i>N</i>-1, where <i>L</i> is the value returned by
225     * {@link #getOffset() getOffset()} and <i>N</i> is the length of the string
226     * table. The element in the string table at index <i>i</i>-<i>L</i> is the
227     * value returned by {@link #toString() toString()} for the enumeration
228     * value whose integer value is <i>i</i>. If an integer within the above
229     * range is not used by any enumeration value, leave the corresponding table
230     * element {@code null}.
231     * <p>
232     * The default implementation returns {@code null}. If the enumeration class
233     * (a subclass of class {@code EnumSyntax}) does not override this method to
234     * return a {@code non-null} string table, and the subclass does not
235     * override the {@link #toString() toString()} method, the base class
236     * {@link #toString() toString()} method will return just a string
237     * representation of this enumeration value's integer value.
238     *
239     * @return the string table
240     */
241    protected String[] getStringTable() {
242        return null;
243    }
244
245    /**
246     * Returns the enumeration value table for this enumeration value's
247     * enumeration class. The enumeration class's integer values are assumed to
248     * lie in the range <i>L</i>..<i>L</i>+<i>N</i>-1, where <i>L</i> is the
249     * value returned by {@link #getOffset() getOffset()} and <i>N</i> is the
250     * length of the enumeration value table. The element in the enumeration
251     * value table at index <i>i</i>-<i>L</i> is the enumeration value object
252     * whose integer value is <i>i</i>; the {@link #readResolve() readResolve()}
253     * method needs this to preserve singleton semantics during deserialization
254     * of an enumeration instance. If an integer within the above range is not
255     * used by any enumeration value, leave the corresponding table element
256     * {@code null}.
257     * <p>
258     * The default implementation returns {@code null}. If the enumeration class
259     * (a subclass of class EnumSyntax) does not override this method to return
260     * a {@code non-null} enumeration value table, and the subclass does not
261     * override the {@link #readResolve() readResolve()} method, the base class
262     * {@link #readResolve() readResolve()} method will throw an exception
263     * whenever an enumeration instance is deserialized from an object input
264     * stream.
265     *
266     * @return the value table
267     */
268    protected EnumSyntax[] getEnumValueTable() {
269        return null;
270    }
271
272    /**
273     * Returns the lowest integer value used by this enumeration value's
274     * enumeration class.
275     * <p>
276     * The default implementation returns 0. If the enumeration class (a
277     * subclass of class {@code EnumSyntax}) uses integer values starting at
278     * other than 0, override this method in the subclass.
279     *
280     * @return the offset of the lowest enumeration value
281     */
282    protected int getOffset() {
283        return 0;
284    }
285}
286