• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2014 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
5"""Generates java source files from a mojom.Module."""
6
7import argparse
8import os
9import re
10
11import mojom.generate.generator as generator
12import mojom.generate.module as mojom
13from mojom.generate.template_expander import UseJinja
14
15
16GENERATOR_PREFIX = 'java'
17
18_spec_to_java_type = {
19  'b':     'boolean',
20  'd':     'double',
21  'f':     'float',
22  'h:d:c': 'org.chromium.mojo.system.DataPipe.ConsumerHandle',
23  'h:d:p': 'org.chromium.mojo.system.DataPipe.ProducerHandle',
24  'h:m':   'org.chromium.mojo.system.MessagePipeHandle',
25  'h':     'org.chromium.mojo.system.UntypedHandle',
26  'h:s':   'org.chromium.mojo.system.SharedBufferHandle',
27  'i16':   'short',
28  'i32':   'int',
29  'i64':   'long',
30  'i8':    'byte',
31  's':     'String',
32  'u16':   'short',
33  'u32':   'int',
34  'u64':   'long',
35  'u8':    'byte',
36}
37
38
39def NameToComponent(name):
40  # insert '_' between anything and a Title name (e.g, HTTPEntry2FooBar ->
41  # HTTP_Entry2_FooBar)
42  name = re.sub('([^_])([A-Z][^A-Z_]+)', r'\1_\2', name)
43  # insert '_' between non upper and start of upper blocks (e.g.,
44  # HTTP_Entry2_FooBar -> HTTP_Entry2_Foo_Bar)
45  name = re.sub('([^A-Z_])([A-Z])', r'\1_\2', name)
46  return [x.lower() for x in name.split('_')]
47
48def CapitalizeFirst(string):
49  return string[0].upper() + string[1:]
50
51def UpperCamelCase(name):
52  return ''.join([CapitalizeFirst(x) for x in NameToComponent(name)])
53
54def CamelCase(name):
55  uccc = UpperCamelCase(name)
56  return uccc[0].lower() + uccc[1:]
57
58def ConstantStyle(name):
59  components = NameToComponent(name)
60  if components[0] == 'k':
61    components = components[1:]
62  return '_'.join([x.upper() for x in components])
63
64def GetNameForElement(element):
65  if (isinstance(element, mojom.Enum) or
66      isinstance(element, mojom.Interface) or
67      isinstance(element, mojom.Struct)):
68    return UpperCamelCase(element.name)
69  if (isinstance(element, mojom.Method) or
70      isinstance(element, mojom.Parameter) or
71      isinstance(element, mojom.Field)):
72    return CamelCase(element.name)
73  if isinstance(element,  mojom.EnumValue):
74    return (UpperCamelCase(element.enum_name) + '.' +
75            ConstantStyle(element.name))
76  if (isinstance(element, mojom.NamedValue) or
77      isinstance(element, mojom.Constant)):
78    return ConstantStyle(element.name)
79  raise Exception("Unexpected element: " % element)
80
81def ParseStringAttribute(attribute):
82  assert isinstance(attribute, basestring)
83  return attribute
84
85def GetPackage(module):
86  if 'JavaPackage' in module.attributes:
87    return ParseStringAttribute(module.attributes['JavaPackage'])
88  # Default package.
89  return "org.chromium.mojom." + module.namespace
90
91def GetNameForKind(kind):
92  def _GetNameHierachy(kind):
93    hierachy = []
94    if kind.parent_kind:
95      hierachy = _GetNameHierachy(kind.parent_kind)
96    hierachy.append(kind.name)
97    return hierachy
98
99  elements = [GetPackage(kind.module)]
100  elements += _GetNameHierachy(kind)
101  return '.'.join(elements)
102
103def GetJavaType(kind):
104  if isinstance(kind, (mojom.Struct, mojom.Interface)):
105    return GetNameForKind(kind)
106  if isinstance(kind, mojom.Array):
107    return "%s[]" % GetJavaType(kind.kind)
108  if isinstance(kind, mojom.Enum):
109    return "int"
110  return _spec_to_java_type[kind.spec]
111
112def ExpressionToText(token):
113  def _TranslateNamedValue(named_value):
114    entity_name = GetNameForElement(named_value)
115    if named_value.parent_kind:
116      return GetJavaType(named_value.parent_kind) + '.' + entity_name
117    # Handle the case where named_value is a module level constant:
118    if not isinstance(named_value, mojom.EnumValue):
119      entity_name = (GetConstantsMainEntityName(named_value.module) + '.' +
120                      entity_name)
121    return GetPackage(named_value.module) + '.' + entity_name
122
123  if isinstance(token, mojom.NamedValue):
124    return _TranslateNamedValue(token)
125  # Add Long suffix to all number literals.
126  if re.match('^[0-9]+$', token):
127    return token + 'L'
128  return token
129
130def GetConstantsMainEntityName(module):
131  if 'JavaConstantsClassName' in module.attributes:
132    return ParseStringAttribute(module.attributes['JavaConstantsClassName'])
133  # This constructs the name of the embedding classes for module level constants
134  # by extracting the mojom's filename and prepending it to Constants.
135  return (UpperCamelCase(module.path.split('/')[-1].rsplit('.', 1)[0]) +
136          'Constants')
137
138class Generator(generator.Generator):
139
140  java_filters = {
141    "expression_to_text": ExpressionToText,
142    "java_type": GetJavaType,
143    "name": GetNameForElement,
144  }
145
146  def GetJinjaExports(self):
147    return {
148      "module": self.module,
149      "package": GetPackage(self.module),
150    }
151
152  @UseJinja("java_templates/enum.java.tmpl", filters=java_filters,
153            lstrip_blocks=True, trim_blocks=True)
154  def GenerateEnumSource(self, enum):
155    exports = self.GetJinjaExports()
156    exports.update({"enum": enum})
157    return exports
158
159  @UseJinja("java_templates/constants.java.tmpl", filters=java_filters,
160            lstrip_blocks=True, trim_blocks=True)
161  def GenerateConstantsSource(self, module):
162    exports = self.GetJinjaExports()
163    exports.update({"main_entity": GetConstantsMainEntityName(module),
164                    "constants": module.constants})
165    return exports
166
167  def GenerateFiles(self, unparsed_args):
168    parser = argparse.ArgumentParser()
169    parser.add_argument("--java_output_directory", dest="java_output_directory")
170    args = parser.parse_args(unparsed_args)
171    if self.output_dir and args.java_output_directory:
172      self.output_dir = os.path.join(args.java_output_directory,
173                                     GetPackage(self.module).replace('.', '/'))
174      if not os.path.exists(self.output_dir):
175        try:
176          os.makedirs(self.output_dir)
177        except:
178          # Ignore errors on directory creation.
179          pass
180
181    for enum in self.module.enums:
182      self.Write(self.GenerateEnumSource(enum),
183                 "%s.java" % GetNameForElement(enum))
184
185    if self.module.constants:
186      self.Write(self.GenerateConstantsSource(self.module),
187                 "%s.java" % GetConstantsMainEntityName(self.module))
188