Skip to content

Commit 1058728

Browse files
committed
[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.
1 parent 72b9ab6 commit 1058728

File tree

9 files changed

+146
-94
lines changed

9 files changed

+146
-94
lines changed

modules/swagger-codegen/src/main/java/io/swagger/codegen/DefaultCodegen.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1955,6 +1955,8 @@ protected void updateDataTypeWithEnumForArray(CodegenProperty property) {
19551955
if (property.defaultValue != null) {
19561956
property.defaultValue = property.defaultValue.replace(baseItem.baseType, toEnumName(baseItem));
19571957
}
1958+
1959+
updateCodegenPropertyEnum(property);
19581960
}
19591961
}
19601962

modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AbstractCSharpCodegen.java

Lines changed: 103 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package io.swagger.codegen.languages;
22

3+
import com.samskivert.mustache.Mustache;
4+
import com.samskivert.mustache.Template;
35
import io.swagger.codegen.*;
46
import io.swagger.codegen.utils.ModelUtils;
57
import io.swagger.models.properties.*;
@@ -8,6 +10,8 @@
810
import org.slf4j.LoggerFactory;
911

1012
import java.io.File;
13+
import java.io.IOException;
14+
import java.io.Writer;
1115
import java.util.*;
1216

1317
public abstract class AbstractCSharpCodegen extends DefaultCodegen implements CodegenConfig {
@@ -343,6 +347,7 @@ public Map<String, Object> postProcessAllModels(Map<String, Object> objs) {
343347
* those vars referencing RefModel'd enums to work the same as inlined enums rather than as objects.
344348
* @param models
345349
*/
350+
@SuppressWarnings({ "unchecked" })
346351
private void postProcessEnumRefs(final Map<String, Object> models) {
347352
Map<String, CodegenModel> enumRefs = new HashMap<String, CodegenModel>();
348353
for (Map.Entry<String, Object> entry : models.entrySet()) {
@@ -354,6 +359,7 @@ private void postProcessEnumRefs(final Map<String, Object> models) {
354359

355360
for (Map.Entry<String, Object> entry : models.entrySet()) {
356361
String swaggerName = entry.getKey();
362+
357363
CodegenModel model = ModelUtils.getModelByName(swaggerName, models);
358364
if (model != null) {
359365
for (CodegenProperty var : model.allVars) {
@@ -363,11 +369,57 @@ private void postProcessEnumRefs(final Map<String, Object> models) {
363369
// while enums in many other languages are true objects.
364370
CodegenModel refModel = enumRefs.get(var.datatype);
365371
var.allowableValues = refModel.allowableValues;
372+
var.isEnum = true;
373+
366374
updateCodegenPropertyEnum(var);
367375

368-
// We do these after updateCodegenPropertyEnum to avoid generalities that don't mesh with C#.
376+
// We do this after updateCodegenPropertyEnum to avoid generalities that don't mesh with C#.
369377
var.isPrimitiveType = true;
370-
var.isEnum = true;
378+
}
379+
}
380+
381+
// We're looping all models here.
382+
if (model.isEnum) {
383+
// We now need to make allowableValues.enumVars look like the context of CodegenProperty
384+
Boolean isString = false;
385+
Boolean isInteger = false;
386+
Boolean isLong = false;
387+
Boolean isByte = false;
388+
389+
if (model.dataType.startsWith("byte")) {
390+
// C# Actually supports byte and short enums, swagger spec only supports byte.
391+
isByte = true;
392+
model.vendorExtensions.put("x-enum-byte", true);
393+
} else if (model.dataType.startsWith("int32")) {
394+
isInteger = true;
395+
model.vendorExtensions.put("x-enum-integer", true);
396+
} else if (model.dataType.startsWith("int64")) {
397+
isLong = true;
398+
model.vendorExtensions.put("x-enum-long", true);
399+
} else {
400+
// 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)
401+
isString = true;
402+
model.vendorExtensions.put("x-enum-string", true);
403+
}
404+
405+
// Since we iterate enumVars for modelnnerEnum and enumClass templates, and CodegenModel is missing some of CodegenProperty's properties,
406+
// 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.
407+
List<Map<String, String>> enumVars = (ArrayList<Map<String, String>>)model.allowableValues.get("enumVars");
408+
List<Map<String, Object>> newEnumVars = new ArrayList<Map<String, Object>>();
409+
for (Map<String, String> enumVar : enumVars) {
410+
Map<String, Object> mixedVars = new HashMap<String, Object>();
411+
mixedVars.putAll(enumVar);
412+
413+
mixedVars.put("isString", isString);
414+
mixedVars.put("isLong", isLong);
415+
mixedVars.put("isInteger", isInteger);
416+
mixedVars.put("isByte", isByte);
417+
418+
newEnumVars.add(mixedVars);
419+
}
420+
421+
if (!newEnumVars.isEmpty()) {
422+
model.allowableValues.put("enumVars", newEnumVars);
371423
}
372424
}
373425
} else {
@@ -376,6 +428,42 @@ private void postProcessEnumRefs(final Map<String, Object> models) {
376428
}
377429
}
378430

431+
/**
432+
* Update codegen property's enum by adding "enumVars" (with name and value)
433+
*
434+
* @param var list of CodegenProperty
435+
*/
436+
@Override
437+
public void updateCodegenPropertyEnum(CodegenProperty var) {
438+
if (var.vendorExtensions == null) {
439+
var.vendorExtensions = new HashMap<>();
440+
}
441+
442+
super.updateCodegenPropertyEnum(var);
443+
444+
// Because C# uses nullable primitives for datatype, and datatype is used in DefaultCodegen for determining enum-ness, guard against weirdness here.
445+
if (var.isEnum) {
446+
if ("byte".equals(var.dataFormat)) {// C# Actually supports byte and short enums.
447+
var.vendorExtensions.put("x-enum-byte", true);
448+
var.isString = false;
449+
var.isLong = false;
450+
var.isInteger = false;
451+
} else if ("int32".equals(var.dataFormat)) {
452+
var.isInteger = true;
453+
var.isString = false;
454+
var.isLong = false;
455+
} else if ("int64".equals(var.dataFormat)) {
456+
var.isLong = true;
457+
var.isString = false;
458+
var.isInteger = false;
459+
} 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)
460+
var.isString = true;
461+
var.isInteger = false;
462+
var.isLong = false;
463+
}
464+
}
465+
}
466+
379467
@Override
380468
public Map<String, Object> postProcessOperations(Map<String, Object> objs) {
381469
super.postProcessOperations(objs);
@@ -746,6 +834,19 @@ public void setInterfacePrefix(final String interfacePrefix) {
746834
this.interfacePrefix = interfacePrefix;
747835
}
748836

837+
@Override
838+
public String toEnumValue(String value, String datatype) {
839+
// C# only supports enums as literals for int, int?, long, long?, byte, and byte?. All else must be treated as strings.
840+
// Per: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/enum
841+
// The approved types for an enum are byte, sbyte, short, ushort, int, uint, long, or ulong.
842+
// but we're not supporting unsigned integral types or shorts.
843+
if(datatype.startsWith("int") || datatype.startsWith("long") || datatype.startsWith("byte")) {
844+
return value;
845+
}
846+
847+
return escapeText(value);
848+
}
849+
749850
@Override
750851
public String toEnumVarName(String name, String datatype) {
751852
if (name.length() == 0) {
@@ -776,32 +877,6 @@ public String toEnumName(CodegenProperty property) {
776877
return sanitizeName(camelize(property.name)) + "Enum";
777878
}
778879

779-
/*
780-
@Override
781-
public String toEnumName(CodegenProperty property) {
782-
String enumName = sanitizeName(property.name);
783-
if (!StringUtils.isEmpty(modelNamePrefix)) {
784-
enumName = modelNamePrefix + "_" + enumName;
785-
}
786-
787-
if (!StringUtils.isEmpty(modelNameSuffix)) {
788-
enumName = enumName + "_" + modelNameSuffix;
789-
}
790-
791-
// model name cannot use reserved keyword, e.g. return
792-
if (isReservedWord(enumName)) {
793-
LOGGER.warn(enumName + " (reserved word) cannot be used as model name. Renamed to " + camelize("model_" + enumName));
794-
enumName = "model_" + enumName; // e.g. return => ModelReturn (after camelize)
795-
}
796-
797-
if (enumName.matches("\\d.*")) { // starts with number
798-
return "_" + enumName;
799-
} else {
800-
return enumName;
801-
}
802-
}
803-
*/
804-
805880
public String testPackageName() {
806881
return this.packageName + ".Test";
807882
}
@@ -816,5 +891,4 @@ public String escapeQuotationMark(String input) {
816891
public String escapeUnsafeCharacters(String input) {
817892
return input.replace("*/", "*_/").replace("/*", "/_*").replace("--", "- -");
818893
}
819-
820894
}

modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/CSharpClientCodegen.java

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -611,19 +611,6 @@ private CodegenModel reconcileInlineEnums(CodegenModel codegenModel, CodegenMode
611611
return codegenModel;
612612
}
613613

614-
@Override
615-
public String toEnumValue(String value, String datatype) {
616-
if ("int?".equalsIgnoreCase(datatype) || "long?".equalsIgnoreCase(datatype) ||
617-
"double?".equalsIgnoreCase(datatype) || "float?".equalsIgnoreCase(datatype)) {
618-
return value;
619-
} else if ("float?".equalsIgnoreCase(datatype)) {
620-
// for float in C#, append "f". e.g. 3.14 => 3.14f
621-
return value + "f";
622-
} else {
623-
return "\"" + escapeText(value) + "\"";
624-
}
625-
}
626-
627614
@Override
628615
public String toEnumVarName(String value, String datatype) {
629616
if (value.length() == 0) {
@@ -636,8 +623,8 @@ public String toEnumVarName(String value, String datatype) {
636623
}
637624

638625
// number
639-
if ("int?".equals(datatype) || "long?".equals(datatype) ||
640-
"double?".equals(datatype) || "float?".equals(datatype)) {
626+
if(datatype.startsWith("int") || datatype.startsWith("long") ||
627+
datatype.startsWith("double") || datatype.startsWith("float")) {
641628
String varName = "NUMBER_" + value;
642629
varName = varName.replaceAll("-", "MINUS_");
643630
varName = varName.replaceAll("\\+", "PLUS_");

modules/swagger-codegen/src/main/resources/csharp/enumClass.mustache

Lines changed: 0 additions & 17 deletions
This file was deleted.

modules/swagger-codegen/src/main/resources/csharp/modelEnum.mustache

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,16 @@
44
{{#description}}
55
/// <value>{{description}}</value>
66
{{/description}}
7+
{{#allowableValues}}{{#enumVars}}{{#-first}}{{#isString}}
78
[JsonConverter(typeof(StringEnumConverter))]
8-
{{>visibility}} enum {{#datatypeWithEnum}}{{.}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}
9+
{{/isString}}{{/-first}}{{/enumVars}}{{/allowableValues}}
10+
{{>visibility}} enum {{#datatypeWithEnum}}{{.}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}{{#vendorExtensions.x-enum-byte}}: byte{{/vendorExtensions.x-enum-byte}}
911
{
1012
{{#allowableValues}}{{#enumVars}}
1113
/// <summary>
12-
/// Enum {{name}} for {{{value}}}
14+
/// Enum {{name}} for value: {{{value}}}
1315
/// </summary>
14-
[EnumMember(Value = {{#isInteger}}"{{/isInteger}}{{#isDouble}}"{{/isDouble}}{{{value}}}{{#isInteger}}"{{/isInteger}}{{#isDouble}}"{{/isDouble}})]
15-
{{name}}{{#isInteger}} = {{{value}}}{{/isInteger}}{{^-last}},
16+
{{#isString}}[EnumMember(Value = "{{{value}}}")]{{/isString}}
17+
{{name}}{{^isString}} = {{{value}}}{{/isString}}{{^-last}},
1618
{{/-last}}{{/enumVars}}{{/allowableValues}}
17-
}
19+
}{{! NOTE: This model's enumVars is modified to look like CodegenProperty}}
Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
11
{{^isContainer}}
22
/// <summary>
3-
/// {{^description}}Gets or Sets {{{name}}}{{/description}}{{#description}}{{description}}{{/description}}
3+
/// {{^description}}Defines {{{name}}}{{/description}}{{#description}}{{description}}{{/description}}
44
/// </summary>
55
{{#description}}
66
/// <value>{{description}}</value>
77
{{/description}}
8+
{{#isString}}
89
[JsonConverter(typeof(StringEnumConverter))]
9-
{{>visibility}} enum {{#datatypeWithEnum}}{{&.}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}
10+
{{/isString}}
11+
{{>visibility}} enum {{#datatypeWithEnum}}{{.}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}{{#vendorExtensions.x-enum-byte}}: byte{{/vendorExtensions.x-enum-byte}}
1012
{
1113
{{#allowableValues}}{{#enumVars}}
1214
/// <summary>
13-
/// Enum {{name}} for {{{value}}}
15+
/// Enum {{name}} for value: {{{value}}}
1416
/// </summary>
15-
[EnumMember(Value = {{#isLong}}"{{/isLong}}{{#isInteger}}"{{/isInteger}}{{#isFloat}}"{{/isFloat}}{{#isDouble}}"{{/isDouble}}{{{value}}}{{#isLong}}"{{/isLong}}{{#isInteger}}"{{/isInteger}}{{#isDouble}}"{{/isDouble}}{{#isFloat}}"{{/isFloat}})]
16-
{{name}}{{#isLong}} = {{{value}}}{{/isLong}}{{#isInteger}} = {{{value}}}{{/isInteger}}{{^-last}},
17+
{{#isString}}[EnumMember(Value = "{{{value}}}")]{{/isString}}
18+
{{name}}{{^isString}} = {{{value}}}{{/isString}}{{^-last}},
1719
{{/-last}}{{/enumVars}}{{/allowableValues}}
1820
}
1921
{{/isContainer}}

modules/swagger-codegen/src/test/resources/integrationtests/csharp/general/enum-support-expected/src/IO.Swagger/Model/MyClassWithOptionalInlineEnum.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,50 +31,50 @@ namespace IO.Swagger.Model
3131
public partial class MyClassWithOptionalInlineEnum : IEquatable<MyClassWithOptionalInlineEnum>, IValidatableObject
3232
{
3333
/// <summary>
34-
/// Gets or Sets Days
34+
/// Defines Days
3535
/// </summary>
3636
[JsonConverter(typeof(StringEnumConverter))]
3737
public enum DaysEnum
3838
{
3939

4040
/// <summary>
41-
/// Enum Sun for "sun"
41+
/// Enum Sun for value: sun
4242
/// </summary>
4343
[EnumMember(Value = "sun")]
4444
Sun,
4545

4646
/// <summary>
47-
/// Enum Mon for "mon"
47+
/// Enum Mon for value: mon
4848
/// </summary>
4949
[EnumMember(Value = "mon")]
5050
Mon,
5151

5252
/// <summary>
53-
/// Enum Tue for "tue"
53+
/// Enum Tue for value: tue
5454
/// </summary>
5555
[EnumMember(Value = "tue")]
5656
Tue,
5757

5858
/// <summary>
59-
/// Enum Wed for "wed"
59+
/// Enum Wed for value: wed
6060
/// </summary>
6161
[EnumMember(Value = "wed")]
6262
Wed,
6363

6464
/// <summary>
65-
/// Enum Thu for "thu"
65+
/// Enum Thu for value: thu
6666
/// </summary>
6767
[EnumMember(Value = "thu")]
6868
Thu,
6969

7070
/// <summary>
71-
/// Enum Fri for "fri"
71+
/// Enum Fri for value: fri
7272
/// </summary>
7373
[EnumMember(Value = "fri")]
7474
Fri,
7575

7676
/// <summary>
77-
/// Enum Sat for "sat"
77+
/// Enum Sat for value: sat
7878
/// </summary>
7979
[EnumMember(Value = "sat")]
8080
Sat

0 commit comments

Comments
 (0)