Skip to content

Commit

Permalink
[java-generator] Correctly handle numeric enums
Browse files Browse the repository at this point in the history
  • Loading branch information
andreaTP authored and manusa committed Sep 21, 2023
1 parent 9b86adc commit ba7d7ba
Show file tree
Hide file tree
Showing 12 changed files with 5,635 additions and 27 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* Fix #5466: OperationSupport should not fail trying to parse Status
* Fix #5382: [java-generator] Allow to deserialize more valid RFC3339 date-time and make the format customizable
* Fix #5380: [java-generator] Avoid to emit Java Keywords as package names
* Fix #5457: [java-generator] Correctly handle numeric enums
* Fix #5463: ensures that onStopLeading is called with releaseOnCancel even when leadership is already lost
* Fix #5423: OkHttpClientImpl supports setting request method for empty payload requests

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
import java.util.Locale;
import java.util.function.Function;

import static io.fabric8.java.generator.nodes.Keywords.JAVA_KEYWORDS;
import static io.fabric8.java.generator.nodes.Keywords.*;

public abstract class AbstractJSONSchema2Pojo {

Expand Down Expand Up @@ -151,6 +151,37 @@ public static String escapeQuotes(String str) {
return str.replace("\"", "\\\"").replace("\'", "\\\'");
}

private static String getRefinedIntegerType(String format) {
if (format == null || format.equals(INT64_CRD_TYPE)) {
return INT64_CRD_TYPE;
} else if (format.equals(INT32_CRD_TYPE)) {
return INT32_CRD_TYPE;
} else {
throw new JavaGeneratorException("Unsupported format for integer found " + format);
}
}

private static String getRefinedNumberType(String format) {
if (format == null || format.equals(DOUBLE_CRD_TYPE)) {
return DOUBLE_CRD_TYPE;
} else if (format.equals(FLOAT_CRD_TYPE)) {
return FLOAT_CRD_TYPE;
} else {
throw new JavaGeneratorException("Unsupported format for number found " + format);
}
}

private static String getRefinedStringType(String format) {
if (format == null || format.equals(STRING_CRD_TYPE)) {
return STRING_CRD_TYPE;
} else if (format.equals(DATETIME_CRD_TYPE)) {
return DATETIME_CRD_TYPE;
} else {
// TODO: there are more string format to support: byte, date etc.
return STRING_CRD_TYPE;
}
}

public static AbstractJSONSchema2Pojo fromJsonSchema(
String key,
JSONSchemaProps prop,
Expand Down Expand Up @@ -179,35 +210,23 @@ public static AbstractJSONSchema2Pojo fromJsonSchema(
case BOOLEAN_CRD_TYPE:
return fromJsonSchema.apply(JPrimitiveNameAndType.BOOL);
case INTEGER_CRD_TYPE:
String intFormat = prop.getFormat();
if (intFormat == null)
intFormat = INT64_CRD_TYPE;

switch (intFormat) {
switch (getRefinedIntegerType(prop.getFormat())) {
case INT32_CRD_TYPE:
return fromJsonSchema.apply(JPrimitiveNameAndType.INTEGER);
case INT64_CRD_TYPE:
default:
return fromJsonSchema.apply(JPrimitiveNameAndType.LONG);
}
case NUMBER_CRD_TYPE:
String numberFormat = prop.getFormat();
if (numberFormat == null)
numberFormat = DOUBLE_CRD_TYPE;

switch (numberFormat) {
switch (getRefinedNumberType(prop.getFormat())) {
case FLOAT_CRD_TYPE:
return fromJsonSchema.apply(JPrimitiveNameAndType.FLOAT);
case DOUBLE_CRD_TYPE:
default:
return fromJsonSchema.apply(JPrimitiveNameAndType.DOUBLE);
}
case STRING_CRD_TYPE:
String stringFormat = prop.getFormat();
if (stringFormat == null)
stringFormat = STRING_CRD_TYPE;

switch (stringFormat) {
switch (getRefinedStringType(prop.getFormat())) {
case DATETIME_CRD_TYPE:
return fromJsonSchema.apply(JPrimitiveNameAndType.DATETIME);
case STRING_CRD_TYPE:
Expand Down Expand Up @@ -286,8 +305,27 @@ private static AbstractJSONSchema2Pojo fromJsonSchema(
isNullable,
prop.getDefault());
case ENUM:
String enumType = JAVA_LANG_STRING;
switch (prop.getType()) {
case INTEGER_CRD_TYPE:
switch (getRefinedIntegerType(prop.getFormat())) {
case INT32_CRD_TYPE:
enumType = JAVA_LANG_INTEGER;
break;
case INT64_CRD_TYPE:
default:
enumType = JAVA_LANG_LONG;
break;
}
break;
case STRING_CRD_TYPE:
break;
default:
throw new JavaGeneratorException("Unsupported enumeration type/format" + prop.getType() + "/" + prop.getFormat());
}
return new JEnum(
key,
enumType,
prop.getEnum(),
config,
prop.getDescription(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,21 +30,24 @@
import java.util.Locale;
import java.util.stream.Collectors;

import static io.fabric8.java.generator.nodes.Keywords.JAVA_LANG_LONG;
import static io.fabric8.java.generator.nodes.Keywords.JAVA_LANG_STRING;

public class JEnum extends AbstractJSONSchema2Pojo {

private static final String VALUE = "value";

private final String type;
// TODO: handle number enum
private final String underlyingType;
private final List<String> values;

public JEnum(String type, List<JsonNode> values, Config config, String description, final boolean isNullable,
public JEnum(String type, String underlyingType, List<JsonNode> values, Config config, String description,
final boolean isNullable,
JsonNode defaultValue) {
super(config, description, isNullable, defaultValue, null);
this.type = AbstractJSONSchema2Pojo.sanitizeString(
type.substring(0, 1).toUpperCase() + type.substring(1));
this.underlyingType = underlyingType;
this.values = values.stream().map(JsonNode::asText).collect(Collectors.toList());
}

Expand All @@ -70,9 +73,9 @@ public GeneratorResult generateJava() {
CompilationUnit cu = new CompilationUnit();
EnumDeclaration en = cu.addEnum(this.type);

en.addField(JAVA_LANG_STRING, VALUE);
en.addField(underlyingType, VALUE);
ConstructorDeclaration cd = en.addConstructor();
cd.addParameter(JAVA_LANG_STRING, VALUE);
cd.addParameter(underlyingType, VALUE);
cd.createBody();

cd.setBody(
Expand All @@ -85,7 +88,7 @@ public GeneratorResult generateJava() {

MethodDeclaration getValue = en
.addMethod("getValue", Modifier.Keyword.PUBLIC);
getValue.setType(JAVA_LANG_STRING);
getValue.setType(underlyingType);
getValue
.setBody(new BlockStmt().addStatement(new ReturnStmt(VALUE)));
getValue.addAnnotation("com.fasterxml.jackson.annotation.JsonValue");
Expand All @@ -101,14 +104,22 @@ public GeneratorResult generateJava() {
constantName = sanitizeEnumEntry(sanitizeString(k));
}
String originalName = AbstractJSONSchema2Pojo.escapeQuotes(k);
Expression valueArgument = new StringLiteralExpr(originalName);
if (!underlyingType.equals(JAVA_LANG_STRING)) {
if (underlyingType.equals(JAVA_LANG_LONG) && !originalName.endsWith("L")) {
valueArgument = new IntegerLiteralExpr(originalName + "L");
} else {
valueArgument = new IntegerLiteralExpr(originalName);
}
}

EnumConstantDeclaration decl = new EnumConstantDeclaration();
decl.addAnnotation(
new SingleMemberAnnotationExpr(
new Name("com.fasterxml.jackson.annotation.JsonProperty"),
new StringLiteralExpr(originalName)));
decl.setName(constantName);
decl.addArgument(new StringLiteralExpr(originalName));
decl.addArgument(valueArgument);
en.addEntry(decl);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,10 @@ private Keywords() {
JAVA_KEYWORDS.add("while");
}

static final String JAVA_UTIL_MAP = "java.util.Map";
static final String JAVA_UTIL_LIST = "java.util.List";
static final String JAVA_LANG_STRING = "java.lang.String";
static final String ADDITIONAL_PROPERTIES = "additionalProperties";
public static final String JAVA_UTIL_MAP = "java.util.Map";
public static final String JAVA_UTIL_LIST = "java.util.List";
public static final String JAVA_LANG_STRING = "java.lang.String";
public static final String JAVA_LANG_LONG = "java.lang.Long";
public static final String JAVA_LANG_INTEGER = "java.lang.Integer";
public static final String ADDITIONAL_PROPERTIES = "additionalProperties";
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ private static Stream<Arguments> compilationTestData() {
Arguments.of("camel-integrationplatforms-crd.yaml", 192),
Arguments.of("two-crds.yml", 6),
Arguments.of("folder", 6),
Arguments.of("calico-ippool-crd.yml", 3));
Arguments.of("calico-ippool-crd.yml", 3),
Arguments.of("emissary-crds.yaml", 242));
}

@ParameterizedTest(name = "{0} should generate {1} source files and compile OK")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import java.util.*;

import static io.fabric8.java.generator.CRGeneratorRunner.groupToPackage;
import static io.fabric8.java.generator.nodes.Keywords.*;
import static org.junit.jupiter.api.Assertions.*;

class GeneratorTest {
Expand Down Expand Up @@ -439,6 +440,7 @@ void testDefaultEnum() {
props.put("e1", newEnum);
JEnum enu = new JEnum(
"t",
JAVA_LANG_STRING,
enumValues,
defaultConfig,
null,
Expand All @@ -461,6 +463,85 @@ void testDefaultEnum() {
assertEquals("BAZ", en.get().getEntries().get(2).getName().asString());
}

@Test
void testLongEnum() {
// Arrange
Map<String, JSONSchemaProps> props = new HashMap<>();
JSONSchemaProps newEnum = new JSONSchemaProps();
newEnum.setType("integer");
List<JsonNode> enumValues = new ArrayList<>();
enumValues.add(new TextNode("1"));
enumValues.add(new TextNode("2"));
enumValues.add(new TextNode("3"));
props.put("e1", newEnum);
JEnum enu = new JEnum(
"t",
JAVA_LANG_LONG,
enumValues,
defaultConfig,
null,
Boolean.FALSE,
null);

// Act
GeneratorResult res = enu.generateJava();

// Assert
assertEquals("T", enu.getType());
assertEquals(1, res.getInnerClasses().size());
assertEquals("T", res.getInnerClasses().get(0).getName());

Optional<EnumDeclaration> en = res.getInnerClasses().get(0).getEnumByName("T");
assertTrue(en.isPresent());
assertEquals(3, en.get().getEntries().size());
assertEquals("V__1", en.get().getEntries().get(0).getName().asString());
assertEquals("V__2", en.get().getEntries().get(1).getName().asString());
assertEquals("V__3", en.get().getEntries().get(2).getName().asString());
assertEquals("1L", en.get().getEntries().get(0).getArgument(0).toString());
assertEquals("2L", en.get().getEntries().get(1).getArgument(0).toString());
assertEquals("3L", en.get().getEntries().get(2).getArgument(0).toString());
}

@Test
void testIntEnum() {
// Arrange
Map<String, JSONSchemaProps> props = new HashMap<>();
JSONSchemaProps newEnum = new JSONSchemaProps();
newEnum.setType("integer");
newEnum.setFormat("int32");
List<JsonNode> enumValues = new ArrayList<>();
enumValues.add(new TextNode("1"));
enumValues.add(new TextNode("2"));
enumValues.add(new TextNode("3"));
props.put("e1", newEnum);
JEnum enu = new JEnum(
"t",
JAVA_LANG_INTEGER,
enumValues,
defaultConfig,
null,
Boolean.FALSE,
null);

// Act
GeneratorResult res = enu.generateJava();

// Assert
assertEquals("T", enu.getType());
assertEquals(1, res.getInnerClasses().size());
assertEquals("T", res.getInnerClasses().get(0).getName());

Optional<EnumDeclaration> en = res.getInnerClasses().get(0).getEnumByName("T");
assertTrue(en.isPresent());
assertEquals(3, en.get().getEntries().size());
assertEquals("V__1", en.get().getEntries().get(0).getName().asString());
assertEquals("V__2", en.get().getEntries().get(1).getName().asString());
assertEquals("V__3", en.get().getEntries().get(2).getName().asString());
assertEquals("1", en.get().getEntries().get(0).getArgument(0).toString());
assertEquals("2", en.get().getEntries().get(1).getArgument(0).toString());
assertEquals("3", en.get().getEntries().get(2).getArgument(0).toString());
}

@Test
void testNotUppercaseEnum() {
// Arrange
Expand All @@ -475,6 +556,7 @@ void testNotUppercaseEnum() {
props.put("e1", newEnum);
JEnum enu = new JEnum(
"t",
JAVA_LANG_STRING,
enumValues,
new Config(false, null, null, new HashMap<>()),
null,
Expand Down
Loading

0 comments on commit ba7d7ba

Please sign in to comment.