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 GetEnumNoneValue(self
, type_
):
39 """Gets the enum value in the given model.Property indicating no value has
42 return '%s_NONE' % self
.FollowRef(type_
).unix_name
.upper()
44 def GetEnumLastValue(self
, type_
):
45 """Gets the enum value in the given model.Property indicating the last value
48 return '%s_LAST' % self
.FollowRef(type_
).unix_name
.upper()
50 def GetEnumValue(self
, type_
, enum_value
):
51 """Gets the enum value of the given model.Property of the given type.
55 value
= cpp_util
.Classname(enum_value
.name
.upper())
56 prefix
= (type_
.cpp_enum_prefix_override
or
57 self
.FollowRef(type_
).unix_name
)
58 value
= '%s_%s' % (prefix
.upper(), value
)
59 # To avoid collisions with built-in OS_* preprocessor definitions, we add a
60 # trailing slash to enum names that start with OS_.
61 if value
.startswith("OS_"):
65 def GetCppType(self
, type_
, is_ptr
=False, is_in_container
=False):
66 """Translates a model.Property or model.Type into its C++ type.
68 If REF types from different namespaces are referenced, will resolve
69 using self._schema_loader.
71 Use |is_ptr| if the type is optional. This will wrap the type in a
72 scoped_ptr if possible (it is not possible to wrap an enum).
74 Use |is_in_container| if the type is appearing in a collection, e.g. a
75 std::vector or std::map. This will wrap it in the correct type with spacing.
78 if type_
.property_type
== PropertyType
.REF
:
79 ref_type
= self
._FindType
(type_
.ref_type
)
81 raise KeyError('Cannot find referenced type: %s' % type_
.ref_type
)
82 cpp_type
= self
.GetCppType(ref_type
)
83 elif type_
.property_type
== PropertyType
.BOOLEAN
:
85 elif type_
.property_type
== PropertyType
.INTEGER
:
87 elif type_
.property_type
== PropertyType
.INT64
:
89 elif type_
.property_type
== PropertyType
.DOUBLE
:
91 elif type_
.property_type
== PropertyType
.STRING
:
92 cpp_type
= 'std::string'
93 elif type_
.property_type
in (PropertyType
.ENUM
,
95 PropertyType
.CHOICES
):
96 if self
._default
_namespace
is type_
.namespace
:
97 cpp_type
= cpp_util
.Classname(type_
.name
)
99 cpp_namespace
= cpp_util
.GetCppNamespace(
100 type_
.namespace
.environment
.namespace_pattern
,
101 type_
.namespace
.unix_name
)
102 cpp_type
= '%s::%s' % (cpp_namespace
,
103 cpp_util
.Classname(type_
.name
))
104 elif type_
.property_type
== PropertyType
.ANY
:
105 cpp_type
= 'base::Value'
106 elif type_
.property_type
== PropertyType
.FUNCTION
:
107 # Functions come into the json schema compiler as empty objects. We can
108 # record these as empty DictionaryValues so that we know if the function
109 # was passed in or not.
110 cpp_type
= 'base::DictionaryValue'
111 elif type_
.property_type
== PropertyType
.ARRAY
:
112 item_cpp_type
= self
.GetCppType(type_
.item_type
, is_in_container
=True)
113 cpp_type
= 'std::vector<%s>' % cpp_util
.PadForGenerics(item_cpp_type
)
114 elif type_
.property_type
== PropertyType
.BINARY
:
115 cpp_type
= 'std::vector<char>'
117 raise NotImplementedError('Cannot get type of %s' % type_
.property_type
)
119 # HACK: optional ENUM is represented elsewhere with a _NONE value, so it
120 # never needs to be wrapped in pointer shenanigans.
121 # TODO(kalman): change this - but it's an exceedingly far-reaching change.
122 if not self
.FollowRef(type_
).property_type
== PropertyType
.ENUM
:
123 if is_in_container
and (is_ptr
or not self
.IsCopyable(type_
)):
124 cpp_type
= 'linked_ptr<%s>' % cpp_util
.PadForGenerics(cpp_type
)
126 cpp_type
= 'scoped_ptr<%s>' % cpp_util
.PadForGenerics(cpp_type
)
130 def IsCopyable(self
, type_
):
131 return not (self
.FollowRef(type_
).property_type
in (PropertyType
.ANY
,
134 PropertyType
.CHOICES
))
136 def GenerateForwardDeclarations(self
):
137 """Returns the forward declarations for self._default_namespace.
140 for namespace
, deps
in self
._NamespaceTypeDependencies
().iteritems():
143 # Add more ways to forward declare things as necessary.
145 dep
.type_
.property_type
in (PropertyType
.CHOICES
,
146 PropertyType
.OBJECT
))]
147 if not filtered_deps
:
150 cpp_namespace
= cpp_util
.GetCppNamespace(
151 namespace
.environment
.namespace_pattern
,
153 c
.Concat(cpp_util
.OpenNamespace(cpp_namespace
))
154 for dep
in filtered_deps
:
155 c
.Append('struct %s;' % dep
.type_
.name
)
156 c
.Concat(cpp_util
.CloseNamespace(cpp_namespace
))
159 def GenerateIncludes(self
, include_soft
=False):
160 """Returns the #include lines for self._default_namespace.
163 for namespace
, dependencies
in self
._NamespaceTypeDependencies
().items():
164 for dependency
in dependencies
:
165 if dependency
.hard
or include_soft
:
166 c
.Append('#include "%s/%s.h"' % (namespace
.source_file_dir
,
167 namespace
.unix_name
))
170 def _FindType(self
, full_name
):
171 """Finds the model.Type with name |qualified_name|. If it's not from
172 |self._default_namespace| then it needs to be qualified.
174 namespace
= self
._schema
_loader
.ResolveType(full_name
,
175 self
._default
_namespace
)
176 if namespace
is None:
177 raise KeyError('Cannot resolve type %s. Maybe it needs a prefix '
178 'if it comes from another namespace?' % full_name
)
179 return namespace
.types
[schema_util
.StripNamespace(full_name
)]
181 def FollowRef(self
, type_
):
182 """Follows $ref link of types to resolve the concrete type a ref refers to.
184 If the property passed in is not of type PropertyType.REF, it will be
187 if type_
.property_type
!= PropertyType
.REF
:
189 return self
.FollowRef(self
._FindType
(type_
.ref_type
))
191 def _NamespaceTypeDependencies(self
):
192 """Returns a dict ordered by namespace name containing a mapping of
193 model.Namespace to every _TypeDependency for |self._default_namespace|,
194 sorted by the type's name.
197 for function
in self
._default
_namespace
.functions
.values():
198 for param
in function
.params
:
199 dependencies |
= self
._TypeDependencies
(param
.type_
,
200 hard
=not param
.optional
)
201 if function
.callback
:
202 for param
in function
.callback
.params
:
203 dependencies |
= self
._TypeDependencies
(param
.type_
,
204 hard
=not param
.optional
)
205 for type_
in self
._default
_namespace
.types
.values():
206 for prop
in type_
.properties
.values():
207 dependencies |
= self
._TypeDependencies
(prop
.type_
,
208 hard
=not prop
.optional
)
209 for event
in self
._default
_namespace
.events
.values():
210 for param
in event
.params
:
211 dependencies |
= self
._TypeDependencies
(param
.type_
,
212 hard
=not param
.optional
)
214 # Make sure that the dependencies are returned in alphabetical order.
215 dependency_namespaces
= OrderedDict()
216 for dependency
in sorted(dependencies
, key
=_TypeDependency
.GetSortKey
):
217 namespace
= dependency
.type_
.namespace
218 if namespace
is self
._default
_namespace
:
220 if namespace
not in dependency_namespaces
:
221 dependency_namespaces
[namespace
] = []
222 dependency_namespaces
[namespace
].append(dependency
)
224 return dependency_namespaces
226 def _TypeDependencies(self
, type_
, hard
=False):
227 """Gets all the type dependencies of a property.
230 if type_
.property_type
== PropertyType
.REF
:
231 deps
.add(_TypeDependency(self
._FindType
(type_
.ref_type
), hard
=hard
))
232 elif type_
.property_type
== PropertyType
.ARRAY
:
233 # Non-copyable types are not hard because they are wrapped in linked_ptrs
234 # when generated. Otherwise they're typedefs, so they're hard (though we
235 # could generate those typedefs in every dependent namespace, but that
237 deps
= self
._TypeDependencies
(type_
.item_type
,
238 hard
=self
.IsCopyable(type_
.item_type
))
239 elif type_
.property_type
== PropertyType
.CHOICES
:
240 for type_
in type_
.choices
:
241 deps |
= self
._TypeDependencies
(type_
, hard
=self
.IsCopyable(type_
))
242 elif type_
.property_type
== PropertyType
.OBJECT
:
243 for p
in type_
.properties
.values():
244 deps |
= self
._TypeDependencies
(p
.type_
, hard
=not p
.optional
)
247 def GeneratePropertyValues(self
, prop
, line
, nodoc
=False):
248 """Generates the Code to display all value-containing properties.
252 c
.Comment(prop
.description
)
254 if prop
.value
is not None:
255 cpp_type
= self
.GetCppType(prop
.type_
)
256 cpp_value
= prop
.value
257 if cpp_type
== 'std::string':
258 cpp_value
= '"%s"' % cpp_type
265 has_child_code
= False
266 c
.Sblock('namespace %s {' % prop
.name
)
267 for child_property
in prop
.type_
.properties
.values():
268 child_code
= self
.GeneratePropertyValues(child_property
,
272 has_child_code
= True
274 c
.Eblock('} // namespace %s' % prop
.name
)
275 if not has_child_code
: