h_generator.py revision 5821806d5e7f356e8fa4b058a389a808ea183019
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.
4
5from code import Code
6from model import PropertyType
7import cpp_util
8import schema_util
9
10class HGenerator(object):
11  """A .h generator for a namespace.
12  """
13  def __init__(self, namespace, cpp_type_generator):
14    self._cpp_type_generator = cpp_type_generator
15    self._namespace = namespace
16    self._target_namespace = (
17        self._cpp_type_generator.GetCppNamespaceName(self._namespace))
18
19  def Generate(self):
20    """Generates a Code object with the .h for a single namespace.
21    """
22    c = Code()
23    (c.Append(cpp_util.CHROMIUM_LICENSE)
24      .Append()
25      .Append(cpp_util.GENERATED_FILE_MESSAGE % self._namespace.source_file)
26      .Append()
27    )
28
29    ifndef_name = cpp_util.GenerateIfndefName(self._namespace.source_file_dir,
30                                              self._target_namespace)
31    (c.Append('#ifndef %s' % ifndef_name)
32      .Append('#define %s' % ifndef_name)
33      .Append()
34      .Append('#include <string>')
35      .Append('#include <vector>')
36      .Append()
37      .Append('#include "base/basictypes.h"')
38      .Append('#include "base/logging.h"')
39      .Append('#include "base/memory/linked_ptr.h"')
40      .Append('#include "base/memory/scoped_ptr.h"')
41      .Append('#include "base/values.h"')
42      .Append('#include "tools/json_schema_compiler/any.h"')
43      .Append()
44    )
45
46    c.Concat(self._cpp_type_generator.GetRootNamespaceStart())
47    # TODO(calamity): These forward declarations should be #includes to allow
48    # $ref types from other files to be used as required params. This requires
49    # some detangling of windows and tabs which will currently lead to circular
50    # #includes.
51    forward_declarations = (
52        self._cpp_type_generator.GenerateForwardDeclarations())
53    if not forward_declarations.IsEmpty():
54      (c.Append()
55        .Concat(forward_declarations)
56        .Append()
57      )
58
59    c.Concat(self._cpp_type_generator.GetNamespaceStart())
60    c.Append()
61    if self._namespace.properties:
62      (c.Append('//')
63        .Append('// Properties')
64        .Append('//')
65        .Append()
66      )
67      for property in self._namespace.properties.values():
68        property_code = self._cpp_type_generator.GeneratePropertyValues(
69            property,
70            'extern const %(type)s %(name)s;')
71        if property_code:
72          c.Concat(property_code).Append()
73    if self._namespace.types:
74      (c.Append('//')
75        .Append('// Types')
76        .Append('//')
77        .Append()
78      )
79      for type_ in self._FieldDependencyOrder():
80        (c.Concat(self._GenerateType(type_))
81          .Append()
82        )
83    if self._namespace.functions:
84      (c.Append('//')
85        .Append('// Functions')
86        .Append('//')
87        .Append()
88      )
89      for function in self._namespace.functions.values():
90        (c.Concat(self._GenerateFunction(function))
91          .Append()
92        )
93    if self._namespace.events:
94      (c.Append('//')
95        .Append('// Events')
96        .Append('//')
97        .Append()
98      )
99      for event in self._namespace.events.values():
100        (c.Concat(self._GenerateEvent(event))
101          .Append()
102        )
103    (c.Concat(self._cpp_type_generator.GetNamespaceEnd())
104      .Concat(self._cpp_type_generator.GetRootNamespaceEnd())
105      .Append()
106      .Append('#endif  // %s' % ifndef_name)
107      .Append()
108    )
109    return c
110
111  def _FieldDependencyOrder(self):
112    """Generates the list of types in the current namespace in an order in which
113    depended-upon types appear before types which depend on them.
114    """
115    dependency_order = []
116
117    def ExpandType(path, type_):
118      if type_ in path:
119        raise ValueError("Illegal circular dependency via cycle " +
120                         ", ".join(map(lambda x: x.name, path + [type_])))
121      for prop in type_.properties.values():
122        if (prop.type_ == PropertyType.REF and
123            schema_util.GetNamespace(prop.ref_type) == self._namespace.name):
124          ExpandType(path + [type_], self._namespace.types[prop.ref_type])
125      if not type_ in dependency_order:
126        dependency_order.append(type_)
127
128    for type_ in self._namespace.types.values():
129      ExpandType([], type_)
130    return dependency_order
131
132  def _GenerateEnumDeclaration(self, enum_name, prop, values):
133    """Generate the declaration of a C++ enum for the given property and
134    values.
135    """
136    c = Code()
137    c.Sblock('enum %s {' % enum_name)
138    c.Append(self._cpp_type_generator.GetEnumNoneValue(prop) + ',')
139    for value in values:
140      c.Append(self._cpp_type_generator.GetEnumValue(prop, value) + ',')
141    (c.Eblock('};')
142      .Append()
143    )
144    return c
145
146  def _GenerateFields(self, props):
147    """Generates the field declarations when declaring a type.
148    """
149    c = Code()
150    # Generate the enums needed for any fields with "choices"
151    for prop in props:
152      if prop.type_ == PropertyType.CHOICES:
153        enum_name = self._cpp_type_generator.GetChoicesEnumType(prop)
154        c.Append('%s %s_type;' % (enum_name, prop.unix_name))
155        c.Append()
156
157    for prop in self._cpp_type_generator.ExpandParams(props):
158      if prop.description:
159        c.Comment(prop.description)
160      (c.Append('%s %s;' % (
161           self._cpp_type_generator.GetCompiledType(prop, wrap_optional=True),
162           prop.unix_name))
163        .Append()
164      )
165    return c
166
167  def _GenerateType(self, type_):
168    """Generates a struct for a type.
169    """
170    classname = cpp_util.Classname(schema_util.StripSchemaNamespace(type_.name))
171    c = Code()
172
173    if type_.functions:
174      c.Sblock('namespace %(classname)s {')
175      for function in type_.functions.values():
176        (c.Concat(self._GenerateFunction(function))
177          .Append()
178        )
179      c.Eblock('}')
180    elif type_.type_ == PropertyType.ARRAY:
181      if type_.description:
182        c.Comment(type_.description)
183      c.Append('typedef std::vector<%(item_type)s> %(classname)s;')
184      c.Substitute({'classname': classname, 'item_type':
185          self._cpp_type_generator.GetCompiledType(type_.item_type,
186                                                   wrap_optional=True)})
187    elif type_.type_ == PropertyType.STRING:
188      if type_.description:
189        c.Comment(type_.description)
190      c.Append('typedef std::string %(classname)s;')
191    elif type_.type_ == PropertyType.ENUM:
192      if type_.description:
193        c.Comment(type_.description)
194      c.Sblock('enum %(classname)s {')
195      c.Append('%s,' % self._cpp_type_generator.GetEnumNoneValue(type_))
196      for value in type_.enum_values:
197        c.Append('%s,' % self._cpp_type_generator.GetEnumValue(type_, value))
198      (c.Eblock('};')
199        .Append()
200        .Append('scoped_ptr<base::Value> CreateEnumValue(%s %s);' %
201                (classname, classname.lower()))
202        .Append('std::string ToString(%s enum_param);' % classname)
203        .Append('%s From%sString(const std::string& enum_string);' %
204            (classname, classname))
205      )
206    else:
207      if type_.description:
208        c.Comment(type_.description)
209      (c.Sblock('struct %(classname)s {')
210          .Append('~%(classname)s();')
211          .Append('%(classname)s();')
212          .Append()
213          .Concat(self._GeneratePropertyStructures(type_.properties.values()))
214          .Concat(self._GenerateFields(type_.properties.values()))
215      )
216      if type_.from_json:
217        (c.Comment('Populates a %s object from a base::Value. Returns'
218                   ' whether |out| was successfully populated.' % classname)
219          .Append('static bool Populate(const base::Value& value, '
220                  '%(classname)s* out);')
221          .Append()
222        )
223
224      if type_.from_client:
225        (c.Comment('Returns a new base::DictionaryValue representing the'
226                   ' serialized form of this %s object. Passes '
227                   'ownership to caller.' % classname)
228          .Append('scoped_ptr<base::DictionaryValue> ToValue() const;')
229        )
230
231      (c.Eblock()
232        .Sblock(' private:')
233          .Concat(self._GeneratePrivatePropertyStructures(
234              type_.properties.values()))
235          .Append()
236          .Append('DISALLOW_COPY_AND_ASSIGN(%(classname)s);')
237        .Eblock('};')
238      )
239    c.Substitute({'classname': classname})
240    return c
241
242  def _GenerateEvent(self, event):
243    """Generates the namespaces for an event.
244    """
245    c = Code()
246    (c.Sblock('namespace %s {' % cpp_util.Classname(event.name))
247        .Concat(self._GenerateCreateCallbackArguments(event,
248                                                      generate_to_json=True))
249      .Eblock('};')
250    )
251    return c
252
253  def _GenerateFunction(self, function):
254    """Generates the namespaces and structs for a function.
255    """
256    c = Code()
257    (c.Sblock('namespace %s {' % cpp_util.Classname(function.name))
258        .Concat(self._GenerateFunctionParams(function))
259        .Append()
260    )
261    if function.callback:
262      (c.Concat(self._GenerateFunctionResults(function.callback))
263        .Append()
264      )
265    c.Eblock('};')
266
267    return c
268
269  def _GenerateFunctionParams(self, function):
270    """Generates the struct for passing parameters from JSON to a function.
271    """
272    c = Code()
273
274    if function.params:
275      (c.Sblock('struct Params {')
276          .Concat(self._GeneratePropertyStructures(function.params))
277          .Concat(self._GenerateFields(function.params))
278          .Append('~Params();')
279          .Append()
280          .Append('static scoped_ptr<Params> Create('
281                  'const base::ListValue& args);')
282        .Eblock()
283        .Sblock(' private:')
284          .Append('Params();')
285          .Append()
286          .Append('DISALLOW_COPY_AND_ASSIGN(Params);')
287        .Eblock('};')
288      )
289
290    return c
291
292  def _GeneratePropertyStructures(self, props):
293    """Generate the structures required by a property such as OBJECT classes
294    and enums.
295    """
296    c = Code()
297    for prop in props:
298      if prop.type_ == PropertyType.OBJECT:
299        c.Concat(self._GenerateType(prop))
300        c.Append()
301      elif prop.type_ == PropertyType.ARRAY:
302        c.Concat(self._GeneratePropertyStructures([prop.item_type]))
303        c.Append()
304      elif prop.type_ == PropertyType.CHOICES:
305        c.Concat(self._GenerateEnumDeclaration(
306            self._cpp_type_generator.GetChoicesEnumType(prop),
307            prop,
308            [choice.type_.name for choice in prop.choices.values()]))
309        c.Concat(self._GeneratePropertyStructures(prop.choices.values()))
310      elif prop.type_ == PropertyType.ENUM:
311        enum_name = self._cpp_type_generator.GetCompiledType(prop)
312        c.Concat(self._GenerateEnumDeclaration(
313            enum_name,
314            prop,
315            prop.enum_values))
316        create_enum_value = ('scoped_ptr<base::Value> CreateEnumValue(%s %s);' %
317                            (enum_name, prop.unix_name))
318        enum_to_string = 'std::string ToString(%s enum_param);' % enum_name
319        enum_from_string = ('%s From%sString(const std::string& enum_string);' %
320            (enum_name, enum_name))
321        # If the property is from the UI then we're in a struct so this function
322        # should be static. If it's from the client, then we're just in a
323        # namespace so we can't have the static keyword.
324        if prop.from_json:
325          create_enum_value = 'static %s' % create_enum_value
326          enum_to_string = 'static %s' % enum_to_string
327          enum_from_string = 'static %s' % enum_from_string
328        (c.Append(create_enum_value)
329          .Append(enum_to_string)
330          .Append(enum_from_string))
331    return c
332
333  def _GeneratePrivatePropertyStructures(self, props):
334    """Generate the private structures required by a property such as OBJECT
335    classes and enums.
336    """
337    c = Code()
338    for prop in props:
339      if prop.type_ == PropertyType.ARRAY:
340        c.Concat(self._GeneratePrivatePropertyStructures([prop.item_type]))
341        c.Append()
342      elif prop.type_ == PropertyType.CHOICES:
343        # We only need GetChoiceValue() if there is a ToValue() method.
344        if prop.from_client:
345          c.Append('scoped_ptr<base::Value> Get%sChoiceValue() const;' % (
346              cpp_util.Classname(prop.name)))
347    return c
348
349  def _GenerateCreateCallbackArguments(self, function, generate_to_json=False):
350    """Generates functions for passing paramaters to a callback.
351    """
352    c = Code()
353    params = function.params
354    c.Concat(self._GeneratePropertyStructures(params))
355
356    param_lists = self._cpp_type_generator.GetAllPossibleParameterLists(params)
357    for param_list in param_lists:
358      declaration_list = []
359      for param in param_list:
360        if param.description:
361          c.Comment(param.description)
362        declaration_list.append('const %s' % cpp_util.GetParameterDeclaration(
363            param, self._cpp_type_generator.GetCompiledType(param)))
364      c.Append('scoped_ptr<base::ListValue> Create(%s);' %
365               ', '.join(declaration_list))
366      if generate_to_json:
367        c.Append('std::string ToJson(%s);' % ', '.join(declaration_list))
368    return c
369
370  def _GenerateFunctionResults(self, callback):
371    """Generates namespace for passing a function's result back.
372    """
373    c = Code()
374    (c.Sblock('namespace Results {')
375        .Concat(self._GenerateCreateCallbackArguments(callback))
376      .Eblock('};')
377    )
378    return c
379