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
5import json
6import struct_generator
7
8def _JSONToCString16(json_string_literal):
9  """Converts a JSON string literal to a C++ UTF-16 string literal. This is
10  done by converting \\u#### to \\x####.
11  """
12  c_string_literal = json_string_literal
13  escape_index = c_string_literal.find('\\')
14  while escape_index > 0:
15    if c_string_literal[escape_index + 1] == 'u':
16      # We close the C string literal after the 4 hex digits and reopen it right
17      # after, otherwise the Windows compiler will sometimes try to get more
18      # than 4 characters in the hex string.
19      c_string_literal = (c_string_literal[0:escape_index + 1] + 'x' +
20          c_string_literal[escape_index + 2:escape_index + 6] + '" L"' +
21          c_string_literal[escape_index + 6:])
22    escape_index = c_string_literal.find('\\', escape_index + 6)
23  return c_string_literal
24
25def _GenerateString(content, lines):
26  """Generates an UTF-8 string to be included in a static structure initializer.
27  If content is not specified, uses NULL.
28  """
29  if content is None:
30    lines.append('  NULL,')
31  else:
32    # json.dumps quotes the string and escape characters as required.
33    lines.append('  %s,' % json.dumps(content))
34
35def _GenerateString16(content, lines):
36  """Generates an UTF-16 string to be included in a static structure
37  initializer. If content is not specified, uses NULL.
38  """
39  if content is None:
40    lines.append('  NULL,')
41  else:
42    # json.dumps quotes the string and escape characters as required.
43    lines.append('  L%s,' % _JSONToCString16(json.dumps(content)))
44
45def _GenerateArray(element_name, field_info, content, lines):
46  """Generates an array to be included in a static structure initializer. If
47  content is not specified, uses NULL. The array is assigned to a temporary
48  variable which is initialized before the structure.
49  """
50  if content is None:
51    lines.append('  NULL,')
52    lines.append('  0,')  # Size of the array.
53    return
54
55  # Create a new array variable and use it in the structure initializer.
56  # This prohibits nested arrays. Add a clash detection and renaming mechanism
57  # to solve the problem.
58  var = 'array_%s_%s' % (element_name, field_info['field']);
59  lines.append('  %s,' % var)
60  lines.append('  %s,' % len(content))  # Size of the array.
61  # Generate the array content.
62  array_lines = []
63  field_info['contents']['field'] = var;
64  array_lines.append(struct_generator.GenerateField(
65                     field_info['contents']) + '[] = {')
66  for subcontent in content:
67    GenerateFieldContent(element_name, field_info['contents'], subcontent,
68                         array_lines)
69  array_lines.append('};')
70  # Prepend the generated array so it is initialized before the structure.
71  lines.reverse()
72  array_lines.reverse()
73  lines.extend(array_lines)
74  lines.reverse()
75
76def GenerateFieldContent(element_name, field_info, content, lines):
77  """Generate the content of a field to be included in the static structure
78  initializer. If the field's content is not specified, uses the default value
79  if one exists.
80  """
81  if content is None:
82    content = field_info.get('default', None)
83  type = field_info['type']
84  if type == 'int' or type == 'enum':
85    lines.append('  %s,' % content)
86  elif type == 'string':
87    _GenerateString(content, lines)
88  elif type == 'string16':
89    _GenerateString16(content, lines)
90  elif type == 'array':
91    _GenerateArray(element_name, field_info, content, lines)
92  else:
93    raise RuntimeError('Unknown field type "%s"' % type)
94
95def GenerateElement(type_name, schema, element_name, element):
96  """Generate the static structure initializer for one element.
97  """
98  lines = [];
99  lines.append('const %s %s = {' % (type_name, element_name));
100  for field_info in schema:
101    content = element.get(field_info['field'], None)
102    if (content == None and not field_info.get('optional', False)):
103      raise RuntimeError('Mandatory field "%s" omitted in element "%s".' %
104                         (field_info['field'], element_name))
105    GenerateFieldContent(element_name, field_info, content, lines)
106  lines.append('};')
107  return '\n'.join(lines)
108
109def GenerateElements(type_name, schema, description):
110  """Generate the static structure initializer for all the elements in the
111  description['elements'] dictionary, as well as for any variables in
112  description['int_variables'].
113  """
114  result = [];
115  for var_name, value in description.get('int_variables', {}).items():
116    result.append('const int %s = %s;' % (var_name, value))
117  result.append('')
118
119  for element_name, element in description.get('elements', {}).items():
120    result.append(GenerateElement(type_name, schema, element_name, element))
121    result.append('')
122  return '\n'.join(result)
123