source: src/main/java/agents/org/apache/commons/lang/enum1/Enum.java

Last change on this file was 127, checked in by Wouter Pasman, 6 years ago

#41 ROLL BACK of rev.126 . So this version is equal to rev. 125

File size: 22.1 KB
Line 
1/*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17package agents.org.apache.commons.lang.enum1;
18
19import java.io.Serializable;
20import java.lang.reflect.InvocationTargetException;
21import java.lang.reflect.Method;
22import java.util.ArrayList;
23import java.util.Collections;
24import java.util.HashMap;
25import java.util.Iterator;
26import java.util.List;
27import java.util.Map;
28import java.util.WeakHashMap;
29
30import agents.org.apache.commons.lang.ClassUtils;
31import agents.org.apache.commons.lang.StringUtils;
32
33/**
34 * <p>
35 * Abstract superclass for type-safe enums.
36 * </p>
37 *
38 * <p>
39 * One feature of the C programming language lacking in Java is enumerations.
40 * The C implementation based on ints was poor and open to abuse. The original
41 * Java recommendation and most of the JDK also uses int constants. It has been
42 * recognised however that a more robust type-safe class-based solution can be
43 * designed. This class follows the basic Java type-safe enumeration pattern.
44 * </p>
45 *
46 * <p>
47 * <em>NOTE:</em>Due to the way in which Java ClassLoaders work, comparing Enum
48 * objects should always be done using <code>equals()</code>, not
49 * <code>==</code>. The equals() method will try == first so in most cases the
50 * effect is the same.
51 * </p>
52 *
53 * <p>
54 * Of course, if you actually want (or don't mind) Enums in different class
55 * loaders being non-equal, then you can use <code>==</code>.
56 * </p>
57 *
58 * <h4>Simple Enums</h4>
59 *
60 * <p>
61 * To use this class, it must be subclassed. For example:
62 * </p>
63 *
64 * <pre>
65 * public final class ColorEnum extends Enum {
66 * public static final ColorEnum RED = new ColorEnum("Red");
67 * public static final ColorEnum GREEN = new ColorEnum("Green");
68 * public static final ColorEnum BLUE = new ColorEnum("Blue");
69 *
70 * private ColorEnum(String color) {
71 * super(color);
72 * }
73 *
74 * public static ColorEnum getEnum(String color) {
75 * return (ColorEnum) getEnum(ColorEnum.class, color);
76 * }
77 *
78 * public static Map getEnumMap() {
79 * return getEnumMap(ColorEnum.class);
80 * }
81 *
82 * public static List getEnumList() {
83 * return getEnumList(ColorEnum.class);
84 * }
85 *
86 * public static Iterator iterator() {
87 * return iterator(ColorEnum.class);
88 * }
89 * }
90 * </pre>
91 *
92 * <p>
93 * As shown, each enum has a name. This can be accessed using
94 * <code>getName</code>.
95 * </p>
96 *
97 * <p>
98 * The <code>getEnum</code> and <code>iterator</code> methods are recommended.
99 * Unfortunately, Java restrictions require these to be coded as shown in each
100 * subclass. An alternative choice is to use the {@link EnumUtils} class.
101 * </p>
102 *
103 * <h4>Subclassed Enums</h4>
104 * <p>
105 * A hierarchy of Enum classes can be built. In this case, the superclass is
106 * unaffected by the addition of subclasses (as per normal Java). The subclasses
107 * may add additional Enum constants <em>of the type of the superclass</em>. The
108 * query methods on the subclass will return all of the Enum constants from the
109 * superclass and subclass.
110 * </p>
111 *
112 * <pre>
113 * public final class ExtraColorEnum extends ColorEnum {
114 * // NOTE: Color enum declared above is final, change that to get this
115 * // example to compile.
116 * public static final ColorEnum YELLOW = new ExtraColorEnum("Yellow");
117 *
118 * private ExtraColorEnum(String color) {
119 * super(color);
120 * }
121 *
122 * public static ColorEnum getEnum(String color) {
123 * return (ColorEnum) getEnum(ExtraColorEnum.class, color);
124 * }
125 *
126 * public static Map getEnumMap() {
127 * return getEnumMap(ExtraColorEnum.class);
128 * }
129 *
130 * public static List getEnumList() {
131 * return getEnumList(ExtraColorEnum.class);
132 * }
133 *
134 * public static Iterator iterator() {
135 * return iterator(ExtraColorEnum.class);
136 * }
137 * }
138 * </pre>
139 *
140 * <p>
141 * This example will return RED, GREEN, BLUE, YELLOW from the List and iterator
142 * methods in that order. The RED, GREEN and BLUE instances will be the same
143 * (==) as those from the superclass ColorEnum. Note that YELLOW is declared as
144 * a ColorEnum and not an ExtraColorEnum.
145 * </p>
146 *
147 * <h4>Functional Enums</h4>
148 *
149 * <p>
150 * The enums can have functionality by defining subclasses and overriding the
151 * <code>getEnumClass()</code> method:
152 * </p>
153 *
154 * <pre>
155 * public static final OperationEnum PLUS = new PlusOperation();
156 * private static final class PlusOperation extends OperationEnum {
157 * private PlusOperation() {
158 * super("Plus");
159 * }
160 * public int eval(int a, int b) {
161 * return a + b;
162 * }
163 * }
164 * public static final OperationEnum MINUS = new MinusOperation();
165 * private static final class MinusOperation extends OperationEnum {
166 * private MinusOperation() {
167 * super("Minus");
168 * }
169 * public int eval(int a, int b) {
170 * return a - b;
171 * }
172 * }
173 *
174 * private OperationEnum(String color) {
175 * super(color);
176 * }
177 *
178 * public final Class getEnumClass() { // NOTE: new method!
179 * return OperationEnum.class;
180 * }
181 *
182 * public abstract double eval(double a, double b);
183 *
184 * public static OperationEnum getEnum(String name) {
185 * return (OperationEnum) getEnum(OperationEnum.class, name);
186 * }
187 *
188 * public static Map getEnumMap() {
189 * return getEnumMap(OperationEnum.class);
190 * }
191 *
192 * public static List getEnumList() {
193 * return getEnumList(OperationEnum.class);
194 * }
195 *
196 * public static Iterator iterator() {
197 * return iterator(OperationEnum.class);
198 * }
199 * }
200 * </pre>
201 * <p>
202 * The code above will work on JDK 1.2. If JDK1.3 and later is used, the
203 * subclasses may be defined as anonymous.
204 * </p>
205 *
206 * <h4>Nested class Enums</h4>
207 *
208 * <p>
209 * Care must be taken with class loading when defining a static nested class for
210 * enums. The static nested class can be loaded without the surrounding outer
211 * class being loaded. This can result in an empty list/map/iterator being
212 * returned. One solution is to define a static block that references the outer
213 * class where the constants are defined. For example:
214 * </p>
215 *
216 * <pre>
217 * public final class Outer {
218 * public static final BWEnum BLACK = new BWEnum("Black");
219 * public static final BWEnum WHITE = new BWEnum("White");
220 *
221 * // static nested enum class
222 * public static final class BWEnum extends Enum {
223 *
224 * static {
225 * // explicitly reference BWEnum class to force constants to load
226 * Object obj = Outer.BLACK;
227 * }
228 *
229 * // ... other methods omitted
230 * }
231 * }
232 * </pre>
233 *
234 * <p>
235 * Although the above solves the problem, it is not recommended. The best
236 * solution is to define the constants in the enum class, and hold references in
237 * the outer class:
238 *
239 * <pre>
240 * public final class Outer {
241 * public static final BWEnum BLACK = BWEnum.BLACK;
242 * public static final BWEnum WHITE = BWEnum.WHITE;
243 *
244 * // static nested enum class
245 * public static final class BWEnum extends Enum {
246 * // only define constants in enum classes - private if desired
247 * private static final BWEnum BLACK = new BWEnum("Black");
248 * private static final BWEnum WHITE = new BWEnum("White");
249 *
250 * // ... other methods omitted
251 * }
252 * }
253 * </pre>
254 *
255 * <p>
256 * For more details, see the 'Nested' test cases.
257 *
258 * @deprecated Replaced by {@link agents.org.apache.commons.lang.enums.Enum
259 * org.apache.commons.lang.enums.Enum} and will be removed in
260 * version 3.0. All classes in this package are deprecated and
261 * repackaged to {@link agents.org.apache.commons.lang.enums} since
262 * <code>enum</code> is a Java 1.5 keyword.
263 * @see agents.org.apache.commons.lang.enums.Enum
264 * @author Apache Avalon project
265 * @author Apache Software Foundation
266 * @author Chris Webb
267 * @author Mike Bowler
268 * @since 1.0
269 * @version $Id: Enum.java 912394 2010-02-21 20:16:22Z niallp $
270 */
271public abstract class Enum implements Comparable, Serializable {
272
273 /**
274 * Required for serialization support. Lang version 1.0.1 serial
275 * compatibility.
276 *
277 * @see java.io.Serializable
278 */
279 private static final long serialVersionUID = -487045951170455942L;
280
281 // After discussion, the default size for HashMaps is used, as the
282 // sizing algorithm changes across the JDK versions
283 /**
284 * An empty <code>Map</code>, as JDK1.2 didn't have an empty map.
285 */
286 private static final Map EMPTY_MAP = Collections.unmodifiableMap(new HashMap(0));
287
288 /**
289 * <code>Map</code>, key of class name, value of <code>Entry</code>.
290 */
291 private static Map cEnumClasses
292 // LANG-334: To avoid exposing a mutating map,
293 // we copy it each time we add to it. This is cheaper than
294 // using a synchronized map since we are almost entirely reads
295 = new WeakHashMap();
296
297 /**
298 * The string representation of the Enum.
299 */
300 private final String iName;
301
302 /**
303 * The hashcode representation of the Enum.
304 */
305 private transient final int iHashCode;
306
307 /**
308 * The toString representation of the Enum.
309 *
310 * @since 2.0
311 */
312 protected transient String iToString = null;
313
314 /**
315 * <p>
316 * Enable the iterator to retain the source code order.
317 * </p>
318 */
319 private static class Entry {
320 /**
321 * Map of Enum name to Enum.
322 */
323 final Map map = new HashMap();
324 /**
325 * Map of Enum name to Enum.
326 */
327 final Map unmodifiableMap = Collections.unmodifiableMap(map);
328 /**
329 * List of Enums in source code order.
330 */
331 final List list = new ArrayList(25);
332 /**
333 * Map of Enum name to Enum.
334 */
335 final List unmodifiableList = Collections.unmodifiableList(list);
336
337 /**
338 * <p>
339 * Restrictive constructor.
340 * </p>
341 */
342 protected Entry() {
343 super();
344 }
345 }
346
347 /**
348 * <p>
349 * Constructor to add a new named item to the enumeration.
350 * </p>
351 *
352 * @param name
353 * the name of the enum object, must not be empty or
354 * <code>null</code>
355 * @throws IllegalArgumentException
356 * if the name is <code>null</code> or an empty string
357 * @throws IllegalArgumentException
358 * if the getEnumClass() method returns a null or invalid Class
359 */
360 protected Enum(String name) {
361 super();
362 init(name);
363 iName = name;
364 iHashCode = 7 + getEnumClass().hashCode() + 3 * name.hashCode();
365 // cannot create toString here as subclasses may want to include other
366 // data
367 }
368
369 /**
370 * Initializes the enumeration.
371 *
372 * @param name
373 * the enum name
374 * @throws IllegalArgumentException
375 * if the name is null or empty or duplicate
376 * @throws IllegalArgumentException
377 * if the enumClass is null or invalid
378 */
379 private void init(String name) {
380 if (StringUtils.isEmpty(name)) {
381 throw new IllegalArgumentException("The Enum name must not be empty or null");
382 }
383
384 Class enumClass = getEnumClass();
385 if (enumClass == null) {
386 throw new IllegalArgumentException("getEnumClass() must not be null");
387 }
388 Class cls = getClass();
389 boolean ok = false;
390 while (cls != null && cls != Enum.class && cls != ValuedEnum.class) {
391 if (cls == enumClass) {
392 ok = true;
393 break;
394 }
395 cls = cls.getSuperclass();
396 }
397 if (ok == false) {
398 throw new IllegalArgumentException("getEnumClass() must return a superclass of this class");
399 }
400
401 Entry entry;
402 synchronized (Enum.class) { // LANG-334
403 // create entry
404 entry = (Entry) cEnumClasses.get(enumClass);
405 if (entry == null) {
406 entry = createEntry(enumClass);
407 Map myMap = new WeakHashMap(); // we avoid the (Map) constructor
408 // to achieve JDK 1.2 support
409 myMap.putAll(cEnumClasses);
410 myMap.put(enumClass, entry);
411 cEnumClasses = myMap;
412 }
413 }
414 if (entry.map.containsKey(name)) {
415 throw new IllegalArgumentException("The Enum name must be unique, '" + name + "' has already been added");
416 }
417 entry.map.put(name, this);
418 entry.list.add(this);
419 }
420
421 /**
422 * <p>
423 * Handle the deserialization of the class to ensure that multiple copies
424 * are not wastefully created, or illegal enum types created.
425 * </p>
426 *
427 * @return the resolved object
428 */
429 protected Object readResolve() {
430 Entry entry = (Entry) cEnumClasses.get(getEnumClass());
431 if (entry == null) {
432 return null;
433 }
434 return entry.map.get(getName());
435 }
436
437 // --------------------------------------------------------------------------------
438
439 /**
440 * <p>
441 * Gets an <code>Enum</code> object by class and name.
442 * </p>
443 *
444 * @param enumClass
445 * the class of the Enum to get, must not be <code>null</code>
446 * @param name
447 * the name of the <code>Enum</code> to get, may be
448 * <code>null</code>
449 * @return the enum object, or <code>null</code> if the enum does not exist
450 * @throws IllegalArgumentException
451 * if the enum class is <code>null</code>
452 */
453 protected static Enum getEnum(Class enumClass, String name) {
454 Entry entry = getEntry(enumClass);
455 if (entry == null) {
456 return null;
457 }
458 return (Enum) entry.map.get(name);
459 }
460
461 /**
462 * <p>
463 * Gets the <code>Map</code> of <code>Enum</code> objects by name using the
464 * <code>Enum</code> class.
465 * </p>
466 *
467 * <p>
468 * If the requested class has no enum objects an empty <code>Map</code> is
469 * returned.
470 * </p>
471 *
472 * @param enumClass
473 * the class of the <code>Enum</code> to get, must not be
474 * <code>null</code>
475 * @return the enum object Map
476 * @throws IllegalArgumentException
477 * if the enum class is <code>null</code>
478 * @throws IllegalArgumentException
479 * if the enum class is not a subclass of Enum
480 */
481 protected static Map getEnumMap(Class enumClass) {
482 Entry entry = getEntry(enumClass);
483 if (entry == null) {
484 return EMPTY_MAP;
485 }
486 return entry.unmodifiableMap;
487 }
488
489 /**
490 * <p>
491 * Gets the <code>List</code> of <code>Enum</code> objects using the
492 * <code>Enum</code> class.
493 * </p>
494 *
495 * <p>
496 * The list is in the order that the objects were created (source code
497 * order). If the requested class has no enum objects an empty
498 * <code>List</code> is returned.
499 * </p>
500 *
501 * @param enumClass
502 * the class of the <code>Enum</code> to get, must not be
503 * <code>null</code>
504 * @return the enum object Map
505 * @throws IllegalArgumentException
506 * if the enum class is <code>null</code>
507 * @throws IllegalArgumentException
508 * if the enum class is not a subclass of Enum
509 */
510 protected static List getEnumList(Class enumClass) {
511 Entry entry = getEntry(enumClass);
512 if (entry == null) {
513 return Collections.EMPTY_LIST;
514 }
515 return entry.unmodifiableList;
516 }
517
518 /**
519 * <p>
520 * Gets an <code>Iterator</code> over the <code>Enum</code> objects in an
521 * <code>Enum</code> class.
522 * </p>
523 *
524 * <p>
525 * The <code>Iterator</code> is in the order that the objects were created
526 * (source code order). If the requested class has no enum objects an empty
527 * <code>Iterator</code> is returned.
528 * </p>
529 *
530 * @param enumClass
531 * the class of the <code>Enum</code> to get, must not be
532 * <code>null</code>
533 * @return an iterator of the Enum objects
534 * @throws IllegalArgumentException
535 * if the enum class is <code>null</code>
536 * @throws IllegalArgumentException
537 * if the enum class is not a subclass of Enum
538 */
539 protected static Iterator iterator(Class enumClass) {
540 return Enum.getEnumList(enumClass).iterator();
541 }
542
543 // -----------------------------------------------------------------------
544 /**
545 * <p>
546 * Gets an <code>Entry</code> from the map of Enums.
547 * </p>
548 *
549 * @param enumClass
550 * the class of the <code>Enum</code> to get
551 * @return the enum entry
552 */
553 private static Entry getEntry(Class enumClass) {
554 if (enumClass == null) {
555 throw new IllegalArgumentException("The Enum Class must not be null");
556 }
557 if (Enum.class.isAssignableFrom(enumClass) == false) {
558 throw new IllegalArgumentException("The Class must be a subclass of Enum");
559 }
560 Entry entry = (Entry) cEnumClasses.get(enumClass);
561
562 if (entry == null) {
563 try {
564 // LANG-76 - try to force class initialization for JDK 1.5+
565 Class.forName(enumClass.getName(), true, enumClass.getClassLoader());
566 entry = (Entry) cEnumClasses.get(enumClass);
567 } catch (Exception e) {
568 // Ignore
569 }
570 }
571
572 return entry;
573 }
574
575 /**
576 * <p>
577 * Creates an <code>Entry</code> for storing the Enums.
578 * </p>
579 *
580 * <p>
581 * This accounts for subclassed Enums.
582 * </p>
583 *
584 * @param enumClass
585 * the class of the <code>Enum</code> to get
586 * @return the enum entry
587 */
588 private static Entry createEntry(Class enumClass) {
589 Entry entry = new Entry();
590 Class cls = enumClass.getSuperclass();
591 while (cls != null && cls != Enum.class && cls != ValuedEnum.class) {
592 Entry loopEntry = (Entry) cEnumClasses.get(cls);
593 if (loopEntry != null) {
594 entry.list.addAll(loopEntry.list);
595 entry.map.putAll(loopEntry.map);
596 break; // stop here, as this will already have had superclasses
597 // added
598 }
599 cls = cls.getSuperclass();
600 }
601 return entry;
602 }
603
604 // -----------------------------------------------------------------------
605 /**
606 * <p>
607 * Retrieve the name of this Enum item, set in the constructor.
608 * </p>
609 *
610 * @return the <code>String</code> name of this Enum item
611 */
612 public final String getName() {
613 return iName;
614 }
615
616 /**
617 * <p>
618 * Retrieves the Class of this Enum item, set in the constructor.
619 * </p>
620 *
621 * <p>
622 * This is normally the same as <code>getClass()</code>, but for advanced
623 * Enums may be different. If overridden, it must return a constant value.
624 * </p>
625 *
626 * @return the <code>Class</code> of the enum
627 * @since 2.0
628 */
629 public Class getEnumClass() {
630 return getClass();
631 }
632
633 /**
634 * <p>
635 * Tests for equality.
636 * </p>
637 *
638 * <p>
639 * Two Enum objects are considered equal if they have the same class names
640 * and the same names. Identity is tested for first, so this method usually
641 * runs fast.
642 * </p>
643 *
644 * <p>
645 * If the parameter is in a different class loader than this instance,
646 * reflection is used to compare the names.
647 * </p>
648 *
649 * @param other
650 * the other object to compare for equality
651 * @return <code>true</code> if the Enums are equal
652 */
653 public final boolean equals(Object other) {
654 if (other == this) {
655 return true;
656 } else if (other == null) {
657 return false;
658 } else if (other.getClass() == this.getClass()) {
659 // Ok to do a class cast to Enum here since the test above
660 // guarantee both
661 // classes are in the same class loader.
662 return iName.equals(((Enum) other).iName);
663 } else {
664 // This and other are in different class loaders, we must check
665 // indirectly
666 if (other.getClass().getName().equals(this.getClass().getName()) == false) {
667 return false;
668 }
669 return iName.equals(getNameInOtherClassLoader(other));
670 }
671 }
672
673 /**
674 * <p>
675 * Returns a suitable hashCode for the enumeration.
676 * </p>
677 *
678 * @return a hashcode based on the name
679 */
680 public final int hashCode() {
681 return iHashCode;
682 }
683
684 /**
685 * <p>
686 * Tests for order.
687 * </p>
688 *
689 * <p>
690 * The default ordering is alphabetic by name, but this can be overridden by
691 * subclasses.
692 * </p>
693 *
694 * <p>
695 * If the parameter is in a different class loader than this instance,
696 * reflection is used to compare the names.
697 * </p>
698 *
699 * @see java.lang.Comparable#compareTo(Object)
700 * @param other
701 * the other object to compare to
702 * @return -ve if this is less than the other object, +ve if greater than,
703 * <code>0</code> of equal
704 * @throws ClassCastException
705 * if other is not an Enum
706 * @throws NullPointerException
707 * if other is <code>null</code>
708 */
709 public int compareTo(Object other) {
710 if (other == this) {
711 return 0;
712 }
713 if (other.getClass() != this.getClass()) {
714 if (other.getClass().getName().equals(this.getClass().getName())) {
715 return iName.compareTo(getNameInOtherClassLoader(other));
716 }
717 throw new ClassCastException(
718 "Different enum class '" + ClassUtils.getShortClassName(other.getClass()) + "'");
719 }
720 return iName.compareTo(((Enum) other).iName);
721 }
722
723 /**
724 * <p>
725 * Use reflection to return an objects class name.
726 * </p>
727 *
728 * @param other
729 * The object to determine the class name for
730 * @return The class name
731 */
732 private String getNameInOtherClassLoader(Object other) {
733 try {
734 Method mth = other.getClass().getMethod("getName", null);
735 String name = (String) mth.invoke(other, null);
736 return name;
737 } catch (NoSuchMethodException e) {
738 // ignore - should never happen
739 } catch (IllegalAccessException e) {
740 // ignore - should never happen
741 } catch (InvocationTargetException e) {
742 // ignore - should never happen
743 }
744 throw new IllegalStateException("This should not happen");
745 }
746
747 /**
748 * <p>
749 * Human readable description of this Enum item.
750 * </p>
751 *
752 * @return String in the form <code>type[name]</code>, for example:
753 * <code>Color[Red]</code>. Note that the package name is stripped
754 * from the type name.
755 */
756 public String toString() {
757 if (iToString == null) {
758 String shortName = ClassUtils.getShortClassName(getEnumClass());
759 iToString = shortName + "[" + getName() + "]";
760 }
761 return iToString;
762 }
763
764}
Note: See TracBrowser for help on using the repository browser.