From 1058728777b64acc11dc2e109758e2c3a2bf0592 Mon Sep 17 00:00:00 2001 From: James Schubert Date: Sat, 4 Nov 2017 17:22:23 -0400 Subject: [PATCH] [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. --- .../io/swagger/codegen/DefaultCodegen.java | 2 + .../languages/AbstractCSharpCodegen.java | 132 ++++++++++++++---- .../languages/CSharpClientCodegen.java | 17 +-- .../main/resources/csharp/enumClass.mustache | 17 --- .../main/resources/csharp/modelEnum.mustache | 12 +- .../resources/csharp/modelInnerEnum.mustache | 12 +- .../Model/MyClassWithOptionalInlineEnum.cs | 16 +-- .../Model/MyClassWithRequiredInlineEnum.cs | 16 +-- .../src/IO.Swagger/Model/WeekDays.cs | 16 ++- 9 files changed, 146 insertions(+), 94 deletions(-) delete mode 100644 modules/swagger-codegen/src/main/resources/csharp/enumClass.mustache diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/DefaultCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/DefaultCodegen.java index 2e8b0ebab5b1..c80f2373bffe 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/DefaultCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/DefaultCodegen.java @@ -1955,6 +1955,8 @@ public class DefaultCodegen { if (property.defaultValue != null) { property.defaultValue = property.defaultValue.replace(baseItem.baseType, toEnumName(baseItem)); } + + updateCodegenPropertyEnum(property); } } diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AbstractCSharpCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AbstractCSharpCodegen.java index 243c75d8bd10..a97545615e35 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AbstractCSharpCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AbstractCSharpCodegen.java @@ -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 models) { Map enumRefs = new HashMap(); for (Map.Entry entry : models.entrySet()) { @@ -354,6 +359,7 @@ public abstract class AbstractCSharpCodegen extends DefaultCodegen implements Co for (Map.Entry 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> enumVars = (ArrayList>)model.allowableValues.get("enumVars"); + List> newEnumVars = new ArrayList>(); + for (Map enumVar : enumVars) { + Map mixedVars = new HashMap(); + 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 postProcessOperations(Map 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("--", "- -"); } - } diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/CSharpClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/CSharpClientCodegen.java index 5816ccd3ea69..b40bdff2a9c6 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/CSharpClientCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/CSharpClientCodegen.java @@ -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_"); diff --git a/modules/swagger-codegen/src/main/resources/csharp/enumClass.mustache b/modules/swagger-codegen/src/main/resources/csharp/enumClass.mustache deleted file mode 100644 index d8d5d4185ddc..000000000000 --- a/modules/swagger-codegen/src/main/resources/csharp/enumClass.mustache +++ /dev/null @@ -1,17 +0,0 @@ - /// - /// {{^description}}Gets or Sets {{{name}}}{{/description}}{{#description}}{{description}}{{/description}} - /// - {{#description}} - /// {{description}} - {{/description}} - [JsonConverter(typeof(StringEnumConverter))] - {{>visibility}} enum {{#datatypeWithEnum}}{{.}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}} - { - {{#allowableValues}}{{#enumVars}} - /// - /// Enum {{name}} for {{{value}}} - /// - [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}} - } diff --git a/modules/swagger-codegen/src/main/resources/csharp/modelEnum.mustache b/modules/swagger-codegen/src/main/resources/csharp/modelEnum.mustache index 64e50883f8de..9d91ab60e0ea 100644 --- a/modules/swagger-codegen/src/main/resources/csharp/modelEnum.mustache +++ b/modules/swagger-codegen/src/main/resources/csharp/modelEnum.mustache @@ -4,14 +4,16 @@ {{#description}} /// {{description}} {{/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}} /// - /// Enum {{name}} for {{{value}}} + /// Enum {{name}} for value: {{{value}}} /// - [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}} diff --git a/modules/swagger-codegen/src/main/resources/csharp/modelInnerEnum.mustache b/modules/swagger-codegen/src/main/resources/csharp/modelInnerEnum.mustache index 7af083a925f8..12ba61de347b 100644 --- a/modules/swagger-codegen/src/main/resources/csharp/modelInnerEnum.mustache +++ b/modules/swagger-codegen/src/main/resources/csharp/modelInnerEnum.mustache @@ -1,19 +1,21 @@ {{^isContainer}} /// - /// {{^description}}Gets or Sets {{{name}}}{{/description}}{{#description}}{{description}}{{/description}} + /// {{^description}}Defines {{{name}}}{{/description}}{{#description}}{{description}}{{/description}} /// {{#description}} /// {{description}} {{/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}} /// - /// Enum {{name}} for {{{value}}} + /// Enum {{name}} for value: {{{value}}} /// - [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}} diff --git a/modules/swagger-codegen/src/test/resources/integrationtests/csharp/general/enum-support-expected/src/IO.Swagger/Model/MyClassWithOptionalInlineEnum.cs b/modules/swagger-codegen/src/test/resources/integrationtests/csharp/general/enum-support-expected/src/IO.Swagger/Model/MyClassWithOptionalInlineEnum.cs index 2146daae4de4..6ab512ac960a 100644 --- a/modules/swagger-codegen/src/test/resources/integrationtests/csharp/general/enum-support-expected/src/IO.Swagger/Model/MyClassWithOptionalInlineEnum.cs +++ b/modules/swagger-codegen/src/test/resources/integrationtests/csharp/general/enum-support-expected/src/IO.Swagger/Model/MyClassWithOptionalInlineEnum.cs @@ -31,50 +31,50 @@ namespace IO.Swagger.Model public partial class MyClassWithOptionalInlineEnum : IEquatable, IValidatableObject { /// - /// Gets or Sets Days + /// Defines Days /// [JsonConverter(typeof(StringEnumConverter))] public enum DaysEnum { /// - /// Enum Sun for "sun" + /// Enum Sun for value: sun /// [EnumMember(Value = "sun")] Sun, /// - /// Enum Mon for "mon" + /// Enum Mon for value: mon /// [EnumMember(Value = "mon")] Mon, /// - /// Enum Tue for "tue" + /// Enum Tue for value: tue /// [EnumMember(Value = "tue")] Tue, /// - /// Enum Wed for "wed" + /// Enum Wed for value: wed /// [EnumMember(Value = "wed")] Wed, /// - /// Enum Thu for "thu" + /// Enum Thu for value: thu /// [EnumMember(Value = "thu")] Thu, /// - /// Enum Fri for "fri" + /// Enum Fri for value: fri /// [EnumMember(Value = "fri")] Fri, /// - /// Enum Sat for "sat" + /// Enum Sat for value: sat /// [EnumMember(Value = "sat")] Sat diff --git a/modules/swagger-codegen/src/test/resources/integrationtests/csharp/general/enum-support-expected/src/IO.Swagger/Model/MyClassWithRequiredInlineEnum.cs b/modules/swagger-codegen/src/test/resources/integrationtests/csharp/general/enum-support-expected/src/IO.Swagger/Model/MyClassWithRequiredInlineEnum.cs index fb43dccce3f9..a131c7e774a0 100644 --- a/modules/swagger-codegen/src/test/resources/integrationtests/csharp/general/enum-support-expected/src/IO.Swagger/Model/MyClassWithRequiredInlineEnum.cs +++ b/modules/swagger-codegen/src/test/resources/integrationtests/csharp/general/enum-support-expected/src/IO.Swagger/Model/MyClassWithRequiredInlineEnum.cs @@ -31,50 +31,50 @@ namespace IO.Swagger.Model public partial class MyClassWithRequiredInlineEnum : IEquatable, IValidatableObject { /// - /// Gets or Sets Days + /// Defines Days /// [JsonConverter(typeof(StringEnumConverter))] public enum DaysEnum { /// - /// Enum Sun for "sun" + /// Enum Sun for value: sun /// [EnumMember(Value = "sun")] Sun, /// - /// Enum Mon for "mon" + /// Enum Mon for value: mon /// [EnumMember(Value = "mon")] Mon, /// - /// Enum Tue for "tue" + /// Enum Tue for value: tue /// [EnumMember(Value = "tue")] Tue, /// - /// Enum Wed for "wed" + /// Enum Wed for value: wed /// [EnumMember(Value = "wed")] Wed, /// - /// Enum Thu for "thu" + /// Enum Thu for value: thu /// [EnumMember(Value = "thu")] Thu, /// - /// Enum Fri for "fri" + /// Enum Fri for value: fri /// [EnumMember(Value = "fri")] Fri, /// - /// Enum Sat for "sat" + /// Enum Sat for value: sat /// [EnumMember(Value = "sat")] Sat diff --git a/modules/swagger-codegen/src/test/resources/integrationtests/csharp/general/enum-support-expected/src/IO.Swagger/Model/WeekDays.cs b/modules/swagger-codegen/src/test/resources/integrationtests/csharp/general/enum-support-expected/src/IO.Swagger/Model/WeekDays.cs index ae81432d79be..92d45a5c028b 100644 --- a/modules/swagger-codegen/src/test/resources/integrationtests/csharp/general/enum-support-expected/src/IO.Swagger/Model/WeekDays.cs +++ b/modules/swagger-codegen/src/test/resources/integrationtests/csharp/general/enum-support-expected/src/IO.Swagger/Model/WeekDays.cs @@ -27,48 +27,50 @@ namespace IO.Swagger.Model /// /// Defines WeekDays /// + [JsonConverter(typeof(StringEnumConverter))] + public enum WeekDays { /// - /// Enum Sun for "sun" + /// Enum Sun for value: sun /// [EnumMember(Value = "sun")] Sun, /// - /// Enum Mon for "mon" + /// Enum Mon for value: mon /// [EnumMember(Value = "mon")] Mon, /// - /// Enum Tue for "tue" + /// Enum Tue for value: tue /// [EnumMember(Value = "tue")] Tue, /// - /// Enum Wed for "wed" + /// Enum Wed for value: wed /// [EnumMember(Value = "wed")] Wed, /// - /// Enum Thu for "thu" + /// Enum Thu for value: thu /// [EnumMember(Value = "thu")] Thu, /// - /// Enum Fri for "fri" + /// Enum Fri for value: fri /// [EnumMember(Value = "fri")] Fri, /// - /// Enum Sat for "sat" + /// Enum Sat for value: sat /// [EnumMember(Value = "sat")] Sat