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

feat!: adds inheritance with interfaces for java #1593

Merged
merged 16 commits into from
Nov 20, 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
109 changes: 109 additions & 0 deletions docs/migrations/version-2-to-3.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,115 @@ public class TestClass {
}
```

#### inheritance will generate interfaces

Please read the section about [allowInheritance](#allowinheritance-set-to-true-will-enable-inheritance) first. When `allowInheritance` is enabled, interfaces will be generated for schemas that uses `allOf`:

```yaml
components:
messages:
Vehicle:
payload:
oneOf:
- $ref: '#/components/schemas/Car'
- $ref: '#/components/schemas/Truck'
schemas:
Vehicle:
title: Vehicle
type: object
discriminator: vehicleType
properties:
vehicleType:
title: VehicleType
type: string
length:
type: number
format: float
required:
- vehicleType
Car:
allOf:
- '#/components/schemas/Vehicle'
- type: object
properties:
vehicleType:
const: Car
Truck:
allOf:
- '#/components/schemas/Vehicle'
- type: object
properties:
vehicleType:
const: Truck
```

will generate

```java
public interface NewVehicle {
VehicleType getVehicleType();
}

public class Car implements NewVehicle, Vehicle {
private final VehicleType vehicleType = VehicleType.CAR;
private Float length;
private Map<String, Object> additionalProperties;

public VehicleType getVehicleType() { return this.vehicleType; }

@Override
public Float getLength() { return this.length; }
@Override
public void setLength(Float length) { this.length = length; }
}

public enum VehicleType {
CAR((String)\\"Car\\"), TRUCK((String)\\"Truck\\");

private String value;

VehicleType(String value) {
this.value = value;
}

public String getValue() {
return value;
}

public static VehicleType fromValue(String value) {
for (VehicleType e : VehicleType.values()) {
if (e.value.equals(value)) {
return e;
}
}
throw new IllegalArgumentException(\\"Unexpected value '\\" + value + \\"'\\");
}

@Override
public String toString() {
return String.valueOf(value);
}
}

public interface Vehicle {
public Float getLength();
public void setLength(Float length);
}

public class Truck implements NewVehicle, Vehicle {
private final VehicleType vehicleType = VehicleType.TRUCK;
private Float length;
private Map<String, Object> additionalProperties;

public VehicleType getVehicleType() { return this.vehicleType; }

@Override
public Float getLength() { return this.length; }
@Override
public void setLength(Float length) { this.length = length; }
}
```

### Kotlin

Is not affected by this change.
Expand Down
4 changes: 4 additions & 0 deletions src/generators/java/presets/CommonPreset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,10 @@ function renderUnmarshalling({
export const JAVA_COMMON_PRESET: JavaPreset<JavaCommonPresetOptions> = {
class: {
additionalContent({ renderer, model, content, options }) {
if (model.options.isExtended) {
return '';
}

options = options || {};
const blocks: string[] = [];
const shouldContainEqual =
Expand Down
6 changes: 5 additions & 1 deletion src/generators/java/presets/ConstraintsPreset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@ export const JAVA_CONSTRAINTS_PRESET: JavaPreset = {
return content;
},
// eslint-disable-next-line sonarjs/cognitive-complexity
property({ renderer, property, content }) {
property({ renderer, property, content, model }) {
if (model.options.isExtended) {
return '';
}

const annotations: string[] = [];

if (property.required) {
Expand Down
10 changes: 9 additions & 1 deletion src/generators/java/presets/DescriptionPreset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { JavaRenderer } from '../JavaRenderer';
import { JavaPreset } from '../JavaPreset';
import { FormatHelpers } from '../../../helpers';
import { ConstrainedMetaModel } from '../../../models';
import { isDiscriminatorOrDictionary } from '../renderers/ClassRenderer';

function renderDescription({
renderer,
Expand Down Expand Up @@ -38,7 +39,14 @@ export const JAVA_DESCRIPTION_PRESET: JavaPreset = {
self({ renderer, model, content }) {
return renderDescription({ renderer, content, item: model });
},
getter({ renderer, property, content }) {
getter({ renderer, property, content, model }) {
if (
model.options.isExtended &&
isDiscriminatorOrDictionary(model, property)
) {
return '';
}

return renderDescription({ renderer, content, item: property.property });
}
},
Expand Down
6 changes: 5 additions & 1 deletion src/generators/java/presets/JacksonPreset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@ export const JAVA_JACKSON_PRESET: JavaPreset = {
renderer.dependencyManager.addDependency(JACKSON_ANNOTATION_DEPENDENCY);
return content;
},
property({ renderer, property, content }) {
property({ renderer, property, content, model }) {
if (model.options.isExtended) {
return '';
}

//Properties that are dictionaries with unwrapped options, cannot get the annotation because it cannot be accurately unwrapped by the jackson library.
const isDictionary =
property.property instanceof ConstrainedDictionaryModel;
Expand Down
100 changes: 89 additions & 11 deletions src/generators/java/renderers/ClassRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
ConstrainedDictionaryModel,
ConstrainedObjectModel,
ConstrainedObjectPropertyModel,
ConstrainedReferenceModel,
ConstrainedUnionModel
} from '../../../models';
import { FormatHelpers } from '../../../helpers';
Expand Down Expand Up @@ -31,15 +32,25 @@ export class ClassRenderer extends JavaRenderer<ConstrainedObjectModel> {
this.dependencyManager.addDependency('import java.util.Map;');
}

const parentUnions = this.getParentUnions();
if (this.model.options.isExtended) {
return `public interface ${this.model.name} {
${this.indent(this.renderBlock(content, 2))}
}`;
}

if (parentUnions) {
for (const parentUnion of parentUnions) {
this.dependencyManager.addModelDependency(parentUnion);
const parentUnions = this.getParentUnions();
const extend = this.model.options.extend?.filter(
(extend) => extend.options.isExtended
);
const implement = [...(parentUnions ?? []), ...(extend ?? [])];

if (implement.length) {
for (const i of implement) {
this.dependencyManager.addModelDependency(i);
}

return `public class ${this.model.name} implements ${parentUnions
.map((pu) => pu.name)
return `public class ${this.model.name} implements ${implement
.map((i) => i.name)
.join(', ')} {
${this.indent(this.renderBlock(content, 2))}
}`;
Expand Down Expand Up @@ -121,28 +132,95 @@ ${this.indent(this.renderBlock(content, 2))}
}
}

const getOverride = (
model: ConstrainedObjectModel,
property: ConstrainedObjectPropertyModel
) => {
const isOverride = model.options.extend?.find((extend) => {
if (
!extend.options.isExtended ||
isDiscriminatorOrDictionary(model, property)
) {
return false;
}

if (
extend instanceof ConstrainedObjectModel &&
extend.properties[property.propertyName]
) {
return true;
}

if (
extend instanceof ConstrainedReferenceModel &&
extend.ref instanceof ConstrainedObjectModel &&
extend.ref.properties[property.propertyName]
) {
return true;
}
});

return isOverride ? '@Override\n' : '';
};

export const isDiscriminatorOrDictionary = (
model: ConstrainedObjectModel,
property: ConstrainedObjectPropertyModel
): boolean =>
model.options.discriminator?.discriminator ===
property.unconstrainedPropertyName ||
property.property instanceof ConstrainedDictionaryModel;

export const JAVA_DEFAULT_CLASS_PRESET: ClassPresetType<JavaOptions> = {
self({ renderer }) {
return renderer.defaultSelf();
},
property({ property }) {
property({ property, model }) {
if (model.options.isExtended) {
return '';
}

if (property.property.options.const?.value) {
return `private final ${property.property.type} ${property.propertyName} = ${property.property.options.const.value};`;
}

return `private ${property.property.type} ${property.propertyName};`;
},
getter({ property }) {
getter({ property, model }) {
const getterName = `get${FormatHelpers.toPascalCase(
property.propertyName
)}`;
return `public ${property.property.type} ${getterName}() { return this.${property.propertyName}; }`;

if (model.options.isExtended) {
if (isDiscriminatorOrDictionary(model, property)) {
return '';
}

return `public ${property.property.type} ${getterName}();`;
}

return `${getOverride(model, property)}public ${
property.property.type
} ${getterName}() { return this.${property.propertyName}; }`;
},
setter({ property }) {
setter({ property, model }) {
if (property.property.options.const?.value) {
return '';
}
const setterName = FormatHelpers.toPascalCase(property.propertyName);
return `public void set${setterName}(${property.property.type} ${property.propertyName}) { this.${property.propertyName} = ${property.propertyName}; }`;

if (model.options.isExtended) {
if (isDiscriminatorOrDictionary(model, 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
}; }`;
}
};
Loading
Loading