1 package com.github.mygreen.supercsv.cellprocessor.format;
2
3 import java.lang.reflect.Method;
4 import java.util.Collections;
5 import java.util.EnumSet;
6 import java.util.HashMap;
7 import java.util.LinkedHashMap;
8 import java.util.Map;
9 import java.util.Objects;
10 import java.util.Optional;
11
12 import com.github.mygreen.supercsv.util.ArgUtils;
13
14
15
16
17
18
19
20
21 public class EnumFormatter<T extends Enum<T>> extends AbstractTextFormatter<T> {
22
23 private final Class<? extends Enum<?>> type;
24
25 private final boolean ignoreCase;
26
27 private final Optional<Method> selectorMethod;
28
29
30
31
32 private final Map<Enum<?>, String> toStringMap;
33
34
35
36
37 private final Map<String, Enum<?>> toObjectMap;
38
39 public EnumFormatter(final Class<T> type, final boolean ignoreCase) {
40 Objects.requireNonNull(type, "type should not be null.");
41
42 this.type = type;
43 this.ignoreCase = ignoreCase;
44 this.selectorMethod = Optional.empty();
45
46 this.toStringMap = createToStringMap(type);
47 this.toObjectMap = createToObjectMap(type, ignoreCase);
48 }
49
50 public EnumFormatter(final Class<T> type, final boolean ignoreCase, final String selector) {
51 Objects.requireNonNull(type, "type should not be null.");
52 ArgUtils.notEmpty(selector, "selector");
53
54 this.type = type;
55 this.ignoreCase = ignoreCase;
56 this.selectorMethod = Optional.of(getEnumValueMethod(type, selector));
57
58 this.toStringMap = createToStringMap(type, selector);
59 this.toObjectMap = createToObjectMap(type, ignoreCase, selector);
60
61 }
62
63 public EnumFormatter(final Class<T> type) {
64 this(type, false);
65 }
66
67 public EnumFormatter(final Class<T> type, final String selector) {
68 this(type, false, selector);
69 }
70
71 private static <T extends Enum<T>> Method getEnumValueMethod(final Class<T> enumClass, final String selector) {
72
73 try {
74 final Method method = enumClass.getMethod(selector);
75 method.setAccessible(true);
76 return method;
77
78 } catch (ReflectiveOperationException e) {
79 throw new IllegalArgumentException(String.format("not found method '%s'", selector), e);
80 }
81
82 }
83
84 private static <T extends Enum<T>> Map<Enum<?>, String> createToStringMap(final Class<T> enumClass) {
85
86 final EnumSet<T> set = EnumSet.allOf(enumClass);
87
88 final Map<Enum<?>, String> map = new LinkedHashMap<>();
89 for(T e : set) {
90 map.put(e, e.name());
91
92 }
93
94 return Collections.unmodifiableMap(map);
95 }
96
97 private static <T extends Enum<T>> Map<Enum<?>, String> createToStringMap(final Class<T> enumClass, final String selector) {
98
99 final Method method = getEnumValueMethod(enumClass, selector);
100
101 final Map<Enum<?>, String> map = new LinkedHashMap<>();
102 try {
103 final EnumSet<T> set = EnumSet.allOf(enumClass);
104 for(T e : set) {
105 Object returnValue = method.invoke(e);
106 map.put(e, returnValue.toString());
107
108 }
109
110 } catch(ReflectiveOperationException e) {
111 throw new RuntimeException("fail get enum value.", e);
112 }
113
114 return Collections.unmodifiableMap(map);
115 }
116
117 private static <T extends Enum<T>> Map<String, Enum<?>> createToObjectMap(final Class<T> enumClass, final boolean ignoreCase) {
118
119 final EnumSet<T> set = EnumSet.allOf(enumClass);
120
121 final Map<String, Enum<?>> map = new LinkedHashMap<>();
122 for(T e : set) {
123 final String key = (ignoreCase ? e.name().toLowerCase() : e.name());
124 map.put(key, e);
125
126 }
127
128 return Collections.unmodifiableMap(map);
129 }
130
131 private static <T extends Enum<T>> Map<String, Enum<?>> createToObjectMap(final Class<T> enumClass, final boolean ignoreCase,
132 final String selector) {
133
134 final Method method = getEnumValueMethod(enumClass, selector);
135
136 final Map<String, Enum<?>> map = new LinkedHashMap<>();
137 try {
138
139 EnumSet<T> set = EnumSet.allOf(enumClass);
140 for(T e : set) {
141 Object returnValue = method.invoke(e);
142 final String key = (ignoreCase ? returnValue.toString().toLowerCase() : returnValue.toString());
143
144 map.put(key, e);
145 }
146
147 } catch(ReflectiveOperationException e) {
148 throw new RuntimeException(e);
149 }
150
151 return Collections.unmodifiableMap(map);
152 }
153
154 @SuppressWarnings("unchecked")
155 @Override
156 public T parse(final String text) {
157
158 final String keyText = ignoreCase ? text.toLowerCase() : text;
159 final Optional<T> obj = Optional.ofNullable((T)toObjectMap.get(keyText));
160
161 return obj.orElseThrow(() -> new TextParseException(text, type));
162
163 }
164
165 @Override
166 public String print(final T object) {
167
168 final Optional<String> text = Optional.ofNullable(toStringMap.get(object));
169 return text.orElseGet(() -> object.toString());
170 }
171
172 public Class<? extends Enum<?>> getType() {
173 return type;
174 }
175
176 public boolean isIgnoreCase() {
177 return ignoreCase;
178 }
179
180 public Optional<Method> getSelectorMethod() {
181 return selectorMethod;
182 }
183
184 public Map<Enum<?>, String> getToStringMap() {
185 return toStringMap;
186 }
187
188 public Map<String, Enum<?>> getToObjectMap() {
189 return toObjectMap;
190 }
191
192 @Override
193 public Map<String, Object> getMessageVariables() {
194
195 final Map<String, Object> vars = new HashMap<>();
196
197 vars.put("type", getType().getName());
198 vars.put("ignoreCase", isIgnoreCase());
199
200 getSelectorMethod().ifPresent(method -> vars.put("selector", method.getName()));
201 vars.put("enums", getToStringMap().values());
202
203 return vars;
204
205 }
206
207 }