[java-micronaut] Generate visitor for subtypes with a discriminator (#12192)

* [java-micronaut] Generate visitor for subtypes with a discriminator

When types which extend a common type and are distinguished based on a
discriminator are consumed they are often cast to their specific Java
type which results in error prone boilerplate code.

By generating a visitor for those kind of types the various subtypes can
be consumed in a type safe manner.

* [java-micronaut] Remove redundant public access modifiers
This commit is contained in:
Auke Schrijnen
2022-04-25 04:54:06 +02:00
committed by GitHub
parent 2dd67aa6fc
commit 85170ee314
9 changed files with 84 additions and 0 deletions

View File

@@ -8,3 +8,4 @@ additionalProperties:
build: "all"
test: "spock"
requiredPropertiesInConstructor: "false"
visitable: "true"

View File

@@ -74,6 +74,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|title|Client service name| |null|
|useBeanValidation|Use BeanValidation API annotations| |true|
|useOptional|Use Optional container for optional parameters| |false|
|visitable|Generate visitor for subtypes with a discriminator| |false|
|withXml|whether to include support for application/xml content type and include XML annotations in the model (works with libraries that provide support for JSON and XML)| |false|
|wrapInHttpResponse|Wrap the response in HttpResponse object| |false|

View File

@@ -76,6 +76,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|useAuth|Whether to import authorization and to annotate controller methods accordingly| |true|
|useBeanValidation|Use BeanValidation API annotations| |true|
|useOptional|Use Optional container for optional parameters| |false|
|visitable|Generate visitor for subtypes with a discriminator| |false|
|withXml|whether to include support for application/xml content type and include XML annotations in the model (works with libraries that provide support for JSON and XML)| |false|
|wrapInHttpResponse|Wrap the response in HttpResponse object| |false|

View File

@@ -28,6 +28,7 @@ public abstract class JavaMicronautAbstractCodegen extends AbstractJavaCodegen i
public static final String OPT_REQUIRED_PROPERTIES_IN_CONSTRUCTOR = "requiredPropertiesInConstructor";
public static final String OPT_MICRONAUT_VERSION = "micronautVersion";
public static final String OPT_USE_AUTH = "useAuth";
public static final String OPT_VISITABLE = "visitable";
public static final String OPT_DATE_LIBRARY_JAVA8 = "java8";
public static final String OPT_DATE_LIBRARY_JAVA8_LOCAL_DATETIME = "java8-localdatetime";
public static final String OPT_DATE_FORMAT = "dateFormat";
@@ -38,6 +39,7 @@ public abstract class JavaMicronautAbstractCodegen extends AbstractJavaCodegen i
protected String title;
protected boolean useBeanValidation;
protected boolean useOptional;
protected boolean visitable;
protected String buildTool;
protected String testTool;
protected boolean requiredPropertiesInConstructor = true;
@@ -59,6 +61,7 @@ public abstract class JavaMicronautAbstractCodegen extends AbstractJavaCodegen i
// Set all the fields
useBeanValidation = true;
useOptional = false;
visitable = false;
buildTool = OPT_BUILD_ALL;
testTool = OPT_TEST_JUNIT;
outputFolder = "generated-code/java-micronaut-client";
@@ -105,6 +108,7 @@ public abstract class JavaMicronautAbstractCodegen extends AbstractJavaCodegen i
cliOptions.add(new CliOption(OPT_MICRONAUT_VERSION, "Micronaut version, only >=3.0.0 versions are supported").defaultValue(micronautVersion));
cliOptions.add(CliOption.newBoolean(USE_BEANVALIDATION, "Use BeanValidation API annotations", useBeanValidation));
cliOptions.add(CliOption.newBoolean(USE_OPTIONAL, "Use Optional container for optional parameters", useOptional));
cliOptions.add(CliOption.newBoolean(OPT_VISITABLE, "Generate visitor for subtypes with a discriminator", visitable));
cliOptions.add(CliOption.newBoolean(OPT_REQUIRED_PROPERTIES_IN_CONSTRUCTOR, "Allow only to create models with all the required properties provided in constructor", requiredPropertiesInConstructor));
cliOptions.add(CliOption.newBoolean(OPT_REACTIVE, "Make the responses use Reactor Mono as wrapper", reactive));
cliOptions.add(CliOption.newBoolean(OPT_WRAP_IN_HTTP_RESPONSE, "Wrap the response in HttpResponse object", wrapInHttpResponse));
@@ -175,6 +179,11 @@ public abstract class JavaMicronautAbstractCodegen extends AbstractJavaCodegen i
}
writePropertyBack(USE_OPTIONAL, useOptional);
if (additionalProperties.containsKey(OPT_VISITABLE)) {
this.setVisitable(convertPropertyToBoolean(OPT_VISITABLE));
}
writePropertyBack(OPT_VISITABLE, visitable);
if (additionalProperties.containsKey(OPT_REQUIRED_PROPERTIES_IN_CONSTRUCTOR)) {
this.requiredPropertiesInConstructor = convertPropertyToBoolean(OPT_REQUIRED_PROPERTIES_IN_CONSTRUCTOR);
}
@@ -338,6 +347,10 @@ public abstract class JavaMicronautAbstractCodegen extends AbstractJavaCodegen i
this.useOptional = useOptional;
}
public void setVisitable(boolean visitable) {
this.visitable = visitable;
}
@Override
public String toApiVarName(String name) {
String apiVarName = super.toApiVarName(name);
@@ -355,6 +368,10 @@ public abstract class JavaMicronautAbstractCodegen extends AbstractJavaCodegen i
return useOptional;
}
public boolean isVisitable() {
return visitable;
}
@Override
public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List<ModelMap> allModels) {
objs = super.postProcessOperationsWithModels(objs, allModels);

View File

@@ -355,4 +355,36 @@ Declare the class with extends and implements
}
};
{{/parcelableModel}}
{{#visitable}}
{{#discriminator}}
/**
* Accept the visitor and invoke it for the specific {{classname}} type.
*
* @param visitor the {{classname}} visitor
* @param <T> the return type of the visitor
* @return the result from the visitor
*/
public abstract <T> T accept(Visitor<T> visitor);
/**
* A {{classname}} visitor implementation allows visiting the various {{classname}} types.
*
* @param <R> the return type of the visitor
*/
public interface Visitor<R> {
{{#discriminator.mappedModels}}
R visit{{modelName}}({{modelName}} value);
{{/discriminator.mappedModels}}
}
{{/discriminator}}
{{#parent}}
{{#interfaces.0}}
@Override
public <T> T accept({{{parent}}}.Visitor<T> visitor) {
return visitor.visit{{classname}}(this);
}
{{/interfaces.0}}
{{/parent}}
{{/visitable}}
}

View File

@@ -141,4 +141,24 @@ public class Animal {
return o.toString().replace("\n", "\n ");
}
/**
* Accept the visitor and invoke it for the specific Animal type.
*
* @param visitor the Animal visitor
* @param <T> the return type of the visitor
* @return the result from the visitor
*/
public abstract <T> T accept(Visitor<T> visitor);
/**
* A Animal visitor implementation allows visiting the various Animal types.
*
* @param <R> the return type of the visitor
*/
public interface Visitor<R> {
R visitBigCat(BigCat value);
R visitCat(Cat value);
R visitDog(Dog value);
}
}

View File

@@ -138,4 +138,8 @@ public class BigCat extends Cat {
return o.toString().replace("\n", "\n ");
}
@Override
public <T> T accept(Cat.Visitor<T> visitor) {
return visitor.visitBigCat(this);
}
}

View File

@@ -103,4 +103,8 @@ public class Cat extends Animal {
return o.toString().replace("\n", "\n ");
}
@Override
public <T> T accept(Animal.Visitor<T> visitor) {
return visitor.visitCat(this);
}
}

View File

@@ -103,4 +103,8 @@ public class Dog extends Animal {
return o.toString().replace("\n", "\n ");
}
@Override
public <T> T accept(Animal.Visitor<T> visitor) {
return visitor.visitDog(this);
}
}