Support the empty string as an enumeration value (#4450)

When a string enumeration has the empty string as one of its available
values, the generated code for many languages is invalid because the
empty string can not be used as an identifier.  As with numbers and
symbols, provide a mapping to an English name which can be used as a
replacement.  In this case, "empty" for the empty string/empty value.

Signed-off-by: Kevin Locke <kevin@kevinlocke.name>
This commit is contained in:
Kevin Locke 2017-01-06 04:30:47 -07:00 committed by wing328
parent 43ab14b200
commit df0c54d043
53 changed files with 160 additions and 29 deletions

View File

@ -303,6 +303,10 @@ public class DefaultCodegen {
* @return the sanitized variable name for enum * @return the sanitized variable name for enum
*/ */
public String toEnumVarName(String value, String datatype) { public String toEnumVarName(String value, String datatype) {
if (value.length() == 0) {
return "EMPTY";
}
String var = value.replaceAll("\\W+", "_").toUpperCase(); String var = value.replaceAll("\\W+", "_").toUpperCase();
if (var.matches("\\d.*")) { if (var.matches("\\d.*")) {
return "_" + var; return "_" + var;

View File

@ -615,6 +615,10 @@ public abstract class AbstractCSharpCodegen extends DefaultCodegen implements Co
@Override @Override
public String toEnumVarName(String name, String datatype) { public String toEnumVarName(String name, String datatype) {
if (name.length() == 0) {
return "Empty";
}
// for symbol, e.g. $, # // for symbol, e.g. $, #
if (getSymbolName(name) != null) { if (getSymbolName(name) != null) {
return camelize(getSymbolName(name)); return camelize(getSymbolName(name));

View File

@ -808,6 +808,10 @@ public abstract class AbstractJavaCodegen extends DefaultCodegen implements Code
@Override @Override
public String toEnumVarName(String value, String datatype) { public String toEnumVarName(String value, String datatype) {
if (value.length() == 0) {
return "EMPTY";
}
// for symbol, e.g. $, # // for symbol, e.g. $, #
if (getSymbolName(value) != null) { if (getSymbolName(value) != null) {
return getSymbolName(value).toUpperCase(); return getSymbolName(value).toUpperCase();

View File

@ -589,6 +589,10 @@ public abstract class AbstractPhpCodegen extends DefaultCodegen implements Codeg
@Override @Override
public String toEnumVarName(String name, String datatype) { public String toEnumVarName(String name, String datatype) {
if (name.length() == 0) {
return "EMPTY";
}
// for symbol, e.g. $, # // for symbol, e.g. $, #
if (getSymbolName(name) != null) { if (getSymbolName(name) != null) {
return (getSymbolName(name)).toUpperCase(); return (getSymbolName(name)).toUpperCase();

View File

@ -286,6 +286,10 @@ public abstract class AbstractTypeScriptClientCodegen extends DefaultCodegen imp
@Override @Override
public String toEnumVarName(String name, String datatype) { public String toEnumVarName(String name, String datatype) {
if (name.length() == 0) {
return "Empty";
}
// for symbol, e.g. $, # // for symbol, e.g. $, #
if (getSymbolName(name) != null) { if (getSymbolName(name) != null) {
return camelize(getSymbolName(name)); return camelize(getSymbolName(name));

View File

@ -494,6 +494,10 @@ public class CSharpClientCodegen extends AbstractCSharpCodegen {
@Override @Override
public String toEnumVarName(String value, String datatype) { public String toEnumVarName(String value, String datatype) {
if (value.length() == 0) {
return "Empty";
}
// for symbol, e.g. $, # // for symbol, e.g. $, #
if (getSymbolName(value) != null) { if (getSymbolName(value) != null) {
return camelize(getSymbolName(value)); return camelize(getSymbolName(value));

View File

@ -1021,6 +1021,10 @@ public class JavascriptClientCodegen extends DefaultCodegen implements CodegenCo
@Override @Override
public String toEnumVarName(String value, String datatype) { public String toEnumVarName(String value, String datatype) {
if (value.length() == 0) {
return "empty";
}
// for symbol, e.g. $, # // for symbol, e.g. $, #
if (getSymbolName(value) != null) { if (getSymbolName(value) != null) {
return (getSymbolName(value)).toUpperCase(); return (getSymbolName(value)).toUpperCase();

View File

@ -292,6 +292,10 @@ public class NancyFXServerCodegen extends AbstractCSharpCodegen {
@Override @Override
public String toEnumVarName(final String name, final String datatype) { public String toEnumVarName(final String name, final String datatype) {
if (name.length() == 0) {
return "Empty";
}
final String enumName = camelize( final String enumName = camelize(
sanitizeName(name) sanitizeName(name)
.replaceFirst("^_", "") .replaceFirst("^_", "")

View File

@ -644,6 +644,10 @@ public class PhpClientCodegen extends DefaultCodegen implements CodegenConfig {
@Override @Override
public String toEnumVarName(String name, String datatype) { public String toEnumVarName(String name, String datatype) {
if (name.length() == 0) {
return "EMPTY";
}
// number // number
if ("int".equals(datatype) || "double".equals(datatype) || "float".equals(datatype)) { if ("int".equals(datatype) || "double".equals(datatype) || "float".equals(datatype)) {
String varName = name; String varName = name;

View File

@ -568,6 +568,10 @@ public class RubyClientCodegen extends DefaultCodegen implements CodegenConfig {
@Override @Override
public String toEnumVarName(String name, String datatype) { public String toEnumVarName(String name, String datatype) {
if (name.length() == 0) {
return "EMPTY";
}
// number // number
if ("Integer".equals(datatype) || "Float".equals(datatype)) { if ("Integer".equals(datatype) || "Float".equals(datatype)) {
String varName = name; String varName = name;

View File

@ -513,6 +513,10 @@ public class Swift3Codegen extends DefaultCodegen implements CodegenConfig {
@Override @Override
public String toEnumVarName(String name, String datatype) { public String toEnumVarName(String name, String datatype) {
if (name.length() == 0) {
return "empty";
}
// for symbol, e.g. $, # // for symbol, e.g. $, #
if (getSymbolName(name) != null) { if (getSymbolName(name) != null) {
return camelize(WordUtils.capitalizeFully(getSymbolName(name).toUpperCase()), true); return camelize(WordUtils.capitalizeFully(getSymbolName(name).toUpperCase()), true);

View File

@ -388,6 +388,10 @@ public class SwiftCodegen extends DefaultCodegen implements CodegenConfig {
@SuppressWarnings("static-method") @SuppressWarnings("static-method")
public String toSwiftyEnumName(String value) { public String toSwiftyEnumName(String value) {
if (value.length() == 0) {
return "Empty";
}
if (value.matches("^-?\\d*\\.{0,1}\\d+.*")) { // starts with number if (value.matches("^-?\\d*\\.{0,1}\\d+.*")) { // starts with number
value = "Number" + value; value = "Number" + value;
value = value.replaceAll("-", "Minus"); value = value.replaceAll("-", "Minus");

View File

@ -1067,6 +1067,7 @@ definitions:
enum: enum:
- UPPER - UPPER
- lower - lower
- ''
enum_integer: enum_integer:
type: integer type: integer
format: int32 format: int32

View File

@ -46,7 +46,13 @@ namespace IO.Swagger.Model
/// Enum Lower for "lower" /// Enum Lower for "lower"
/// </summary> /// </summary>
[EnumMember(Value = "lower")] [EnumMember(Value = "lower")]
Lower Lower,
/// <summary>
/// Enum Empty for ""
/// </summary>
[EnumMember(Value = "")]
Empty
} }
/// <summary> /// <summary>

View File

@ -49,7 +49,13 @@ namespace IO.Swagger.Model
/// Enum Lower for "lower" /// Enum Lower for "lower"
/// </summary> /// </summary>
[EnumMember(Value = "lower")] [EnumMember(Value = "lower")]
Lower Lower,
/// <summary>
/// Enum Empty for ""
/// </summary>
[EnumMember(Value = "")]
Empty
} }
/// <summary> /// <summary>

View File

@ -31,7 +31,9 @@ public class EnumTest {
public enum EnumStringEnum { public enum EnumStringEnum {
UPPER("UPPER"), UPPER("UPPER"),
LOWER("lower"); LOWER("lower"),
EMPTY("");
private String value; private String value;

View File

@ -16,6 +16,7 @@ Name | Value
---- | ----- ---- | -----
UPPER | &quot;UPPER&quot; UPPER | &quot;UPPER&quot;
LOWER | &quot;lower&quot; LOWER | &quot;lower&quot;
EMPTY | &quot;&quot;
<a name="EnumIntegerEnum"></a> <a name="EnumIntegerEnum"></a>

View File

@ -31,7 +31,9 @@ public class EnumTest {
public enum EnumStringEnum { public enum EnumStringEnum {
UPPER("UPPER"), UPPER("UPPER"),
LOWER("lower"); LOWER("lower"),
EMPTY("");
private String value; private String value;

View File

@ -15,6 +15,7 @@ Name | Value
---- | ----- ---- | -----
UPPER | &quot;UPPER&quot; UPPER | &quot;UPPER&quot;
LOWER | &quot;lower&quot; LOWER | &quot;lower&quot;
EMPTY | &quot;&quot;
<a name="EnumIntegerEnum"></a> <a name="EnumIntegerEnum"></a>

View File

@ -42,7 +42,9 @@ public class EnumTest {
public enum EnumStringEnum { public enum EnumStringEnum {
UPPER("UPPER"), UPPER("UPPER"),
LOWER("lower"); LOWER("lower"),
EMPTY("");
private String value; private String value;

View File

@ -16,6 +16,7 @@ Name | Value
---- | ----- ---- | -----
UPPER | &quot;UPPER&quot; UPPER | &quot;UPPER&quot;
LOWER | &quot;lower&quot; LOWER | &quot;lower&quot;
EMPTY | &quot;&quot;
<a name="EnumIntegerEnum"></a> <a name="EnumIntegerEnum"></a>

View File

@ -31,7 +31,9 @@ public class EnumTest {
public enum EnumStringEnum { public enum EnumStringEnum {
UPPER("UPPER"), UPPER("UPPER"),
LOWER("lower"); LOWER("lower"),
EMPTY("");
private String value; private String value;

View File

@ -16,6 +16,7 @@ Name | Value
---- | ----- ---- | -----
UPPER | &quot;UPPER&quot; UPPER | &quot;UPPER&quot;
LOWER | &quot;lower&quot; LOWER | &quot;lower&quot;
EMPTY | &quot;&quot;
<a name="EnumIntegerEnum"></a> <a name="EnumIntegerEnum"></a>

View File

@ -31,7 +31,9 @@ public class EnumTest {
public enum EnumStringEnum { public enum EnumStringEnum {
UPPER("UPPER"), UPPER("UPPER"),
LOWER("lower"); LOWER("lower"),
EMPTY("");
private String value; private String value;

View File

@ -15,6 +15,7 @@ Name | Value
---- | ----- ---- | -----
UPPER | &quot;UPPER&quot; UPPER | &quot;UPPER&quot;
LOWER | &quot;lower&quot; LOWER | &quot;lower&quot;
EMPTY | &quot;&quot;
<a name="EnumIntegerEnum"></a> <a name="EnumIntegerEnum"></a>

View File

@ -45,7 +45,10 @@ public class EnumTest implements Parcelable {
UPPER("UPPER"), UPPER("UPPER"),
@SerializedName("lower") @SerializedName("lower")
LOWER("lower"); LOWER("lower"),
@SerializedName("")
EMPTY("");
private String value; private String value;

View File

@ -16,6 +16,7 @@ Name | Value
---- | ----- ---- | -----
UPPER | &quot;UPPER&quot; UPPER | &quot;UPPER&quot;
LOWER | &quot;lower&quot; LOWER | &quot;lower&quot;
EMPTY | &quot;&quot;
<a name="EnumIntegerEnum"></a> <a name="EnumIntegerEnum"></a>

View File

@ -32,7 +32,10 @@ public class EnumTest {
UPPER("UPPER"), UPPER("UPPER"),
@SerializedName("lower") @SerializedName("lower")
LOWER("lower"); LOWER("lower"),
@SerializedName("")
EMPTY("");
private String value; private String value;

View File

@ -32,7 +32,10 @@ public class EnumTest {
UPPER("UPPER"), UPPER("UPPER"),
@SerializedName("lower") @SerializedName("lower")
LOWER("lower"); LOWER("lower"),
@SerializedName("")
EMPTY("");
private String value; private String value;

View File

@ -16,6 +16,7 @@ Name | Value
---- | ----- ---- | -----
UPPER | &quot;UPPER&quot; UPPER | &quot;UPPER&quot;
LOWER | &quot;lower&quot; LOWER | &quot;lower&quot;
EMPTY | &quot;&quot;
<a name="EnumIntegerEnum"></a> <a name="EnumIntegerEnum"></a>

View File

@ -32,7 +32,9 @@ public class EnumTest {
public enum EnumStringEnum { public enum EnumStringEnum {
UPPER("UPPER"), UPPER("UPPER"),
LOWER("lower"); LOWER("lower"),
EMPTY("");
private String value; private String value;

View File

@ -16,6 +16,7 @@ Name | Value
---- | ----- ---- | -----
UPPER | &quot;UPPER&quot; UPPER | &quot;UPPER&quot;
LOWER | &quot;lower&quot; LOWER | &quot;lower&quot;
EMPTY | &quot;&quot;
<a name="EnumIntegerEnum"></a> <a name="EnumIntegerEnum"></a>

View File

@ -32,7 +32,10 @@ public class EnumTest {
UPPER("UPPER"), UPPER("UPPER"),
@SerializedName("lower") @SerializedName("lower")
LOWER("lower"); LOWER("lower"),
@SerializedName("")
EMPTY("");
private String value; private String value;

View File

@ -16,6 +16,7 @@ Name | Value
---- | ----- ---- | -----
UPPER | &quot;UPPER&quot; UPPER | &quot;UPPER&quot;
LOWER | &quot;lower&quot; LOWER | &quot;lower&quot;
EMPTY | &quot;&quot;
<a name="EnumIntegerEnum"></a> <a name="EnumIntegerEnum"></a>

View File

@ -32,7 +32,10 @@ public class EnumTest {
UPPER("UPPER"), UPPER("UPPER"),
@SerializedName("lower") @SerializedName("lower")
LOWER("lower"); LOWER("lower"),
@SerializedName("")
EMPTY("");
private String value; private String value;

View File

@ -17,6 +17,8 @@ Name | Type | Description | Notes
* `lower` (value: `"lower"`) * `lower` (value: `"lower"`)
* `empty` (value: `""`)

View File

@ -111,7 +111,12 @@
* value: "lower" * value: "lower"
* @const * @const
*/ */
"lower": "lower" }; "lower": "lower",
/**
* value: ""
* @const
*/
"empty": "" };
/** /**
* Allowed values for the <code>enum_integer</code> property. * Allowed values for the <code>enum_integer</code> property.

View File

@ -17,6 +17,8 @@ Name | Type | Description | Notes
* `lower` (value: `"lower"`) * `lower` (value: `"lower"`)
* `empty` (value: `""`)

View File

@ -111,7 +111,12 @@
* value: "lower" * value: "lower"
* @const * @const
*/ */
"lower": "lower" }; "lower": "lower",
/**
* value: ""
* @const
*/
"empty": "" };
/** /**
* Allowed values for the <code>enum_integer</code> property. * Allowed values for the <code>enum_integer</code> property.

View File

@ -118,6 +118,7 @@ class EnumTest implements ArrayAccess
const ENUM_STRING_UPPER = 'UPPER'; const ENUM_STRING_UPPER = 'UPPER';
const ENUM_STRING_LOWER = 'lower'; const ENUM_STRING_LOWER = 'lower';
const ENUM_STRING_EMPTY = '';
const ENUM_INTEGER_1 = 1; const ENUM_INTEGER_1 = 1;
const ENUM_INTEGER_MINUS_1 = -1; const ENUM_INTEGER_MINUS_1 = -1;
const ENUM_NUMBER_1_DOT_1 = 1.1; const ENUM_NUMBER_1_DOT_1 = 1.1;
@ -134,6 +135,7 @@ class EnumTest implements ArrayAccess
return [ return [
self::ENUM_STRING_UPPER, self::ENUM_STRING_UPPER,
self::ENUM_STRING_LOWER, self::ENUM_STRING_LOWER,
self::ENUM_STRING_EMPTY,
]; ];
} }
@ -188,7 +190,7 @@ class EnumTest implements ArrayAccess
public function listInvalidProperties() public function listInvalidProperties()
{ {
$invalid_properties = []; $invalid_properties = [];
$allowed_values = ["UPPER", "lower"]; $allowed_values = ["UPPER", "lower", ""];
if (!in_array($this->container['enum_string'], $allowed_values)) { if (!in_array($this->container['enum_string'], $allowed_values)) {
$invalid_properties[] = "invalid value for 'enum_string', must be one of #{allowed_values}."; $invalid_properties[] = "invalid value for 'enum_string', must be one of #{allowed_values}.";
} }
@ -214,7 +216,7 @@ class EnumTest implements ArrayAccess
*/ */
public function valid() public function valid()
{ {
$allowed_values = ["UPPER", "lower"]; $allowed_values = ["UPPER", "lower", ""];
if (!in_array($this->container['enum_string'], $allowed_values)) { if (!in_array($this->container['enum_string'], $allowed_values)) {
return false; return false;
} }
@ -246,9 +248,9 @@ class EnumTest implements ArrayAccess
*/ */
public function setEnumString($enum_string) public function setEnumString($enum_string)
{ {
$allowed_values = array('UPPER', 'lower'); $allowed_values = array('UPPER', 'lower', '');
if (!is_null($enum_string) && (!in_array($enum_string, $allowed_values))) { if (!is_null($enum_string) && (!in_array($enum_string, $allowed_values))) {
throw new \InvalidArgumentException("Invalid value for 'enum_string', must be one of 'UPPER', 'lower'"); throw new \InvalidArgumentException("Invalid value for 'enum_string', must be one of 'UPPER', 'lower', ''");
} }
$this->container['enum_string'] = $enum_string; $this->container['enum_string'] = $enum_string;

View File

@ -67,7 +67,7 @@ class EnumTest(object):
:param enum_string: The enum_string of this EnumTest. :param enum_string: The enum_string of this EnumTest.
:type: str :type: str
""" """
allowed_values = ["UPPER", "lower"] allowed_values = ["UPPER", "lower", ""]
if enum_string not in allowed_values: if enum_string not in allowed_values:
raise ValueError( raise ValueError(
"Invalid value for `enum_string` ({0}), must be one of {1}" "Invalid value for `enum_string` ({0}), must be one of {1}"

View File

@ -100,7 +100,7 @@ module Petstore
# Check to see if the all the properties in the model are valid # Check to see if the all the properties in the model are valid
# @return true if the model is valid # @return true if the model is valid
def valid? def valid?
enum_string_validator = EnumAttributeValidator.new('String', ["UPPER", "lower"]) enum_string_validator = EnumAttributeValidator.new('String', ["UPPER", "lower", ""])
return false unless enum_string_validator.valid?(@enum_string) return false unless enum_string_validator.valid?(@enum_string)
enum_integer_validator = EnumAttributeValidator.new('Integer', ["1", "-1"]) enum_integer_validator = EnumAttributeValidator.new('Integer', ["1", "-1"])
return false unless enum_integer_validator.valid?(@enum_integer) return false unless enum_integer_validator.valid?(@enum_integer)
@ -112,7 +112,7 @@ module Petstore
# Custom attribute writer method checking allowed values (enum). # Custom attribute writer method checking allowed values (enum).
# @param [Object] enum_string Object to be assigned # @param [Object] enum_string Object to be assigned
def enum_string=(enum_string) def enum_string=(enum_string)
validator = EnumAttributeValidator.new('String', ["UPPER", "lower"]) validator = EnumAttributeValidator.new('String', ["UPPER", "lower", ""])
unless validator.valid?(enum_string) unless validator.valid?(enum_string)
fail ArgumentError, "invalid value for 'enum_string', must be one of #{validator.allowable_values}." fail ArgumentError, "invalid value for 'enum_string', must be one of #{validator.allowable_values}."
end end

View File

@ -12,6 +12,7 @@ open class EnumTest: JSONEncodable {
public enum EnumString: String { public enum EnumString: String {
case upper = "UPPER" case upper = "UPPER"
case lower = "lower" case lower = "lower"
case empty = ""
} }
public enum EnumInteger: Int32 { public enum EnumInteger: Int32 {
case number1 = 1 case number1 = 1

View File

@ -12,6 +12,7 @@ open class EnumTest: JSONEncodable {
public enum EnumString: String { public enum EnumString: String {
case upper = "UPPER" case upper = "UPPER"
case lower = "lower" case lower = "lower"
case empty = ""
} }
public enum EnumInteger: Int32 { public enum EnumInteger: Int32 {
case number1 = 1 case number1 = 1

View File

@ -12,6 +12,7 @@ open class EnumTest: JSONEncodable {
public enum EnumString: String { public enum EnumString: String {
case upper = "UPPER" case upper = "UPPER"
case lower = "lower" case lower = "lower"
case empty = ""
} }
public enum EnumInteger: Int32 { public enum EnumInteger: Int32 {
case number1 = 1 case number1 = 1

View File

@ -19,7 +19,9 @@ public class EnumTest {
public enum EnumStringEnum { public enum EnumStringEnum {
UPPER("UPPER"), UPPER("UPPER"),
LOWER("lower"); LOWER("lower"),
EMPTY("");
private String value; private String value;

View File

@ -1100,6 +1100,7 @@ definitions:
enum: enum:
- "UPPER" - "UPPER"
- "lower" - "lower"
- ""
enum_integer: enum_integer:
type: "integer" type: "integer"
format: "int32" format: "int32"

View File

@ -18,7 +18,9 @@ public class EnumTest {
public enum EnumStringEnum { public enum EnumStringEnum {
UPPER("UPPER"), UPPER("UPPER"),
LOWER("lower"); LOWER("lower"),
EMPTY("");
private String value; private String value;

View File

@ -31,7 +31,9 @@ public class EnumTest {
public enum EnumStringEnum { public enum EnumStringEnum {
UPPER("UPPER"), UPPER("UPPER"),
LOWER("lower"); LOWER("lower"),
EMPTY("");
private String value; private String value;

View File

@ -31,7 +31,9 @@ public class EnumTest {
public enum EnumStringEnum { public enum EnumStringEnum {
UPPER("UPPER"), UPPER("UPPER"),
LOWER("lower"); LOWER("lower"),
EMPTY("");
private String value; private String value;

View File

@ -18,7 +18,9 @@ public class EnumTest {
public enum EnumStringEnum { public enum EnumStringEnum {
UPPER("UPPER"), UPPER("UPPER"),
LOWER("lower"); LOWER("lower"),
EMPTY("");
private String value; private String value;

View File

@ -18,7 +18,9 @@ public class EnumTest {
public enum EnumStringEnum { public enum EnumStringEnum {
UPPER("UPPER"), UPPER("UPPER"),
LOWER("lower"); LOWER("lower"),
EMPTY("");
private String value; private String value;

View File

@ -18,7 +18,9 @@ public class EnumTest {
public enum EnumStringEnum { public enum EnumStringEnum {
UPPER("UPPER"), UPPER("UPPER"),
LOWER("lower"); LOWER("lower"),
EMPTY("");
private String value; private String value;