[csharp] Clean up ref/inner enum struture

Enums defines as ref models have a different object structure
(CodegenModel) than those defined as inner enums (CodegenProperty). To
make these look as similar as possible, we walk all ref model enums and
reassign enumVars with the same properties inherited from the top level
CodegenProperty so CodegenModel enums can use the same templates as
inner enums.
This commit is contained in:
James Schubert
2017-11-04 17:22:23 -04:00
parent 72b9ab6cab
commit 1058728777
9 changed files with 146 additions and 94 deletions

View File

@@ -1955,6 +1955,8 @@ public class DefaultCodegen {
if (property.defaultValue != null) {
property.defaultValue = property.defaultValue.replace(baseItem.baseType, toEnumName(baseItem));
}
updateCodegenPropertyEnum(property);
}
}

View File

@@ -1,5 +1,7 @@
package io.swagger.codegen.languages;
import com.samskivert.mustache.Mustache;
import com.samskivert.mustache.Template;
import io.swagger.codegen.*;
import io.swagger.codegen.utils.ModelUtils;
import io.swagger.models.properties.*;
@@ -8,6 +10,8 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.util.*;
public abstract class AbstractCSharpCodegen extends DefaultCodegen implements CodegenConfig {
@@ -343,6 +347,7 @@ public abstract class AbstractCSharpCodegen extends DefaultCodegen implements Co
* those vars referencing RefModel'd enums to work the same as inlined enums rather than as objects.
* @param models
*/
@SuppressWarnings({ "unchecked" })
private void postProcessEnumRefs(final Map<String, Object> models) {
Map<String, CodegenModel> enumRefs = new HashMap<String, CodegenModel>();
for (Map.Entry<String, Object> entry : models.entrySet()) {
@@ -354,6 +359,7 @@ public abstract class AbstractCSharpCodegen extends DefaultCodegen implements Co
for (Map.Entry<String, Object> entry : models.entrySet()) {
String swaggerName = entry.getKey();
CodegenModel model = ModelUtils.getModelByName(swaggerName, models);
if (model != null) {
for (CodegenProperty var : model.allVars) {
@@ -363,11 +369,57 @@ public abstract class AbstractCSharpCodegen extends DefaultCodegen implements Co
// while enums in many other languages are true objects.
CodegenModel refModel = enumRefs.get(var.datatype);
var.allowableValues = refModel.allowableValues;
var.isEnum = true;
updateCodegenPropertyEnum(var);
// We do these after updateCodegenPropertyEnum to avoid generalities that don't mesh with C#.
// We do this after updateCodegenPropertyEnum to avoid generalities that don't mesh with C#.
var.isPrimitiveType = true;
var.isEnum = true;
}
}
// We're looping all models here.
if (model.isEnum) {
// We now need to make allowableValues.enumVars look like the context of CodegenProperty
Boolean isString = false;
Boolean isInteger = false;
Boolean isLong = false;
Boolean isByte = false;
if (model.dataType.startsWith("byte")) {
// C# Actually supports byte and short enums, swagger spec only supports byte.
isByte = true;
model.vendorExtensions.put("x-enum-byte", true);
} else if (model.dataType.startsWith("int32")) {
isInteger = true;
model.vendorExtensions.put("x-enum-integer", true);
} else if (model.dataType.startsWith("int64")) {
isLong = true;
model.vendorExtensions.put("x-enum-long", true);
} else {
// C# doesn't support non-integral enums, so we need to treat everything else as strings (e.g. to not lose precision or data integrity)
isString = true;
model.vendorExtensions.put("x-enum-string", true);
}
// Since we iterate enumVars for modelnnerEnum and enumClass templates, and CodegenModel is missing some of CodegenProperty's properties,
// we can take advantage of Mustache's contextual lookup to add the same "properties" to the model's enumVars scope rather than CodegenProperty's scope.
List<Map<String, String>> enumVars = (ArrayList<Map<String, String>>)model.allowableValues.get("enumVars");
List<Map<String, Object>> newEnumVars = new ArrayList<Map<String, Object>>();
for (Map<String, String> enumVar : enumVars) {
Map<String, Object> mixedVars = new HashMap<String, Object>();
mixedVars.putAll(enumVar);
mixedVars.put("isString", isString);
mixedVars.put("isLong", isLong);
mixedVars.put("isInteger", isInteger);
mixedVars.put("isByte", isByte);
newEnumVars.add(mixedVars);
}
if (!newEnumVars.isEmpty()) {
model.allowableValues.put("enumVars", newEnumVars);
}
}
} else {
@@ -376,6 +428,42 @@ public abstract class AbstractCSharpCodegen extends DefaultCodegen implements Co
}
}
/**
* Update codegen property's enum by adding "enumVars" (with name and value)
*
* @param var list of CodegenProperty
*/
@Override
public void updateCodegenPropertyEnum(CodegenProperty var) {
if (var.vendorExtensions == null) {
var.vendorExtensions = new HashMap<>();
}
super.updateCodegenPropertyEnum(var);
// Because C# uses nullable primitives for datatype, and datatype is used in DefaultCodegen for determining enum-ness, guard against weirdness here.
if (var.isEnum) {
if ("byte".equals(var.dataFormat)) {// C# Actually supports byte and short enums.
var.vendorExtensions.put("x-enum-byte", true);
var.isString = false;
var.isLong = false;
var.isInteger = false;
} else if ("int32".equals(var.dataFormat)) {
var.isInteger = true;
var.isString = false;
var.isLong = false;
} else if ("int64".equals(var.dataFormat)) {
var.isLong = true;
var.isString = false;
var.isInteger = false;
} else {// C# doesn't support non-integral enums, so we need to treat everything else as strings (e.g. to not lose precision or data integrity)
var.isString = true;
var.isInteger = false;
var.isLong = false;
}
}
}
@Override
public Map<String, Object> postProcessOperations(Map<String, Object> objs) {
super.postProcessOperations(objs);
@@ -746,6 +834,19 @@ public abstract class AbstractCSharpCodegen extends DefaultCodegen implements Co
this.interfacePrefix = interfacePrefix;
}
@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.
// Per: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/enum
// The approved types for an enum are byte, sbyte, short, ushort, int, uint, long, or ulong.
// but we're not supporting unsigned integral types or shorts.
if(datatype.startsWith("int") || datatype.startsWith("long") || datatype.startsWith("byte")) {
return value;
}
return escapeText(value);
}
@Override
public String toEnumVarName(String name, String datatype) {
if (name.length() == 0) {
@@ -776,32 +877,6 @@ public abstract class AbstractCSharpCodegen extends DefaultCodegen implements Co
return sanitizeName(camelize(property.name)) + "Enum";
}
/*
@Override
public String toEnumName(CodegenProperty property) {
String enumName = sanitizeName(property.name);
if (!StringUtils.isEmpty(modelNamePrefix)) {
enumName = modelNamePrefix + "_" + enumName;
}
if (!StringUtils.isEmpty(modelNameSuffix)) {
enumName = enumName + "_" + modelNameSuffix;
}
// model name cannot use reserved keyword, e.g. return
if (isReservedWord(enumName)) {
LOGGER.warn(enumName + " (reserved word) cannot be used as model name. Renamed to " + camelize("model_" + enumName));
enumName = "model_" + enumName; // e.g. return => ModelReturn (after camelize)
}
if (enumName.matches("\\d.*")) { // starts with number
return "_" + enumName;
} else {
return enumName;
}
}
*/
public String testPackageName() {
return this.packageName + ".Test";
}
@@ -816,5 +891,4 @@ public abstract class AbstractCSharpCodegen extends DefaultCodegen implements Co
public String escapeUnsafeCharacters(String input) {
return input.replace("*/", "*_/").replace("/*", "/_*").replace("--", "- -");
}
}

View File

@@ -611,19 +611,6 @@ public class CSharpClientCodegen extends AbstractCSharpCodegen {
return codegenModel;
}
@Override
public String toEnumValue(String value, String datatype) {
if ("int?".equalsIgnoreCase(datatype) || "long?".equalsIgnoreCase(datatype) ||
"double?".equalsIgnoreCase(datatype) || "float?".equalsIgnoreCase(datatype)) {
return value;
} else if ("float?".equalsIgnoreCase(datatype)) {
// for float in C#, append "f". e.g. 3.14 => 3.14f
return value + "f";
} else {
return "\"" + escapeText(value) + "\"";
}
}
@Override
public String toEnumVarName(String value, String datatype) {
if (value.length() == 0) {
@@ -636,8 +623,8 @@ public class CSharpClientCodegen extends AbstractCSharpCodegen {
}
// number
if ("int?".equals(datatype) || "long?".equals(datatype) ||
"double?".equals(datatype) || "float?".equals(datatype)) {
if(datatype.startsWith("int") || datatype.startsWith("long") ||
datatype.startsWith("double") || datatype.startsWith("float")) {
String varName = "NUMBER_" + value;
varName = varName.replaceAll("-", "MINUS_");
varName = varName.replaceAll("\\+", "PLUS_");

View File

@@ -1,17 +0,0 @@
/// <summary>
/// {{^description}}Gets or Sets {{{name}}}{{/description}}{{#description}}{{description}}{{/description}}
/// </summary>
{{#description}}
/// <value>{{description}}</value>
{{/description}}
[JsonConverter(typeof(StringEnumConverter))]
{{>visibility}} enum {{#datatypeWithEnum}}{{.}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}
{
{{#allowableValues}}{{#enumVars}}
/// <summary>
/// Enum {{name}} for {{{value}}}
/// </summary>
[EnumMember(Value = {{#isLong}}"{{/isLong}}{{#isInteger}}"{{/isInteger}}{{#isFloat}}"{{/isFloat}}{{#isDouble}}"{{/isDouble}}{{{value}}}{{#isLong}}"{{/isLong}}{{#isInteger}}"{{/isInteger}}{{#isDouble}}"{{/isDouble}}{{#isFloat}}"{{/isFloat}})]
{{name}}{{#isLong}} = {{{value}}}{{/isLong}}{{#isInteger}} = {{{value}}}{{/isInteger}}{{^-last}},
{{/-last}}{{/enumVars}}{{/allowableValues}}
}

View File

@@ -4,14 +4,16 @@
{{#description}}
/// <value>{{description}}</value>
{{/description}}
{{#allowableValues}}{{#enumVars}}{{#-first}}{{#isString}}
[JsonConverter(typeof(StringEnumConverter))]
{{>visibility}} enum {{#datatypeWithEnum}}{{.}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}
{{/isString}}{{/-first}}{{/enumVars}}{{/allowableValues}}
{{>visibility}} enum {{#datatypeWithEnum}}{{.}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}{{#vendorExtensions.x-enum-byte}}: byte{{/vendorExtensions.x-enum-byte}}
{
{{#allowableValues}}{{#enumVars}}
/// <summary>
/// Enum {{name}} for {{{value}}}
/// Enum {{name}} for value: {{{value}}}
/// </summary>
[EnumMember(Value = {{#isInteger}}"{{/isInteger}}{{#isDouble}}"{{/isDouble}}{{{value}}}{{#isInteger}}"{{/isInteger}}{{#isDouble}}"{{/isDouble}})]
{{name}}{{#isInteger}} = {{{value}}}{{/isInteger}}{{^-last}},
{{#isString}}[EnumMember(Value = "{{{value}}}")]{{/isString}}
{{name}}{{^isString}} = {{{value}}}{{/isString}}{{^-last}},
{{/-last}}{{/enumVars}}{{/allowableValues}}
}
}{{! NOTE: This model's enumVars is modified to look like CodegenProperty}}

View File

@@ -1,19 +1,21 @@
{{^isContainer}}
/// <summary>
/// {{^description}}Gets or Sets {{{name}}}{{/description}}{{#description}}{{description}}{{/description}}
/// {{^description}}Defines {{{name}}}{{/description}}{{#description}}{{description}}{{/description}}
/// </summary>
{{#description}}
/// <value>{{description}}</value>
{{/description}}
{{#isString}}
[JsonConverter(typeof(StringEnumConverter))]
{{>visibility}} enum {{#datatypeWithEnum}}{{&.}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}
{{/isString}}
{{>visibility}} enum {{#datatypeWithEnum}}{{.}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}{{#vendorExtensions.x-enum-byte}}: byte{{/vendorExtensions.x-enum-byte}}
{
{{#allowableValues}}{{#enumVars}}
/// <summary>
/// Enum {{name}} for {{{value}}}
/// Enum {{name}} for value: {{{value}}}
/// </summary>
[EnumMember(Value = {{#isLong}}"{{/isLong}}{{#isInteger}}"{{/isInteger}}{{#isFloat}}"{{/isFloat}}{{#isDouble}}"{{/isDouble}}{{{value}}}{{#isLong}}"{{/isLong}}{{#isInteger}}"{{/isInteger}}{{#isDouble}}"{{/isDouble}}{{#isFloat}}"{{/isFloat}})]
{{name}}{{#isLong}} = {{{value}}}{{/isLong}}{{#isInteger}} = {{{value}}}{{/isInteger}}{{^-last}},
{{#isString}}[EnumMember(Value = "{{{value}}}")]{{/isString}}
{{name}}{{^isString}} = {{{value}}}{{/isString}}{{^-last}},
{{/-last}}{{/enumVars}}{{/allowableValues}}
}
{{/isContainer}}

View File

@@ -31,50 +31,50 @@ namespace IO.Swagger.Model
public partial class MyClassWithOptionalInlineEnum : IEquatable<MyClassWithOptionalInlineEnum>, IValidatableObject
{
/// <summary>
/// Gets or Sets Days
/// Defines Days
/// </summary>
[JsonConverter(typeof(StringEnumConverter))]
public enum DaysEnum
{
/// <summary>
/// Enum Sun for "sun"
/// Enum Sun for value: sun
/// </summary>
[EnumMember(Value = "sun")]
Sun,
/// <summary>
/// Enum Mon for "mon"
/// Enum Mon for value: mon
/// </summary>
[EnumMember(Value = "mon")]
Mon,
/// <summary>
/// Enum Tue for "tue"
/// Enum Tue for value: tue
/// </summary>
[EnumMember(Value = "tue")]
Tue,
/// <summary>
/// Enum Wed for "wed"
/// Enum Wed for value: wed
/// </summary>
[EnumMember(Value = "wed")]
Wed,
/// <summary>
/// Enum Thu for "thu"
/// Enum Thu for value: thu
/// </summary>
[EnumMember(Value = "thu")]
Thu,
/// <summary>
/// Enum Fri for "fri"
/// Enum Fri for value: fri
/// </summary>
[EnumMember(Value = "fri")]
Fri,
/// <summary>
/// Enum Sat for "sat"
/// Enum Sat for value: sat
/// </summary>
[EnumMember(Value = "sat")]
Sat

View File

@@ -31,50 +31,50 @@ namespace IO.Swagger.Model
public partial class MyClassWithRequiredInlineEnum : IEquatable<MyClassWithRequiredInlineEnum>, IValidatableObject
{
/// <summary>
/// Gets or Sets Days
/// Defines Days
/// </summary>
[JsonConverter(typeof(StringEnumConverter))]
public enum DaysEnum
{
/// <summary>
/// Enum Sun for "sun"
/// Enum Sun for value: sun
/// </summary>
[EnumMember(Value = "sun")]
Sun,
/// <summary>
/// Enum Mon for "mon"
/// Enum Mon for value: mon
/// </summary>
[EnumMember(Value = "mon")]
Mon,
/// <summary>
/// Enum Tue for "tue"
/// Enum Tue for value: tue
/// </summary>
[EnumMember(Value = "tue")]
Tue,
/// <summary>
/// Enum Wed for "wed"
/// Enum Wed for value: wed
/// </summary>
[EnumMember(Value = "wed")]
Wed,
/// <summary>
/// Enum Thu for "thu"
/// Enum Thu for value: thu
/// </summary>
[EnumMember(Value = "thu")]
Thu,
/// <summary>
/// Enum Fri for "fri"
/// Enum Fri for value: fri
/// </summary>
[EnumMember(Value = "fri")]
Fri,
/// <summary>
/// Enum Sat for "sat"
/// Enum Sat for value: sat
/// </summary>
[EnumMember(Value = "sat")]
Sat

View File

@@ -27,48 +27,50 @@ namespace IO.Swagger.Model
/// <summary>
/// Defines WeekDays
/// </summary>
[JsonConverter(typeof(StringEnumConverter))]
public enum WeekDays
{
/// <summary>
/// Enum Sun for "sun"
/// Enum Sun for value: sun
/// </summary>
[EnumMember(Value = "sun")]
Sun,
/// <summary>
/// Enum Mon for "mon"
/// Enum Mon for value: mon
/// </summary>
[EnumMember(Value = "mon")]
Mon,
/// <summary>
/// Enum Tue for "tue"
/// Enum Tue for value: tue
/// </summary>
[EnumMember(Value = "tue")]
Tue,
/// <summary>
/// Enum Wed for "wed"
/// Enum Wed for value: wed
/// </summary>
[EnumMember(Value = "wed")]
Wed,
/// <summary>
/// Enum Thu for "thu"
/// Enum Thu for value: thu
/// </summary>
[EnumMember(Value = "thu")]
Thu,
/// <summary>
/// Enum Fri for "fri"
/// Enum Fri for value: fri
/// </summary>
[EnumMember(Value = "fri")]
Fri,
/// <summary>
/// Enum Sat for "sat"
/// Enum Sat for value: sat
/// </summary>
[EnumMember(Value = "sat")]
Sat