| #!/usr/bin/env python | |
| # | |
| # Copyright (C) 2011 Google Inc. All rights reserved. | |
| # | |
| # Redistribution and use in source and binary forms, with or without | |
| # modification, are permitted provided that the following conditions are | |
| # met: | |
| # | |
| # * Redistributions of source code must retain the above copyright | |
| # notice, this list of conditions and the following disclaimer. | |
| # * Redistributions in binary form must reproduce the above | |
| # copyright notice, this list of conditions and the following disclaimer | |
| # in the documentation and/or other materials provided with the | |
| # distribution. | |
| # * Neither the name of Google Inc. nor the names of its | |
| # contributors may be used to endorse or promote products derived from | |
| # this software without specific prior written permission. | |
| # | |
| # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
| # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
| # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| import codecs | |
| import logging | |
| import os | |
| import os.path | |
| import re | |
| import sys | |
| from webkitpy.common.checkout.scm import SCMDetector | |
| from webkitpy.common.system.filesystem import FileSystem | |
| from webkitpy.common.system.executive import Executive | |
| from webkitpy.common.system.logutils import configure_logging | |
| from webkitpy.style.checker import ProcessorBase | |
| from webkitpy.style.filereader import TextFileReader | |
| from webkitpy.style.main import change_directory | |
| _inspector_directory = "Source/WebCore/inspector/front-end" | |
| _localized_strings = "Source/WebCore/English.lproj/localizedStrings.js" | |
| _log = logging.getLogger("check-inspector-strings") | |
| class StringsExtractor(ProcessorBase): | |
| def __init__(self, patterns): | |
| self._patterns = patterns | |
| self.strings = [] | |
| for p in self._patterns: | |
| self.strings.append([]) | |
| def should_process(self, file_path): | |
| return file_path.endswith(".js") and (not file_path.endswith("InjectedScript.js")) | |
| def process(self, lines, file_path, line_numbers=None): | |
| for line in lines: | |
| comment_start = line.find("//") | |
| if comment_start != -1: | |
| line = line[:comment_start] | |
| index = 0 | |
| for pattern in self._patterns: | |
| line_strings = re.findall(pattern, line) | |
| for string in line_strings: | |
| self.strings[index].append(string) | |
| index += 1 | |
| class LocalizedStringsExtractor: | |
| def __init__(self): | |
| self.localized_strings = [] | |
| def process_file(self, file_path): | |
| localized_strings_file = codecs.open(file_path, encoding="utf-8", mode="r") | |
| try: | |
| contents = localized_strings_file.read() | |
| lines = contents.split("\n") | |
| for line in lines: | |
| match = re.match(r"localizedStrings\[\"((?:[^\"\\]|\\.)*?)\"", line) | |
| if match: | |
| self.localized_strings.append(match.group(1)) | |
| finally: | |
| localized_strings_file.close() | |
| def extract_ui_strings(str, out): | |
| line_unrecognized = False | |
| idx = 0 | |
| while idx < len(str): | |
| idx = str.find("WebInspector.UIString(", idx) | |
| if idx == -1: | |
| break | |
| idx = idx + len("WebInspector.UIString(") | |
| balance = 1 | |
| item_recognized = False | |
| while idx < len(str): | |
| if str[idx] == ')': | |
| balance = balance - 1 | |
| if balance == 0: | |
| break | |
| elif str[idx] == '(': | |
| balance = balance + 1 | |
| elif balance == 1: | |
| if str[idx] == ',': | |
| break | |
| elif str[idx] == '"': | |
| str_idx = idx + 1 | |
| while str_idx < len(str): | |
| if str[str_idx] == '\\': | |
| str_idx = str_idx + 1 | |
| elif str[str_idx] == '"': | |
| out.add(str[idx + 1 : str_idx]) | |
| idx = str_idx | |
| item_recognized = True | |
| break | |
| str_idx = str_idx + 1 | |
| idx = idx + 1 | |
| if not item_recognized: | |
| line_unrecognized = True | |
| if line_unrecognized: | |
| _log.info("Unrecognized: %s" % str) | |
| if __name__ == "__main__": | |
| configure_logging() | |
| cwd = os.path.abspath(os.curdir) | |
| filesystem = FileSystem() | |
| scm = SCMDetector(filesystem, Executive()).detect_scm_system(cwd) | |
| if scm is None: | |
| _log.error("WebKit checkout not found: You must run this script " | |
| "from within a WebKit checkout.") | |
| sys.exit(1) | |
| checkout_root = scm.checkout_root | |
| _log.debug("WebKit checkout found with root: %s" % checkout_root) | |
| change_directory(filesystem, checkout_root=checkout_root, paths=None) | |
| strings_extractor = StringsExtractor([r"(WebInspector\.UIString\(.*)", r"\"((?:[^\"\\]|\\.)*?)\""]) | |
| file_reader = TextFileReader(filesystem, strings_extractor) | |
| file_reader.process_paths([_inspector_directory]) | |
| localized_strings_extractor = LocalizedStringsExtractor() | |
| localized_strings_extractor.process_file(_localized_strings) | |
| raw_ui_strings = frozenset(strings_extractor.strings[0]) | |
| ui_strings = set() | |
| for s in raw_ui_strings: | |
| extract_ui_strings(s, ui_strings) | |
| strings = frozenset(strings_extractor.strings[1]) | |
| localized_strings = frozenset(localized_strings_extractor.localized_strings) | |
| new_strings = ui_strings - localized_strings | |
| for s in new_strings: | |
| _log.info("New: \"%s\"" % (s)) | |
| old_strings = localized_strings - ui_strings | |
| suspicious_strings = strings & old_strings | |
| for s in suspicious_strings: | |
| _log.info("Suspicious: \"%s\"" % (s)) | |
| unused_strings = old_strings - strings | |
| for s in unused_strings: | |
| _log.info("Unused: \"%s\"" % (s)) | |
| localized_strings_duplicates = {} | |
| for s in localized_strings_extractor.localized_strings: | |
| if s in localized_strings_duplicates: | |
| _log.info("Duplicate: \"%s\"" % (s)) | |
| else: | |
| localized_strings_duplicates.setdefault(s) |