1 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
6 from model
import PropertyType
8 from json_parse
import OrderedDict
11 class _TypeDependency(object):
12 """Contains information about a dependency a namespace has on a type: the
13 type's model, and whether that dependency is "hard" meaning that it cannot be
16 def __init__(self
, type_
, hard
=False):
21 return '%s.%s' % (self
.type_
.namespace
.name
, self
.type_
.name
)
24 class CppTypeGenerator(object):
25 """Manages the types of properties and provides utilities for getting the
26 C++ type out of a model.Property
28 def __init__(self
, model
, schema_loader
, default_namespace
=None):
29 """Creates a cpp_type_generator. The given root_namespace should be of the
30 format extensions::api::sub. The generator will generate code suitable for
31 use in the given model's namespace.
33 self
._default
_namespace
= default_namespace
34 if self
._default
_namespace
is None:
35 self
._default
_namespace
= model
.namespaces
.values()[0]
36 self
._schema
_loader
= schema_loader
38 def GetCppNamespaceName(self
, namespace
):
39 """Gets the mapped C++ namespace name for the given namespace relative to
42 return namespace
.unix_name
44 def GetNamespaceStart(self
):
45 """Get opening self._default_namespace namespace declaration.
47 return Code().Append('namespace %s {' %
48 self
.GetCppNamespaceName(self
._default
_namespace
))
50 def GetNamespaceEnd(self
):
51 """Get closing self._default_namespace namespace declaration.
53 return Code().Append('} // %s' %
54 self
.GetCppNamespaceName(self
._default
_namespace
))
56 def GetEnumNoneValue(self
, type_
):
57 """Gets the enum value in the given model.Property indicating no value has
60 return '%s_NONE' % self
.FollowRef(type_
).unix_name
.upper()
62 def GetEnumValue(self
, type_
, enum_value
):
63 """Gets the enum value of the given model.Property of the given type.
67 value
= cpp_util
.Classname(enum_value
.name
.upper())
68 if not type_
.cpp_omit_enum_type
:
69 value
= '%s_%s' % (self
.FollowRef(type_
).unix_name
.upper(), value
)
70 # To avoid collisions with built-in OS_* preprocessor definitions, we add a
71 # trailing slash to enum names that start with OS_.
72 if value
.startswith("OS_"):
76 def GetCppType(self
, type_
, is_ptr
=False, is_in_container
=False):
77 """Translates a model.Property or model.Type into its C++ type.
79 If REF types from different namespaces are referenced, will resolve
80 using self._schema_loader.
82 Use |is_ptr| if the type is optional. This will wrap the type in a
83 scoped_ptr if possible (it is not possible to wrap an enum).
85 Use |is_in_container| if the type is appearing in a collection, e.g. a
86 std::vector or std::map. This will wrap it in the correct type with spacing.
89 if type_
.property_type
== PropertyType
.REF
:
90 ref_type
= self
._FindType
(type_
.ref_type
)
92 raise KeyError('Cannot find referenced type: %s' % type_
.ref_type
)
93 if self
._default
_namespace
is ref_type
.namespace
:
94 cpp_type
= ref_type
.name
96 cpp_type
= '%s::%s' % (ref_type
.namespace
.unix_name
, ref_type
.name
)
97 elif type_
.property_type
== PropertyType
.BOOLEAN
:
99 elif type_
.property_type
== PropertyType
.INTEGER
:
101 elif type_
.property_type
== PropertyType
.INT64
:
103 elif type_
.property_type
== PropertyType
.DOUBLE
:
105 elif type_
.property_type
== PropertyType
.STRING
:
106 cpp_type
= 'std::string'
107 elif type_
.property_type
== PropertyType
.ENUM
:
108 cpp_type
= cpp_util
.Classname(type_
.name
)
109 elif type_
.property_type
== PropertyType
.ANY
:
110 cpp_type
= 'base::Value'
111 elif (type_
.property_type
== PropertyType
.OBJECT
or
112 type_
.property_type
== PropertyType
.CHOICES
):
113 cpp_type
= cpp_util
.Classname(type_
.name
)
114 elif type_
.property_type
== PropertyType
.FUNCTION
:
115 # Functions come into the json schema compiler as empty objects. We can
116 # record these as empty DictionaryValues so that we know if the function
117 # was passed in or not.
118 cpp_type
= 'base::DictionaryValue'
119 elif type_
.property_type
== PropertyType
.ARRAY
:
120 item_cpp_type
= self
.GetCppType(type_
.item_type
, is_in_container
=True)
121 cpp_type
= 'std::vector<%s>' % cpp_util
.PadForGenerics(item_cpp_type
)
122 elif type_
.property_type
== PropertyType
.BINARY
:
123 cpp_type
= 'std::string'
125 raise NotImplementedError('Cannot get type of %s' % type_
.property_type
)
127 # HACK: optional ENUM is represented elsewhere with a _NONE value, so it
128 # never needs to be wrapped in pointer shenanigans.
129 # TODO(kalman): change this - but it's an exceedingly far-reaching change.
130 if not self
.FollowRef(type_
).property_type
== PropertyType
.ENUM
:
131 if is_in_container
and (is_ptr
or not self
.IsCopyable(type_
)):
132 cpp_type
= 'linked_ptr<%s>' % cpp_util
.PadForGenerics(cpp_type
)
134 cpp_type
= 'scoped_ptr<%s>' % cpp_util
.PadForGenerics(cpp_type
)
138 def IsCopyable(self
, type_
):
139 return not (self
.FollowRef(type_
).property_type
in (PropertyType
.ANY
,
142 PropertyType
.CHOICES
))
144 def GenerateForwardDeclarations(self
):
145 """Returns the forward declarations for self._default_namespace.
149 for namespace
, dependencies
in self
._NamespaceTypeDependencies
().items():
150 c
.Append('namespace %s {' % namespace
.unix_name
)
151 for dependency
in dependencies
:
152 # No point forward-declaring hard dependencies.
155 # Add more ways to forward declare things as necessary.
156 if dependency
.type_
.property_type
in (PropertyType
.CHOICES
,
157 PropertyType
.OBJECT
):
158 c
.Append('struct %s;' % dependency
.type_
.name
)
163 def GenerateIncludes(self
, include_soft
=False):
164 """Returns the #include lines for self._default_namespace.
167 for namespace
, dependencies
in self
._NamespaceTypeDependencies
().items():
168 for dependency
in dependencies
:
169 if dependency
.hard
or include_soft
:
170 c
.Append('#include "%s/%s.h"' % (namespace
.source_file_dir
,
171 namespace
.unix_name
))
174 def _FindType(self
, full_name
):
175 """Finds the model.Type with name |qualified_name|. If it's not from
176 |self._default_namespace| then it needs to be qualified.
178 namespace
= self
._schema
_loader
.ResolveType(full_name
,
179 self
._default
_namespace
)
180 if namespace
is None:
181 raise KeyError('Cannot resolve type %s. Maybe it needs a prefix '
182 'if it comes from another namespace?' % full_name
)
183 return namespace
.types
[schema_util
.StripNamespace(full_name
)]
185 def FollowRef(self
, type_
):
186 """Follows $ref link of types to resolve the concrete type a ref refers to.
188 If the property passed in is not of type PropertyType.REF, it will be
191 if type_
.property_type
!= PropertyType
.REF
:
193 return self
.FollowRef(self
._FindType
(type_
.ref_type
))
195 def _NamespaceTypeDependencies(self
):
196 """Returns a dict ordered by namespace name containing a mapping of
197 model.Namespace to every _TypeDependency for |self._default_namespace|,
198 sorted by the type's name.
201 for function
in self
._default
_namespace
.functions
.values():
202 for param
in function
.params
:
203 dependencies |
= self
._TypeDependencies
(param
.type_
,
204 hard
=not param
.optional
)
205 if function
.callback
:
206 for param
in function
.callback
.params
:
207 dependencies |
= self
._TypeDependencies
(param
.type_
,
208 hard
=not param
.optional
)
209 for type_
in self
._default
_namespace
.types
.values():
210 for prop
in type_
.properties
.values():
211 dependencies |
= self
._TypeDependencies
(prop
.type_
,
212 hard
=not prop
.optional
)
213 for event
in self
._default
_namespace
.events
.values():
214 for param
in event
.params
:
215 dependencies |
= self
._TypeDependencies
(param
.type_
,
216 hard
=not param
.optional
)
218 # Make sure that the dependencies are returned in alphabetical order.
219 dependency_namespaces
= OrderedDict()
220 for dependency
in sorted(dependencies
, key
=_TypeDependency
.GetSortKey
):
221 namespace
= dependency
.type_
.namespace
222 if namespace
is self
._default
_namespace
:
224 if namespace
not in dependency_namespaces
:
225 dependency_namespaces
[namespace
] = []
226 dependency_namespaces
[namespace
].append(dependency
)
228 return dependency_namespaces
230 def _TypeDependencies(self
, type_
, hard
=False):
231 """Gets all the type dependencies of a property.
234 if type_
.property_type
== PropertyType
.REF
:
235 deps
.add(_TypeDependency(self
._FindType
(type_
.ref_type
), hard
=hard
))
236 elif type_
.property_type
== PropertyType
.ARRAY
:
237 # Non-copyable types are not hard because they are wrapped in linked_ptrs
238 # when generated. Otherwise they're typedefs, so they're hard (though we
239 # could generate those typedefs in every dependent namespace, but that
241 deps
= self
._TypeDependencies
(type_
.item_type
,
242 hard
=self
.IsCopyable(type_
.item_type
))
243 elif type_
.property_type
== PropertyType
.CHOICES
:
244 for type_
in type_
.choices
:
245 deps |
= self
._TypeDependencies
(type_
, hard
=self
.IsCopyable(type_
))
246 elif type_
.property_type
== PropertyType
.OBJECT
:
247 for p
in type_
.properties
.values():
248 deps |
= self
._TypeDependencies
(p
.type_
, hard
=not p
.optional
)
251 def GeneratePropertyValues(self
, property, line
, nodoc
=False):
252 """Generates the Code to display all value-containing properties.
256 c
.Comment(property.description
)
258 if property.value
is not None:
260 "type": self
.GetCppType(property.type_
),
261 "name": property.name
,
262 "value": property.value
265 has_child_code
= False
266 c
.Sblock('namespace %s {' % property.name
)
267 for child_property
in property.type_
.properties
.values():
268 child_code
= self
.GeneratePropertyValues(child_property
,
272 has_child_code
= True
274 c
.Eblock('} // namespace %s' % property.name
)
275 if not has_child_code
: