from util import build_utils
+# List of C++ types that are compatible with the Java code generated by this
+# script.
+ENUM_FIXED_TYPE_WHITELIST = ['char', 'unsigned char',
+ 'short', 'unsigned short',
+ 'int', 'int8_t', 'int16_t', 'int32_t', 'uint8_t', 'uint16_t']
+
class EnumDefinition(object):
- def __init__(self, class_name=None, class_package=None, entries=None):
- self.class_name = class_name
- self.class_package = class_package
+ def __init__(self, original_enum_name=None, class_name_override=None,
+ enum_package=None, entries=None, fixed_type=None):
+ self.original_enum_name = original_enum_name
+ self.class_name_override = class_name_override
+ self.enum_package = enum_package
self.entries = collections.OrderedDict(entries or [])
- self.prefix_to_strip = ''
+ self.prefix_to_strip = None
+ self.fixed_type = fixed_type
def AppendEntry(self, key, value):
if key in self.entries:
raise Exception('Multiple definitions of key %s found.' % key)
self.entries[key] = value
+ @property
+ def class_name(self):
+ return self.class_name_override or self.original_enum_name
+
def Finalize(self):
self._Validate()
self._AssignEntryIndices()
def _Validate(self):
assert self.class_name
- assert self.class_package
+ assert self.enum_package
assert self.entries
+ if self.fixed_type and self.fixed_type not in ENUM_FIXED_TYPE_WHITELIST:
+ raise Exception('Fixed type %s for enum %s not whitelisted.' %
+ (self.fixed_type, self.class_name))
def _AssignEntryIndices(self):
- # Supporting the same set enum value assignments the compiler does is rather
- # complicated, so we limit ourselves to these cases:
- # - all the enum constants have values assigned,
- # - enum constants reference other enum constants or have no value assigned.
-
+ # Enums, if given no value, are given the value of the previous enum + 1.
if not all(self.entries.values()):
- index = 0
+ prev_enum_value = -1
for key, value in self.entries.iteritems():
if not value:
- self.entries[key] = index
- index = index + 1
+ self.entries[key] = prev_enum_value + 1
elif value in self.entries:
self.entries[key] = self.entries[value]
else:
- raise Exception('You can only reference other enum constants unless '
- 'you assign values to all of the constants.')
+ try:
+ self.entries[key] = int(value)
+ except ValueError:
+ raise Exception('Could not interpret integer from enum value "%s" '
+ 'for key %s.' % (value, key))
+ prev_enum_value = self.entries[key]
+
def _StripPrefix(self):
- if not self.prefix_to_strip:
- prefix_to_strip = re.sub('(?!^)([A-Z]+)', r'_\1', self.class_name).upper()
+ prefix_to_strip = self.prefix_to_strip
+ if not prefix_to_strip:
+ prefix_to_strip = self.original_enum_name
+ prefix_to_strip = re.sub('(?!^)([A-Z]+)', r'_\1', prefix_to_strip).upper()
prefix_to_strip += '_'
if not all([w.startswith(prefix_to_strip) for w in self.entries.keys()]):
prefix_to_strip = ''
- else:
- prefix_to_strip = self.prefix_to_strip
- entries = ((k.replace(prefix_to_strip, '', 1), v) for (k, v) in
- self.entries.iteritems())
- self.entries = collections.OrderedDict(entries)
+
+ entries = collections.OrderedDict()
+ for (k, v) in self.entries.iteritems():
+ stripped_key = k.replace(prefix_to_strip, '', 1)
+ if isinstance(v, basestring):
+ stripped_value = v.replace(prefix_to_strip, '', 1)
+ else:
+ stripped_value = v
+ entries[stripped_key] = stripped_value
+
+ self.entries = entries
+
+class DirectiveSet(object):
+ class_name_override_key = 'CLASS_NAME_OVERRIDE'
+ enum_package_key = 'ENUM_PACKAGE'
+ prefix_to_strip_key = 'PREFIX_TO_STRIP'
+
+ known_keys = [class_name_override_key, enum_package_key, prefix_to_strip_key]
+
+ def __init__(self):
+ self._directives = {}
+
+ def Update(self, key, value):
+ if key not in DirectiveSet.known_keys:
+ raise Exception("Unknown directive: " + key)
+ self._directives[key] = value
+
+ @property
+ def empty(self):
+ return len(self._directives) == 0
+
+ def UpdateDefinition(self, definition):
+ definition.class_name_override = self._directives.get(
+ DirectiveSet.class_name_override_key, '')
+ definition.enum_package = self._directives.get(
+ DirectiveSet.enum_package_key)
+ definition.prefix_to_strip = self._directives.get(
+ DirectiveSet.prefix_to_strip_key)
+
class HeaderParser(object):
single_line_comment_re = re.compile(r'\s*//')
multi_line_comment_start_re = re.compile(r'\s*/\*')
- enum_start_re = re.compile(r'^\s*enum\s+(\w+)\s+{\s*$')
- enum_line_re = re.compile(r'^\s*(\w+)(\s*\=\s*([^,\n]+))?,?\s*$')
- enum_end_re = re.compile(r'^\s*}\s*;\s*$')
+ enum_line_re = re.compile(r'^\s*(\w+)(\s*\=\s*([^,\n]+))?,?')
+ enum_end_re = re.compile(r'^\s*}\s*;\.*$')
generator_directive_re = re.compile(
r'^\s*//\s+GENERATED_JAVA_(\w+)\s*:\s*([\.\w]+)$')
+ optional_class_or_struct_re = r'(class|struct)?'
+ enum_name_re = r'(\w+)'
+ optional_fixed_type_re = r'(\:\s*(\w+\s*\w+?))?'
+ enum_start_re = re.compile(r'^\s*enum\s+' + optional_class_or_struct_re +
+ '\s*' + enum_name_re + '\s*' + optional_fixed_type_re + '\s*{\s*$')
+
def __init__(self, lines):
self._lines = lines
self._enum_definitions = []
self._in_enum = False
self._current_definition = None
- self._generator_directives = {}
+ self._generator_directives = DirectiveSet()
def ParseDefinitions(self):
for line in self._lines:
enum_value = enum_entry.groups()[2]
self._current_definition.AppendEntry(enum_key, enum_value)
- def _GetCurrentEnumPackageName(self):
- return self._generator_directives.get('ENUM_PACKAGE')
-
- def _GetCurrentEnumPrefixToStrip(self):
- return self._generator_directives.get('PREFIX_TO_STRIP', '')
-
def _ApplyGeneratorDirectives(self):
- current_definition = self._current_definition
- current_definition.class_package = self._GetCurrentEnumPackageName()
- current_definition.prefix_to_strip = self._GetCurrentEnumPrefixToStrip()
- self._generator_directives = {}
+ self._generator_directives.UpdateDefinition(self._current_definition)
+ self._generator_directives = DirectiveSet()
def _ParseRegularLine(self, line):
enum_start = HeaderParser.enum_start_re.match(line)
generator_directive = HeaderParser.generator_directive_re.match(line)
if enum_start:
- if not self._GetCurrentEnumPackageName():
+ if self._generator_directives.empty:
return
- self._current_definition = EnumDefinition()
- self._current_definition.class_name = enum_start.groups()[0]
+ self._current_definition = EnumDefinition(
+ original_enum_name=enum_start.groups()[1],
+ fixed_type=enum_start.groups()[3])
self._in_enum = True
elif generator_directive:
directive_name = generator_directive.groups()[0]
directive_value = generator_directive.groups()[1]
- self._generator_directives[directive_name] = directive_value
+ self._generator_directives.Update(directive_name, directive_value)
def GetScriptName():
for source_path in source_paths:
enum_definitions = DoParseHeaderFile(source_path)
for enum_definition in enum_definitions:
- package_path = enum_definition.class_package.replace('.', os.path.sep)
+ package_path = enum_definition.enum_package.replace('.', os.path.sep)
file_name = enum_definition.class_name + '.java'
output_path = os.path.join(options.output_dir, package_path, file_name)
output_paths.append(output_path)
values = {
'CLASS_NAME': enum_definition.class_name,
'ENUM_ENTRIES': enum_entries_string,
- 'PACKAGE': enum_definition.class_package,
+ 'PACKAGE': enum_definition.enum_package,
'SCRIPT_NAME': GetScriptName(),
'SOURCE_PATH': source_path,
}