diff options
author | mitchellwrosen@chromium.org <mitchellwrosen@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-07-31 22:47:50 +0000 |
---|---|---|
committer | mitchellwrosen@chromium.org <mitchellwrosen@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-07-31 22:47:50 +0000 |
commit | 1d0f57e1473a43c653d9f7afb8a81435ed0bdfa8 (patch) | |
tree | 21bc3582153fbe5002fbca466181512c064f2240 /tools/json_schema_compiler | |
parent | 0933b60eecd7c538e7833331bdf665aff2a4490a (diff) | |
download | chromium_src-1d0f57e1473a43c653d9f7afb8a81435ed0bdfa8.zip chromium_src-1d0f57e1473a43c653d9f7afb8a81435ed0bdfa8.tar.gz chromium_src-1d0f57e1473a43c653d9f7afb8a81435ed0bdfa8.tar.bz2 |
Added JSON schema compiler support for serialized types
Currently supports string->int, string->int64, and int->string.
BUG=139076
Review URL: https://chromiumcodereview.appspot.com/10825029
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@149302 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'tools/json_schema_compiler')
-rw-r--r-- | tools/json_schema_compiler/cc_generator.py | 83 | ||||
-rw-r--r-- | tools/json_schema_compiler/cpp_type_generator.py | 44 | ||||
-rwxr-xr-x | tools/json_schema_compiler/cpp_type_generator_test.py | 11 | ||||
-rw-r--r-- | tools/json_schema_compiler/cpp_util.py | 48 | ||||
-rw-r--r-- | tools/json_schema_compiler/h_generator.py | 12 | ||||
-rw-r--r-- | tools/json_schema_compiler/model.py | 53 | ||||
-rwxr-xr-x | tools/json_schema_compiler/model_test.py | 2 |
7 files changed, 190 insertions, 63 deletions
diff --git a/tools/json_schema_compiler/cc_generator.py b/tools/json_schema_compiler/cc_generator.py index c7465651..6875187 100644 --- a/tools/json_schema_compiler/cc_generator.py +++ b/tools/json_schema_compiler/cc_generator.py @@ -253,20 +253,36 @@ class CCGenerator(object): else: if prop.optional: if prop.type_ == PropertyType.ENUM: - c.Sblock('if (%s != %s)' % + c.Sblock('if (%s != %s) {' % (prop.unix_name, self._cpp_type_generator.GetEnumNoneValue(prop))) elif prop.type_ == PropertyType.CHOICES: - c.Sblock('if (%s_type != %s)' % + c.Sblock('if (%s_type != %s) {' % (prop.unix_name, self._cpp_type_generator.GetEnumNoneValue(prop))) else: - c.Sblock('if (%s.get())' % prop.unix_name) - c.Append('value->SetWithoutPathExpansion("%s", %s);' % ( - prop.name, - self._CreateValueFromProperty(prop, 'this->' + prop.unix_name))) + c.Sblock('if (%s.get()) {' % prop.unix_name) + + if prop.type_ == prop.compiled_type: + c.Append('value->SetWithoutPathExpansion("%s", %s);' % ( + prop.name, + self._CreateValueFromProperty(prop, 'this->' + prop.unix_name))) + else: + conversion_src = 'this->' + prop.unix_name + if prop.optional: + conversion_src = '*' + conversion_src + (c.Append('%s %s;' % (self._cpp_type_generator.GetType(prop), + prop.unix_name)) + .Append(cpp_util.GenerateCompiledTypeToTypeConversion( + self._cpp_type_generator.GetReferencedProperty(prop), + conversion_src, + prop.unix_name) + ';') + .Append('value->SetWithoutPathExpansion("%s", %s);' % ( + prop.unix_name, + self._CreateValueFromProperty(prop, prop.unix_name))) + ) if prop.optional: - c.Eblock(); + c.Eblock('}'); (c.Append() .Append('return value.Pass();') .Eblock('}') @@ -337,7 +353,11 @@ class CCGenerator(object): self._cpp_type_generator.GetReferencedProperty(prop), var, prop.optional) elif self._IsFundamentalOrFundamentalRef(prop): - if prop.optional: + # If prop.type != prop.compiled_type, then no asterisk is necessary + # because the target is a local variable and not a dereferenced scoped + # pointer. The asterisk is instead prepended to conversion_src around line + # 273. + if prop.optional and prop.type_ == prop.compiled_type: var = '*' + var prop = self._cpp_type_generator.GetReferencedProperty(prop); return { @@ -448,16 +468,42 @@ class CCGenerator(object): value_var, '&temp')) .Append(' return %(failure_value)s;') - .Append('%(dst)s->%(name)s.reset(new %(ctype)s(temp));') ) + if prop.type_ != prop.compiled_type: + (c.Append('%(compiled_ctype)s temp2;') + .Append('if (!%s)' % + cpp_util.GenerateTypeToCompiledTypeConversion( + self._cpp_type_generator.GetReferencedProperty(prop), + 'temp', + 'temp2')) + .Append(' return %(failure_value)s;') + .Append('%(dst)s->%(name)s.reset(new %(compiled_ctype)s(temp2));') + ) + else: + c.Append('%(dst)s->%(name)s.reset(new %(ctype)s(temp));') + else: + if prop.type_ == prop.compiled_type: + assignment_target = '&%s->%s' % (dst, prop.unix_name) + else: + c.Append('%(ctype)s temp;') + assignment_target = '&temp' (c.Append('if (!%s)' % cpp_util.GetAsFundamentalValue( self._cpp_type_generator.GetReferencedProperty(prop), value_var, - '&%s->%s' % (dst, prop.unix_name))) + assignment_target)) .Append(' return %(failure_value)s;') ) + if prop.type_ != prop.compiled_type: + (c.Append('if (!%s)' % + cpp_util.GenerateTypeToCompiledTypeConversion( + self._cpp_type_generator.GetReferencedProperty(prop), + 'temp', + '%s->%s' % (dst, prop.unix_name))) + .Append(' return %(failure_value)s;') + ) + elif self._IsObjectOrObjectRef(prop): if prop.optional: (c.Append('const base::DictionaryValue* dictionary = NULL;') @@ -546,6 +592,7 @@ class CCGenerator(object): } if prop.type_ not in (PropertyType.CHOICES, PropertyType.ANY): sub['ctype'] = self._cpp_type_generator.GetType(prop) + sub['compiled_ctype'] = self._cpp_type_generator.GetCompiledType(prop) sub['value_type'] = cpp_util.GetValueType(self._cpp_type_generator .GetReferencedProperty(prop).type_) c.Substitute(sub) @@ -721,10 +768,20 @@ class CCGenerator(object): # scoped_ptr if it's optional. param_copy = param.Copy() param_copy.optional = False - c.Append('create_results->Append(%s);' % - self._CreateValueFromProperty(param_copy, param_copy.unix_name)) declaration_list.append("const %s" % cpp_util.GetParameterDeclaration( - param_copy, self._cpp_type_generator.GetType(param_copy))) + param_copy, self._cpp_type_generator.GetCompiledType(param_copy))) + param_name = param_copy.unix_name + if param_copy.type_ != param_copy.compiled_type: + param_name = 'temp_' + param_name + (c.Append('%s %s;' % (self._cpp_type_generator.GetType(param_copy), + param_name)) + .Append(cpp_util.GenerateCompiledTypeToTypeConversion( + param_copy, + param_copy.unix_name, + param_name) + ';') + ) + c.Append('create_results->Append(%s);' % + self._CreateValueFromProperty(param_copy, param_name)) c.Append('return create_results.Pass();') c.Eblock('}') diff --git a/tools/json_schema_compiler/cpp_type_generator.py b/tools/json_schema_compiler/cpp_type_generator.py index a305b75..a0f5752 100644 --- a/tools/json_schema_compiler/cpp_type_generator.py +++ b/tools/json_schema_compiler/cpp_type_generator.py @@ -116,6 +116,14 @@ class CppTypeGenerator(object): return cpp_util.Classname(prop.name) + 'Type' def GetType(self, prop, pad_for_generics=False, wrap_optional=False): + return self._GetTypeHelper(prop, pad_for_generics, wrap_optional) + + def GetCompiledType(self, prop, pad_for_generics=False, wrap_optional=False): + return self._GetTypeHelper(prop, pad_for_generics, wrap_optional, + use_compiled_type=True) + + def _GetTypeHelper(self, prop, pad_for_generics=False, wrap_optional=False, + use_compiled_type=False): """Translates a model.Property into its C++ type. If REF types from different namespaces are referenced, will resolve @@ -125,9 +133,13 @@ class CppTypeGenerator(object): Use wrap_optional to wrap the type in a scoped_ptr<T> if the Property is optional. + + Use use_compiled_type when converting from prop.type_ to prop.compiled_type. """ cpp_type = None - if prop.type_ == PropertyType.REF: + type_ = prop.type_ if not use_compiled_type else prop.compiled_type + + if type_ == PropertyType.REF: dependency_namespace = self._ResolveTypeNamespace(prop.ref_type) if not dependency_namespace: raise KeyError('Cannot find referenced type: %s' % prop.ref_type) @@ -136,28 +148,30 @@ class CppTypeGenerator(object): schema_util.StripSchemaNamespace(prop.ref_type)) else: cpp_type = schema_util.StripSchemaNamespace(prop.ref_type) - elif prop.type_ == PropertyType.BOOLEAN: + elif type_ == PropertyType.BOOLEAN: cpp_type = 'bool' - elif prop.type_ == PropertyType.INTEGER: + elif type_ == PropertyType.INTEGER: cpp_type = 'int' - elif prop.type_ == PropertyType.DOUBLE: + elif type_ == PropertyType.INT64: + cpp_type = 'int64' + elif type_ == PropertyType.DOUBLE: cpp_type = 'double' - elif prop.type_ == PropertyType.STRING: + elif type_ == PropertyType.STRING: cpp_type = 'std::string' - elif prop.type_ == PropertyType.ENUM: + elif type_ == PropertyType.ENUM: cpp_type = cpp_util.Classname(prop.name) - elif prop.type_ == PropertyType.ADDITIONAL_PROPERTIES: + elif type_ == PropertyType.ADDITIONAL_PROPERTIES: cpp_type = 'base::DictionaryValue' - elif prop.type_ == PropertyType.ANY: + elif type_ == PropertyType.ANY: cpp_type = any_helper.ANY_CLASS - elif prop.type_ == PropertyType.OBJECT: + elif type_ == PropertyType.OBJECT: cpp_type = cpp_util.Classname(prop.name) - elif prop.type_ == PropertyType.FUNCTION: + elif type_ == PropertyType.FUNCTION: # Functions come into the json schema compiler as empty objects. We can # record these as empty DictionaryValue's so that we know if the function # was passed in or not. cpp_type = 'base::DictionaryValue' - elif prop.type_ == PropertyType.ARRAY: + elif type_ == PropertyType.ARRAY: item_type = prop.item_type if item_type.type_ == PropertyType.REF: item_type = self.GetReferencedProperty(item_type) @@ -168,14 +182,14 @@ class CppTypeGenerator(object): cpp_type = 'std::vector<%s> ' cpp_type = cpp_type % self.GetType( prop.item_type, pad_for_generics=True) - elif prop.type_ == PropertyType.BINARY: + elif type_ == PropertyType.BINARY: cpp_type = 'std::string' else: - raise NotImplementedError(prop.type_) + raise NotImplementedError(type_) # 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: + if wrap_optional and prop.optional and type_ != PropertyType.ENUM: cpp_type = 'scoped_ptr<%s> ' % cpp_type if pad_for_generics: return cpp_type @@ -222,6 +236,8 @@ class CppTypeGenerator(object): self._cpp_namespaces[dependency]) for dependency in self._NamespaceTypeDependencies().keys()]): c.Append('#include "%s"' % header) + c.Append('#include "base/string_number_conversions.h"') + if self._namespace.events: c.Append('#include "base/json/json_writer.h"') return c diff --git a/tools/json_schema_compiler/cpp_type_generator_test.py b/tools/json_schema_compiler/cpp_type_generator_test.py index 17b501e..decf1e9 100755 --- a/tools/json_schema_compiler/cpp_type_generator_test.py +++ b/tools/json_schema_compiler/cpp_type_generator_test.py @@ -48,6 +48,7 @@ class CppTypeGeneratorTest(unittest.TestCase): manager = CppTypeGenerator('', self.windows, self.windows.unix_name) manager.AddNamespace(self.tabs, self.tabs.unix_name) self.assertEquals('#include "path/to/tabs.h"\n' + '#include "base/string_number_conversions.h"\n' '#include "base/json/json_writer.h"', manager.GenerateIncludes().Render()) self.assertEquals('namespace tabs {\n' @@ -58,7 +59,8 @@ class CppTypeGeneratorTest(unittest.TestCase): '} // windows', manager.GenerateForwardDeclarations().Render()) manager = CppTypeGenerator('', self.permissions, self.permissions.unix_name) - self.assertEquals('#include "base/json/json_writer.h"', + self.assertEquals('#include "base/string_number_conversions.h"\n' + '#include "base/json/json_writer.h"', manager.GenerateIncludes().Render()) self.assertEquals('namespace permissions {\n' 'struct Permissions;\n' @@ -66,7 +68,8 @@ class CppTypeGeneratorTest(unittest.TestCase): manager.GenerateForwardDeclarations().Render()) manager = CppTypeGenerator('', self.content_settings, self.content_settings.unix_name) - self.assertEquals('', manager.GenerateIncludes().Render()) + self.assertEquals('#include "base/string_number_conversions.h"', + manager.GenerateIncludes().Render()) def testGenerateIncludesAndForwardDeclarationsMultipleTypes(self): @@ -83,6 +86,7 @@ class CppTypeGeneratorTest(unittest.TestCase): manager = CppTypeGenerator('', windows, self.windows.unix_name) manager.AddNamespace(tabs_namespace, self.tabs.unix_name) self.assertEquals('#include "path/to/tabs.h"\n' + '#include "base/string_number_conversions.h"\n' '#include "base/json/json_writer.h"', manager.GenerateIncludes().Render()) self.assertEquals('namespace tabs {\n' @@ -109,7 +113,8 @@ class CppTypeGeneratorTest(unittest.TestCase): manager.AddNamespace(browser_action_namespace, self.browser_action.unix_name) self.assertEquals('#include "path/to/browser_action.h"\n' - '#include "path/to/font_settings.h"', + '#include "path/to/font_settings.h"\n' + '#include "base/string_number_conversions.h"', manager.GenerateIncludes().Render()) self.assertEquals('namespace browserAction {\n' 'typedef std::vector<int> ColorArray;\n' diff --git a/tools/json_schema_compiler/cpp_util.py b/tools/json_schema_compiler/cpp_util.py index 1229fb8..96a5cd3 100644 --- a/tools/json_schema_compiler/cpp_util.py +++ b/tools/json_schema_compiler/cpp_util.py @@ -75,9 +75,47 @@ def GetParameterDeclaration(param, type_): } def GenerateIfndefName(path, filename): - """Formats a path and filename as a #define name. + """Formats a path and filename as a #define name. - e.g chrome/extensions/gen, file.h becomes CHROME_EXTENSIONS_GEN_FILE_H__. - """ - return (('%s_%s_H__' % (path, filename)) - .upper().replace(os.sep, '_').replace('/', '_')) + e.g chrome/extensions/gen, file.h becomes CHROME_EXTENSIONS_GEN_FILE_H__. + """ + return (('%s_%s_H__' % (path, filename)) + .upper().replace(os.sep, '_').replace('/', '_')) + +def GenerateTypeToCompiledTypeConversion(prop, from_, to): + try: + return _GenerateTypeConversionHelper(prop.type_, prop.compiled_type, from_, + to) + except KeyError: + raise NotImplementedError('Conversion from %s to %s in %s not supported' % + (prop.type_, prop.compiled_type, prop.name)) + +def GenerateCompiledTypeToTypeConversion(prop, from_, to): + try: + return _GenerateTypeConversionHelper(prop.compiled_type, prop.type_, from_, + to) + except KeyError: + raise NotImplementedError('Conversion from %s to %s in %s not supported' % + (prop.compiled_type, prop.type_, prop.name)) + +def _GenerateTypeConversionHelper(from_type, to_type, from_, to): + """Converts from PropertyType from_type to PropertyType to_type. + + from_type: The PropertyType to be converted from. + to_type: The PropertyType to be converted to. + from_: The variable name of the type to be converted from. + to: The variable name of the type to be converted to. + """ + # TODO(mwrosen): Add support for more from/to combinations as necessary. + return { + PropertyType.STRING: { + PropertyType.INTEGER: 'base::StringToInt(%(from)s, &%(to)s)', + PropertyType.INT64: 'base::StringToInt64(%(from)s, &%(to)s)', + }, + PropertyType.INTEGER: { + PropertyType.STRING: '%(to)s = base::IntToString(%(from)s)', + }, + PropertyType.INT64: { + PropertyType.STRING: '%(to)s = base::Int64ToString(%(from)s)', + } + }[from_type][to_type] % {'from': from_, 'to': to} diff --git a/tools/json_schema_compiler/h_generator.py b/tools/json_schema_compiler/h_generator.py index 03879ca..e1d9f48 100644 --- a/tools/json_schema_compiler/h_generator.py +++ b/tools/json_schema_compiler/h_generator.py @@ -159,8 +159,8 @@ class HGenerator(object): if prop.description: c.Comment(prop.description) (c.Append('%s %s;' % ( - self._cpp_type_generator.GetType(prop, wrap_optional=True), - prop.unix_name)) + self._cpp_type_generator.GetCompiledType(prop, wrap_optional=True), + prop.unix_name)) .Append() ) return c @@ -183,8 +183,8 @@ class HGenerator(object): c.Comment(type_.description) c.Append('typedef std::vector<%(item_type)s> %(classname)s;') c.Substitute({'classname': classname, 'item_type': - self._cpp_type_generator.GetType(type_.item_type, - wrap_optional=True)}) + self._cpp_type_generator.GetCompiledType(type_.item_type, + wrap_optional=True)}) elif type_.type_ == PropertyType.STRING: if type_.description: c.Comment(type_.description) @@ -294,7 +294,7 @@ class HGenerator(object): [choice.type_.name for choice in prop.choices.values()])) c.Concat(self._GeneratePropertyStructures(prop.choices.values())) elif prop.type_ == PropertyType.ENUM: - enum_name = self._cpp_type_generator.GetType(prop) + enum_name = self._cpp_type_generator.GetCompiledType(prop) c.Concat(self._GenerateEnumDeclaration( enum_name, prop, @@ -339,7 +339,7 @@ class HGenerator(object): if param.description: c.Comment(param.description) declaration_list.append('const %s' % cpp_util.GetParameterDeclaration( - param, self._cpp_type_generator.GetType(param))) + param, self._cpp_type_generator.GetCompiledType(param))) c.Append('scoped_ptr<base::ListValue> Create(%s);' % ', '.join(declaration_list)) if generate_to_json: diff --git a/tools/json_schema_compiler/model.py b/tools/json_schema_compiler/model.py index 4de04c4..b7a094a5 100644 --- a/tools/json_schema_compiler/model.py +++ b/tools/json_schema_compiler/model.py @@ -151,6 +151,8 @@ class Property(object): - |optional| a boolean representing whether the property is optional - |description| a description of the property (if provided) - |type_| the model.PropertyType of this property + - |compiled_type| the model.PropertyType that this property should be + compiled to from the JSON. Defaults to |type_|. - |ref_type| the type that the REF property is referencing. Can be used to map to its model.Type - |item_type| a model.Property representing the type of each element in an @@ -190,40 +192,23 @@ class Property(object): self.enum_values.append(value) self.type_ = PropertyType.ENUM elif 'type' in json: - json_type = json['type'] - if json_type == 'string': - self.type_ = PropertyType.STRING - elif json_type == 'any': - self.type_ = PropertyType.ANY - elif json_type == 'boolean': - self.type_ = PropertyType.BOOLEAN - elif json_type == 'integer': - self.type_ = PropertyType.INTEGER - elif json_type == 'number': - self.type_ = PropertyType.DOUBLE - elif json_type == 'array': + self.type_ = self._JsonTypeToPropertyType(json['type']) + if self.type_ == PropertyType.ARRAY: self.item_type = Property(self, name + "Element", json['items'], from_json=from_json, from_client=from_client) - self.type_ = PropertyType.ARRAY - elif json_type == 'object': - self.type_ = PropertyType.OBJECT + elif self.type_ == PropertyType.OBJECT: # These members are read when this OBJECT Property is used as a Type type_ = Type(self, self.name, json) # self.properties will already have some value from |_AddProperties|. self.properties.update(type_.properties) self.functions = type_.functions - elif json_type == 'function': - self.type_ = PropertyType.FUNCTION - elif json_type == 'binary': - self.type_ = PropertyType.BINARY - else: - raise ParseException(self, 'type ' + json_type + ' not recognized') elif 'choices' in json: if not json['choices'] or len(json['choices']) == 0: raise ParseException(self, 'Choices has no choices') self.choices = {} self.type_ = PropertyType.CHOICES + self.compiled_type = self.type_ for choice_json in json['choices']: choice = Property(self, self.name, choice_json, from_json=from_json, @@ -237,6 +222,7 @@ class Property(object): self.value = json['value'] if type(self.value) == int: self.type_ = PropertyType.INTEGER + self.compiled_type = self.type_ else: # TODO(kalman): support more types as necessary. raise ParseException( @@ -244,6 +230,30 @@ class Property(object): else: raise ParseException( self, 'Property has no type, $ref, choices, or value') + if 'compiled_type' in json: + if 'type' in json: + self.compiled_type = self._JsonTypeToPropertyType(json['compiled_type']) + else: + raise ParseException(self, 'Property has compiled_type but no type') + else: + self.compiled_type = self.type_ + + def _JsonTypeToPropertyType(self, json_type): + try: + return { + 'any': PropertyType.ANY, + 'array': PropertyType.ARRAY, + 'binary': PropertyType.BINARY, + 'boolean': PropertyType.BOOLEAN, + 'integer': PropertyType.INTEGER, + 'int64': PropertyType.INT64, + 'function': PropertyType.FUNCTION, + 'number': PropertyType.DOUBLE, + 'object': PropertyType.OBJECT, + 'string': PropertyType.STRING, + }[json_type] + except KeyError: + raise NotImplementedError('Type %s not recognized' % json_type) def GetUnixName(self): """Gets the property's unix_name. Raises AttributeError if not set. @@ -288,6 +298,7 @@ class PropertyType(object): return self.name INTEGER = _Info(True, "INTEGER") + INT64 = _Info(True, "INT64") DOUBLE = _Info(True, "DOUBLE") BOOLEAN = _Info(True, "BOOLEAN") STRING = _Info(True, "STRING") diff --git a/tools/json_schema_compiler/model_test.py b/tools/json_schema_compiler/model_test.py index 880ae8d..8cd43e8 100755 --- a/tools/json_schema_compiler/model_test.py +++ b/tools/json_schema_compiler/model_test.py @@ -66,7 +66,7 @@ class ModelTest(unittest.TestCase): def testPropertyNotImplemented(self): (self.permissions_json[0]['types'][0] ['properties']['permissions']['type']) = 'something' - self.assertRaises(model.ParseException, self.model.AddNamespace, + self.assertRaises(NotImplementedError, self.model.AddNamespace, self.permissions_json[0], 'path/to/something.json') def testDescription(self): |