Skip to content

Commit dca079a

Browse files
Merge pull request #226 from mike-plummer/feat/expanded-enum-values
feat(expanded-enum-values): Expand @jsonvalue scenarios for enums
2 parents 7bf68dd + a8c42f7 commit dca079a

File tree

3 files changed

+128
-15
lines changed

3 files changed

+128
-15
lines changed

typescript-generator-core/src/main/java/cz/habarta/typescript/generator/parser/Jackson2Parser.java

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
import cz.habarta.typescript.generator.util.Predicate;
1818
import java.beans.BeanInfo;
1919
import java.beans.Introspector;
20-
import java.beans.PropertyDescriptor;
20+
import java.beans.MethodDescriptor;
2121
import java.lang.annotation.Annotation;
2222
import java.lang.reflect.AccessibleObject;
2323
import java.lang.reflect.Field;
@@ -26,6 +26,7 @@
2626
import java.lang.reflect.Modifier;
2727
import java.lang.reflect.Type;
2828
import java.util.*;
29+
import java.util.stream.Collectors;
2930

3031

3132
public class Jackson2Parser extends ModelParser {
@@ -295,24 +296,41 @@ private DeclarationModel parseEnumOrObjectEnum(SourceType<Class<?>> sourceClass)
295296

296297
try {
297298
Method valueMethod = null;
299+
Field valueField = null;
300+
301+
Field[] allEnumFields = enumClass.getDeclaredFields();
302+
List<Field> constants = Arrays.stream(allEnumFields).filter(Field::isEnumConstant).collect(Collectors.toList());
303+
298304
final BeanInfo beanInfo = Introspector.getBeanInfo(enumClass);
299-
for (PropertyDescriptor propertyDescriptor : beanInfo.getPropertyDescriptors()) {
300-
final Method readMethod = propertyDescriptor.getReadMethod();
301-
if (readMethod.isAnnotationPresent(JsonValue.class)) {
302-
valueMethod = readMethod;
303-
}
305+
valueMethod = Arrays.stream(beanInfo.getMethodDescriptors())
306+
.map(MethodDescriptor::getMethod)
307+
.filter(method -> method.isAnnotationPresent(JsonValue.class))
308+
.findAny().orElse(null);
309+
310+
if (valueMethod == null) {
311+
List<Field> instanceFields = Arrays.stream(allEnumFields).filter(field -> !field.isEnumConstant()).collect(Collectors.toList());
312+
valueField = instanceFields.stream()
313+
.filter(field -> field.isAnnotationPresent(JsonValue.class))
314+
.findAny().orElse(null);
304315
}
305316

306317
int index = 0;
307-
for (Field field : enumClass.getFields()) {
308-
if (field.isEnumConstant()) {
309-
if (isNumberBased) {
310-
final Number value = getNumberEnumValue(field, valueMethod, index++);
311-
enumMembers.add(new EnumMemberModel(field.getName(), value, null));
312-
} else {
313-
final String value = getStringEnumValue(field, valueMethod);
314-
enumMembers.add(new EnumMemberModel(field.getName(), value, null));
315-
}
318+
for (Field constant : constants) {
319+
Object value;
320+
if (valueField != null) {
321+
value = getFieldJsonValue(constant, valueField);
322+
} else if (isNumberBased) {
323+
value = getNumberEnumValue(constant, valueMethod, index++);
324+
} else {
325+
value = getStringEnumValue(constant, valueMethod);
326+
}
327+
328+
if (value instanceof String) {
329+
enumMembers.add(new EnumMemberModel(constant.getName(), (String) value, null));
330+
} else if (value instanceof Number) {
331+
enumMembers.add(new EnumMemberModel(constant.getName(), (Number) value, null));
332+
} else {
333+
System.out.println(String.format("'%s' enum as a @JsonValue that isn't a String or Number, ignoring", enumClass.getName()));
316334
}
317335
}
318336
} catch (Exception e) {
@@ -358,4 +376,10 @@ private Object invokeJsonValueMethod(Field field, Method valueMethod) throws Ref
358376
return valueObject;
359377
}
360378

379+
private Object getFieldJsonValue(Field field, Field jsonValueField) throws ReflectiveOperationException {
380+
field.setAccessible(true);
381+
final Object constant = field.get(null);
382+
jsonValueField.setAccessible(true);
383+
return jsonValueField.get(constant);
384+
}
361385
}

typescript-generator-core/src/test/java/cz/habarta/typescript/generator/Jackson2ParserTest.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,4 +175,44 @@ public String getXname4() {
175175
}
176176
}
177177

178+
@Test
179+
public void testStandardEnumValue() {
180+
testEnumByType(TestEnums.StandardEnum.class, "A", "B", "C");
181+
}
182+
183+
@Test
184+
public void testStringPropertyEnumValue() {
185+
testEnumByType(TestEnums.StringPropertyValuedEnum.class, "_A", "_B", "_C");
186+
}
187+
188+
@Test
189+
public void testNumberPropertyEnumValue() {
190+
testEnumByType(TestEnums.NumberPropertyValuedEnum.class, 0, 1, 2);
191+
}
192+
193+
@Test
194+
public void testMethodEnumValue() {
195+
testEnumByType(TestEnums.GeneralMethodValuedEnum.class, "_A", "_B", "_C");
196+
}
197+
198+
@Test
199+
public void testToStringEnumValue() {
200+
testEnumByType(TestEnums.ToStringValuedEnum.class, "_A", "_B", "_C");
201+
}
202+
203+
@Test
204+
public void testJsonPropertyEnumValue() {
205+
testEnumByType(TestEnums.JsonPropertyValuedEnum.class, "_A", "_B", "_C");
206+
}
207+
208+
private void testEnumByType(Class<? extends Enum<?>> type, Object... expectedValues) {
209+
final Jackson2Parser jacksonParser = getJackson2Parser();
210+
final Model model = jacksonParser.parseModel(type);
211+
Assert.assertEquals(1, model.getEnums().size());
212+
final EnumModel enumModel = model.getEnums().get(0);
213+
Assert.assertEquals(expectedValues.length, enumModel.getMembers().size());
214+
for (int i = 0; i < expectedValues.length; i++) {
215+
Assert.assertEquals(expectedValues[i], enumModel.getMembers().get(i).getEnumValue());
216+
}
217+
}
178218
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package cz.habarta.typescript.generator;
2+
3+
import com.fasterxml.jackson.annotation.JsonProperty;
4+
import com.fasterxml.jackson.annotation.JsonValue;
5+
6+
public class TestEnums {
7+
public enum StandardEnum {
8+
A, B, C
9+
}
10+
11+
public enum GeneralMethodValuedEnum {
12+
A, B, C;
13+
14+
@JsonValue
15+
public String getValue() {
16+
return "_" + name();
17+
}
18+
}
19+
20+
public enum ToStringValuedEnum {
21+
A, B, C;
22+
23+
@JsonValue
24+
@Override
25+
public String toString() {
26+
return "_" + name();
27+
}
28+
}
29+
30+
public enum StringPropertyValuedEnum {
31+
A, B, C;
32+
33+
@JsonValue
34+
private final String value = "_" + name();
35+
}
36+
37+
public enum NumberPropertyValuedEnum {
38+
A, B, C;
39+
40+
@JsonValue
41+
private final Integer value = ordinal();
42+
}
43+
44+
public enum JsonPropertyValuedEnum {
45+
@JsonProperty("_A") A,
46+
@JsonProperty("_B") B,
47+
@JsonProperty("_C") C
48+
}
49+
}

0 commit comments

Comments
 (0)