| 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')
|
|
|