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 7 import argparse 8 import os 9 import re 10 11 import mojom.generate.generator as generator 12 import mojom.generate.module as mojom 13 from mojom.generate.template_expander import UseJinja 14 15 16 GENERATOR_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 39 def 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 48 def CapitalizeFirst(string): 49 return string[0].upper() + string[1:] 50 51 def UpperCamelCase(name): 52 return ''.join([CapitalizeFirst(x) for x in NameToComponent(name)]) 53 54 def CamelCase(name): 55 uccc = UpperCamelCase(name) 56 return uccc[0].lower() + uccc[1:] 57 58 def 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 64 def 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 81 def ParseStringAttribute(attribute): 82 assert isinstance(attribute, basestring) 83 return attribute 84 85 def 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 91 def 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 103 def 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 112 def 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 130 def 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 138 class 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