forked from loafle/openapi-generator-original
[kotlin] Nested enum naming fix, and naming options via CLI (#6858)
* [kotlin] Nested enum naming fix, and naming options via CLI * [kotlin] Add option test for enum property naming * [kotlin] Escape all reserved/keywords for enums
This commit is contained in:
parent
86b266b02f
commit
e7594c42d0
@ -146,6 +146,10 @@ public class CodegenConstants {
|
|||||||
public static final String DOTNET_FRAMEWORK_DESC = "The target .NET framework version.";
|
public static final String DOTNET_FRAMEWORK_DESC = "The target .NET framework version.";
|
||||||
|
|
||||||
public static enum MODEL_PROPERTY_NAMING_TYPE {camelCase, PascalCase, snake_case, original}
|
public static enum MODEL_PROPERTY_NAMING_TYPE {camelCase, PascalCase, snake_case, original}
|
||||||
|
public static enum ENUM_PROPERTY_NAMING_TYPE {camelCase, PascalCase, snake_case, original, UPPERCASE}
|
||||||
|
|
||||||
|
public static final String ENUM_PROPERTY_NAMING = "enumPropertyNaming";
|
||||||
|
public static final String ENUM_PROPERTY_NAMING_DESC = "Naming convention for enum properties: 'camelCase', 'PascalCase', 'snake_case', 'UPPERCASE', and 'original'";
|
||||||
|
|
||||||
public static final String MODEL_NAME_PREFIX = "modelNamePrefix";
|
public static final String MODEL_NAME_PREFIX = "modelNamePrefix";
|
||||||
public static final String MODEL_NAME_PREFIX_DESC = "Prefix that will be prepended to all model names. Default is the empty string.";
|
public static final String MODEL_NAME_PREFIX_DESC = "Prefix that will be prepended to all model names. Default is the empty string.";
|
||||||
|
@ -23,6 +23,7 @@ public class KotlinClientCodegen extends DefaultCodegen implements CodegenConfig
|
|||||||
protected String packageName = "io.swagger.client";
|
protected String packageName = "io.swagger.client";
|
||||||
protected String apiDocPath = "docs/";
|
protected String apiDocPath = "docs/";
|
||||||
protected String modelDocPath = "docs/";
|
protected String modelDocPath = "docs/";
|
||||||
|
protected CodegenConstants.ENUM_PROPERTY_NAMING_TYPE enumPropertyNaming = CodegenConstants.ENUM_PROPERTY_NAMING_TYPE.camelCase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs an instance of `KotlinClientCodegen`.
|
* Constructs an instance of `KotlinClientCodegen`.
|
||||||
@ -56,17 +57,26 @@ public class KotlinClientCodegen extends DefaultCodegen implements CodegenConfig
|
|||||||
));
|
));
|
||||||
|
|
||||||
// this includes hard reserved words defined by https://github.com/JetBrains/kotlin/blob/master/core/descriptors/src/org/jetbrains/kotlin/renderer/KeywordStringsGenerated.java
|
// this includes hard reserved words defined by https://github.com/JetBrains/kotlin/blob/master/core/descriptors/src/org/jetbrains/kotlin/renderer/KeywordStringsGenerated.java
|
||||||
// as well as select soft (contextual) keywords
|
// as well as keywords from https://kotlinlang.org/docs/reference/keyword-reference.html
|
||||||
reservedWords = new HashSet<String>(Arrays.asList(
|
reservedWords = new HashSet<String>(Arrays.asList(
|
||||||
"abstract",
|
"abstract",
|
||||||
|
"annotation",
|
||||||
"as",
|
"as",
|
||||||
"break",
|
"break",
|
||||||
"case",
|
"case",
|
||||||
"catch",
|
"catch",
|
||||||
"class",
|
"class",
|
||||||
|
"companion",
|
||||||
|
"const",
|
||||||
|
"constructor",
|
||||||
"continue",
|
"continue",
|
||||||
|
"crossinline",
|
||||||
|
"data",
|
||||||
|
"delegate",
|
||||||
"do",
|
"do",
|
||||||
"else",
|
"else",
|
||||||
|
"enum",
|
||||||
|
"external",
|
||||||
"false",
|
"false",
|
||||||
"final",
|
"final",
|
||||||
"finally",
|
"finally",
|
||||||
@ -74,20 +84,33 @@ public class KotlinClientCodegen extends DefaultCodegen implements CodegenConfig
|
|||||||
"fun",
|
"fun",
|
||||||
"if",
|
"if",
|
||||||
"in",
|
"in",
|
||||||
|
"infix",
|
||||||
|
"init",
|
||||||
|
"inline",
|
||||||
|
"inner",
|
||||||
"interface",
|
"interface",
|
||||||
|
"internal",
|
||||||
"is",
|
"is",
|
||||||
"it",
|
"it",
|
||||||
|
"lateinit",
|
||||||
"lazy",
|
"lazy",
|
||||||
|
"noinline",
|
||||||
"null",
|
"null",
|
||||||
"object",
|
"object",
|
||||||
|
"open",
|
||||||
|
"operator",
|
||||||
|
"out",
|
||||||
"override",
|
"override",
|
||||||
"package",
|
"package",
|
||||||
"private",
|
"private",
|
||||||
"protected",
|
"protected",
|
||||||
"public",
|
"public",
|
||||||
|
"reified",
|
||||||
"return",
|
"return",
|
||||||
"sealed",
|
"sealed",
|
||||||
"super",
|
"super",
|
||||||
|
"suspend",
|
||||||
|
"tailrec",
|
||||||
"this",
|
"this",
|
||||||
"throw",
|
"throw",
|
||||||
"true",
|
"true",
|
||||||
@ -96,6 +119,7 @@ public class KotlinClientCodegen extends DefaultCodegen implements CodegenConfig
|
|||||||
"typeof",
|
"typeof",
|
||||||
"val",
|
"val",
|
||||||
"var",
|
"var",
|
||||||
|
"vararg",
|
||||||
"when",
|
"when",
|
||||||
"while"
|
"while"
|
||||||
));
|
));
|
||||||
@ -149,12 +173,17 @@ public class KotlinClientCodegen extends DefaultCodegen implements CodegenConfig
|
|||||||
importMapping.put("LocalDate", "java.time.LocalDate");
|
importMapping.put("LocalDate", "java.time.LocalDate");
|
||||||
importMapping.put("LocalTime", "java.time.LocalTime");
|
importMapping.put("LocalTime", "java.time.LocalTime");
|
||||||
|
|
||||||
|
specialCharReplacements.put(";", "Semicolon");
|
||||||
|
|
||||||
cliOptions.clear();
|
cliOptions.clear();
|
||||||
cliOptions.add(new CliOption(CodegenConstants.SOURCE_FOLDER, CodegenConstants.SOURCE_FOLDER_DESC).defaultValue(sourceFolder));
|
cliOptions.add(new CliOption(CodegenConstants.SOURCE_FOLDER, CodegenConstants.SOURCE_FOLDER_DESC).defaultValue(sourceFolder));
|
||||||
cliOptions.add(new CliOption(CodegenConstants.PACKAGE_NAME, "Client package name (e.g. io.swagger).").defaultValue(this.packageName));
|
cliOptions.add(new CliOption(CodegenConstants.PACKAGE_NAME, "Client package name (e.g. io.swagger).").defaultValue(this.packageName));
|
||||||
cliOptions.add(new CliOption(CodegenConstants.GROUP_ID, "Client package's organization (i.e. maven groupId).").defaultValue(groupId));
|
cliOptions.add(new CliOption(CodegenConstants.GROUP_ID, "Client package's organization (i.e. maven groupId).").defaultValue(groupId));
|
||||||
cliOptions.add(new CliOption(CodegenConstants.ARTIFACT_ID, "Client artifact id (name of generated jar).").defaultValue(artifactId));
|
cliOptions.add(new CliOption(CodegenConstants.ARTIFACT_ID, "Client artifact id (name of generated jar).").defaultValue(artifactId));
|
||||||
cliOptions.add(new CliOption(CodegenConstants.ARTIFACT_VERSION, "Client package version.").defaultValue(artifactVersion));
|
cliOptions.add(new CliOption(CodegenConstants.ARTIFACT_VERSION, "Client package version.").defaultValue(artifactVersion));
|
||||||
|
|
||||||
|
CliOption enumPropertyNamingOpt = new CliOption(CodegenConstants.ENUM_PROPERTY_NAMING, CodegenConstants.ENUM_PROPERTY_NAMING_DESC);
|
||||||
|
cliOptions.add(enumPropertyNamingOpt.defaultValue(enumPropertyNaming.name()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public CodegenType getTag() {
|
public CodegenType getTag() {
|
||||||
@ -193,6 +222,10 @@ public class KotlinClientCodegen extends DefaultCodegen implements CodegenConfig
|
|||||||
public void processOpts() {
|
public void processOpts() {
|
||||||
super.processOpts();
|
super.processOpts();
|
||||||
|
|
||||||
|
if (additionalProperties.containsKey(CodegenConstants.ENUM_PROPERTY_NAMING)) {
|
||||||
|
setEnumPropertyNaming((String) additionalProperties.get(CodegenConstants.ENUM_PROPERTY_NAMING));
|
||||||
|
}
|
||||||
|
|
||||||
if (additionalProperties.containsKey(CodegenConstants.SOURCE_FOLDER)) {
|
if (additionalProperties.containsKey(CodegenConstants.SOURCE_FOLDER)) {
|
||||||
this.setSourceFolder((String) additionalProperties.get(CodegenConstants.SOURCE_FOLDER));
|
this.setSourceFolder((String) additionalProperties.get(CodegenConstants.SOURCE_FOLDER));
|
||||||
} else {
|
} else {
|
||||||
@ -255,6 +288,27 @@ public class KotlinClientCodegen extends DefaultCodegen implements CodegenConfig
|
|||||||
supportingFiles.add(new SupportingFile("infrastructure/Errors.kt.mustache", infrastructureFolder, "Errors.kt"));
|
supportingFiles.add(new SupportingFile("infrastructure/Errors.kt.mustache", infrastructureFolder, "Errors.kt"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the naming convention for Kotlin enum properties
|
||||||
|
*
|
||||||
|
* @param enumPropertyNamingType The string representation of the naming convention, as defined by {@link CodegenConstants.ENUM_PROPERTY_NAMING_TYPE}
|
||||||
|
*/
|
||||||
|
public void setEnumPropertyNaming(final String enumPropertyNamingType) {
|
||||||
|
try {
|
||||||
|
this.enumPropertyNaming = CodegenConstants.ENUM_PROPERTY_NAMING_TYPE.valueOf(enumPropertyNamingType);
|
||||||
|
} catch (IllegalArgumentException ex) {
|
||||||
|
StringBuilder sb = new StringBuilder(enumPropertyNamingType + " is an invalid enum property naming option. Please choose from:");
|
||||||
|
for (CodegenConstants.ENUM_PROPERTY_NAMING_TYPE t : CodegenConstants.ENUM_PROPERTY_NAMING_TYPE.values()) {
|
||||||
|
sb.append("\n ").append(t.name());
|
||||||
|
}
|
||||||
|
throw new RuntimeException(sb.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public CodegenConstants.ENUM_PROPERTY_NAMING_TYPE getEnumPropertyNaming() {
|
||||||
|
return this.enumPropertyNaming;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String escapeUnsafeCharacters(String input) {
|
public String escapeUnsafeCharacters(String input) {
|
||||||
return input.replace("*/", "*_/").replace("/*", "/_*");
|
return input.replace("*/", "*_/").replace("/*", "/_*");
|
||||||
@ -386,4 +440,69 @@ public class KotlinClientCodegen extends DefaultCodegen implements CodegenConfig
|
|||||||
public Map<String, Object> postProcessModels(Map<String, Object> objs) {
|
public Map<String, Object> postProcessModels(Map<String, Object> objs) {
|
||||||
return postProcessModelsEnum(super.postProcessModels(objs));
|
return postProcessModelsEnum(super.postProcessModels(objs));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the sanitized variable name for enum
|
||||||
|
*
|
||||||
|
* @param value enum variable name
|
||||||
|
* @param datatype data type
|
||||||
|
* @return the sanitized variable name for enum
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toEnumVarName(String value, String datatype) {
|
||||||
|
String modified;
|
||||||
|
if (value.length() == 0) {
|
||||||
|
modified = "EMPTY";
|
||||||
|
} else {
|
||||||
|
modified = value;
|
||||||
|
|
||||||
|
for (Map.Entry<String, String> specialCharacters : specialCharReplacements.entrySet()) {
|
||||||
|
// Underscore is the only special character we'll allow
|
||||||
|
if (!specialCharacters.getKey().equals("_")) {
|
||||||
|
modified = modified.replaceAll("\\Q" + specialCharacters.getKey() + "\\E", specialCharacters.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback, replace unknowns with underscore.
|
||||||
|
modified = modified.replaceAll("\\W+", "_");
|
||||||
|
if (modified.matches("\\d.*")) {
|
||||||
|
modified = "_" + modified;
|
||||||
|
}
|
||||||
|
|
||||||
|
// _, __, and ___ are reserved in Kotlin. Treat all names with only underscores consistently, regardless of count.
|
||||||
|
if (modified.matches("^_*$")) {
|
||||||
|
modified = modified.replaceAll("\\Q_\\E", "Underscore");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (getEnumPropertyNaming()) {
|
||||||
|
case original:
|
||||||
|
// NOTE: This is provided as a last-case allowance, but will still result in reserved words being escaped.
|
||||||
|
modified = value;
|
||||||
|
break;
|
||||||
|
case camelCase:
|
||||||
|
// NOTE: Removes hyphens and underscores
|
||||||
|
modified = camelize(modified, true);
|
||||||
|
break;
|
||||||
|
case PascalCase:
|
||||||
|
// NOTE: Removes hyphens and underscores
|
||||||
|
String result = camelize(modified);
|
||||||
|
modified = result.substring(0, 1).toUpperCase() + result.substring(1);
|
||||||
|
break;
|
||||||
|
case snake_case:
|
||||||
|
// NOTE: Removes hyphens
|
||||||
|
modified = underscore(modified);
|
||||||
|
break;
|
||||||
|
case UPPERCASE:
|
||||||
|
modified = modified.toUpperCase();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reservedWords.contains(modified)) {
|
||||||
|
// TODO: Allow enum escaping as an option (e.g. backticks vs append/prepend underscore vs match model property escaping).
|
||||||
|
return String.format("`%s`", modified);
|
||||||
|
}
|
||||||
|
|
||||||
|
return modified;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,11 +12,14 @@ data class {{classname}} (
|
|||||||
{{/-last}}{{/optionalVars}}
|
{{/-last}}{{/optionalVars}}
|
||||||
) {
|
) {
|
||||||
{{#hasEnums}}{{#vars}}{{#isEnum}}
|
{{#hasEnums}}{{#vars}}{{#isEnum}}
|
||||||
enum class {{nameInCamelCase}}(val value: {{datatype}}) {
|
/**
|
||||||
{{#_enum}}
|
* {{{description}}}
|
||||||
{{{this}}}({{#isString}}"{{/isString}}{{{this}}}{{#isString}}"{{/isString}}){{^-last}},{{/-last}}{{#-last}};{{/-last}}
|
* Values: {{#allowableValues}}{{#enumVars}}{{&name}}{{^-last}},{{/-last}}{{/enumVars}}{{/allowableValues}}
|
||||||
{{/_enum}}
|
*/
|
||||||
|
enum class {{nameInCamelCase}}(val value: {{dataType}}){
|
||||||
|
{{#allowableValues}}{{#enumVars}}
|
||||||
|
{{&name}}({{{value}}}){{^-last}},{{/-last}}{{#-last}};{{/-last}}
|
||||||
|
{{/enumVars}}{{/allowableValues}}
|
||||||
}
|
}
|
||||||
|
|
||||||
{{/isEnum}}{{/vars}}{{/hasEnums}}
|
{{/isEnum}}{{/vars}}{{/hasEnums}}
|
||||||
}
|
}
|
@ -1,9 +1,9 @@
|
|||||||
/**
|
/**
|
||||||
* {{{description}}}
|
* {{{description}}}
|
||||||
* Values: {{#allowableValues}}{{#enumVars}}{{name}}{{^-last}},{{/-last}}{{/enumVars}}{{/allowableValues}}
|
* Values: {{#allowableValues}}{{#enumVars}}{{&name}}{{^-last}},{{/-last}}{{/enumVars}}{{/allowableValues}}
|
||||||
*/
|
*/
|
||||||
enum class {{classname}}(val value: {{dataType}}){
|
enum class {{classname}}(val value: {{dataType}}){
|
||||||
{{#allowableValues}}{{#enumVars}}
|
{{#allowableValues}}{{#enumVars}}
|
||||||
{{name}}({{{value}}}){{^-last}},{{/-last}}{{#-last}};{{/-last}}
|
{{&name}}({{{value}}}){{^-last}},{{/-last}}{{#-last}};{{/-last}}
|
||||||
{{/enumVars}}{{/allowableValues}}
|
{{/enumVars}}{{/allowableValues}}
|
||||||
}
|
}
|
@ -36,6 +36,8 @@ public class KotlinClientCodegenOptionsTest extends AbstractOptionsTest {
|
|||||||
times = 1;
|
times = 1;
|
||||||
codegen.setSourceFolder(KotlinClientCodegenOptionsProvider.SOURCE_FOLDER);
|
codegen.setSourceFolder(KotlinClientCodegenOptionsProvider.SOURCE_FOLDER);
|
||||||
times = 1;
|
times = 1;
|
||||||
|
codegen.setEnumPropertyNaming(KotlinClientCodegenOptionsProvider.ENUM_PROPERTY_NAMING);
|
||||||
|
times = 1;
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ public class KotlinClientCodegenOptionsProvider implements OptionsProvider {
|
|||||||
public static final String ARTIFACT_ID = "swagger-kotlin-test";
|
public static final String ARTIFACT_ID = "swagger-kotlin-test";
|
||||||
public static final String GROUP_ID = "io.swagger.tests";
|
public static final String GROUP_ID = "io.swagger.tests";
|
||||||
public static final String SOURCE_FOLDER = "./generated/kotlin";
|
public static final String SOURCE_FOLDER = "./generated/kotlin";
|
||||||
|
public static final String ENUM_PROPERTY_NAMING = "camelCase";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getLanguage() {
|
public String getLanguage() {
|
||||||
@ -27,6 +28,7 @@ public class KotlinClientCodegenOptionsProvider implements OptionsProvider {
|
|||||||
.put(CodegenConstants.ARTIFACT_ID, ARTIFACT_ID)
|
.put(CodegenConstants.ARTIFACT_ID, ARTIFACT_ID)
|
||||||
.put(CodegenConstants.GROUP_ID, GROUP_ID)
|
.put(CodegenConstants.GROUP_ID, GROUP_ID)
|
||||||
.put(CodegenConstants.SOURCE_FOLDER, SOURCE_FOLDER)
|
.put(CodegenConstants.SOURCE_FOLDER, SOURCE_FOLDER)
|
||||||
|
.put(CodegenConstants.ENUM_PROPERTY_NAMING, ENUM_PROPERTY_NAMING)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user