summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/build/android/gyp/util/java_cpp_utils.py
blob: 5180400d6161de628e789ff320295ec97d150d78 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
# 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