Chromium Code Reviews| Index: mojo/public/tools/bindings/generators/mojom_java_generator.py |
| diff --git a/mojo/public/tools/bindings/generators/mojom_java_generator.py b/mojo/public/tools/bindings/generators/mojom_java_generator.py |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..b514ecb553b7a7f43d798854dd03e516db22af2b |
| --- /dev/null |
| +++ b/mojo/public/tools/bindings/generators/mojom_java_generator.py |
| @@ -0,0 +1,272 @@ |
| +# Copyright 2013 The Chromium Authors. All rights reserved. |
| +# Use of this source code is governed by a BSD-style license that can be |
| +# found in the LICENSE file. |
| + |
| +"""Generates java source files from a mojom.Module.""" |
| + |
| +import argparse |
| +import os |
| + |
| +import mojom.generate.generator as generator |
| +import mojom.generate.module as mojom |
| +import mojom.generate.pack as pack |
| +from mojom.generate.template_expander import UseJinja |
| + |
| + |
| +GENERATOR_PREFIX = 'java' |
|
rmcilroy
2014/05/25 22:55:10
Is this needed - it doesn't seem to be referenced.
qsr
2014/05/26 08:41:20
It is used by mojo/public/tools/bindings/mojom_bin
|
| + |
| +_HEADER_SIZE = 8 |
| + |
| +_spec_to_java_type = { |
| + 'b': "boolean", |
| + 'd': "double", |
| + 'f': "float", |
| + 'h:d:c': "org.chromium.mojo.system.DataPipe.ConsumerHandle", |
| + 'h:d:p': "org.chromium.mojo.system.DataPipe.ProducerHandle", |
| + 'h:m': "org.chromium.mojo.system.MessagePipeHandle", |
| + 'h': "org.chromium.mojo.system.UntypedHandle", |
| + 'h:s': "org.chromium.mojo.system.SharedBufferHandle", |
| + 'i16': "short", |
| + 'i32': "int", |
| + 'i64': "long", |
| + 'i8': "byte", |
| + 's': "String", |
| + 'u16': "short", |
| + 'u32': "int", |
| + 'u64': "long", |
| + 'u8': "byte", |
| +} |
| + |
| +_spec_to_decode_method = { |
| + 'b': "readBoolean", |
| + 'd': "readDouble", |
| + 'f': "readFloat", |
| + 'h:d:c': "readConsumerHandle", |
| + 'h:d:p': "readProducerHandle", |
| + 'h:m': "readMessagePipeHandle", |
| + 'h': "readUntypedHandle", |
| + 'h:s': "readSharedBufferHandle", |
| + 'i16': "readShort", |
| + 'i32': "readInt", |
| + 'i64': "readLong", |
| + 'i8': "readByte", |
| + 's': "readString", |
| + 'u16': "readShort", |
| + 'u32': "readInt", |
| + 'u64': "readLong", |
| + 'u8': "readByte", |
| +} |
| + |
| + |
| +def DecodeMethod(kind): |
| + if isinstance(kind, mojom.Array): |
| + return DecodeMethod(kind.kind) + 's' |
| + if isinstance(kind, mojom.Enum): |
| + return DecodeMethod(mojom.INT32) |
| + if isinstance(kind, mojom.Interface): |
| + return "readServiceInterface" |
| + return _spec_to_decode_method[kind.spec] |
| + |
| +def NamespaceToArray(namespace): |
|
rmcilroy
2014/05/25 22:55:10
I think this should be called PackageToArray(packa
qsr
2014/05/26 08:41:20
Removed.
|
| + return namespace.split('.') |
| + |
| +def GetNameFromString(string): |
|
rmcilroy
2014/05/25 22:55:10
I would call this "UpperCamelCase" and move it wit
qsr
2014/05/26 08:41:20
Refactored naming function. This method doesn't ex
|
| + return CapitalizeFirst(CamelCase(string)) |
| + |
| +def GetName(kind): |
|
rmcilroy
2014/05/25 22:55:10
I would get rid of this method - the name is confu
qsr
2014/05/26 08:41:20
Done.
|
| + return GetNameFromString(kind.name) |
| + |
| +def GetPackage(module): |
| + if 'JavaPackage' in module.attributes: |
| + package = module.attributes['JavaPackage'] |
| + if isinstance(package, basestring): |
| + return package |
| + assert package[0] == 'EXPRESSION' |
|
rmcilroy
2014/05/25 22:55:10
It's probably clearer to include "else" branches e
qsr
2014/05/26 08:41:20
That's not the style other python file use. Revers
|
| + assert len(package[1]) == 1 |
| + return package[1][0][1:-1].encode('string_escape') |
| + return "org.chromium.mojom." + module.namespace |
| + |
| +def GetSuperClass(method): |
| + if method: |
| + if method.response_parameters: |
| + return "org.chromium.mojo.bindings.MessageWithRequestId" |
| + return "org.chromium.mojo.bindings.Message" |
| + return "org.chromium.mojo.bindings.Struct" |
|
rmcilroy
2014/05/25 22:55:10
I think this would be clearer as:
if not method:
qsr
2014/05/26 08:41:20
Method removed until further CL.
|
| + |
| +def GetFlags(method, is_parameter): |
|
rmcilroy
2014/05/25 22:55:10
Could you remove this until it's used in a later C
qsr
2014/05/26 08:41:20
Done.
|
| + if method.response_parameters: |
| + if is_parameter: |
| + return "MESSAGE_EXPECTS_RESPONSE_FLAG" |
| + return "MESSAGE_IS_RESPONSE_FLAG" |
| + return "0" |
| + |
| +def NewArray(kind, size): |
|
rmcilroy
2014/05/25 22:55:10
nit - move below GetMethodName
qsr
2014/05/26 08:41:20
Removed.
|
| + if isinstance(kind.kind, mojom.Array): |
| + return NewArray(kind.kind, size) + '[]' |
| + return 'new %s[%s]' % (GetJavaType(kind.kind), size) |
| + |
| +def GetNameHierachy(kind): |
|
rmcilroy
2014/05/25 22:55:10
Could you make GetNameHierarchy an inner function
qsr
2014/05/26 08:41:20
Done.
|
| + hierachy = [] |
| + if kind.parent_kind: |
| + hierachy = GetNameHierachy(kind.parent_kind) |
| + hierachy.append(kind.name) |
| + return hierachy |
| + |
| +def GetNameForKind(kind): |
| + elements = [GetPackage(kind.module)] |
| + elements += GetNameHierachy(kind) |
| + return '.'.join(elements) |
| + |
| +def GetJavaGetterPrefix(kind): |
| + if kind == mojom.BOOL: |
| + return "is" |
| + return "get" |
| + |
| +def GetJavaType(kind): |
| + if isinstance(kind, mojom.Struct): |
| + return GetNameForKind(kind) |
| + if isinstance(kind, mojom.Array): |
| + return "%s[]" % GetJavaType(kind.kind) |
| + if isinstance(kind, mojom.Interface): |
| + return "org.chromium.mojo.bindings.ServiceHandle<%s>" % GetNameForKind(kind) |
| + if isinstance(kind, mojom.Enum): |
| + return "int" |
| + return _spec_to_java_type[kind.spec] |
| + |
| +def TranslateConstants(token, module): |
| + if isinstance(token, (mojom.NamedValue, mojom.EnumValue)): |
| + if isinstance(token, mojom.EnumValue): |
| + entity_value = token.enum_name + '.' + token.name |
| + else: |
| + entity_value = ConstantName(token) |
| + if not token.parent_kind: |
| + entity_value = (GetConstantsMainEntityName(token.module) + |
| + '.' + entity_value) |
| + if token.parent_kind: |
| + return GetJavaType(token.parent_kind) + '.' + entity_value |
| + return GetPackage(token.module) + '.' + entity_value |
|
rmcilroy
2014/05/25 22:55:10
Could you please reduce this method to the bare mi
qsr
2014/05/26 08:41:20
This is mostly the bare minimum. A constant can re
|
| + return token |
| + |
| +def ExpressionToText(value, module): |
|
rmcilroy
2014/05/25 22:55:10
This looks the same as the method in the CPP bindi
qsr
2014/05/26 08:41:20
We can do this in a further CL if you do not mind.
rmcilroy
2014/05/26 11:13:48
Doing this in a separate cl is fine. BTW, I was on
|
| + if value[0] != "EXPRESSION": |
| + raise Exception("Expected EXPRESSION, got" + value) |
| + return "".join(generator.ExpressionMapper(value, |
| + lambda token: TranslateConstants(token, module))) |
| + |
| +def CapitalizeFirst(string): |
| + return string[0].upper() + string[1:] |
| + |
| +def CamelCase(string): |
| + elements = string.split('_') |
| + return elements[0] + ''.join([x.capitalize() for x in elements[1:]]) |
| + |
| +def UpperCaseConstant(name): |
| + def _IsCut(i): |
| + if i == 0 or i == len(name): |
| + return True |
| + if name[i].islower(): |
| + return False |
| + if name[i-1].isupper() and (i == len(name) -1 or name[i+1].isupper()): |
| + return False |
| + return True |
| + pos = [i for i in xrange(len(name) + 1) if _IsCut(i)] |
| + parts = [name[pos[j]:pos[j+1]] for j in xrange(len(pos)-1)] |
| + return '_'.join([x.upper() for x in parts]) |
|
rmcilroy
2014/05/25 22:55:10
I think you could do this more easily with a regex
qsr
2014/05/26 08:41:20
This has been completely refactored.
|
| + |
| +def DefaultValueConstant(field): |
| + return 'DEFAULT_' + UpperCaseConstant(GetName(field)) |
|
rmcilroy
2014/05/25 22:55:10
Please remove this until the CL where it is used.
qsr
2014/05/26 08:41:20
Done.
|
| + |
| +def FieldName(field): |
| + return 'm' + GetName(field) |
| + |
| +def ConstantName(constant): |
| + main_name = constant.name |
| + if main_name[0] == 'k': |
| + main_name = main_name[1:] |
| + return UpperCaseConstant(main_name) |
| + |
| +def IsPointerArrayKind(kind): |
|
rmcilroy
2014/05/25 22:55:10
Please remove IsPointerArrayKind, GetResponseStruc
qsr
2014/05/26 08:41:20
Done.
|
| + if not isinstance(kind, mojom.Array): |
| + return False |
| + sub_kind = kind.kind |
| + return generator.IsObjectKind(sub_kind) |
| + |
| +def GetResponseStructFromMethod(method): |
| + return generator.GetDataHeader( |
| + False, generator.GetResponseStructFromMethod(method)) |
| + |
| +def GetStructFromMethod(method): |
| + return generator.GetDataHeader( |
| + False, generator.GetStructFromMethod(method)) |
| + |
| +def GetMethodName(method): |
| + return (UpperCaseConstant(method.interface.name) + "_" + |
| + UpperCaseConstant(method.name) + "_NAME") |
| + |
| +def GetConstantsMainEntityFullyQualifiedName(module): |
|
rmcilroy
2014/05/25 22:55:10
This doesn't seem to be used - can it be removed (
qsr
2014/05/26 08:41:20
This is now used.
|
| + package = GetPackage(module) |
| + return GetPackage(module) + '.' + GetConstantsMainEntityName(module) |
| + |
| +def GetConstantsMainEntityName(module): |
| + return (GetNameFromString(module.path.split('/')[-1].rsplit('.', 1)[0]) + |
|
rmcilroy
2014/05/25 22:55:10
Please add a comment describing that this is build
qsr
2014/05/26 08:41:20
Added the comment. I'd rather do the annotation in
|
| + 'Constants') |
| + |
| +class Generator(generator.Generator): |
| + |
| + java_filters = { |
| + "camelcase": CamelCase, |
| + "capitalize_first": CapitalizeFirst, |
| + "default_value_constant": DefaultValueConstant, |
| + "decode_method": DecodeMethod, |
| + "expression_to_text": ExpressionToText, |
| + "field_name": FieldName, |
| + "flags": GetFlags, |
| + "is_bool": lambda kind: kind == mojom.BOOL, |
| + "is_array_kind": lambda kind: isinstance(kind, mojom.Array), |
| + "is_object_kind": generator.IsObjectKind, |
| + "is_pointer_array_kind": IsPointerArrayKind, |
| + "is_string_kind": generator.IsStringKind, |
| + "is_struct_kind": lambda kind: isinstance(kind, mojom.Struct), |
| + "java_getter_prefix": GetJavaGetterPrefix, |
| + "java_type": GetJavaType, |
| + "method_name": GetMethodName, |
| + "new_array": NewArray, |
| + "response_struct_from_method": GetResponseStructFromMethod, |
| + "struct_from_method": GetStructFromMethod, |
| + "struct_size": lambda ps: ps.GetTotalSize() + _HEADER_SIZE, |
| + "super_class": GetSuperClass, |
| + "constant_name": ConstantName, |
| + "verify_token_type": generator.VerifyTokenType, |
| + } |
| + |
| + def GetJinjaExports(self): |
| + return { |
| + "interfaces": self.module.interfaces, |
| + "kinds": self.module.kinds, |
| + "method_structs": self.GetStructsFromMethods(), |
|
rmcilroy
2014/05/25 22:55:10
I can't see any uses of "method_structs" either he
qsr
2014/05/26 08:41:20
Done.
|
| + "module": self.module, |
| + "namespace": self.module.namespace, |
| + "package": GetPackage(self.module), |
| + } |
| + |
| + @UseJinja("java_templates/constants.java.tmpl", filters=java_filters, |
| + lstrip_blocks=True, trim_blocks=True) |
| + def GenerateConstantsSource(self, module): |
| + exports = self.GetJinjaExports() |
| + exports.update({"main_entity": GetConstantsMainEntityName(module), |
| + "constants": module.constants}) |
| + return exports |
| + |
| + def GenerateFiles(self, unparsed_args): |
| + parser = argparse.ArgumentParser() |
| + parser.add_argument("--java_output_directory", dest="java_output_directory") |
| + args = parser.parse_args(unparsed_args) |
| + if self.output_dir and args.java_output_directory: |
| + self.output_dir = os.path.join(args.java_output_directory, |
| + GetPackage(self.module).replace('.', '/')) |
| + if not os.path.exists(self.output_dir): |
| + os.makedirs(self.output_dir) |
| + |
| + if self.module.constants: |
| + self.Write(self.GenerateConstantsSource(self.module), |
| + "%s.java" % GetConstantsMainEntityName(self.module)) |