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)) |