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