xref: /aosp_15_r20/external/jackson-databind/src/main/java/com/fasterxml/jackson/databind/util/EnumResolver.java (revision 0ed15c778abdfe0f5f51f6133673e1619d6e56e4)
1 package com.fasterxml.jackson.databind.util;
2 
3 import java.util.*;
4 
5 import com.fasterxml.jackson.databind.AnnotationIntrospector;
6 import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
7 
8 /**
9  * Helper class used to resolve String values (either JSON Object field
10  * names or regular String values) into Java Enum instances.
11  */
12 public class EnumResolver implements java.io.Serializable
13 {
14     private static final long serialVersionUID = 1L;
15 
16     protected final Class<Enum<?>> _enumClass;
17 
18     protected final Enum<?>[] _enums;
19 
20     protected final HashMap<String, Enum<?>> _enumsById;
21 
22     protected final Enum<?> _defaultValue;
23 
EnumResolver(Class<Enum<?>> enumClass, Enum<?>[] enums, HashMap<String, Enum<?>> map, Enum<?> defaultValue)24     protected EnumResolver(Class<Enum<?>> enumClass, Enum<?>[] enums,
25             HashMap<String, Enum<?>> map, Enum<?> defaultValue)
26     {
27         _enumClass = enumClass;
28         _enums = enums;
29         _enumsById = map;
30         _defaultValue = defaultValue;
31     }
32 
33     /**
34      * Factory method for constructing resolver that maps from Enum.name() into
35      * Enum value
36      */
constructFor(Class<Enum<?>> enumCls, AnnotationIntrospector ai)37     public static EnumResolver constructFor(Class<Enum<?>> enumCls, AnnotationIntrospector ai)
38     {
39         Enum<?>[] enumValues = enumCls.getEnumConstants();
40         if (enumValues == null) {
41             throw new IllegalArgumentException("No enum constants for class "+enumCls.getName());
42         }
43         String[] names = ai.findEnumValues(enumCls, enumValues, new String[enumValues.length]);
44         final String[][] allAliases = new String[names.length][];
45         ai.findEnumAliases(enumCls, enumValues, allAliases);
46         HashMap<String, Enum<?>> map = new HashMap<String, Enum<?>>();
47         for (int i = 0, len = enumValues.length; i < len; ++i) {
48             final Enum<?> enumValue = enumValues[i];
49             String name = names[i];
50             if (name == null) {
51                 name = enumValue.name();
52             }
53             map.put(name, enumValue);
54             String[] aliases = allAliases[i];
55             if (aliases != null) {
56                 for (String alias : aliases) {
57                     // TODO: JDK 1.8, use Map.putIfAbsent()
58                     // Avoid overriding any primary names
59                     if (!map.containsKey(alias)) {
60                         map.put(alias, enumValue);
61                     }
62                 }
63             }
64         }
65         return new EnumResolver(enumCls, enumValues, map,  ai.findDefaultEnumValue(enumCls));
66     }
67 
68     /**
69      * @deprecated Since 2.8, use {@link #constructUsingToString(Class, AnnotationIntrospector)} instead
70      */
71     @Deprecated
constructUsingToString(Class<Enum<?>> enumCls)72     public static EnumResolver constructUsingToString(Class<Enum<?>> enumCls) {
73         return constructUsingToString(enumCls, null);
74     }
75 
76     /**
77      * Factory method for constructing resolver that maps from Enum.toString() into
78      * Enum value
79      *
80      * @since 2.8
81      */
constructUsingToString(Class<Enum<?>> enumCls, AnnotationIntrospector ai)82     public static EnumResolver constructUsingToString(Class<Enum<?>> enumCls,
83             AnnotationIntrospector ai)
84     {
85         Enum<?>[] enumConstants = enumCls.getEnumConstants();
86         HashMap<String, Enum<?>> map = new HashMap<String, Enum<?>>();
87         final String[][] allAliases = new String[enumConstants.length][];
88         ai.findEnumAliases(enumCls, enumConstants, allAliases);
89 
90         // from last to first, so that in case of duplicate values, first wins
91         for (int i = enumConstants.length; --i >= 0; ) {
92             Enum<?> enumValue = enumConstants[i];
93             map.put(enumValue.toString(), enumValue);
94             String[] aliases = allAliases[i];
95             if (aliases != null) {
96                 for (String alias : aliases) {
97                     // TODO: JDK 1.8, use Map.putIfAbsent()
98                     // Avoid overriding any primary names
99                     if (!map.containsKey(alias)) {
100                         map.put(alias, enumValue);
101                     }
102                 }
103             }
104         }
105         return new EnumResolver(enumCls, enumConstants, map, ai.findDefaultEnumValue(enumCls));
106     }
107 
108     /**
109      * @since 2.9
110      */
constructUsingMethod(Class<Enum<?>> enumCls, AnnotatedMember accessor, AnnotationIntrospector ai)111     public static EnumResolver constructUsingMethod(Class<Enum<?>> enumCls,
112             AnnotatedMember accessor,
113             AnnotationIntrospector ai)
114     {
115         Enum<?>[] enumValues = enumCls.getEnumConstants();
116         HashMap<String, Enum<?>> map = new HashMap<String, Enum<?>>();
117         // from last to first, so that in case of duplicate values, first wins
118         for (int i = enumValues.length; --i >= 0; ) {
119             Enum<?> en = enumValues[i];
120             try {
121                 Object o = accessor.getValue(en);
122                 if (o != null) {
123                     map.put(o.toString(), en);
124                 }
125             } catch (Exception e) {
126                 throw new IllegalArgumentException("Failed to access @JsonValue of Enum value "+en+": "+e.getMessage());
127             }
128         }
129         Enum<?> defaultEnum = (ai != null) ? ai.findDefaultEnumValue(enumCls) : null;
130         return new EnumResolver(enumCls, enumValues, map, defaultEnum);
131     }
132 
133     /**
134      * This method is needed because of the dynamic nature of constructing Enum
135      * resolvers.
136      */
137     @SuppressWarnings({ "unchecked" })
constructUnsafe(Class<?> rawEnumCls, AnnotationIntrospector ai)138     public static EnumResolver constructUnsafe(Class<?> rawEnumCls, AnnotationIntrospector ai)
139     {
140         /* This is oh so wrong... but at least ugliness is mostly hidden in just
141          * this one place.
142          */
143         Class<Enum<?>> enumCls = (Class<Enum<?>>) rawEnumCls;
144         return constructFor(enumCls, ai);
145     }
146 
147     /**
148      * Method that needs to be used instead of {@link #constructUsingToString}
149      * if static type of enum is not known.
150      *
151      * @since 2.8
152      */
153     @SuppressWarnings({ "unchecked" })
constructUnsafeUsingToString(Class<?> rawEnumCls, AnnotationIntrospector ai)154     public static EnumResolver constructUnsafeUsingToString(Class<?> rawEnumCls,
155             AnnotationIntrospector ai)
156     {
157         // oh so wrong... not much that can be done tho
158         Class<Enum<?>> enumCls = (Class<Enum<?>>) rawEnumCls;
159         return constructUsingToString(enumCls, ai);
160     }
161 
162     /**
163      * Method used when actual String serialization is indicated using @JsonValue
164      * on a method.
165      *
166      * @since 2.9
167      */
168     @SuppressWarnings({ "unchecked" })
constructUnsafeUsingMethod(Class<?> rawEnumCls, AnnotatedMember accessor, AnnotationIntrospector ai)169     public static EnumResolver constructUnsafeUsingMethod(Class<?> rawEnumCls,
170             AnnotatedMember accessor,
171             AnnotationIntrospector ai)
172     {
173         // wrong as ever but:
174         Class<Enum<?>> enumCls = (Class<Enum<?>>) rawEnumCls;
175         return constructUsingMethod(enumCls, accessor, ai);
176     }
177 
constructLookup()178     public CompactStringObjectMap constructLookup() {
179         return CompactStringObjectMap.construct(_enumsById);
180     }
181 
findEnum(String key)182     public Enum<?> findEnum(String key) { return _enumsById.get(key); }
183 
getEnum(int index)184     public Enum<?> getEnum(int index) {
185         if (index < 0 || index >= _enums.length) {
186             return null;
187         }
188         return _enums[index];
189     }
190 
getDefaultValue()191     public Enum<?> getDefaultValue(){
192         return _defaultValue;
193     }
194 
getRawEnums()195     public Enum<?>[] getRawEnums() {
196         return _enums;
197     }
198 
getEnums()199     public List<Enum<?>> getEnums() {
200         ArrayList<Enum<?>> enums = new ArrayList<Enum<?>>(_enums.length);
201         for (Enum<?> e : _enums) {
202             enums.add(e);
203         }
204         return enums;
205     }
206 
207     /**
208      * @since 2.7.3
209      */
getEnumIds()210     public Collection<String> getEnumIds() {
211         return _enumsById.keySet();
212     }
213 
getEnumClass()214     public Class<Enum<?>> getEnumClass() { return _enumClass; }
215 
lastValidIndex()216     public int lastValidIndex() { return _enums.length-1; }
217 }
218 
219