Skip to content

Commit 5095fe1

Browse files
authored
Merge pull request #3199 from wing328/better_param_enum
[C#] Add array and map of enum support for C#
2 parents 5047ab3 + 908243b commit 5095fe1

23 files changed

+1495
-103
lines changed

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

Lines changed: 207 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -223,58 +223,12 @@ public Map<String, Object> postProcessModelsEnum(Map<String, Object> objs) {
223223
cm.allowableValues.put("enumVars", enumVars);
224224
}
225225

226-
// for enum model's properties
226+
// update codegen property enum with proper naming convention
227+
// and handling of numbers, special characters
227228
for (CodegenProperty var : cm.vars) {
228-
Map<String, Object> allowableValues = var.allowableValues;
229-
230-
// handle ArrayProperty
231-
if (var.items != null) {
232-
allowableValues = var.items.allowableValues;
233-
}
234-
235-
if (allowableValues == null) {
236-
continue;
237-
}
238-
//List<String> values = (List<String>) allowableValues.get("values");
239-
List<Object> values = (List<Object>) allowableValues.get("values");
240-
if (values == null) {
241-
continue;
242-
}
243-
244-
// put "enumVars" map into `allowableValues", including `name` and `value`
245-
List<Map<String, String>> enumVars = new ArrayList<Map<String, String>>();
246-
String commonPrefix = findCommonPrefixOfVars(values);
247-
int truncateIdx = commonPrefix.length();
248-
for (Object value : values) {
249-
Map<String, String> enumVar = new HashMap<String, String>();
250-
String enumName;
251-
if (truncateIdx == 0) {
252-
enumName = value.toString();
253-
} else {
254-
enumName = value.toString().substring(truncateIdx);
255-
if ("".equals(enumName)) {
256-
enumName = value.toString();
257-
}
258-
}
259-
enumVar.put("name", toEnumVarName(enumName, var.datatype));
260-
enumVar.put("value", toEnumValue(value.toString(), var.datatype));
261-
enumVars.add(enumVar);
262-
}
263-
allowableValues.put("enumVars", enumVars);
264-
// handle default value for enum, e.g. available => StatusEnum.AVAILABLE
265-
if (var.defaultValue != null) {
266-
String enumName = null;
267-
for (Map<String, String> enumVar : enumVars) {
268-
if (toEnumValue(var.defaultValue, var.datatype).equals(enumVar.get("value"))) {
269-
enumName = enumVar.get("name");
270-
break;
271-
}
272-
}
273-
if (enumName != null) {
274-
var.defaultValue = toEnumDefaultValue(enumName, var.datatypeWithEnum);
275-
}
276-
}
229+
updateCodegenPropertyEnum(var);
277230
}
231+
278232
}
279233
return objs;
280234
}
@@ -1572,46 +1526,135 @@ public CodegenProperty fromProperty(String name, Property p) {
15721526
property.baseType = getSwaggerType(p);
15731527

15741528
if (p instanceof ArrayProperty) {
1575-
property.isContainer = true;
1576-
property.isListContainer = true;
1577-
property.containerType = "array";
1578-
ArrayProperty ap = (ArrayProperty) p;
1579-
CodegenProperty cp = fromProperty(property.name, ap.getItems());
1580-
if (cp == null) {
1581-
LOGGER.warn("skipping invalid property " + Json.pretty(p));
1582-
} else {
1583-
property.baseType = getSwaggerType(p);
1584-
if (!languageSpecificPrimitives.contains(cp.baseType)) {
1585-
property.complexType = cp.baseType;
1586-
} else {
1587-
property.isPrimitiveType = true;
1588-
}
1589-
property.items = cp;
1590-
if (property.items.isEnum) {
1591-
property.datatypeWithEnum = property.datatypeWithEnum.replace(property.items.baseType,
1592-
property.items.datatypeWithEnum);
1593-
if(property.defaultValue != null)
1594-
property.defaultValue = property.defaultValue.replace(property.items.baseType, property.items.datatypeWithEnum);
1595-
}
1596-
}
1529+
property.isContainer = true;
1530+
property.isListContainer = true;
1531+
property.containerType = "array";
1532+
property.baseType = getSwaggerType(p);
1533+
// handle inner property
1534+
ArrayProperty ap = (ArrayProperty) p;
1535+
CodegenProperty cp = fromProperty(property.name, ap.getItems());
1536+
updatePropertyForArray(property, cp);
15971537
} else if (p instanceof MapProperty) {
15981538
property.isContainer = true;
15991539
property.isMapContainer = true;
16001540
property.containerType = "map";
1541+
property.baseType = getSwaggerType(p);
1542+
// handle inner property
16011543
MapProperty ap = (MapProperty) p;
16021544
CodegenProperty cp = fromProperty("inner", ap.getAdditionalProperties());
1603-
property.items = cp;
1545+
updatePropertyForMap(property, cp);
1546+
} else {
1547+
setNonArrayMapProperty(property, type);
1548+
}
1549+
return property;
1550+
}
16041551

1605-
property.baseType = getSwaggerType(p);
1606-
if (!languageSpecificPrimitives.contains(cp.baseType)) {
1607-
property.complexType = cp.baseType;
1552+
/**
1553+
* Update property for array(list) container
1554+
* @param property Codegen property
1555+
* @param innerProperty Codegen inner property of map or list
1556+
*/
1557+
protected void updatePropertyForArray(CodegenProperty property, CodegenProperty innerProperty) {
1558+
if (innerProperty == null) {
1559+
LOGGER.warn("skipping invalid array property " + Json.pretty(property));
1560+
} else {
1561+
if (!languageSpecificPrimitives.contains(innerProperty.baseType)) {
1562+
property.complexType = innerProperty.baseType;
16081563
} else {
16091564
property.isPrimitiveType = true;
16101565
}
1566+
property.items = innerProperty;
1567+
// inner item is Enum
1568+
if (isPropertyInnerMostEnum(property)) {
1569+
property.isEnum = true;
1570+
// update datatypeWithEnum for array
1571+
// e.g. List<string> => List<StatusEnum>
1572+
updateDataTypeWithEnumForArray(property);
1573+
1574+
// TOOD need to revise the default value for enum
1575+
if (property.defaultValue != null) {
1576+
property.defaultValue = property.defaultValue.replace(property.items.baseType, property.items.datatypeWithEnum);
1577+
}
1578+
}
1579+
}
1580+
}
1581+
1582+
/**
1583+
* Update property for map container
1584+
* @param property Codegen property
1585+
* @param innerProperty Codegen inner property of map or list
1586+
*/
1587+
protected void updatePropertyForMap(CodegenProperty property, CodegenProperty innerProperty) {
1588+
if (innerProperty == null) {
1589+
LOGGER.warn("skipping invalid map property " + Json.pretty(property));
1590+
return;
16111591
} else {
1612-
setNonArrayMapProperty(property, type);
1592+
if (!languageSpecificPrimitives.contains(innerProperty.baseType)) {
1593+
property.complexType = innerProperty.baseType;
1594+
} else {
1595+
property.isPrimitiveType = true;
1596+
}
1597+
property.items = innerProperty;
1598+
// inner item is Enum
1599+
if (isPropertyInnerMostEnum(property)) {
1600+
property.isEnum = true;
1601+
// update datatypeWithEnum for map
1602+
// e.g. Dictionary<string, string> => Dictionary<string, StatusEnum>
1603+
updateDataTypeWithEnumForMap(property);
1604+
1605+
// TOOD need to revise the default value for enum
1606+
// set default value
1607+
if (property.defaultValue != null) {
1608+
property.defaultValue = property.defaultValue.replace(property.items.baseType, property.items.datatypeWithEnum);
1609+
}
1610+
}
16131611
}
1614-
return property;
1612+
1613+
}
1614+
1615+
/**
1616+
* Update property for map container
1617+
* @param property Codegen property
1618+
* @return True if the inner most type is enum
1619+
*/
1620+
protected Boolean isPropertyInnerMostEnum(CodegenProperty property) {
1621+
CodegenProperty currentProperty = property;
1622+
while (currentProperty != null && (Boolean.TRUE.equals(currentProperty.isMapContainer)
1623+
|| Boolean.TRUE.equals(currentProperty.isListContainer))) {
1624+
currentProperty = currentProperty.items;
1625+
}
1626+
1627+
return currentProperty.isEnum;
1628+
}
1629+
1630+
/**
1631+
* Update datatypeWithEnum for array container
1632+
* @param property Codegen property
1633+
*/
1634+
protected void updateDataTypeWithEnumForArray(CodegenProperty property) {
1635+
CodegenProperty baseItem = property.items;
1636+
while (baseItem != null && (Boolean.TRUE.equals(baseItem.isMapContainer)
1637+
|| Boolean.TRUE.equals(baseItem.isListContainer))) {
1638+
baseItem = baseItem.items;
1639+
}
1640+
// set both datatype and datetypeWithEnum as only the inner type is enum
1641+
property.datatypeWithEnum = property.datatypeWithEnum.replace(baseItem.baseType, toEnumName(baseItem));
1642+
//property.datatype = property.datatypeWithEnum;
1643+
}
1644+
1645+
/**
1646+
* Update datatypeWithEnum for map container
1647+
* @param property Codegen property
1648+
*/
1649+
protected void updateDataTypeWithEnumForMap(CodegenProperty property) {
1650+
CodegenProperty baseItem = property.items;
1651+
while (baseItem != null && (Boolean.TRUE.equals(baseItem.isMapContainer)
1652+
|| Boolean.TRUE.equals(baseItem.isListContainer))) {
1653+
baseItem = baseItem.items;
1654+
}
1655+
// set both datatype and datetypeWithEnum as only the inner type is enum
1656+
property.datatypeWithEnum = property.datatypeWithEnum.replace(", " + baseItem.baseType, ", " + toEnumName(baseItem));
1657+
//property.datatype = property.datatypeWithEnum;
16151658
}
16161659

16171660
protected void setNonArrayMapProperty(CodegenProperty property, String type) {
@@ -2064,33 +2107,41 @@ public CodegenParameter fromParameter(Parameter param, Set<String> imports) {
20642107
LOGGER.warn("warning! Property type \"" + type + "\" not found for parameter \"" + param.getName() + "\", using String");
20652108
property = new StringProperty().description("//TODO automatically added by swagger-codegen. Type was " + type + " but not supported");
20662109
}
2110+
20672111
property.setRequired(param.getRequired());
2068-
CodegenProperty model = fromProperty(qp.getName(), property);
2112+
CodegenProperty cp = fromProperty(qp.getName(), property);
20692113

20702114
// set boolean flag (e.g. isString)
2071-
setParameterBooleanFlagWithCodegenProperty(p, model);
2115+
setParameterBooleanFlagWithCodegenProperty(p, cp);
20722116

2073-
p.dataType = model.datatype;
2074-
if(model.isEnum) {
2075-
p.datatypeWithEnum = model.datatypeWithEnum;
2117+
p.dataType = cp.datatype;
2118+
if(cp.isEnum) {
2119+
p.datatypeWithEnum = cp.datatypeWithEnum;
20762120
}
2077-
p.isEnum = model.isEnum;
2078-
p._enum = model._enum;
2079-
p.allowableValues = model.allowableValues;
2080-
if(model.items != null && model.items.isEnum) {
2081-
p.datatypeWithEnum = model.datatypeWithEnum;
2082-
p.items = model.items;
2121+
2122+
// enum
2123+
updateCodegenPropertyEnum(cp);
2124+
p.isEnum = cp.isEnum;
2125+
p._enum = cp._enum;
2126+
p.allowableValues = cp.allowableValues;
2127+
2128+
2129+
if (cp.items != null && cp.items.isEnum) {
2130+
p.datatypeWithEnum = cp.datatypeWithEnum;
2131+
p.items = cp.items;
20832132
}
20842133
p.collectionFormat = collectionFormat;
20852134
if(collectionFormat != null && collectionFormat.equals("multi")) {
20862135
p.isCollectionFormatMulti = true;
20872136
}
20882137
p.paramName = toParamName(qp.getName());
20892138

2090-
if (model.complexType != null) {
2091-
imports.add(model.complexType);
2139+
// import
2140+
if (cp.complexType != null) {
2141+
imports.add(cp.complexType);
20922142
}
20932143

2144+
// validation
20942145
p.maximum = qp.getMaximum();
20952146
p.exclusiveMaximum = qp.isExclusiveMaximum();
20962147
p.minimum = qp.getMinimum();
@@ -2936,7 +2987,12 @@ public String sanitizeName(String name) {
29362987
return name.replaceAll("[^a-zA-Z0-9_]", "");
29372988
}
29382989

2939-
@SuppressWarnings("static-method")
2990+
/**
2991+
* Sanitize tag
2992+
*
2993+
* @param tag Tag
2994+
* @return Sanitized tag
2995+
*/
29402996
public String sanitizeTag(String tag) {
29412997
// remove spaces and make strong case
29422998
String[] parts = tag.split(" ");
@@ -3026,4 +3082,63 @@ public void setParameterBooleanFlagWithCodegenProperty(CodegenParameter paramete
30263082
LOGGER.debug("Property type is not primitive: " + property.datatype);
30273083
}
30283084
}
3085+
3086+
3087+
/**
3088+
* Update codegen property's enum by adding "enumVars" (with name and value)
3089+
*
3090+
* @param var list of CodegenProperty
3091+
*/
3092+
public void updateCodegenPropertyEnum(CodegenProperty var) {
3093+
Map<String, Object> allowableValues = var.allowableValues;
3094+
3095+
// handle ArrayProperty
3096+
if (var.items != null) {
3097+
allowableValues = var.items.allowableValues;
3098+
}
3099+
3100+
if (allowableValues == null) {
3101+
return;
3102+
}
3103+
3104+
List<Object> values = (List<Object>) allowableValues.get("values");
3105+
if (values == null) {
3106+
return;
3107+
}
3108+
3109+
// put "enumVars" map into `allowableValues", including `name` and `value`
3110+
List<Map<String, String>> enumVars = new ArrayList<Map<String, String>>();
3111+
String commonPrefix = findCommonPrefixOfVars(values);
3112+
int truncateIdx = commonPrefix.length();
3113+
for (Object value : values) {
3114+
Map<String, String> enumVar = new HashMap<String, String>();
3115+
String enumName;
3116+
if (truncateIdx == 0) {
3117+
enumName = value.toString();
3118+
} else {
3119+
enumName = value.toString().substring(truncateIdx);
3120+
if ("".equals(enumName)) {
3121+
enumName = value.toString();
3122+
}
3123+
}
3124+
enumVar.put("name", toEnumVarName(enumName, var.datatype));
3125+
enumVar.put("value", toEnumValue(value.toString(), var.datatype));
3126+
enumVars.add(enumVar);
3127+
}
3128+
allowableValues.put("enumVars", enumVars);
3129+
3130+
// handle default value for enum, e.g. available => StatusEnum.AVAILABLE
3131+
if (var.defaultValue != null) {
3132+
String enumName = null;
3133+
for (Map<String, String> enumVar : enumVars) {
3134+
if (toEnumValue(var.defaultValue, var.datatype).equals(enumVar.get("value"))) {
3135+
enumName = enumVar.get("name");
3136+
break;
3137+
}
3138+
}
3139+
if (enumName != null) {
3140+
var.defaultValue = toEnumDefaultValue(enumName, var.datatypeWithEnum);
3141+
}
3142+
}
3143+
}
30293144
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
/// </summary>{{#description}}
2222
/// <value>{{{description}}}</value>{{/description}}
2323
[DataMember(Name="{{baseName}}", EmitDefaultValue={{emitDefaultValue}})]
24-
public {{{datatypeWithEnum}}}{{#isEnum}}?{{/isEnum}} {{name}} { get; set; }
24+
public {{{datatypeWithEnum}}}{{#isEnum}}{{^isContainer}}?{{/isContainer}}{{/isEnum}} {{name}} { get; set; }
2525
{{/isEnum}}
2626
{{/vars}}
2727
{{#hasRequired}}
@@ -44,7 +44,7 @@
4444
{{#hasOnlyReadOnly}}
4545
[JsonConstructorAttribute]
4646
{{/hasOnlyReadOnly}}
47-
public {{classname}}({{#readWriteVars}}{{{datatypeWithEnum}}}{{#isEnum}}?{{/isEnum}} {{name}} = null{{^-last}}, {{/-last}}{{/readWriteVars}})
47+
public {{classname}}({{#readWriteVars}}{{{datatypeWithEnum}}}{{#isEnum}}{{^isContainer}}?{{/isContainer}}{{/isEnum}} {{name}} = null{{^-last}}, {{/-last}}{{/readWriteVars}})
4848
{
4949
{{#vars}}
5050
{{^isReadOnly}}

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1+
{{^isContainer}}
12
/// <summary>
23
/// {{^description}}Gets or Sets {{{name}}}{{/description}}{{#description}}{{{description}}}{{/description}}
34
/// </summary>{{#description}}
45
/// <value>{{{description}}}</value>{{/description}}
56
[JsonConverter(typeof(StringEnumConverter))]
6-
public enum {{#datatypeWithEnum}}{{.}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}
7+
public enum {{#datatypeWithEnum}}{{&.}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}
78
{
89
{{#allowableValues}}{{#enumVars}}
910
/// <summary>
@@ -13,3 +14,4 @@
1314
{{name}}{{#isLong}} = {{{value}}}{{/isLong}}{{#isInteger}} = {{{value}}}{{/isInteger}}{{^-last}},
1415
{{/-last}}{{/enumVars}}{{/allowableValues}}
1516
}
17+
{{/isContainer}}

0 commit comments

Comments
 (0)