1# Copyright 2019 The Chromium Authors 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import os 6import re 7import sys 8 9 10def GetScriptName(): 11 return os.path.basename(os.path.abspath(sys.argv[0])) 12 13 14def GetJavaFilePath(java_package, class_name): 15 package_path = java_package.replace('.', os.path.sep) 16 file_name = class_name + '.java' 17 return os.path.join(package_path, file_name) 18 19 20def KCamelToShouty(s): 21 """Convert |s| from kCamelCase or CamelCase to SHOUTY_CASE. 22 23 kFooBar -> FOO_BAR 24 FooBar -> FOO_BAR 25 FooBAR9 -> FOO_BAR9 26 FooBARBaz -> FOO_BAR_BAZ 27 """ 28 if not re.match(r'^k?([A-Z][^A-Z]+|[A-Z0-9]+)+$', s): 29 return s 30 # Strip the leading k. 31 s = re.sub(r'^k', '', s) 32 # Treat "WebView" like one word. 33 s = re.sub(r'WebView', r'Webview', s) 34 # Add _ between title words and anything else. 35 s = re.sub(r'([^_])([A-Z][^A-Z_0-9]+)', r'\1_\2', s) 36 # Add _ between lower -> upper transitions. 37 s = re.sub(r'([^A-Z_0-9])([A-Z])', r'\1_\2', s) 38 return s.upper() 39 40 41class JavaString: 42 def __init__(self, name, value, comments): 43 self.name = KCamelToShouty(name) 44 self.value = value 45 self.comments = '\n'.join(' ' + x for x in comments) 46 47 def Format(self): 48 return '%s\n public static final String %s = %s;' % ( 49 self.comments, self.name, self.value) 50 51 52def ParseTemplateFile(lines): 53 package_re = re.compile(r'^package (.*);') 54 class_re = re.compile(r'.*class (.*) {') 55 package = '' 56 class_name = '' 57 for line in lines: 58 package_line = package_re.match(line) 59 if package_line: 60 package = package_line.groups()[0] 61 class_line = class_re.match(line) 62 if class_line: 63 class_name = class_line.groups()[0] 64 break 65 return package, class_name 66 67 68# TODO(crbug.com/937282): Work will be needed if we want to annotate specific 69# constants in the file to be parsed. 70class CppConstantParser: 71 """Parses C++ constants, retaining their comments. 72 73 The Delegate subclass is responsible for matching and extracting the 74 constant's variable name and value, as well as generating an object to 75 represent the Java representation of this value. 76 """ 77 SINGLE_LINE_COMMENT_RE = re.compile(r'\s*(// [^\n]*)') 78 79 class Delegate: 80 def ExtractConstantName(self, line): 81 """Extracts a constant's name from line or None if not a match.""" 82 raise NotImplementedError() 83 84 def ExtractValue(self, line): 85 """Extracts a constant's value from line or None if not a match.""" 86 raise NotImplementedError() 87 88 def CreateJavaConstant(self, name, value, comments): 89 """Creates an object representing the Java analog of a C++ constant. 90 91 CppConstantParser will not interact with the object created by this 92 method. Instead, it will store this value in a list and return a list of 93 all objects from the Parse() method. In this way, the caller may define 94 whatever class suits their need. 95 96 Args: 97 name: the constant's variable name, as extracted by 98 ExtractConstantName() 99 value: the constant's value, as extracted by ExtractValue() 100 comments: the code comments describing this constant 101 """ 102 raise NotImplementedError() 103 104 def __init__(self, delegate, lines): 105 self._delegate = delegate 106 self._lines = lines 107 self._in_variable = False 108 self._in_comment = False 109 self._package = '' 110 self._current_comments = [] 111 self._current_name = '' 112 self._current_value = '' 113 self._constants = [] 114 115 def _ExtractVariable(self, line): 116 match = StringFileParser.STRING_RE.match(line) 117 return match.group(1) if match else None 118 119 def _ExtractValue(self, line): 120 match = StringFileParser.VALUE_RE.search(line) 121 return match.group(1) if match else None 122 123 def _Reset(self): 124 self._current_comments = [] 125 self._current_name = '' 126 self._current_value = '' 127 self._in_variable = False 128 self._in_comment = False 129 130 def _AppendConstant(self): 131 self._constants.append( 132 self._delegate.CreateJavaConstant(self._current_name, 133 self._current_value, 134 self._current_comments)) 135 self._Reset() 136 137 def _ParseValue(self, line): 138 current_value = self._delegate.ExtractValue(line) 139 if current_value is not None: 140 self._current_value = current_value 141 self._AppendConstant() 142 else: 143 self._Reset() 144 145 def _ParseComment(self, line): 146 comment_line = CppConstantParser.SINGLE_LINE_COMMENT_RE.match(line) 147 if comment_line: 148 self._current_comments.append(comment_line.groups()[0]) 149 self._in_comment = True 150 self._in_variable = True 151 return True 152 self._in_comment = False 153 return False 154 155 def _ParseVariable(self, line): 156 current_name = self._delegate.ExtractConstantName(line) 157 if current_name is not None: 158 self._current_name = current_name 159 current_value = self._delegate.ExtractValue(line) 160 if current_value is not None: 161 self._current_value = current_value 162 self._AppendConstant() 163 else: 164 self._in_variable = True 165 return True 166 self._in_variable = False 167 return False 168 169 def _ParseLine(self, line): 170 if not self._in_variable: 171 if not self._ParseVariable(line): 172 self._ParseComment(line) 173 return 174 175 if self._in_comment: 176 if self._ParseComment(line): 177 return 178 if not self._ParseVariable(line): 179 self._Reset() 180 return 181 182 if self._in_variable: 183 self._ParseValue(line) 184 185 def Parse(self): 186 """Returns a list of objects representing C++ constants. 187 188 Each object in the list was created by Delegate.CreateJavaValue(). 189 """ 190 for line in self._lines: 191 self._ParseLine(line) 192 return self._constants 193