Home | History | Annotate | Download | only in generators
      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