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.
5 """Generates java source files from a mojom.Module."""
12 from jinja2 import contextfilter
14 import mojom.generate.generator as generator
15 import mojom.generate.module as mojom
16 from mojom.generate.template_expander import UseJinja
19 GENERATOR_PREFIX = 'java'
23 _spec_to_java_type = {
24 mojom.BOOL.spec: 'boolean',
25 mojom.DCPIPE.spec: 'org.chromium.mojo.system.DataPipe.ConsumerHandle',
26 mojom.DOUBLE.spec: 'double',
27 mojom.DPPIPE.spec: 'org.chromium.mojo.system.DataPipe.ProducerHandle',
28 mojom.FLOAT.spec: 'float',
29 mojom.HANDLE.spec: 'org.chromium.mojo.system.UntypedHandle',
30 mojom.INT16.spec: 'short',
31 mojom.INT32.spec: 'int',
32 mojom.INT64.spec: 'long',
33 mojom.INT8.spec: 'byte',
34 mojom.MSGPIPE.spec: 'org.chromium.mojo.system.MessagePipeHandle',
35 mojom.NULLABLE_DCPIPE.spec:
36 'org.chromium.mojo.system.DataPipe.ConsumerHandle',
37 mojom.NULLABLE_DPPIPE.spec:
38 'org.chromium.mojo.system.DataPipe.ProducerHandle',
39 mojom.NULLABLE_HANDLE.spec: 'org.chromium.mojo.system.UntypedHandle',
40 mojom.NULLABLE_MSGPIPE.spec: 'org.chromium.mojo.system.MessagePipeHandle',
41 mojom.NULLABLE_SHAREDBUFFER.spec:
42 'org.chromium.mojo.system.SharedBufferHandle',
43 mojom.NULLABLE_STRING.spec: 'String',
44 mojom.SHAREDBUFFER.spec: 'org.chromium.mojo.system.SharedBufferHandle',
45 mojom.STRING.spec: 'String',
46 mojom.UINT16.spec: 'short',
47 mojom.UINT32.spec: 'int',
48 mojom.UINT64.spec: 'long',
49 mojom.UINT8.spec: 'byte',
52 _spec_to_decode_method = {
53 mojom.BOOL.spec: 'readBoolean',
54 mojom.DCPIPE.spec: 'readConsumerHandle',
55 mojom.DOUBLE.spec: 'readDouble',
56 mojom.DPPIPE.spec: 'readProducerHandle',
57 mojom.FLOAT.spec: 'readFloat',
58 mojom.HANDLE.spec: 'readUntypedHandle',
59 mojom.INT16.spec: 'readShort',
60 mojom.INT32.spec: 'readInt',
61 mojom.INT64.spec: 'readLong',
62 mojom.INT8.spec: 'readByte',
63 mojom.MSGPIPE.spec: 'readMessagePipeHandle',
64 mojom.NULLABLE_DCPIPE.spec: 'readConsumerHandle',
65 mojom.NULLABLE_DPPIPE.spec: 'readProducerHandle',
66 mojom.NULLABLE_HANDLE.spec: 'readUntypedHandle',
67 mojom.NULLABLE_MSGPIPE.spec: 'readMessagePipeHandle',
68 mojom.NULLABLE_SHAREDBUFFER.spec: 'readSharedBufferHandle',
69 mojom.NULLABLE_STRING.spec: 'readString',
70 mojom.SHAREDBUFFER.spec: 'readSharedBufferHandle',
71 mojom.STRING.spec: 'readString',
72 mojom.UINT16.spec: 'readShort',
73 mojom.UINT32.spec: 'readInt',
74 mojom.UINT64.spec: 'readLong',
75 mojom.UINT8.spec: 'readByte',
78 _java_primitive_to_boxed_type = {
89 def NameToComponent(name):
90 # insert '_' between anything and a Title name (e.g, HTTPEntry2FooBar ->
92 name = re.sub('([^_])([A-Z][^A-Z_]+)', r'\1_\2', name)
93 # insert '_' between non upper and start of upper blocks (e.g.,
94 # HTTP_Entry2_FooBar -> HTTP_Entry2_Foo_Bar)
95 name = re.sub('([^A-Z_])([A-Z])', r'\1_\2', name)
96 return [x.lower() for x in name.split('_')]
98 def CapitalizeFirst(string):
99 return string[0].upper() + string[1:]
101 def UpperCamelCase(name):
102 return ''.join([CapitalizeFirst(x) for x in NameToComponent(name)])
105 uccc = UpperCamelCase(name)
106 return uccc[0].lower() + uccc[1:]
108 def ConstantStyle(name):
109 components = NameToComponent(name)
110 if components[0] == 'k':
111 components = components[1:]
112 return '_'.join([x.upper() for x in components])
114 def GetNameForElement(element):
115 if (mojom.IsEnumKind(element) or mojom.IsInterfaceKind(element) or
116 mojom.IsStructKind(element)):
117 return UpperCamelCase(element.name)
118 if mojom.IsInterfaceRequestKind(element):
119 return GetNameForElement(element.kind)
120 if isinstance(element, (mojom.Method,
123 return CamelCase(element.name)
124 if isinstance(element, mojom.EnumValue):
125 return (UpperCamelCase(element.enum_name) + '.' +
126 ConstantStyle(element.name))
127 if isinstance(element, (mojom.NamedValue,
129 return ConstantStyle(element.name)
130 raise Exception("Unexpected element: " % element)
132 def GetInterfaceResponseName(method):
133 return UpperCamelCase(method.name + 'Response')
135 def ParseStringAttribute(attribute):
136 assert isinstance(attribute, basestring)
140 def DecodeMethod(context, kind, offset, bit):
141 def _DecodeMethodName(kind):
142 if mojom.IsAnyArrayKind(kind):
143 return _DecodeMethodName(kind.kind) + 's'
144 if mojom.IsEnumKind(kind):
145 return _DecodeMethodName(mojom.INT32)
146 if mojom.IsInterfaceRequestKind(kind):
147 return "readInterfaceRequest"
148 if mojom.IsInterfaceKind(kind):
149 return "readServiceInterface"
150 return _spec_to_decode_method[kind.spec]
151 methodName = _DecodeMethodName(kind)
152 additionalParams = ''
153 if (kind == mojom.BOOL):
154 additionalParams = ', %d' % bit
155 if mojom.IsInterfaceKind(kind):
156 additionalParams = ', %s.BUILDER' % GetJavaType(context, kind)
157 if mojom.IsAnyArrayKind(kind) and mojom.IsInterfaceKind(kind.kind):
158 additionalParams = ', %s.BUILDER' % GetJavaType(context, kind.kind)
159 return '%s(%s%s)' % (methodName, offset, additionalParams)
162 def EncodeMethod(context, kind, variable, offset, bit):
163 additionalParams = ''
164 if (kind == mojom.BOOL):
165 additionalParams = ', %d' % bit
166 if mojom.IsInterfaceKind(kind):
167 additionalParams = ', %s.BUILDER' % GetJavaType(context, kind)
168 if mojom.IsAnyArrayKind(kind) and mojom.IsInterfaceKind(kind.kind):
169 additionalParams = ', %s.BUILDER' % GetJavaType(context, kind.kind)
170 return 'encode(%s, %s%s)' % (variable, offset, additionalParams)
172 def GetPackage(module):
173 if 'JavaPackage' in module.attributes:
174 return ParseStringAttribute(module.attributes['JavaPackage'])
176 return "org.chromium.mojom." + module.namespace
178 def GetNameForKind(context, kind):
179 def _GetNameHierachy(kind):
182 hierachy = _GetNameHierachy(kind.parent_kind)
183 hierachy.append(GetNameForElement(kind))
186 module = context.resolve('module')
188 if GetPackage(module) != GetPackage(kind.module):
189 elements += [GetPackage(kind.module)]
190 elements += _GetNameHierachy(kind)
191 return '.'.join(elements)
193 def GetBoxedJavaType(context, kind):
194 unboxed_type = GetJavaType(context, kind, False)
195 if unboxed_type in _java_primitive_to_boxed_type:
196 return _java_primitive_to_boxed_type[unboxed_type]
200 def GetJavaType(context, kind, boxed=False):
202 return GetBoxedJavaType(context, kind)
203 if mojom.IsStructKind(kind) or mojom.IsInterfaceKind(kind):
204 return GetNameForKind(context, kind)
205 if mojom.IsInterfaceRequestKind(kind):
206 return ("org.chromium.mojo.bindings.InterfaceRequest<%s>" %
207 GetNameForKind(context, kind.kind))
208 if mojom.IsAnyArrayKind(kind):
209 return "%s[]" % GetJavaType(context, kind.kind)
210 if mojom.IsEnumKind(kind):
212 return _spec_to_java_type[kind.spec]
215 def DefaultValue(context, field):
217 if isinstance(field.kind, mojom.Struct):
218 assert field.default == "default"
219 return "new %s()" % GetJavaType(context, field.kind)
221 GetJavaType(context, field.kind),
222 ExpressionToText(context, field.default, kind_spec=field.kind.spec))
225 def ConstantValue(context, constant):
227 GetJavaType(context, constant.kind),
228 ExpressionToText(context, constant.value, kind_spec=constant.kind.spec))
231 def NewArray(context, kind, size):
232 if mojom.IsAnyArrayKind(kind.kind):
233 return NewArray(context, kind.kind, size) + '[]'
234 return 'new %s[%s]' % (GetJavaType(context, kind.kind), size)
237 def ExpressionToText(context, token, kind_spec=''):
238 def _TranslateNamedValue(named_value):
239 entity_name = GetNameForElement(named_value)
240 if named_value.parent_kind:
241 return GetJavaType(context, named_value.parent_kind) + '.' + entity_name
242 # Handle the case where named_value is a module level constant:
243 if not isinstance(named_value, mojom.EnumValue):
244 entity_name = (GetConstantsMainEntityName(named_value.module) + '.' +
246 if GetPackage(named_value.module) == GetPackage(context.resolve('module')):
248 return GetPackage(named_value.module) + '.' + entity_name
250 if isinstance(token, mojom.NamedValue):
251 return _TranslateNamedValue(token)
252 if kind_spec.startswith('i') or kind_spec.startswith('u'):
253 # Add Long suffix to all integer literals.
254 number = ast.literal_eval(token.lstrip('+ '))
255 if not isinstance(number, (int, long)):
256 raise ValueError('got unexpected type %r for int literal %r' % (
257 type(number), token))
258 # If the literal is too large to fit a signed long, convert it to the
259 # equivalent signed long.
260 if number >= 2 ** 63:
262 return '%dL' % number
265 def IsPointerArrayKind(kind):
266 if not mojom.IsAnyArrayKind(kind):
269 return mojom.IsObjectKind(sub_kind)
271 def GetConstantsMainEntityName(module):
272 if 'JavaConstantsClassName' in module.attributes:
273 return ParseStringAttribute(module.attributes['JavaConstantsClassName'])
274 # This constructs the name of the embedding classes for module level constants
275 # by extracting the mojom's filename and prepending it to Constants.
276 return (UpperCamelCase(module.path.split('/')[-1].rsplit('.', 1)[0]) +
279 class Generator(generator.Generator):
282 "interface_response_name": GetInterfaceResponseName,
283 "constant_value": ConstantValue,
284 "default_value": DefaultValue,
285 "decode_method": DecodeMethod,
286 "expression_to_text": ExpressionToText,
287 "encode_method": EncodeMethod,
288 "is_handle": mojom.IsNonInterfaceHandleKind,
289 "is_pointer_array_kind": IsPointerArrayKind,
290 "is_struct_kind": mojom.IsStructKind,
291 "java_type": GetJavaType,
292 "name": GetNameForElement,
293 "new_array": NewArray,
294 "struct_size": lambda ps: ps.GetTotalSize() + _HEADER_SIZE,
297 def GetJinjaExports(self):
299 "module": self.module,
300 "package": GetPackage(self.module),
303 @UseJinja("java_templates/enum.java.tmpl", filters=java_filters)
304 def GenerateEnumSource(self, enum):
305 exports = self.GetJinjaExports()
306 exports.update({"enum": enum})
309 @UseJinja("java_templates/struct.java.tmpl", filters=java_filters)
310 def GenerateStructSource(self, struct):
311 exports = self.GetJinjaExports()
312 exports.update({"struct": struct})
315 @UseJinja("java_templates/interface.java.tmpl", filters=java_filters)
316 def GenerateInterfaceSource(self, interface):
317 exports = self.GetJinjaExports()
318 exports.update({"interface": interface})
320 for client in self.module.interfaces:
321 if client.name == interface.client:
322 exports.update({"client": client})
325 @UseJinja("java_templates/constants.java.tmpl", filters=java_filters)
326 def GenerateConstantsSource(self, module):
327 exports = self.GetJinjaExports()
328 exports.update({"main_entity": GetConstantsMainEntityName(module),
329 "constants": module.constants})
332 def GenerateFiles(self, unparsed_args):
333 parser = argparse.ArgumentParser()
334 parser.add_argument("--java_output_directory", dest="java_output_directory")
335 args = parser.parse_args(unparsed_args)
336 if self.output_dir and args.java_output_directory:
337 self.output_dir = os.path.join(args.java_output_directory,
338 GetPackage(self.module).replace('.', '/'))
339 if not os.path.exists(self.output_dir):
341 os.makedirs(self.output_dir)
343 # Ignore errors on directory creation.
346 for enum in self.module.enums:
347 self.Write(self.GenerateEnumSource(enum),
348 "%s.java" % GetNameForElement(enum))
350 for struct in self.module.structs:
351 self.Write(self.GenerateStructSource(struct),
352 "%s.java" % GetNameForElement(struct))
354 for interface in self.module.interfaces:
355 self.Write(self.GenerateInterfaceSource(interface),
356 "%s.java" % GetNameForElement(interface))
358 if self.module.constants:
359 self.Write(self.GenerateConstantsSource(self.module),
360 "%s.java" % GetConstantsMainEntityName(self.module))
362 def GetJinjaParameters(self):
364 'lstrip_blocks': True,
368 def GetGlobals(self):
370 'module': self.module,