# Copyright 2019 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. import os import re import sys def GetScriptName(): return os.path.basename(os.path.abspath(sys.argv[0])) def GetJavaFilePath(java_package, class_name): package_path = java_package.replace('.', os.path.sep) file_name = class_name + '.java' return os.path.join(package_path, file_name) def KCamelToShouty(s): """Convert |s| from kCamelCase or CamelCase to SHOUTY_CASE. kFooBar -> FOO_BAR FooBar -> FOO_BAR FooBAR9 -> FOO_BAR9 FooBARBaz -> FOO_BAR_BAZ """ if not re.match(r'^k?([A-Z][^A-Z]+|[A-Z0-9]+)+$', s): return s # Strip the leading k. s = re.sub(r'^k', '', s) # Treat "WebView" like one word. s = re.sub(r'WebView', r'Webview', s) # Add _ between title words and anything else. s = re.sub(r'([^_])([A-Z][^A-Z_0-9]+)', r'\1_\2', s) # Add _ between lower -> upper transitions. s = re.sub(r'([^A-Z_0-9])([A-Z])', r'\1_\2', s) return s.upper() class JavaString(object): def __init__(self, name, value, comments): self.name = KCamelToShouty(name) self.value = value self.comments = '\n'.join(' ' + x for x in comments) def Format(self): return '%s\n public static final String %s = %s;' % ( self.comments, self.name, self.value) def ParseTemplateFile(lines): package_re = re.compile(r'^package (.*);') class_re = re.compile(r'.*class (.*) {') package = '' class_name = '' for line in lines: package_line = package_re.match(line) if package_line: package = package_line.groups()[0] class_line = class_re.match(line) if class_line: class_name = class_line.groups()[0] break return package, class_name # TODO(crbug.com/937282): Work will be needed if we want to annotate specific # constants in the file to be parsed. class CppConstantParser(object): """Parses C++ constants, retaining their comments. The Delegate subclass is responsible for matching and extracting the constant's variable name and value, as well as generating an object to represent the Java representation of this value. """ SINGLE_LINE_COMMENT_RE = re.compile(r'\s*(// [^\n]*)') class Delegate(object): def ExtractConstantName(self, line): """Extracts a constant's name from line or None if not a match.""" raise NotImplementedError() def ExtractValue(self, line): """Extracts a constant's value from line or None if not a match.""" raise NotImplementedError() def CreateJavaConstant(self, name, value, comments): """Creates an object representing the Java analog of a C++ constant. CppConstantParser will not interact with the object created by this method. Instead, it will store this value in a list and return a list of all objects from the Parse() method. In this way, the caller may define whatever class suits their need. Args: name: the constant's variable name, as extracted by ExtractConstantName() value: the constant's value, as extracted by ExtractValue() comments: the code comments describing this constant """ raise NotImplementedError() def __init__(self, delegate, lines): self._delegate = delegate self._lines = lines self._in_variable = False self._in_comment = False self._package = '' self._current_comments = [] self._current_name = '' self._current_value = '' self._constants = [] def _ExtractVariable(self, line): match = StringFileParser.STRING_RE.match(line) return match.group(1) if match else None def _ExtractValue(self, line): match = StringFileParser.VALUE_RE.search(line) return match.group(1) if match else None def _Reset(self): self._current_comments = [] self._current_name = '' self._current_value = '' self._in_variable = False self._in_comment = False def _AppendConstant(self): self._constants.append( self._delegate.CreateJavaConstant(self._current_name, self._current_value, self._current_comments)) self._Reset() def _ParseValue(self, line): current_value = self._delegate.ExtractValue(line) if current_value is not None: self._current_value = current_value self._AppendConstant() else: self._Reset() def _ParseComment(self, line): comment_line = CppConstantParser.SINGLE_LINE_COMMENT_RE.match(line) if comment_line: self._current_comments.append(comment_line.groups()[0]) self._in_comment = True self._in_variable = True return True else: self._in_comment = False return False def _ParseVariable(self, line): current_name = self._delegate.ExtractConstantName(line) if current_name is not None: self._current_name = current_name current_value = self._delegate.ExtractValue(line) if current_value is not None: self._current_value = current_value self._AppendConstant() else: self._in_variable = True return True else: self._in_variable = False return False def _ParseLine(self, line): if not self._in_variable: if not self._ParseVariable(line): self._ParseComment(line) return if self._in_comment: if self._ParseComment(line): return if not self._ParseVariable(line): self._Reset() return if self._in_variable: self._ParseValue(line) def Parse(self): """Returns a list of objects representing C++ constants. Each object in the list was created by Delegate.CreateJavaValue(). """ for line in self._lines: self._ParseLine(line) return self._constants