Upstream version 11.40.271.0
[platform/framework/web/crosswalk.git] / src / third_party / google_input_tools / third_party / closure_library / closure / bin / scopify.py
1 #!/usr/bin/python
2 #
3 # Copyright 2010 The Closure Library Authors. All Rights Reserved.
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 #      http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS-IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17
18 """Automatically converts codebases over to goog.scope.
19
20 Usage:
21 cd path/to/my/dir;
22 ../../../../javascript/closure/bin/scopify.py
23
24 Scans every file in this directory, recursively. Looks for existing
25 goog.scope calls, and goog.require'd symbols. If it makes sense to
26 generate a goog.scope call for the file, then we will do so, and
27 try to auto-generate some aliases based on the goog.require'd symbols.
28
29 Known Issues:
30
31   When a file is goog.scope'd, the file contents will be indented +2.
32   This may put some lines over 80 chars. These will need to be fixed manually.
33
34   We will only try to create aliases for capitalized names. We do not check
35   to see if those names will conflict with any existing locals.
36
37   This creates merge conflicts for every line of every outstanding change.
38   If you intend to run this on your codebase, make sure your team members
39   know. Better yet, send them this script so that they can scopify their
40   outstanding changes and "accept theirs".
41
42   When an alias is "captured", it can no longer be stubbed out for testing.
43   Run your tests.
44
45 """
46
47 __author__ = 'nicksantos@google.com (Nick Santos)'
48
49 import os.path
50 import re
51 import sys
52
53 REQUIRES_RE = re.compile(r"goog.require\('([^']*)'\)")
54
55 # Edit this manually if you want something to "always" be aliased.
56 # TODO(nicksantos): Add a flag for this.
57 DEFAULT_ALIASES = {}
58
59 def Transform(lines):
60   """Converts the contents of a file into javascript that uses goog.scope.
61
62   Arguments:
63     lines: A list of strings, corresponding to each line of the file.
64   Returns:
65     A new list of strings, or None if the file was not modified.
66   """
67   requires = []
68
69   # Do an initial scan to be sure that this file can be processed.
70   for line in lines:
71     # Skip this file if it has already been scopified.
72     if line.find('goog.scope') != -1:
73       return None
74
75     # If there are any global vars or functions, then we also have
76     # to skip the whole file. We might be able to deal with this
77     # more elegantly.
78     if line.find('var ') == 0 or line.find('function ') == 0:
79       return None
80
81     for match in REQUIRES_RE.finditer(line):
82       requires.append(match.group(1))
83
84   if len(requires) == 0:
85     return None
86
87   # Backwards-sort the requires, so that when one is a substring of another,
88   # we match the longer one first.
89   for val in DEFAULT_ALIASES.values():
90     if requires.count(val) == 0:
91       requires.append(val)
92
93   requires.sort()
94   requires.reverse()
95
96   # Generate a map of requires to their aliases
97   aliases_to_globals = DEFAULT_ALIASES.copy()
98   for req in requires:
99     index = req.rfind('.')
100     if index == -1:
101       alias = req
102     else:
103       alias = req[(index + 1):]
104
105     # Don't scopify lowercase namespaces, because they may conflict with
106     # local variables.
107     if alias[0].isupper():
108       aliases_to_globals[alias] = req
109
110   aliases_to_matchers = {}
111   globals_to_aliases = {}
112   for alias, symbol in aliases_to_globals.items():
113     globals_to_aliases[symbol] = alias
114     aliases_to_matchers[alias] = re.compile('\\b%s\\b' % symbol)
115
116   # Insert a goog.scope that aliases all required symbols.
117   result = []
118
119   START = 0
120   SEEN_REQUIRES = 1
121   IN_SCOPE = 2
122
123   mode = START
124   aliases_used = set()
125   insertion_index = None
126   num_blank_lines = 0
127   for line in lines:
128     if mode == START:
129       result.append(line)
130
131       if re.search(REQUIRES_RE, line):
132         mode = SEEN_REQUIRES
133
134     elif mode == SEEN_REQUIRES:
135       if (line and
136           not re.search(REQUIRES_RE, line) and
137           not line.isspace()):
138         # There should be two blank lines before goog.scope
139         result += ['\n'] * 2
140         result.append('goog.scope(function() {\n')
141         insertion_index = len(result)
142         result += ['\n'] * num_blank_lines
143         mode = IN_SCOPE
144       elif line.isspace():
145         # Keep track of the number of blank lines before each block of code so
146         # that we can move them after the goog.scope line if necessary.
147         num_blank_lines += 1
148       else:
149         # Print the blank lines we saw before this code block
150         result += ['\n'] * num_blank_lines
151         num_blank_lines = 0
152         result.append(line)
153
154     if mode == IN_SCOPE:
155       for symbol in requires:
156         if not symbol in globals_to_aliases:
157           continue
158
159         alias = globals_to_aliases[symbol]
160         matcher = aliases_to_matchers[alias]
161         for match in matcher.finditer(line):
162           # Check to make sure we're not in a string.
163           # We do this by being as conservative as possible:
164           # if there are any quote or double quote characters
165           # before the symbol on this line, then bail out.
166           before_symbol = line[:match.start(0)]
167           if before_symbol.count('"') > 0 or before_symbol.count("'") > 0:
168             continue
169
170           line = line.replace(match.group(0), alias)
171           aliases_used.add(alias)
172
173       if line.isspace():
174         # Truncate all-whitespace lines
175         result.append('\n')
176       else:
177         result.append(line)
178
179   if len(aliases_used):
180     aliases_used = [alias for alias in aliases_used]
181     aliases_used.sort()
182     aliases_used.reverse()
183     for alias in aliases_used:
184       symbol = aliases_to_globals[alias]
185       result.insert(insertion_index,
186                     'var %s = %s;\n' % (alias, symbol))
187     result.append('});  // goog.scope\n')
188     return result
189   else:
190     return None
191
192 def TransformFileAt(path):
193   """Converts a file into javascript that uses goog.scope.
194
195   Arguments:
196     path: A path to a file.
197   """
198   f = open(path)
199   lines = Transform(f.readlines())
200   if lines:
201     f = open(path, 'w')
202     for l in lines:
203       f.write(l)
204     f.close()
205
206 if __name__ == '__main__':
207   args = sys.argv[1:]
208   if not len(args):
209     args = '.'
210
211   for file_name in args:
212     if os.path.isdir(file_name):
213       for root, dirs, files in os.walk(file_name):
214         for name in files:
215           if name.endswith('.js') and \
216               not os.path.islink(os.path.join(root, name)):
217             TransformFileAt(os.path.join(root, name))
218     else:
219       if file_name.endswith('.js') and \
220           not os.path.islink(file_name):
221         TransformFileAt(file_name)