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