Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[java-generator] Correctly handle numeric enums #5462

Merged
merged 1 commit into from
Sep 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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