Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(68)

Unified Diff: tools/json_schema_compiler/cpp_type_generator.py

Issue 11827026: Overhaul JSON Schema Compiler to support a number of features required to (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: rebase Created 7 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « tools/json_schema_compiler/code.py ('k') | tools/json_schema_compiler/cpp_type_generator_test.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tools/json_schema_compiler/cpp_type_generator.py
diff --git a/tools/json_schema_compiler/cpp_type_generator.py b/tools/json_schema_compiler/cpp_type_generator.py
index deade36d839e2cbdf7b2d221aa10f9589f7d9cf4..296eb41440bbab0799a11a91d96a647337f915b9 100644
--- a/tools/json_schema_compiler/cpp_type_generator.py
+++ b/tools/json_schema_compiler/cpp_type_generator.py
@@ -3,8 +3,7 @@
# found in the LICENSE file.
from code import Code
-from model import PropertyType
-import any_helper
+from model import Namespace, PropertyType, Type
import cpp_util
import operator
import schema_util
@@ -24,42 +23,22 @@ class CppTypeGenerator(object):
if namespace and cpp_namespace:
self._namespace = namespace
self.AddNamespace(namespace, cpp_namespace)
+ else:
+ self._namespace = None
def AddNamespace(self, namespace, cpp_namespace):
"""Maps a model.Namespace to its C++ namespace name. All mappings are
beneath the root namespace.
"""
- for type_ in namespace.types:
- if type_ in self._type_namespaces:
- raise ValueError('Type %s is declared in both %s and %s' %
- (type_, namespace.name, self._type_namespaces[type_].name))
- self._type_namespaces[type_] = namespace
self._cpp_namespaces[namespace] = cpp_namespace
-
- def ExpandParams(self, params):
- """Returns the given parameters with PropertyType.CHOICES parameters
- expanded so that each choice is a separate parameter.
- """
- expanded = []
- for param in params:
- if param.type_ == PropertyType.CHOICES:
- for choice in param.choices.values():
- expanded.append(choice)
- else:
- expanded.append(param)
- return expanded
-
- def GetAllPossibleParameterLists(self, params):
- """Returns all possible parameter lists for the given set of parameters.
- Every combination of arguments passed to any of the PropertyType.CHOICES
- parameters will have a corresponding parameter list returned here.
- """
- if not params:
- return [[]]
- partial_parameter_lists = self.GetAllPossibleParameterLists(params[1:])
- return [[param] + partial_list
- for param in self.ExpandParams(params[:1])
- for partial_list in partial_parameter_lists]
+ for type_name in namespace.types:
+ # Allow $refs to refer to just 'Type' within namespaces. Otherwise they
+ # must be qualified with 'namespace.Type'.
+ type_aliases = ['%s.%s' % (namespace.name, type_name)]
+ if namespace is self._namespace:
+ type_aliases.append(type_name)
+ for alias in type_aliases:
+ self._type_namespaces[alias] = namespace
def GetCppNamespaceName(self, namespace):
"""Gets the mapped C++ namespace name for the given namespace relative to
@@ -95,106 +74,89 @@ class CppTypeGenerator(object):
return Code().Append('} // %s' %
self.GetCppNamespaceName(self._namespace))
- def GetEnumNoneValue(self, prop):
+ def GetEnumNoneValue(self, type_):
"""Gets the enum value in the given model.Property indicating no value has
been set.
"""
- return '%s_NONE' % self.GetReferencedProperty(prop).unix_name.upper()
+ return '%s_NONE' % self.FollowRef(type_).unix_name.upper()
- def GetEnumValue(self, prop, enum_value):
+ def GetEnumValue(self, type_, enum_value):
"""Gets the enum value of the given model.Property of the given type.
e.g VAR_STRING
"""
- return '%s_%s' % (self.GetReferencedProperty(prop).unix_name.upper(),
+ return '%s_%s' % (self.FollowRef(type_).unix_name.upper(),
cpp_util.Classname(enum_value.upper()))
- def GetChoicesEnumType(self, prop):
- """Gets the type of the enum for the given model.Property.
-
- e.g VarType
- """
- 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.
+ def GetCppType(self, type_, is_ptr=False, is_in_container=False):
+ """Translates a model.Property or model.Type into its C++ type.
If REF types from different namespaces are referenced, will resolve
using self._type_namespaces.
- Use pad_for_generics when using as a generic to avoid operator ambiguity.
+ Use |is_ptr| if the type is optional. This will wrap the type in a
+ scoped_ptr if possible (it is not possible to wrap an enum).
- 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.
+ Use |is_in_container| if the type is appearing in a collection, e.g. a
+ std::vector or std::map. This will wrap it in the correct type with spacing.
"""
cpp_type = None
- type_ = prop.type_ if not use_compiled_type else prop.compiled_type
-
- if type_ == PropertyType.REF:
- dependency_namespace = self._ResolveTypeNamespace(prop.ref_type)
+ if type_.property_type == PropertyType.REF:
+ ref = type_.ref_type
+ dependency_namespace = self._ResolveTypeNamespace(ref)
if not dependency_namespace:
- raise KeyError('Cannot find referenced type: %s' % prop.ref_type)
+ raise KeyError('Cannot find referenced type: %s' % ref)
if self._namespace != dependency_namespace:
cpp_type = '%s::%s' % (self._cpp_namespaces[dependency_namespace],
- schema_util.StripSchemaNamespace(prop.ref_type))
+ schema_util.StripSchemaNamespace(ref))
else:
- cpp_type = schema_util.StripSchemaNamespace(prop.ref_type)
- elif type_ == PropertyType.BOOLEAN:
+ cpp_type = schema_util.StripSchemaNamespace(ref)
+ elif type_.property_type == PropertyType.BOOLEAN:
cpp_type = 'bool'
- elif type_ == PropertyType.INTEGER:
+ elif type_.property_type == PropertyType.INTEGER:
cpp_type = 'int'
- elif type_ == PropertyType.INT64:
+ elif type_.property_type == PropertyType.INT64:
cpp_type = 'int64'
- elif type_ == PropertyType.DOUBLE:
+ elif type_.property_type == PropertyType.DOUBLE:
cpp_type = 'double'
- elif type_ == PropertyType.STRING:
+ elif type_.property_type == PropertyType.STRING:
cpp_type = 'std::string'
- elif type_ == PropertyType.ENUM:
- cpp_type = cpp_util.Classname(prop.name)
- elif type_ == PropertyType.ADDITIONAL_PROPERTIES:
- cpp_type = 'base::DictionaryValue'
- elif type_ == PropertyType.ANY:
- cpp_type = any_helper.ANY_CLASS
- elif type_ == PropertyType.OBJECT:
- cpp_type = cpp_util.Classname(prop.name)
- elif type_ == PropertyType.FUNCTION:
+ elif type_.property_type == PropertyType.ENUM:
+ cpp_type = cpp_util.Classname(type_.name)
+ elif type_.property_type == PropertyType.ANY:
+ cpp_type = 'base::Value'
+ elif (type_.property_type == PropertyType.OBJECT or
+ type_.property_type == PropertyType.CHOICES):
+ cpp_type = cpp_util.Classname(type_.name)
+ elif type_.property_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
+ # record these as empty DictionaryValues so that we know if the function
# was passed in or not.
cpp_type = 'base::DictionaryValue'
- elif type_ == PropertyType.ARRAY:
- item_type = prop.item_type
- if item_type.type_ == PropertyType.REF:
- item_type = self.GetReferencedProperty(item_type)
- if item_type.type_ in (
- PropertyType.REF, PropertyType.ANY, PropertyType.OBJECT):
- cpp_type = 'std::vector<linked_ptr<%s> > '
- else:
- cpp_type = 'std::vector<%s> '
- cpp_type = cpp_type % self.GetType(
- prop.item_type, pad_for_generics=True)
- elif type_ == PropertyType.BINARY:
+ elif type_.property_type == PropertyType.ARRAY:
+ item_cpp_type = self.GetCppType(type_.item_type, is_in_container=True)
+ cpp_type = 'std::vector<%s>' % cpp_util.PadForGenerics(item_cpp_type)
+ elif type_.property_type == PropertyType.BINARY:
cpp_type = 'std::string'
else:
- raise NotImplementedError(type_)
+ raise NotImplementedError('Cannot get type of %s' % type_.property_type)
+
+ # HACK: optional ENUM is represented elsewhere with a _NONE value, so it
+ # never needs to be wrapped in pointer shenanigans.
+ # TODO(kalman): change this - but it's an exceedingly far-reaching change.
+ if not self.FollowRef(type_).property_type == PropertyType.ENUM:
+ if is_in_container and (is_ptr or not self.IsCopyable(type_)):
+ cpp_type = 'linked_ptr<%s>' % cpp_util.PadForGenerics(cpp_type)
+ elif is_ptr:
+ cpp_type = 'scoped_ptr<%s>' % cpp_util.PadForGenerics(cpp_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 not self.IsEnumOrEnumRef(prop):
- cpp_type = 'scoped_ptr<%s> ' % cpp_type
- if pad_for_generics:
- return cpp_type
- return cpp_type.strip()
+ return cpp_type
+
+ def IsCopyable(self, type_):
+ return not (self.FollowRef(type_).property_type in (PropertyType.ANY,
+ PropertyType.ARRAY,
+ PropertyType.OBJECT,
+ PropertyType.CHOICES))
def GenerateForwardDeclarations(self):
"""Returns the forward declarations for self._namespace.
@@ -207,24 +169,17 @@ class CppTypeGenerator(object):
for namespace in sorted(namespace_type_dependencies.keys(),
key=operator.attrgetter('name')):
c.Append('namespace %s {' % namespace.name)
- for type_ in sorted(namespace_type_dependencies[namespace],
- key=schema_util.StripSchemaNamespace):
- type_name = schema_util.StripSchemaNamespace(type_)
- if namespace.types[type_].type_ == PropertyType.STRING:
- c.Append('typedef std::string %s;' % type_name)
- elif namespace.types[type_].type_ == PropertyType.ARRAY:
- c.Append('typedef std::vector<%(item_type)s> %(name)s;')
- c.Substitute({
- 'name': type_name,
- 'item_type': self.GetType(namespace.types[type_].item_type,
- wrap_optional=True)})
- # Enums cannot be forward declared.
- elif namespace.types[type_].type_ != PropertyType.ENUM:
- c.Append('struct %s;' % type_name)
+ for type_name in sorted(namespace_type_dependencies[namespace],
+ key=schema_util.StripSchemaNamespace):
+ simple_type_name = schema_util.StripSchemaNamespace(type_name)
+ type_ = namespace.types[simple_type_name]
+ # Add more ways to forward declare things as necessary.
+ if type_.property_type == PropertyType.OBJECT:
+ c.Append('struct %s;' % simple_type_name)
c.Append('}')
c.Concat(self.GetNamespaceStart())
for (name, type_) in self._namespace.types.items():
- if not type_.functions and type_.type_ == PropertyType.OBJECT:
+ if not type_.functions and type_.property_type == PropertyType.OBJECT:
c.Append('struct %s;' % schema_util.StripSchemaNamespace(name))
c.Concat(self.GetNamespaceEnd())
return c
@@ -244,37 +199,32 @@ class CppTypeGenerator(object):
c.Append('#include "base/json/json_writer.h"')
return c
- def _ResolveTypeNamespace(self, ref_type):
+ def _ResolveTypeNamespace(self, qualified_name):
"""Resolves a type, which must be explicitly qualified, to its enclosing
namespace.
"""
- if ref_type in self._type_namespaces:
- return self._type_namespaces[ref_type]
- raise KeyError('Cannot resolve type: %s. Maybe it needs a namespace prefix '
- 'if it comes from another namespace?' % ref_type)
- return None
+ namespace = self._type_namespaces.get(qualified_name, None)
+ if namespace is None:
+ raise KeyError('Cannot resolve type %s. Maybe it needs a prefix '
+ 'if it comes from another namespace?' % qualified_type)
+ return namespace
- def GetReferencedProperty(self, prop):
- """Returns the property a property of type REF is referring to.
+ def FollowRef(self, type_):
+ """Follows $ref link of types to resolve the concrete type a ref refers to.
If the property passed in is not of type PropertyType.REF, it will be
returned unchanged.
"""
- if prop.type_ != PropertyType.REF:
- return prop
- return self._ResolveTypeNamespace(prop.ref_type).types.get(prop.ref_type,
- None)
+ if not type_.property_type == PropertyType.REF:
+ return type_
+ ref = type_.ref_type
- def IsEnumOrEnumRef(self, prop):
- """Returns true if the property is an ENUM or a reference to an ENUM.
- """
- return self.GetReferencedProperty(prop).type_ == PropertyType.ENUM
+ without_namespace = ref
+ if '.' in ref:
+ without_namespace = ref.split('.', 1)[1]
- def IsEnumRef(self, prop):
- """Returns true if the property is a reference to an ENUM.
- """
- return (prop.type_ == PropertyType.REF and
- self.GetReferencedProperty(prop).type_ == PropertyType.ENUM)
+ # TODO(kalman): Do we need to keep on resolving?
+ return self._ResolveTypeNamespace(ref).types[without_namespace]
def _NamespaceTypeDependencies(self):
"""Returns a dict containing a mapping of model.Namespace to the C++ type
@@ -283,16 +233,16 @@ class CppTypeGenerator(object):
dependencies = set()
for function in self._namespace.functions.values():
for param in function.params:
- dependencies |= self._PropertyTypeDependencies(param)
+ dependencies |= self._TypeDependencies(param.type_)
if function.callback:
for param in function.callback.params:
- dependencies |= self._PropertyTypeDependencies(param)
+ dependencies |= self._TypeDependencies(param.type_)
for type_ in self._namespace.types.values():
for prop in type_.properties.values():
- dependencies |= self._PropertyTypeDependencies(prop)
+ dependencies |= self._TypeDependencies(prop.type_)
for event in self._namespace.events.values():
for param in event.params:
- dependencies |= self._PropertyTypeDependencies(param)
+ dependencies |= self._TypeDependencies(param.type_)
dependency_namespaces = dict()
for dependency in dependencies:
@@ -302,18 +252,17 @@ class CppTypeGenerator(object):
dependency_namespaces[namespace].append(dependency)
return dependency_namespaces
- def _PropertyTypeDependencies(self, prop):
+ def _TypeDependencies(self, type_):
"""Recursively gets all the type dependencies of a property.
"""
deps = set()
- if prop:
- if prop.type_ == PropertyType.REF:
- deps.add(prop.ref_type)
- elif prop.type_ == PropertyType.ARRAY:
- deps = self._PropertyTypeDependencies(prop.item_type)
- elif prop.type_ == PropertyType.OBJECT:
- for p in prop.properties.values():
- deps |= self._PropertyTypeDependencies(p)
+ if type_.property_type == PropertyType.REF:
+ deps.add(type_.ref_type)
+ elif type_.property_type == PropertyType.ARRAY:
+ deps = self._TypeDependencies(type_.item_type)
+ elif type_.property_type == PropertyType.OBJECT:
+ for p in type_.properties.values():
+ deps |= self._TypeDependencies(p.type_)
return deps
def GeneratePropertyValues(self, property, line, nodoc=False):
@@ -323,20 +272,19 @@ class CppTypeGenerator(object):
if not nodoc:
c.Comment(property.description)
- if property.has_value:
+ if property.value is not None:
c.Append(line % {
- "type": self._GetPrimitiveType(property.type_),
+ "type": self.GetCppType(property.type_),
"name": property.name,
"value": property.value
})
else:
has_child_code = False
c.Sblock('namespace %s {' % property.name)
- for child_property in property.properties.values():
- child_code = self.GeneratePropertyValues(
- child_property,
- line,
- nodoc=nodoc)
+ for child_property in property.type_.properties.values():
+ child_code = self.GeneratePropertyValues(child_property,
+ line,
+ nodoc=nodoc)
if child_code:
has_child_code = True
c.Concat(child_code)
@@ -344,16 +292,3 @@ class CppTypeGenerator(object):
if not has_child_code:
c = None
return c
-
- def _GetPrimitiveType(self, type_):
- """Like |GetType| but only accepts and returns C++ primitive types.
- """
- if type_ == PropertyType.BOOLEAN:
- return 'bool'
- elif type_ == PropertyType.INTEGER:
- return 'int'
- elif type_ == PropertyType.DOUBLE:
- return 'double'
- elif type_ == PropertyType.STRING:
- return 'char*'
- raise Exception(type_ + ' is not primitive')
« no previous file with comments | « tools/json_schema_compiler/code.py ('k') | tools/json_schema_compiler/cpp_type_generator_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698