Kotlin sanitize model names 6864 (#6874)

* [kotlin] better sanitize model names

* [kotlin] Regenerate samples
This commit is contained in:
Jim Schubert
2017-11-04 11:23:08 -04:00
committed by wing328
parent 659e64206a
commit 7b269eaf4a
8 changed files with 116 additions and 30 deletions
@@ -343,7 +343,8 @@ public class KotlinClientCodegen extends DefaultCodegen implements CodegenConfig
@Override
public String escapeReservedWord(String name) {
return "_" + name;
// TODO: Allow enum escaping as an option (e.g. backticks vs append/prepend underscore vs match model property escaping).
return String.format("`%s`", name);
}
/**
@@ -354,12 +355,25 @@ public class KotlinClientCodegen extends DefaultCodegen implements CodegenConfig
* @return capitalized model name
*/
@Override
public String toModelName(String name) {
if(!name.startsWith("kotlin.") && !name.startsWith("java.")) {
return initialCaps(modelNamePrefix + name + modelNameSuffix);
} else {
public String toModelName(final String name) {
// Allow for explicitly configured kotlin.* and java.* types
if (name.startsWith("kotlin.") || name.startsWith("java.")) {
return name;
}
// If importMapping contains name, assume this is a legitimate model name.
if (importMapping.containsKey(name)) {
return importMapping.get(name);
}
String modifiedName = name.replaceAll("\\.", "");
modifiedName = sanitizeKotlinSpecificNames(modifiedName);
if (reservedWords.contains(modifiedName)) {
modifiedName = escapeReservedWord(modifiedName);
}
return titleCase(modifiedName);
}
/**
@@ -455,24 +469,7 @@ public class KotlinClientCodegen extends DefaultCodegen implements CodegenConfig
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");
}
modified = sanitizeKotlinSpecificNames(modified);
}
switch (getEnumPropertyNaming()) {
@@ -487,7 +484,7 @@ public class KotlinClientCodegen extends DefaultCodegen implements CodegenConfig
case PascalCase:
// NOTE: Removes hyphens and underscores
String result = camelize(modified);
modified = result.substring(0, 1).toUpperCase() + result.substring(1);
modified = titleCase(result);
break;
case snake_case:
// NOTE: Removes hyphens
@@ -499,10 +496,42 @@ public class KotlinClientCodegen extends DefaultCodegen implements CodegenConfig
}
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 escapeReservedWord(modified);
}
return modified;
}
private String titleCase(final String input) {
return input.substring(0, 1).toUpperCase() + input.substring(1);
}
/**
* Sanitize against Kotlin specific naming conventions, which may differ from those required by {@link DefaultCodegen#sanitizeName}.
*
* @param name string to be sanitize
* @return sanitized string
*/
private String sanitizeKotlinSpecificNames(final String name) {
String word = name;
for (Map.Entry<String, String> specialCharacters : specialCharReplacements.entrySet()) {
// Underscore is the only special character we'll allow
if (!specialCharacters.getKey().equals("_")) {
word = word.replaceAll("\\Q" + specialCharacters.getKey() + "\\E", specialCharacters.getValue());
}
}
// Fallback, replace unknowns with underscore.
word = word.replaceAll("\\W+", "_");
if (word.matches("\\d.*")) {
word = "_" + word;
}
// _, __, and ___ are reserved in Kotlin. Treat all names with only underscores consistently, regardless of count.
if (word.matches("^_*$")) {
word = word.replaceAll("\\Q_\\E", "Underscore");
}
return word;
}
}
@@ -6,6 +6,7 @@ import io.swagger.models.*;
import io.swagger.models.properties.*;
import org.testng.Assert;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
@SuppressWarnings("static-method")
@@ -153,5 +154,41 @@ public class KotlinClientCodegenModelTest {
Assert.assertFalse(property1.required);
Assert.assertTrue(property1.isNotContainer);
}
@DataProvider(name = "modelNames")
public static Object[][] modelNames(){
return new Object[][] {
{ "TestNs.TestClass" , new ModelNameTest("TestNs.TestClass", "TestNsTestClass") },
{ "$", new ModelNameTest("$", "Dollar") },
{ "for", new ModelNameTest("`for`","`for`")},
{ "One<Two", new ModelNameTest("One<Two", "OneLess_ThanTwo")},
{ "this is a test", new ModelNameTest("this is a test", "This_is_a_test")}
};
}
@Test(dataProvider = "modelNames", description = "sanitize model names")
public void sanitizeModelNames(final String name, final ModelNameTest testCase) {
final Model model = getComplexModel();
final DefaultCodegen codegen = new KotlinClientCodegen();
final CodegenModel cm = codegen.fromModel(name, model);
Assert.assertEquals(cm.name, testCase.expectedName);
Assert.assertEquals(cm.classname, testCase.expectedClassName);
}
private static class ModelNameTest {
private String expectedName;
private String expectedClassName;
private ModelNameTest(String nameAndClass) {
this.expectedName = nameAndClass;
this.expectedClassName = nameAndClass;
}
private ModelNameTest(String expectedName, String expectedClassName) {
this.expectedName = expectedName;
this.expectedClassName = expectedClassName;
}
}
}
@@ -25,3 +25,4 @@ data class ApiResponse (
) {
}
@@ -23,3 +23,4 @@ data class Category (
) {
}
@@ -31,11 +31,19 @@ data class Order (
val complete: kotlin.Boolean? = null
) {
enum class Status(val value: kotlin.String) {
/**
* Order Status
* Values: placed,approved,delivered
*/
enum class Status(val value: kotlin.Any){
placed("placed"),
approved("approved"),
delivered("delivered");
}
}
@@ -33,11 +33,19 @@ data class Pet (
val status: Pet.Status? = null
) {
enum class Status(val value: kotlin.String) {
/**
* pet status in the store
* Values: available,pending,sold
*/
enum class Status(val value: kotlin.Any){
available("available"),
pending("pending"),
sold("sold");
}
}
@@ -23,3 +23,4 @@ data class Tag (
) {
}
@@ -36,3 +36,4 @@ data class User (
) {
}