From be3e077566e867d31e7a6c7e873388e4af551fa9 Mon Sep 17 00:00:00 2001 From: Kenneth Aasan Date: Thu, 11 Jul 2024 14:41:10 +0200 Subject: [PATCH 1/2] feat: fake setter for java when allowInheritance is used with const --- docs/migrations/version-3-to-4.md | 8 +- .../java/renderers/ClassRenderer.ts | 17 ++- test/generators/java/JavaGenerator.spec.ts | 12 +- .../__snapshots__/JavaGenerator.spec.ts.snap | 110 +++++++++--------- 4 files changed, 83 insertions(+), 64 deletions(-) diff --git a/docs/migrations/version-3-to-4.md b/docs/migrations/version-3-to-4.md index e21906dd62..89ca99a0d0 100644 --- a/docs/migrations/version-3-to-4.md +++ b/docs/migrations/version-3-to-4.md @@ -226,7 +226,7 @@ class Address: ### Import style deprecation -All models are from this point onwards imported using explicit styles `from . import ${model.name}` to allow for circular model dependencies to work. This means that the option `importsStyle` is deprecated and is no longer in use. It will be removed at some point in the future. +All models are from this point onwards imported using explicit styles `from . import ${model.name}` to allow for circular model dependencies to work. This means that the option `importsStyle` is deprecated and is no longer in use. It will be removed at some point in the future. ## Go @@ -318,3 +318,9 @@ type info struct { isDevelopment *bool } ``` + +## Java + +### when allowInheritance is true, Modelina now renders a fake setter for the classes that implements interfaces when const is used + +In Java, when a class implements an interface, it must implement all the methods of that interface. When a schema has a `const` property, Modelina generates a fake setter for that property in the class that implements the interface to avoid compilation errors. diff --git a/src/generators/java/renderers/ClassRenderer.ts b/src/generators/java/renderers/ClassRenderer.ts index 2abd8b8c47..a85753c240 100644 --- a/src/generators/java/renderers/ClassRenderer.ts +++ b/src/generators/java/renderers/ClassRenderer.ts @@ -197,10 +197,25 @@ export const JAVA_DEFAULT_CLASS_PRESET: ClassPresetType = { } ${getterName}() { return this.${property.propertyName}; }`; }, setter({ property, model }) { + const setterName = FormatHelpers.toPascalCase(property.propertyName); + if (property.property.options.const?.value) { + if (model.options.extend) { + return `${getOverride(model, property)}public void set${setterName}(${ + property.property.type + } ${property.propertyName}) { + if (this.${property.propertyName} != ${property.propertyName}) { + throw new UnsupportedOperationException("Changing the ${ + property.propertyName + } is not supported for ${model.name}"); + } + + this.${property.propertyName} = ${property.propertyName}; +}`; + } + return ''; } - const setterName = FormatHelpers.toPascalCase(property.propertyName); if (model.options.isExtended) { if (isDiscriminatorOrDictionary(model, property)) { diff --git a/test/generators/java/JavaGenerator.spec.ts b/test/generators/java/JavaGenerator.spec.ts index ee88e35b71..3b5b2a3b45 100644 --- a/test/generators/java/JavaGenerator.spec.ts +++ b/test/generators/java/JavaGenerator.spec.ts @@ -343,9 +343,6 @@ describe('JavaGenerator', () => { properties: { type: { const: 'Cat' - }, - test: { - $ref: '#/components/schemas/Test' } } } @@ -379,8 +376,10 @@ describe('JavaGenerator', () => { title: 'Test', type: 'object', properties: { - testProp: { - type: 'string' + testEnum: { + title: 'TestEnum', + type: 'string', + enum: ['FOO', 'BAR'] } } }, @@ -391,6 +390,9 @@ describe('JavaGenerator', () => { { type: 'object', properties: { + testEnum: { + const: 'FOO' + }, testProp2: { type: 'string' } diff --git a/test/generators/java/__snapshots__/JavaGenerator.spec.ts.snap b/test/generators/java/__snapshots__/JavaGenerator.spec.ts.snap index f031f078bf..3b4a92492b 100644 --- a/test/generators/java/__snapshots__/JavaGenerator.spec.ts.snap +++ b/test/generators/java/__snapshots__/JavaGenerator.spec.ts.snap @@ -38,6 +38,13 @@ public interface Pet { public void setId(String id) { this.id = id; } public CloudEventType getType() { return this.type; } + public void setType(CloudEventType type) { + if (this.type != type) { + throw new UnsupportedOperationException(\\"Changing the type is not supported for Dog\\"); + } + + this.type = type; + } @Override public CloudEventDotSequenceType getSequencetype() { return this.sequencetype; } @@ -157,18 +164,26 @@ public interface Pet { return String.valueOf(value); } }", - "public class TestAllOf { - @JsonProperty(\\"testProp\\") + "public class TestAllOf implements Test { + @JsonProperty(\\"testEnum\\") @JsonInclude(JsonInclude.Include.NON_NULL) - private String testProp; + private final TestEnum testEnum = TestEnum.FOO; @JsonProperty(\\"testProp2\\") @JsonInclude(JsonInclude.Include.NON_NULL) private String testProp2; @JsonInclude(JsonInclude.Include.NON_NULL) private Map additionalProperties; - public String getTestProp() { return this.testProp; } - public void setTestProp(String testProp) { this.testProp = testProp; } + @Override + public TestEnum getTestEnum() { return this.testEnum; } + @Override + public void setTestEnum(TestEnum testEnum) { + if (this.testEnum != testEnum) { + throw new UnsupportedOperationException(\\"Changing the testEnum is not supported for TestAllOf\\"); + } + + this.testEnum = testEnum; + } public String getTestProp2() { return this.testProp2; } public void setTestProp2(String testProp2) { this.testProp2 = testProp2; } @@ -186,20 +201,20 @@ public interface Pet { } TestAllOf self = (TestAllOf) o; return - Objects.equals(this.testProp, self.testProp) && + Objects.equals(this.testEnum, self.testEnum) && Objects.equals(this.testProp2, self.testProp2) && Objects.equals(this.additionalProperties, self.additionalProperties); } @Override public int hashCode() { - return Objects.hash((Object)testProp, (Object)testProp2, (Object)additionalProperties); + return Objects.hash((Object)testEnum, (Object)testProp2, (Object)additionalProperties); } @Override public String toString() { return \\"class TestAllOf {\\\\n\\" + - \\" testProp: \\" + toIndentedString(testProp) + \\"\\\\n\\" + + \\" testEnum: \\" + toIndentedString(testEnum) + \\"\\\\n\\" + \\" testProp2: \\" + toIndentedString(testProp2) + \\"\\\\n\\" + \\" additionalProperties: \\" + toIndentedString(additionalProperties) + \\"\\\\n\\" + \\"}\\"; @@ -216,56 +231,38 @@ public interface Pet { return o.toString().replace(\\"\\\\n\\", \\"\\\\n \\"); } }", - "public class Test { - @JsonProperty(\\"testProp\\") - @JsonInclude(JsonInclude.Include.NON_NULL) - private String testProp; - @JsonInclude(JsonInclude.Include.NON_NULL) - private Map additionalProperties; + "public enum TestEnum { + FOO((String)\\"FOO\\"), BAR((String)\\"BAR\\"); - public String getTestProp() { return this.testProp; } - public void setTestProp(String testProp) { this.testProp = testProp; } + private String value; - public Map getAdditionalProperties() { return this.additionalProperties; } - public void setAdditionalProperties(Map additionalProperties) { this.additionalProperties = additionalProperties; } + TestEnum(String value) { + this.value = value; + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - Test self = (Test) o; - return - Objects.equals(this.testProp, self.testProp) && - Objects.equals(this.additionalProperties, self.additionalProperties); + @JsonValue + public String getValue() { + return value; } - @Override - public int hashCode() { - return Objects.hash((Object)testProp, (Object)additionalProperties); + @JsonCreator + public static TestEnum fromValue(String value) { + for (TestEnum e : TestEnum.values()) { + if (e.value.equals(value)) { + return e; + } + } + throw new IllegalArgumentException(\\"Unexpected value '\\" + value + \\"'\\"); } @Override public String toString() { - return \\"class Test {\\\\n\\" + - \\" testProp: \\" + toIndentedString(testProp) + \\"\\\\n\\" + - \\" additionalProperties: \\" + toIndentedString(additionalProperties) + \\"\\\\n\\" + - \\"}\\"; - } - - /** - * Convert the given object to string with each line indented by 4 spaces - * (except the first line). - */ - private String toIndentedString(Object o) { - if (o == null) { - return \\"null\\"; - } - return o.toString().replace(\\"\\\\n\\", \\"\\\\n \\"); + return String.valueOf(value); } +}", + "public interface Test { + public TestEnum getTestEnum(); + public void setTestEnum(TestEnum testEnum); }", "public interface CloudEvent { public String getId(); @@ -284,9 +281,6 @@ public interface Pet { @JsonProperty(\\"sequencetype\\") @JsonInclude(JsonInclude.Include.NON_NULL) private CloudEventDotSequenceType sequencetype; - @JsonProperty(\\"test\\") - @JsonInclude(JsonInclude.Include.NON_NULL) - private Test test; @JsonInclude(JsonInclude.Include.NON_NULL) private Map additionalProperties; @@ -296,15 +290,19 @@ public interface Pet { public void setId(String id) { this.id = id; } public CloudEventType getType() { return this.type; } + public void setType(CloudEventType type) { + if (this.type != type) { + throw new UnsupportedOperationException(\\"Changing the type is not supported for Cat\\"); + } + + this.type = type; + } @Override public CloudEventDotSequenceType getSequencetype() { return this.sequencetype; } @Override public void setSequencetype(CloudEventDotSequenceType sequencetype) { this.sequencetype = sequencetype; } - public Test getTest() { return this.test; } - public void setTest(Test test) { this.test = test; } - public Map getAdditionalProperties() { return this.additionalProperties; } public void setAdditionalProperties(Map additionalProperties) { this.additionalProperties = additionalProperties; } @@ -321,13 +319,12 @@ public interface Pet { Objects.equals(this.id, self.id) && Objects.equals(this.type, self.type) && Objects.equals(this.sequencetype, self.sequencetype) && - Objects.equals(this.test, self.test) && Objects.equals(this.additionalProperties, self.additionalProperties); } @Override public int hashCode() { - return Objects.hash((Object)id, (Object)type, (Object)sequencetype, (Object)test, (Object)additionalProperties); + return Objects.hash((Object)id, (Object)type, (Object)sequencetype, (Object)additionalProperties); } @Override @@ -336,7 +333,6 @@ public interface Pet { \\" id: \\" + toIndentedString(id) + \\"\\\\n\\" + \\" type: \\" + toIndentedString(type) + \\"\\\\n\\" + \\" sequencetype: \\" + toIndentedString(sequencetype) + \\"\\\\n\\" + - \\" test: \\" + toIndentedString(test) + \\"\\\\n\\" + \\" additionalProperties: \\" + toIndentedString(additionalProperties) + \\"\\\\n\\" + \\"}\\"; } From 831708ce6e149227c4877db2121bc12bd9970378 Mon Sep 17 00:00:00 2001 From: Kenneth Aasan Date: Fri, 12 Jul 2024 13:38:35 +0200 Subject: [PATCH 2/2] feat: change to skip rendering override in class and enum in interface --- docs/migrations/version-3-to-4.md | 4 +-- .../java/renderers/ClassRenderer.ts | 31 ++++++------------- .../__snapshots__/JavaGenerator.spec.ts.snap | 26 ---------------- 3 files changed, 11 insertions(+), 50 deletions(-) diff --git a/docs/migrations/version-3-to-4.md b/docs/migrations/version-3-to-4.md index 89ca99a0d0..fc27a2a0d8 100644 --- a/docs/migrations/version-3-to-4.md +++ b/docs/migrations/version-3-to-4.md @@ -321,6 +321,6 @@ type info struct { ## Java -### when allowInheritance is true, Modelina now renders a fake setter for the classes that implements interfaces when const is used +### when allowInheritance is true, Modelina now don't render the setter for enums in interfaces because the classes that implement the interface might use a constant value -In Java, when a class implements an interface, it must implement all the methods of that interface. When a schema has a `const` property, Modelina generates a fake setter for that property in the class that implements the interface to avoid compilation errors. +In Java, when a class implements an interface, it must implement all the methods of that interface. Because of that, Modelina now doesn't render the setter for enums in interfaces when allowInheritance is true because the classes that implement the interface might use a constant value. diff --git a/src/generators/java/renderers/ClassRenderer.ts b/src/generators/java/renderers/ClassRenderer.ts index a85753c240..a604528148 100644 --- a/src/generators/java/renderers/ClassRenderer.ts +++ b/src/generators/java/renderers/ClassRenderer.ts @@ -10,6 +10,7 @@ import { FormatHelpers } from '../../../helpers'; import { JavaOptions } from '../JavaGenerator'; import { ClassPresetType } from '../JavaPreset'; import { unionIncludesBuiltInTypes } from '../JavaConstrainer'; +import { isEnum } from '../../csharp/Constants'; /** * Renderer for Java's `class` type @@ -197,38 +198,24 @@ export const JAVA_DEFAULT_CLASS_PRESET: ClassPresetType = { } ${getterName}() { return this.${property.propertyName}; }`; }, setter({ property, model }) { - const setterName = FormatHelpers.toPascalCase(property.propertyName); - if (property.property.options.const?.value) { - if (model.options.extend) { - return `${getOverride(model, property)}public void set${setterName}(${ - property.property.type - } ${property.propertyName}) { - if (this.${property.propertyName} != ${property.propertyName}) { - throw new UnsupportedOperationException("Changing the ${ - property.propertyName - } is not supported for ${model.name}"); - } - - this.${property.propertyName} = ${property.propertyName}; -}`; - } - return ''; } + const setterName = FormatHelpers.toPascalCase(property.propertyName); + if (model.options.isExtended) { - if (isDiscriminatorOrDictionary(model, property)) { + // don't render setters for discriminator, dictionary properties, or enums (because they can be set to constants in the models that extend them) + if (isDiscriminatorOrDictionary(model, property) || isEnum(property)) { return ''; } return `public void set${setterName}(${property.property.type} ${property.propertyName});`; } - return `${getOverride(model, property)}public void set${setterName}(${ - property.property.type - } ${property.propertyName}) { this.${property.propertyName} = ${ - property.propertyName - }; }`; + // don't render override for enums because enum setters in the interfaces are not rendered + const override = !isEnum(property) ? getOverride(model, property) : ''; + + return `${override}public void set${setterName}(${property.property.type} ${property.propertyName}) { this.${property.propertyName} = ${property.propertyName}; }`; } }; diff --git a/test/generators/java/__snapshots__/JavaGenerator.spec.ts.snap b/test/generators/java/__snapshots__/JavaGenerator.spec.ts.snap index 3b4a92492b..26725d7cd8 100644 --- a/test/generators/java/__snapshots__/JavaGenerator.spec.ts.snap +++ b/test/generators/java/__snapshots__/JavaGenerator.spec.ts.snap @@ -38,17 +38,9 @@ public interface Pet { public void setId(String id) { this.id = id; } public CloudEventType getType() { return this.type; } - public void setType(CloudEventType type) { - if (this.type != type) { - throw new UnsupportedOperationException(\\"Changing the type is not supported for Dog\\"); - } - - this.type = type; - } @Override public CloudEventDotSequenceType getSequencetype() { return this.sequencetype; } - @Override public void setSequencetype(CloudEventDotSequenceType sequencetype) { this.sequencetype = sequencetype; } public String getData() { return this.data; } @@ -176,14 +168,6 @@ public interface Pet { @Override public TestEnum getTestEnum() { return this.testEnum; } - @Override - public void setTestEnum(TestEnum testEnum) { - if (this.testEnum != testEnum) { - throw new UnsupportedOperationException(\\"Changing the testEnum is not supported for TestAllOf\\"); - } - - this.testEnum = testEnum; - } public String getTestProp2() { return this.testProp2; } public void setTestProp2(String testProp2) { this.testProp2 = testProp2; } @@ -262,14 +246,12 @@ public interface Pet { }", "public interface Test { public TestEnum getTestEnum(); - public void setTestEnum(TestEnum testEnum); }", "public interface CloudEvent { public String getId(); public void setId(String id); public CloudEventDotSequenceType getSequencetype(); - public void setSequencetype(CloudEventDotSequenceType sequencetype); }", "public class Cat implements Pet, CloudEvent { @NotNull @@ -290,17 +272,9 @@ public interface Pet { public void setId(String id) { this.id = id; } public CloudEventType getType() { return this.type; } - public void setType(CloudEventType type) { - if (this.type != type) { - throw new UnsupportedOperationException(\\"Changing the type is not supported for Cat\\"); - } - - this.type = type; - } @Override public CloudEventDotSequenceType getSequencetype() { return this.sequencetype; } - @Override public void setSequencetype(CloudEventDotSequenceType sequencetype) { this.sequencetype = sequencetype; } public Map getAdditionalProperties() { return this.additionalProperties; }