diff options
author | calamity@chromium.org <calamity@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-02-22 12:06:51 +0000 |
---|---|---|
committer | calamity@chromium.org <calamity@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-02-22 12:06:51 +0000 |
commit | 2111fff9b9b9cb753c2041bb101d91a5288ec642 (patch) | |
tree | af944382f924324f9a85b578efc44c4272cdec6c /tools/json_schema_compiler | |
parent | 800892e772f745e18f0a57058e9dc2696bc6a167 (diff) | |
download | chromium_src-2111fff9b9b9cb753c2041bb101d91a5288ec642.zip chromium_src-2111fff9b9b9cb753c2041bb101d91a5288ec642.tar.gz chromium_src-2111fff9b9b9cb753c2041bb101d91a5288ec642.tar.bz2 |
Add enum support to json_schema_compiler
json_schema_compiler: add support for enum properties and tests for enums
BUG=
TEST=
Review URL: http://codereview.chromium.org/9424045
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@123026 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'tools/json_schema_compiler')
-rw-r--r-- | tools/json_schema_compiler/cc_generator.py | 86 | ||||
-rw-r--r-- | tools/json_schema_compiler/compiler.py | 9 | ||||
-rw-r--r-- | tools/json_schema_compiler/cpp_type_generator.py | 21 | ||||
-rw-r--r-- | tools/json_schema_compiler/cpp_util.py | 1 | ||||
-rw-r--r-- | tools/json_schema_compiler/h_generator.py | 43 | ||||
-rw-r--r-- | tools/json_schema_compiler/model.py | 7 | ||||
-rw-r--r-- | tools/json_schema_compiler/test/enums.json | 89 | ||||
-rw-r--r-- | tools/json_schema_compiler/test/enums_unittest.cc | 131 | ||||
-rw-r--r-- | tools/json_schema_compiler/test/json_schema_compiler_tests.gyp | 1 |
9 files changed, 343 insertions, 45 deletions
diff --git a/tools/json_schema_compiler/cc_generator.py b/tools/json_schema_compiler/cc_generator.py index 24007d4..cc9214e 100644 --- a/tools/json_schema_compiler/cc_generator.py +++ b/tools/json_schema_compiler/cc_generator.py @@ -106,6 +106,8 @@ class CCGenerator(object): .Append() ) for prop in type_.properties.values(): + c.Concat(self._InitializePropertyToDefault(prop, 'out')) + for prop in type_.properties.values(): c.Concat(self._GenerateTypePopulateProperty(prop, 'dict', 'out')) (c.Append('return true;') .Eblock('}') @@ -165,14 +167,34 @@ class CCGenerator(object): var: variable with value to create from """ c = code.Code() - if prop.optional: - c.Sblock('if (%s.get())' % var) - if prop.type_ == PropertyType.ARRAY: - c.Append('%s;' % self._util_cc_helper.PopulateDictionaryFromArray( - prop, var, prop.name, dst)) + if prop.type_ == PropertyType.ENUM: + c.Sblock('switch (%s) {' % var) + for enum_value in prop.enum_values: + (c.Append('case %s: {' % + self._cpp_type_generator.GetEnumValue(prop, enum_value)) + .Append(' %s->SetWithoutPathExpansion(' + '"%s", Value::CreateStringValue("%s"));' % + (dst, prop.name, enum_value)) + .Append(' break;') + .Append('}') + ) + # C++ requires the entire enum to be handled by a switch. + if prop.optional: + (c.Append('case %s: {' % + self._cpp_type_generator.GetEnumNoneValue(prop)) + .Append(' break;') + .Append('}') + ) + c.Eblock('}') else: - c.Append('%s->SetWithoutPathExpansion("%s", %s);' % - (dst, prop.name, cpp_util.CreateValueFromSingleProperty(prop, var))) + if prop.optional: + c.Sblock('if (%s.get())' % var) + if prop.type_ == PropertyType.ARRAY: + c.Append('%s;' % self._util_cc_helper.PopulateDictionaryFromArray( + prop, var, prop.name, dst)) + else: + c.Append('%s->SetWithoutPathExpansion("%s", %s);' % + (dst, prop.name, cpp_util.CreateValueFromSingleProperty(prop, var))) return c def _GenerateFunction(self, function): @@ -242,6 +264,9 @@ class CCGenerator(object): ) c.Substitute({'classname': classname}) + for param in function.params: + c.Concat(self._InitializePropertyToDefault(param, 'params')) + for i, param in enumerate(function.params): # Any failure will cause this function to return. If any argument is # incorrect or missing, those following it are not processed. Note that @@ -327,13 +352,30 @@ class CCGenerator(object): # util_cc_helper deals with optional and required arrays (c.Append('ListValue* list = NULL;') .Append('if (!%(value_var)s->GetAsList(&list))') - .Append(' return %s;' % failure_value) + .Append(' return %(failure_value)s;') .Append('if (!%s)' % self._util_cc_helper.PopulateArrayFromList( prop, 'list', dst + '->' + prop.unix_name)) - .Append(' return %s;' % failure_value) + .Append(' return %(failure_value)s;') ) elif prop.type_ == PropertyType.CHOICES: return self._GeneratePopulateChoices(prop, value_var, dst, failure_value) + elif prop.type_ == PropertyType.ENUM: + (c.Append('std::string enum_temp;') + .Append('if (!%(value_var)s->GetAsString(&enum_temp))') + .Append(' return %(failure_value)s;') + ) + for i, enum_value in enumerate(prop.enum_values): + (c.Append( + ('if' if i == 0 else 'else if') + + '(enum_temp == "%s")' % enum_value) + .Append(' %s->%s = %s;' % ( + dst, + prop.unix_name, + self._cpp_type_generator.GetEnumValue(prop, enum_value))) + ) + (c.Append('else') + .Append(' return %(failure_value)s;') + ) else: raise NotImplementedError(prop.type_) c.Eblock('}') @@ -356,13 +398,11 @@ class CCGenerator(object): value_var: a Value* that should represent |prop|. dst: the object with |prop| as a member. failure_value: the value to return if |prop| cannot be extracted from - |value_var| + |value_var|. """ type_var = '%s->%s_type' % (dst, prop.unix_name) c = code.Code() - c.Append('%s = %s;' % - (type_var, self._cpp_type_generator.GetChoiceEnumNoneValue(prop))) c.Sblock('switch (%s->GetType()) {' % value_var) for choice in self._cpp_type_generator.GetExpandedChoicesInParams([prop]): (c.Sblock('case %s: {' % cpp_util.GetValueType(choice)) @@ -370,8 +410,8 @@ class CCGenerator(object): choice, value_var, dst, failure_value, check_type=False)) .Append('%s = %s;' % (type_var, - self._cpp_type_generator.GetChoiceEnumValue( - prop, choice.type_))) + self._cpp_type_generator.GetEnumValue( + prop, choice.type_.name))) .Append('break;') .Eblock('}') ) @@ -421,3 +461,21 @@ class CCGenerator(object): c.Eblock('}') return c + + def _InitializePropertyToDefault(self, prop, dst): + """Initialize a model.Property to its default value inside an object. + + dst: Type* + """ + c = code.Code() + if prop.type_ in (PropertyType.ENUM, PropertyType.CHOICES): + if prop.optional: + prop_name = prop.unix_name + if prop.type_ == PropertyType.CHOICES: + prop_name = prop.unix_name + '_type' + c.Append('%s->%s = %s;' % ( + dst, + prop_name, + self._cpp_type_generator.GetEnumNoneValue(prop))) + return c + diff --git a/tools/json_schema_compiler/compiler.py b/tools/json_schema_compiler/compiler.py index d5f0802..6d3dd63 100644 --- a/tools/json_schema_compiler/compiler.py +++ b/tools/json_schema_compiler/compiler.py @@ -78,10 +78,11 @@ if __name__ == '__main__': type_generator.AddNamespace( referenced_namespace, referenced_namespace.unix_name) - cc_generator = cc_generator.CCGenerator(namespace, type_generator) - cc_code = cc_generator.Generate().Render() - h_generator = h_generator.HGenerator(namespace, type_generator) - h_code = h_generator.Generate().Render() + + h_code = (h_generator.HGenerator(namespace, type_generator) + .Generate().Render()) + cc_code = (cc_generator.CCGenerator(namespace, type_generator) + .Generate().Render()) if dest_dir: with open( diff --git a/tools/json_schema_compiler/cpp_type_generator.py b/tools/json_schema_compiler/cpp_type_generator.py index 793ae13..186a616 100644 --- a/tools/json_schema_compiler/cpp_type_generator.py +++ b/tools/json_schema_compiler/cpp_type_generator.py @@ -82,20 +82,19 @@ class CppTypeGenerator(object): return Code().Append('} // %s' % self.GetCppNamespaceName(self._namespace)) - def GetChoiceEnumNoneValue(self, prop): - """Gets the enum value of the choices in the given model.Property - indicating no choice has been set. + def GetEnumNoneValue(self, prop): + """Gets the enum value in the given model.Property indicating no value has + been set. """ return '%s_NONE' % prop.unix_name.upper() - def GetChoiceEnumValue(self, prop, type_): - """Gets the enum value of the choices in the given model.Property of the - given type. + def GetEnumValue(self, prop, enum_value): + """Gets the enum value of the given model.Property of the given type. e.g VAR_STRING """ - assert prop.choices[type_] - return '%s_%s' % (prop.unix_name.upper(), type_.name) + return '%s_%s' % ( + prop.unix_name.upper(), cpp_util.Classname(enum_value.upper())) def GetChoicesEnumType(self, prop): """Gets the type of the enum for the given model.Property. @@ -133,6 +132,8 @@ class CppTypeGenerator(object): cpp_type = 'double' elif prop.type_ == PropertyType.STRING: cpp_type = 'std::string' + elif prop.type_ == PropertyType.ENUM: + cpp_type = cpp_util.Classname(prop.name) elif prop.type_ == PropertyType.ANY: cpp_type = 'DictionaryValue' elif prop.type_ == PropertyType.ARRAY: @@ -148,7 +149,9 @@ class CppTypeGenerator(object): else: raise NotImplementedError(prop.type_) - if wrap_optional and prop.optional: + # Enums aren't wrapped because C++ won't allow it. Optional enums have a + # NONE value generated instead. + if wrap_optional and prop.optional and prop.type_ != PropertyType.ENUM: cpp_type = 'scoped_ptr<%s> ' % cpp_type if pad_for_generics: return cpp_type diff --git a/tools/json_schema_compiler/cpp_util.py b/tools/json_schema_compiler/cpp_util.py index 1c4575a..83d5265 100644 --- a/tools/json_schema_compiler/cpp_util.py +++ b/tools/json_schema_compiler/cpp_util.py @@ -48,6 +48,7 @@ def GetValueType(prop): PropertyType.INTEGER: 'Value::TYPE_INTEGER', PropertyType.BOOLEAN: 'Value::TYPE_BOOLEAN', PropertyType.DOUBLE: 'Value::TYPE_DOUBLE', + PropertyType.ENUM: 'Value::TYPE_STRING', PropertyType.REF: 'Value::TYPE_DICTIONARY', PropertyType.OBJECT: 'Value::TYPE_DICTIONARY', PropertyType.ARRAY: 'Value::TYPE_LIST' diff --git a/tools/json_schema_compiler/h_generator.py b/tools/json_schema_compiler/h_generator.py index 7b83622..dfa4494 100644 --- a/tools/json_schema_compiler/h_generator.py +++ b/tools/json_schema_compiler/h_generator.py @@ -79,10 +79,36 @@ class HGenerator(object): ) return c + def _GenerateEnumDeclaration(self, enum_name, prop, values): + c = code.Code() + c.Sblock('enum %s {' % enum_name) + if prop.optional: + c.Append(self._cpp_type_generator.GetEnumNoneValue(prop) + ',') + for value in values: + c.Append(self._cpp_type_generator.GetEnumValue(prop, value) + ',') + (c.Eblock('};') + .Append() + ) + return c + def _GenerateFields(self, props): """Generates the field declarations when declaring a type. """ c = code.Code() + # Generate the enums needed for any fields with "choices" + for prop in props: + if prop.type_ == PropertyType.CHOICES: + enum_name = self._cpp_type_generator.GetChoicesEnumType(prop) + c.Concat(self._GenerateEnumDeclaration( + enum_name, + prop, + [choice.type_.name for choice in prop.choices.values()])) + c.Append('%s %s_type;' % (enum_name, prop.unix_name)) + elif prop.type_ == PropertyType.ENUM: + c.Concat(self._GenerateEnumDeclaration( + self._cpp_type_generator.GetType(prop), + prop, + prop.enum_values)) for prop in self._cpp_type_generator.GetExpandedChoicesInParams(props): if prop.description: c.Comment(prop.description) @@ -90,23 +116,6 @@ class HGenerator(object): self._cpp_type_generator.GetType(prop, wrap_optional=True), prop.unix_name)) c.Append() - # Generate the enums needed for any fields with "choices" - for prop in props: - if prop.type_ == PropertyType.CHOICES: - c.Sblock('enum %(enum_type)s {') - c.Append(self._cpp_type_generator.GetChoiceEnumNoneValue(prop) + ',') - for choice in prop.choices.values(): - c.Append( - self._cpp_type_generator.GetChoiceEnumValue(prop, choice.type_) - + ',') - (c.Eblock('};') - .Append() - .Append('%(enum_type)s %(name)s_type;') - ) - c.Substitute({ - 'enum_type': self._cpp_type_generator.GetChoicesEnumType(prop), - 'name': prop.unix_name - }) return c def _GenerateType(self, type_, serializable=True): diff --git a/tools/json_schema_compiler/model.py b/tools/json_schema_compiler/model.py index c8a3c9a..5ff394a 100644 --- a/tools/json_schema_compiler/model.py +++ b/tools/json_schema_compiler/model.py @@ -109,7 +109,6 @@ class Function(object): self.params.append(Property(param['name'], param)) assert (self.callback), self.name + " does not support callback" -# TODO(calamity): handle Enum class Property(object): """A property of a type OR a parameter to a function. @@ -137,6 +136,11 @@ class Property(object): if '$ref' in json: self.ref_type = json['$ref'] self.type_ = PropertyType.REF + elif 'enum' in json: + self.enum_values = [] + for value in json['enum']: + self.enum_values.append(value) + self.type_ = PropertyType.ENUM elif 'type' in json: json_type = json['type'] if json_type == 'string': @@ -220,6 +224,7 @@ class PropertyType(object): DOUBLE = _Info(True, "DOUBLE") BOOLEAN = _Info(True, "BOOLEAN") STRING = _Info(True, "STRING") + ENUM = _Info(False, "ENUM") ARRAY = _Info(False, "ARRAY") REF = _Info(False, "REF") CHOICES = _Info(False, "CHOICES") diff --git a/tools/json_schema_compiler/test/enums.json b/tools/json_schema_compiler/test/enums.json new file mode 100644 index 0000000..51e1fd41 --- /dev/null +++ b/tools/json_schema_compiler/test/enums.json @@ -0,0 +1,89 @@ +[ + { + "namespace": "enums", + "types": [ + { + "id": "EnumType", + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["one", "two", "three"] + } + } + }, + { + "id": "OptionalEnumType", + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["one", "two", "three"], + "optional": true + } + } + } + ], + "functions": [ + { + "name": "takesEnum", + "type": "function", + "description": "Takes an enum as its parameter.", + "parameters": [ + { + "name": "state", + "type": "string", + "enum": ["foo", "bar", "baz"] + }, + { + "name": "callback", + "type": "function", + "parameters": [] + } + ] + }, + { + "name": "takesOptionalEnum", + "type": "function", + "description": "Takes an enum as its parameter.", + "parameters": [ + { + "name": "state", + "type": "string", + "enum": ["foo", "bar", "baz"], + "optional": true + }, + { + "name": "callback", + "type": "function", + "parameters": [] + } + ] + }, + { + "name": "takesMultipleOptionalEnums", + "type": "function", + "description": "Takes two enums as parameters.", + "parameters": [ + { + "name": "state", + "type": "string", + "enum": ["foo", "bar", "baz"], + "optional": true + }, + { + "name": "type", + "type": "string", + "enum": ["foo", "ding", "dong"], + "optional": true + }, + { + "name": "callback", + "type": "function", + "parameters": [] + } + ] + } + ] + } +] diff --git a/tools/json_schema_compiler/test/enums_unittest.cc b/tools/json_schema_compiler/test/enums_unittest.cc new file mode 100644 index 0000000..14eece2 --- /dev/null +++ b/tools/json_schema_compiler/test/enums_unittest.cc @@ -0,0 +1,131 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "tools/json_schema_compiler/test/enums.h" + +#include "testing/gtest/include/gtest/gtest.h" + +using namespace test::api::enums; + +TEST(JsonSchemaCompilerEnumsTest, EnumTypePopulate) { + { + scoped_ptr<EnumType> enum_type(new EnumType()); + scoped_ptr<DictionaryValue> value(new DictionaryValue()); + value->Set("type", Value::CreateStringValue("one")); + EXPECT_TRUE(EnumType::Populate(*value, enum_type.get())); + EXPECT_EQ(EnumType::TYPE_ONE, enum_type->type); + EXPECT_TRUE(value->Equals(enum_type->ToValue().get())); + } + { + scoped_ptr<EnumType> enum_type(new EnumType()); + scoped_ptr<DictionaryValue> value(new DictionaryValue()); + value->Set("type", Value::CreateStringValue("invalid")); + EXPECT_FALSE(EnumType::Populate(*value, enum_type.get())); + } +} + +TEST(JsonSchemaCompilerEnumsTest, OptionalEnumTypePopulate) { + { + scoped_ptr<OptionalEnumType> enum_type(new OptionalEnumType()); + scoped_ptr<DictionaryValue> value(new DictionaryValue()); + value->Set("type", Value::CreateStringValue("two")); + EXPECT_TRUE(OptionalEnumType::Populate(*value, enum_type.get())); + EXPECT_EQ(OptionalEnumType::TYPE_TWO, enum_type->type); + EXPECT_TRUE(value->Equals(enum_type->ToValue().get())); + } + { + scoped_ptr<OptionalEnumType> enum_type(new OptionalEnumType()); + scoped_ptr<DictionaryValue> value(new DictionaryValue()); + EXPECT_TRUE(OptionalEnumType::Populate(*value, enum_type.get())); + EXPECT_EQ(OptionalEnumType::TYPE_NONE, enum_type->type); + EXPECT_TRUE(value->Equals(enum_type->ToValue().get())); + } + { + scoped_ptr<OptionalEnumType> enum_type(new OptionalEnumType()); + scoped_ptr<DictionaryValue> value(new DictionaryValue()); + value->Set("type", Value::CreateStringValue("invalid")); + EXPECT_FALSE(OptionalEnumType::Populate(*value, enum_type.get())); + } +} + +TEST(JsonSchemaCompilerEnumsTest, TakesEnumParamsCreate) { + { + scoped_ptr<ListValue> params_value(new ListValue()); + params_value->Append(Value::CreateStringValue("baz")); + scoped_ptr<TakesEnum::Params> params( + TakesEnum::Params::Create(*params_value)); + EXPECT_TRUE(params.get()); + EXPECT_EQ(TakesEnum::Params::STATE_BAZ, params->state); + } + { + scoped_ptr<ListValue> params_value(new ListValue()); + params_value->Append(Value::CreateStringValue("invalid")); + scoped_ptr<TakesEnum::Params> params( + TakesEnum::Params::Create(*params_value)); + EXPECT_FALSE(params.get()); + } +} + +TEST(JsonSchemaCompilerEnumsTest, TakesOptionalEnumParamsCreate) { + { + scoped_ptr<ListValue> params_value(new ListValue()); + params_value->Append(Value::CreateStringValue("baz")); + scoped_ptr<TakesOptionalEnum::Params> params( + TakesOptionalEnum::Params::Create(*params_value)); + EXPECT_TRUE(params.get()); + EXPECT_EQ(TakesOptionalEnum::Params::STATE_BAZ, params->state); + } + { + scoped_ptr<ListValue> params_value(new ListValue()); + scoped_ptr<TakesOptionalEnum::Params> params( + TakesOptionalEnum::Params::Create(*params_value)); + EXPECT_TRUE(params.get()); + EXPECT_EQ(TakesOptionalEnum::Params::STATE_NONE, params->state); + } + { + scoped_ptr<ListValue> params_value(new ListValue()); + params_value->Append(Value::CreateStringValue("invalid")); + scoped_ptr<TakesOptionalEnum::Params> params( + TakesOptionalEnum::Params::Create(*params_value)); + EXPECT_FALSE(params.get()); + } +} + +TEST(JsonSchemaCompilerEnumsTest, TakesMultipleOptionalEnumsParamsCreate) { + { + scoped_ptr<ListValue> params_value(new ListValue()); + params_value->Append(Value::CreateStringValue("foo")); + params_value->Append(Value::CreateStringValue("foo")); + scoped_ptr<TakesMultipleOptionalEnums::Params> params( + TakesMultipleOptionalEnums::Params::Create(*params_value)); + EXPECT_TRUE(params.get()); + EXPECT_EQ(TakesMultipleOptionalEnums::Params::STATE_FOO, params->state); + EXPECT_EQ(TakesMultipleOptionalEnums::Params::TYPE_FOO, params->type); + } + { + scoped_ptr<ListValue> params_value(new ListValue()); + params_value->Append(Value::CreateStringValue("foo")); + scoped_ptr<TakesMultipleOptionalEnums::Params> params( + TakesMultipleOptionalEnums::Params::Create(*params_value)); + EXPECT_TRUE(params.get()); + EXPECT_EQ(TakesMultipleOptionalEnums::Params::STATE_FOO, params->state); + EXPECT_EQ(TakesMultipleOptionalEnums::Params::TYPE_NONE, params->type); + } + { + scoped_ptr<ListValue> params_value(new ListValue()); + scoped_ptr<TakesMultipleOptionalEnums::Params> params( + TakesMultipleOptionalEnums::Params::Create(*params_value)); + EXPECT_TRUE(params.get()); + EXPECT_EQ(TakesMultipleOptionalEnums::Params::STATE_NONE, params->state); + EXPECT_EQ(TakesMultipleOptionalEnums::Params::TYPE_NONE, params->type); + } + { + scoped_ptr<ListValue> params_value(new ListValue()); + params_value->Append(Value::CreateStringValue("baz")); + params_value->Append(Value::CreateStringValue("invalid")); + scoped_ptr<TakesMultipleOptionalEnums::Params> params( + TakesMultipleOptionalEnums::Params::Create(*params_value)); + EXPECT_FALSE(params.get()); + } +} diff --git a/tools/json_schema_compiler/test/json_schema_compiler_tests.gyp b/tools/json_schema_compiler/test/json_schema_compiler_tests.gyp index 7781842..d22d83f 100644 --- a/tools/json_schema_compiler/test/json_schema_compiler_tests.gyp +++ b/tools/json_schema_compiler/test/json_schema_compiler_tests.gyp @@ -13,6 +13,7 @@ 'array.json', 'choices.json', 'crossref.json', + 'enums.json', 'simple_api.json', ], 'cc_dir': 'tools/json_schema_compiler/test', |