1# Copyright 2019 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 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(object): 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(object): 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(object): 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 else: 153 self._in_comment = False 154 return False 155 156 def _ParseVariable(self, line): 157 current_name = self._delegate.ExtractConstantName(line) 158 if current_name is not None: 159 self._current_name = current_name 160 current_value = self._delegate.ExtractValue(line) 161 if current_value is not None: 162 self._current_value = current_value 163 self._AppendConstant() 164 else: 165 self._in_variable = True 166 return True 167 else: 168 self._in_variable = False 169 return False 170 171 def _ParseLine(self, line): 172 if not self._in_variable: 173 if not self._ParseVariable(line): 174 self._ParseComment(line) 175 return 176 177 if self._in_comment: 178 if self._ParseComment(line): 179 return 180 if not self._ParseVariable(line): 181 self._Reset() 182 return 183 184 if self._in_variable: 185 self._ParseValue(line) 186 187 def Parse(self): 188 """Returns a list of objects representing C++ constants. 189 190 Each object in the list was created by Delegate.CreateJavaValue(). 191 """ 192 for line in self._lines: 193 self._ParseLine(line) 194 return self._constants 195