Skip to content

Commit

Permalink
[csharp] Introducing the enumPropertyNaming option to the C# Genera…
Browse files Browse the repository at this point in the history
…tor (#16981)

* [csharp] Support the enumPropertyNaming option

* Add a doc comment to adjustNamingStyle
  • Loading branch information
fujieda committed Nov 7, 2023
1 parent 65ccf04 commit 8e98671
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@

import static org.openapitools.codegen.utils.CamelizeOption.LOWERCASE_FIRST_LETTER;
import static org.openapitools.codegen.utils.StringUtils.camelize;
import static org.openapitools.codegen.utils.StringUtils.underscore;

public abstract class AbstractCSharpCodegen extends DefaultCodegen implements CodegenConfig {

Expand Down Expand Up @@ -84,6 +85,7 @@ public abstract class AbstractCSharpCodegen extends DefaultCodegen implements Co

protected String sourceFolder = "src";
protected String invalidNamePrefix = "var";
protected CodegenConstants.ENUM_PROPERTY_NAMING_TYPE enumPropertyNaming = CodegenConstants.ENUM_PROPERTY_NAMING_TYPE.PascalCase;

// TODO: Add option for test folder output location. Nice to allow e.g. ./test instead of ./src.
// This would require updating relative paths (e.g. path to main project file in test project file)
Expand Down Expand Up @@ -391,6 +393,10 @@ public void processOpts() {
setEnumValueSuffix(additionalProperties.get(CodegenConstants.ENUM_VALUE_SUFFIX).toString());
}

if (additionalProperties.containsKey(CodegenConstants.ENUM_PROPERTY_NAMING)) {
setEnumPropertyNaming((String) additionalProperties.get(CodegenConstants.ENUM_PROPERTY_NAMING));
}

// This either updates additionalProperties with the above fixes, or sets the default if the option was not specified.
additionalProperties.put(CodegenConstants.INTERFACE_PREFIX, interfacePrefix);

Expand Down Expand Up @@ -1598,6 +1604,22 @@ public void setSupportNullable(final boolean supportNullable) {
this.supportNullable = supportNullable;
}

public CodegenConstants.ENUM_PROPERTY_NAMING_TYPE getEnumPropertyNaming() {
return this.enumPropertyNaming;
}

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());
}
}

@Override
public String toEnumValue(String value, String datatype) {
// C# only supports enums as literals for int, int?, long, long?, byte, and byte?. All else must be treated as strings.
Expand All @@ -1622,20 +1644,20 @@ public String toEnumValue(String value, String datatype) {
@Override
public String toEnumVarName(String name, String datatype) {
if (name.length() == 0) {
return "Empty";
return adjustNamingStyle("Empty");
}

// for symbol, e.g. $, #
if (getSymbolName(name) != null) {
return camelize(getSymbolName(name));
return adjustNamingStyle(getSymbolName(name));
}

String enumName = sanitizeName(name);

enumName = enumName.replaceFirst("^_", "");
enumName = enumName.replaceFirst("_$", "");

enumName = camelize(enumName) + this.enumValueSuffix;
enumName = adjustNamingStyle(enumName) + this.enumValueSuffix;

if (enumName.matches("\\d.*")) { // starts with number
return "_" + enumName;
Expand All @@ -1644,6 +1666,33 @@ public String toEnumVarName(String name, String datatype) {
}
}

/**
* Adjust the naming style of a given name based on the enumPropertyNaming option.
*
* @param name The original name
* @return The adjusted name
*/
private String adjustNamingStyle(String name)
{
switch (getEnumPropertyNaming()) {
case original:
return name;
case camelCase:
// NOTE: Removes hyphens and underscores
return camelize(name, LOWERCASE_FIRST_LETTER);
case PascalCase:
// NOTE: Removes hyphens and underscores
return camelize(name);
case snake_case:
// NOTE: Removes hyphens
return underscore(name);
case UPPERCASE:
return underscore(name).toUpperCase(Locale.ROOT);
default:
return name;
}
}

@Override
public String toEnumName(CodegenProperty property) {
return sanitizeName(camelize(property.name)) + this.enumNameSuffix;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,15 @@
import org.openapitools.codegen.languages.CSharpClientCodegen;
import org.testng.Assert;
import org.testng.annotations.Test;
import org.testng.annotations.DataProvider;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import org.openapitools.codegen.CodegenConstants.ENUM_PROPERTY_NAMING_TYPE;
import static org.openapitools.codegen.CodegenConstants.ENUM_PROPERTY_NAMING_TYPE.*;

public class CSharpModelEnumTest {
// TODO there's no parent/child method in ComposeSchema so we will need to revise the code
// before we can re-enable the test case below
Expand Down Expand Up @@ -148,4 +152,54 @@ public void useEmptyEnumSuffixes() {
Assert.assertEquals(codegen.toEnumVarName("Aaaa", ""), "Aaaa");
}

@Test(description = "support enum naming style options", dataProvider = "enumVarName")
public void testToEnumVarName(String name, String expected, ENUM_PROPERTY_NAMING_TYPE naming) throws Exception {
final AspNetServerCodegen codegen = new AspNetServerCodegen();
codegen.setEnumPropertyNaming(naming.name());
codegen.setEnumValueSuffix("");

Assert.assertEquals(codegen.toEnumVarName(name, "string"), expected);
}

@DataProvider(name = "enumVarName")
public Object[][] provideTestData()
{
return new Object[][] {
{ "FooBar", "fooBar", camelCase },
{ "fooBar", "fooBar", camelCase },
{ "foo-bar", "fooBar", camelCase },
{ "foo_bar", "fooBar", camelCase },
{ "foo bar", "fooBar", camelCase },
{ "FOO-BAR", "fOOBAR", camelCase }, // camelize doesn't support uppercase
{ "FOO_BAR", "fOOBAR", camelCase }, // ditto
{ "FooBar", "FooBar", PascalCase },
{ "fooBar", "FooBar", PascalCase },
{ "foo-bar", "FooBar", PascalCase },
{ "foo_bar", "FooBar", PascalCase },
{ "foo bar", "FooBar", PascalCase },
{ "FOO-BAR", "FOOBAR", PascalCase }, // ditto
{ "FOO_BAR", "FOOBAR", PascalCase }, // ditto
{ "FooBar", "foo_bar", snake_case },
{ "fooBar", "foo_bar", snake_case },
{ "foo-bar", "foo_bar", snake_case },
{ "foo_bar", "foo_bar", snake_case },
{ "foo bar", "foo_bar", snake_case },
{ "FOO-BAR", "foo_bar", snake_case },
{ "FOO_BAR", "foo_bar", snake_case },
{ "FooBar", "FOO_BAR", UPPERCASE },
{ "fooBar", "FOO_BAR", UPPERCASE },
{ "foo-bar", "FOO_BAR", UPPERCASE },
{ "foo_bar", "FOO_BAR", UPPERCASE },
{ "foo bar", "FOO_BAR", UPPERCASE },
{ "FOO-BAR", "FOO_BAR", UPPERCASE },
{ "FOO_BAR", "FOO_BAR", UPPERCASE },
{ "FooBar", "FooBar", original },
{ "fooBar", "fooBar", original },
{ "foo-bar", "foo_bar", original },
{ "foo_bar", "foo_bar", original },
{ "foo bar", "foo_bar", original },
{ "FOO-BAR", "FOO_BAR", original },
{ "FOO_BAR", "FOO_BAR", original },
};
}
}

0 comments on commit 8e98671

Please sign in to comment.