Root/
Source at commit 1322 created 12 years 8 months ago. By meklort, Add doxygen to utils folder | |
---|---|
1 | """Script to generate reports on translator classes from Doxygen sources.␊ |
2 | ␊ |
3 | The main purpose of the script is to extract the information from sources␊ |
4 | related to internationalization (the translator classes). It uses the␊ |
5 | information to generate documentation (language.doc,␊ |
6 | translator_report.txt) from templates (language.tpl, maintainers.txt).␊ |
7 | ␊ |
8 | Simply run the script without parameters to get the reports and␊ |
9 | documentation for all supported languages. If you want to generate the␊ |
10 | translator report only for some languages, pass their codes as arguments␊ |
11 | to the script. In that case, the language.doc will not be generated.␊ |
12 | Example:␊ |
13 | ␊ |
14 | python translator.py en nl cz␊ |
15 | ␊ |
16 | Originally, the script was written in Perl and was known as translator.pl.␊ |
17 | The last Perl version was dated 2002/05/21 (plus some later corrections)␊ |
18 | ␊ |
19 | $Id: translator.py 742 2010-09-20 18:19:55Z dimitri $␊ |
20 | ␊ |
21 | Petr Prikryl (prikrylp@skil.cz)␊ |
22 | ␊ |
23 | History:␊ |
24 | --------␊ |
25 | 2002/05/21 - This was the last Perl version. ␊ |
26 | 2003/05/16 - List of language marks can be passed as arguments.␊ |
27 | 2004/01/24 - Total reimplementation started: classes TrManager, and Transl.␊ |
28 | 2004/02/05 - First version that produces translator report. No language.doc yet.␊ |
29 | 2004/02/10 - First fully functional version that generates both the translator␊ |
30 | report and the documentation. It is a bit slower than the␊ |
31 | Perl version, but is much less tricky and much more flexible.␊ |
32 | It also solves some problems that were not solved by the Perl␊ |
33 | version. The translator report content should be more useful␊ |
34 | for developers.␊ |
35 | 2004/02/11 - Some tuning-up to provide more useful information.␊ |
36 | 2004/04/16 - Added new tokens to the tokenizer (to remove some warnings).␊ |
37 | 2004/05/25 - Added from __future__ import generators not to force Python 2.3.␊ |
38 | 2004/06/03 - Removed dependency on textwrap module.␊ |
39 | 2004/07/07 - Fixed the bug in the fill() function.␊ |
40 | 2004/07/21 - Better e-mail mangling for HTML part of language.doc.␊ |
41 | - Plural not used for reporting a single missing method.␊ |
42 | - Removal of not used translator adapters is suggested only␊ |
43 | when the report is not restricted to selected languages␊ |
44 | explicitly via script arguments.␊ |
45 | 2004/07/26 - Better reporting of not-needed adapters.␊ |
46 | 2004/10/04 - Reporting of not called translator methods added.␊ |
47 | 2004/10/05 - Modified to check only doxygen/src sources for the previous report.␊ |
48 | 2005/02/28 - Slight modification to generate "mailto.txt" auxiliary file.␊ |
49 | 2005/08/15 - Doxygen's root directory determined primarily from DOXYGEN ␊ |
50 | environment variable. When not found, then relatively to the script.␊ |
51 | 2007/03/20 - The "translate me!" searched in comments and reported if found.␊ |
52 | 2008/06/09 - Warning when the MAX_DOT_GRAPH_HEIGHT is still part of trLegendDocs().␊ |
53 | 2009/05/09 - Changed HTML output to fit it with XHTML DTD␊ |
54 | 2009/09/02 - Added percentage info to the report (implemented / to be implemented).␊ |
55 | 2010/02/09 - Added checking/suggestion 'Reimplementation using UTF-8 suggested.␊ |
56 | 2010/03/03 - Added [unreachable] prefix used in maintainers.txt.␊ |
57 | 2010/05/28 - BOM skipped; minor code cleaning.␊ |
58 | 2010/05/31 - e-mail mangled already in maintainers.txt␊ |
59 | 2010/08/20 - maintainers.txt to UTF-8, related processin of unicode strings␊ |
60 | - [any mark] introduced instead of [unreachable] only␊ |
61 | - marks hihglighted in HTML␊ |
62 | 2010/08/30 - Highlighting in what will be the table in langhowto.html modified.␊ |
63 | 2010/09/27 - The underscore in \latexonly part of the generated language.doc␊ |
64 | was prefixed by backslash (was LaTeX related error).␊ |
65 | """ ␊ |
66 | ␊ |
67 | from __future__ import generators␊ |
68 | import codecs␊ |
69 | import os␊ |
70 | import re␊ |
71 | import sys␊ |
72 | ␊ |
73 | ␊ |
74 | def fill(s):␊ |
75 | """Returns string formated to the wrapped paragraph multiline string.␊ |
76 | ␊ |
77 | Replaces whitespaces by one space and then uses he textwrap.fill()."""␊ |
78 | ␊ |
79 | # Replace all whitespace by spaces, remove whitespaces that are not␊ |
80 | # necessary, strip the left and right whitespaces, and break the string ␊ |
81 | # to list of words.␊ |
82 | rexWS = re.compile(r'\s+')␊ |
83 | lst = rexWS.sub(' ', s).strip().split()␊ |
84 | ␊ |
85 | # If the list is not empty, put the words together and form the lines ␊ |
86 | # of maximum 70 characters. Build the list of lines.␊ |
87 | lines = []␊ |
88 | if lst:␊ |
89 | line = lst.pop(0) # no separation space in front of the first word␊ |
90 | for word in lst:␊ |
91 | if len(line) + len(word) < 70:␊ |
92 | line += ' ' + word␊ |
93 | else:␊ |
94 | lines.append(line) # another full line formed␊ |
95 | line = word # next line started␊ |
96 | lines.append(line) # the last line␊ |
97 | return '\n'.join(lines)␊ |
98 | ␊ |
99 | ␊ |
100 | # The following function dedent() is the verbatim copy from the textwrap.py␊ |
101 | # module. The textwrap.py was introduced in Python 2.3. To make this script␊ |
102 | # working also in older Python versions, I have decided to copy it. ␊ |
103 | # Notice that the textwrap.py is copyrighted:␊ |
104 | #␊ |
105 | # Copyright (C) 1999-2001 Gregory P. Ward.␊ |
106 | # Copyright (C) 2002, 2003 Python Software Foundation.␊ |
107 | # Written by Greg Ward <gward@python.net>␊ |
108 | #␊ |
109 | # The explicit permission to use the code here was sent by Guido van Rossum ␊ |
110 | # (4th June, 2004).␊ |
111 | #␊ |
112 | def dedent(text):␊ |
113 | """dedent(text : string) -> string␊ |
114 | ␊ |
115 | Remove any whitespace than can be uniformly removed from the left␊ |
116 | of every line in `text`.␊ |
117 | ␊ |
118 | This can be used e.g. to make triple-quoted strings line up with␊ |
119 | the left edge of screen/whatever, while still presenting it in the␊ |
120 | source code in indented form.␊ |
121 | ␊ |
122 | For example:␊ |
123 | ␊ |
124 | def test():␊ |
125 | # end first line with \ to avoid the empty line!␊ |
126 | s = '''\␊ |
127 | hello␊ |
128 | world␊ |
129 | '''␊ |
130 | print repr(s) # prints ' hello\n world\n '␊ |
131 | print repr(dedent(s)) # prints 'hello\n world\n'␊ |
132 | """␊ |
133 | lines = text.expandtabs().split('\n')␊ |
134 | margin = None␊ |
135 | for line in lines:␊ |
136 | content = line.lstrip()␊ |
137 | if not content:␊ |
138 | continue␊ |
139 | indent = len(line) - len(content)␊ |
140 | if margin is None:␊ |
141 | margin = indent␊ |
142 | else:␊ |
143 | margin = min(margin, indent)␊ |
144 | ␊ |
145 | if margin is not None and margin > 0:␊ |
146 | for i in range(len(lines)):␊ |
147 | lines[i] = lines[i][margin:]␊ |
148 | ␊ |
149 | return '\n'.join(lines)␊ |
150 | ␊ |
151 | ␊ |
152 | class Transl:␊ |
153 | """One instance is build for each translator.␊ |
154 | ␊ |
155 | The abbreviation of the source file--part after 'translator_'--is used as␊ |
156 | the identification of the object. The empty string is used for the␊ |
157 | abstract Translator class from translator.h. The other information is␊ |
158 | extracted from inside the source file."""␊ |
159 | ␊ |
160 | def __init__(self, fname, manager):␊ |
161 | """Bind to the manager and initialize."""␊ |
162 | ␊ |
163 | # Store the filename and the reference to the manager object.␊ |
164 | self.fname = fname␊ |
165 | self.manager = manager␊ |
166 | ␊ |
167 | # The instance is responsible for loading the source file, so it checks␊ |
168 | # for its existence and quits if something goes wrong.␊ |
169 | if not os.path.isfile(fname):␊ |
170 | sys.stderr.write("\a\nFile '%s' not found!\n" % fname)␊ |
171 | sys.exit(1)␊ |
172 | ␊ |
173 | # Initialize the other collected information.␊ |
174 | self.classId = None␊ |
175 | self.baseClassId = None␊ |
176 | self.readableStatus = None # 'up-to-date', '1.2.3', '1.3', etc.␊ |
177 | self.status = None # '', '1.2.03', '1.3.00', etc.␊ |
178 | self.lang = None # like 'Brasilian'␊ |
179 | self.langReadable = None # like 'Brasilian Portuguese'␊ |
180 | self.note = None # like 'should be cleaned up'␊ |
181 | self.prototypeDic = {} # uniPrototype -> prototype␊ |
182 | self.translateMeText = 'translate me!'␊ |
183 | self.translateMeFlag = False # comments with "translate me!" found␊ |
184 | self.txtMAX_DOT_GRAPH_HEIGHT_flag = False # found in string in trLegendDocs()␊ |
185 | self.obsoleteMethods = None # list of prototypes to be removed␊ |
186 | self.missingMethods = None # list of prototypes to be implemented␊ |
187 | self.implementedMethods = None # list of implemented required methods␊ |
188 | self.adaptMinClass = None # The newest adapter class that can be used␊ |
189 | self.isDecodedTranslator = None # Flag related to internal usage of UTF-8 ␊ |
190 | ␊ |
191 | def __tokenGenerator(self):␊ |
192 | """Generator that reads the file and yields tokens as 4-tuples.␊ |
193 | ␊ |
194 | The tokens have the form (tokenId, tokenString, lineNo). The␊ |
195 | last returned token has the form ('eof', None, None). When trying␊ |
196 | to access next token afer that, the exception would be raised."""␊ |
197 | ␊ |
198 | # Set the dictionary for recognizing tokenId for keywords, separators␊ |
199 | # and the similar categories. The key is the string to be recognized,␊ |
200 | # the value says its token identification.␊ |
201 | tokenDic = { 'class': 'class',␊ |
202 | 'const': 'const',␊ |
203 | 'public': 'public',␊ |
204 | 'protected': 'protected',␊ |
205 | 'private': 'private',␊ |
206 | 'static': 'static',␊ |
207 | 'virtual': 'virtual',␊ |
208 | ':': 'colon', ␊ |
209 | ';': 'semic',␊ |
210 | ',': 'comma',␊ |
211 | '[': 'lsqbra', ␊ |
212 | ']': 'rsqbra',␊ |
213 | '(': 'lpar', ␊ |
214 | ')': 'rpar',␊ |
215 | '{': 'lcurly', ␊ |
216 | '}': 'rcurly',␊ |
217 | '=': 'assign',␊ |
218 | '*': 'star',␊ |
219 | '&': 'amp',␊ |
220 | '+': 'plus',␊ |
221 | '-': 'minus',␊ |
222 | '!': 'excl',␊ |
223 | '?': 'qmark',␊ |
224 | '<': 'lt',␊ |
225 | '>': 'gt',␊ |
226 | "'": 'quot',␊ |
227 | '"': 'dquot',␊ |
228 | '.': 'dot',␊ |
229 | '%': 'perc',␊ |
230 | '~': 'tilde',␊ |
231 | '^': 'caret',␊ |
232 | }␊ |
233 | ␊ |
234 | # Regular expression for recognizing identifiers.␊ |
235 | rexId = re.compile(r'^[a-zA-Z]\w*$')␊ |
236 | ␊ |
237 | # Open the file for reading and extracting tokens until the eof.␊ |
238 | # Initialize the finite automaton.␊ |
239 | f = open(self.fname)␊ |
240 | lineNo = 0␊ |
241 | line = '' # init -- see the pos initialization below␊ |
242 | linelen = 0 # init␊ |
243 | pos = 100 # init -- pos after the end of line␊ |
244 | status = 0 ␊ |
245 | ␊ |
246 | tokenId = None # init␊ |
247 | tokenStr = '' # init -- the characters will be appended.␊ |
248 | tokenLineNo = 0␊ |
249 | ␊ |
250 | while status != 777:␊ |
251 | ␊ |
252 | # Get the next character. Read next line first, if necessary.␊ |
253 | if pos < linelen:␊ |
254 | c = line[pos]␊ |
255 | else:␊ |
256 | lineNo += 1␊ |
257 | line = f.readline()␊ |
258 | if line.startswith('\xef\xbb\xbf'):␊ |
259 | line = line[3:] # skip the BOM␊ |
260 | linelen = len(line)␊ |
261 | pos = 0␊ |
262 | if line == '': # eof␊ |
263 | status = 777␊ |
264 | else:␊ |
265 | c = line[pos]␊ |
266 | ␊ |
267 | # Consume the character based on the status␊ |
268 | ␊ |
269 | if status == 0: # basic status␊ |
270 | ␊ |
271 | # This is the initial status. If tokenId is set, yield the␊ |
272 | # token here and only here (except when eof is found).␊ |
273 | # Initialize the token variables after the yield.␊ |
274 | if tokenId:␊ |
275 | # If it is an unknown item, it can still be recognized␊ |
276 | # here. Keywords and separators are the example.␊ |
277 | if tokenId == 'unknown': ␊ |
278 | if tokenDic.has_key(tokenStr):␊ |
279 | tokenId = tokenDic[tokenStr]␊ |
280 | elif tokenStr.isdigit():␊ |
281 | tokenId = 'num'␊ |
282 | elif rexId.match(tokenStr):␊ |
283 | tokenId = 'id'␊ |
284 | else:␊ |
285 | msg = '\aWarning: unknown token "' + tokenStr + '"'␊ |
286 | msg += '\tfound on line %d' % tokenLineNo ␊ |
287 | msg += ' in "' + self.fname + '".\n'␊ |
288 | sys.stderr.write(msg)␊ |
289 | ␊ |
290 | yield (tokenId, tokenStr, tokenLineNo)␊ |
291 | ␊ |
292 | # If it is a comment that contains the self.translateMeText ␊ |
293 | # string, set the flag -- the situation will be reported.␊ |
294 | if tokenId == 'comment' and tokenStr.find(self.translateMeText) >= 0:␊ |
295 | self.translateMeFlag = True␊ |
296 | ␊ |
297 | tokenId = None␊ |
298 | tokenStr = ''␊ |
299 | tokenLineNo = 0 ␊ |
300 | ␊ |
301 | # Now process the character. When we just skip it (spaces),␊ |
302 | # stay in this status. All characters that will be part of␊ |
303 | # some token cause moving to the specific status. And only␊ |
304 | # when moving to the status == 0 (or the final state 777), ␊ |
305 | # the token is yielded. With respect to that the automaton␊ |
306 | # behaves as Moore's one (output bound to status). When ␊ |
307 | # collecting tokens, the automaton is the Mealy's one ␊ |
308 | # (actions bound to transitions).␊ |
309 | if c.isspace():␊ |
310 | pass # just skip whitespace characters␊ |
311 | elif c == '/': # Possibly comment starts here, but␊ |
312 | tokenId = 'unknown' # it could be only a slash in code.␊ |
313 | tokenStr = c␊ |
314 | tokenLineNo = lineNo␊ |
315 | status = 1␊ |
316 | elif c == '#':␊ |
317 | tokenId = 'preproc' # preprocessor directive␊ |
318 | tokenStr = c␊ |
319 | tokenLineNo = lineNo␊ |
320 | status = 5␊ |
321 | elif c == '"': # string starts here␊ |
322 | tokenId = 'string'␊ |
323 | tokenStr = c␊ |
324 | tokenLineNo = lineNo␊ |
325 | status = 6␊ |
326 | elif c == "'": # char literal starts here␊ |
327 | tokenId = 'charlit'␊ |
328 | tokenStr = c␊ |
329 | tokenLineNo = lineNo␊ |
330 | status = 8␊ |
331 | elif tokenDic.has_key(c): # known one-char token ␊ |
332 | tokenId = tokenDic[c]␊ |
333 | tokenStr = c␊ |
334 | tokenLineNo = lineNo␊ |
335 | # stay in this state to yield token immediately␊ |
336 | else:␊ |
337 | tokenId = 'unknown' # totally unknown␊ |
338 | tokenStr = c␊ |
339 | tokenLineNo = lineNo␊ |
340 | status = 333␊ |
341 | ␊ |
342 | pos += 1 # move position in any case␊ |
343 | ␊ |
344 | elif status == 1: # possibly a comment␊ |
345 | if c == '/': # ... definitely the C++ comment␊ |
346 | tokenId = 'comment'␊ |
347 | tokenStr += c␊ |
348 | pos += 1 ␊ |
349 | status = 2␊ |
350 | elif c == '*': # ... definitely the C comment␊ |
351 | tokenId = 'comment'␊ |
352 | tokenStr += c␊ |
353 | pos += 1 ␊ |
354 | status = 3␊ |
355 | else:␊ |
356 | status = 0 # unrecognized, don't move pos␊ |
357 | ␊ |
358 | elif status == 2: # inside the C++ comment␊ |
359 | if c == '\n': # the end of C++ comment␊ |
360 | status = 0 # yield the token␊ |
361 | else:␊ |
362 | tokenStr += c # collect the C++ comment␊ |
363 | pos += 1␊ |
364 | ␊ |
365 | elif status == 3: # inside the C comment␊ |
366 | if c == '*': # possibly the end of the C comment␊ |
367 | tokenStr += c␊ |
368 | status = 4␊ |
369 | else:␊ |
370 | tokenStr += c # collect the C comment␊ |
371 | pos += 1␊ |
372 | ␊ |
373 | elif status == 4: # possibly the end of the C comment␊ |
374 | if c == '/': # definitely the end of the C comment␊ |
375 | tokenStr += c␊ |
376 | status = 0 # yield the token␊ |
377 | elif c == '*': # more stars inside the comment␊ |
378 | tokenStr += c␊ |
379 | else:␊ |
380 | tokenStr += c # this cannot be the end of comment␊ |
381 | status = 3␊ |
382 | pos += 1␊ |
383 | ␊ |
384 | elif status == 5: # inside the preprocessor directive␊ |
385 | if c == '\n': # the end of the preproc. command␊ |
386 | status = 0 # yield the token␊ |
387 | else:␊ |
388 | tokenStr += c # collect the preproc␊ |
389 | pos += 1␊ |
390 | ␊ |
391 | elif status == 6: # inside the string␊ |
392 | if c == '\\': # escaped char inside the string␊ |
393 | tokenStr += c␊ |
394 | status = 7␊ |
395 | elif c == '"': # end of the string␊ |
396 | tokenStr += c␊ |
397 | status = 0␊ |
398 | else:␊ |
399 | tokenStr += c # collect the chars of the string␊ |
400 | pos += 1␊ |
401 | ␊ |
402 | elif status == 7: # escaped char inside the string␊ |
403 | tokenStr += c # collect the char of the string␊ |
404 | status = 6␊ |
405 | pos += 1␊ |
406 | ␊ |
407 | elif status == 8: # inside the char literal␊ |
408 | tokenStr += c # collect the char of the literal␊ |
409 | status = 9␊ |
410 | pos += 1␊ |
411 | ␊ |
412 | elif status == 9: # end of char literal expected␊ |
413 | if c == "'": # ... and found␊ |
414 | tokenStr += c␊ |
415 | status = 0␊ |
416 | pos += 1␊ |
417 | else:␊ |
418 | tokenId = 'error' # end of literal was expected␊ |
419 | tokenStr += c␊ |
420 | status = 0␊ |
421 | ␊ |
422 | elif status == 333: # start of the unknown token␊ |
423 | if c.isspace():␊ |
424 | pos += 1␊ |
425 | status = 0 # tokenId may be determined later␊ |
426 | elif tokenDic.has_key(c): # separator, don't move pos␊ |
427 | status = 0␊ |
428 | else:␊ |
429 | tokenStr += c # collect␊ |
430 | pos += 1␊ |
431 | ␊ |
432 | # We should have finished in the final status. If some token ␊ |
433 | # have been extracted, yield it first.␊ |
434 | assert(status == 777)␊ |
435 | if tokenId:␊ |
436 | yield (tokenId, tokenStr, tokenLineNo)␊ |
437 | tokenId = None␊ |
438 | tokenStr = ''␊ |
439 | tokenLineNo = 0 ␊ |
440 | ␊ |
441 | # The file content is processed. Close the file. Then always yield ␊ |
442 | # the eof token.␊ |
443 | f.close()␊ |
444 | yield ('eof', None, None)␊ |
445 | ␊ |
446 | ␊ |
447 | def __collectClassInfo(self, tokenIterator):␊ |
448 | """Collect the information about the class and base class.␊ |
449 | ␊ |
450 | The tokens including the opening left curly brace of the class are␊ |
451 | consumed."""␊ |
452 | ␊ |
453 | status = 0 # initial state␊ |
454 | ␊ |
455 | while status != 777: # final state␊ |
456 | ␊ |
457 | # Always assume that the previous tokens were processed. Get␊ |
458 | # the next one.␊ |
459 | tokenId, tokenStr, tokenLineNo = tokenIterator.next()␊ |
460 | ␊ |
461 | # Process the token and never return back.␊ |
462 | if status == 0: # waiting for the 'class' keyword.␊ |
463 | if tokenId == 'class':␊ |
464 | status = 1␊ |
465 | ␊ |
466 | elif status == 1: # expecting the class identification␊ |
467 | if tokenId == 'id':␊ |
468 | self.classId = tokenStr␊ |
469 | status = 2␊ |
470 | else:␊ |
471 | self.__unexpectedToken(status, tokenId, tokenLineNo)␊ |
472 | ␊ |
473 | elif status == 2: # expecting the curly brace or base class info␊ |
474 | if tokenId == 'lcurly':␊ |
475 | status = 777 # correctly finished␊ |
476 | elif tokenId == 'colon':␊ |
477 | status = 3␊ |
478 | else:␊ |
479 | self.__unexpectedToken(status, tokenId, tokenLineNo)␊ |
480 | ␊ |
481 | elif status == 3: # expecting the 'public' in front of base class id␊ |
482 | if tokenId == 'public':␊ |
483 | status = 4␊ |
484 | else:␊ |
485 | self.__unexpectedToken(status, tokenId, tokenLineNo)␊ |
486 | ␊ |
487 | elif status == 4: # expecting the base class id␊ |
488 | if tokenId == 'id':␊ |
489 | self.baseClassId = tokenStr␊ |
490 | status = 5␊ |
491 | else:␊ |
492 | self.__unexpectedToken(status, tokenId, tokenLineNo)␊ |
493 | ␊ |
494 | elif status == 5: # expecting the curly brace and quitting␊ |
495 | if tokenId == 'lcurly':␊ |
496 | status = 777 # correctly finished␊ |
497 | elif tokenId == 'comment':␊ |
498 | pass␊ |
499 | else:␊ |
500 | self.__unexpectedToken(status, tokenId, tokenLineNo)␊ |
501 | ␊ |
502 | # Extract the status of the TranslatorXxxx class. The readable form␊ |
503 | # will be used in reports the status form is a string that can be␊ |
504 | # compared lexically (unified length, padding with zeros, etc.).␊ |
505 | if self.baseClassId:␊ |
506 | lst = self.baseClassId.split('_')␊ |
507 | if lst[0] == 'Translator':␊ |
508 | self.readableStatus = 'up-to-date'␊ |
509 | self.status = ''␊ |
510 | elif lst[0] == 'TranslatorAdapter':␊ |
511 | self.status = lst[1] + '.' + lst[2]␊ |
512 | self.readableStatus = self.status␊ |
513 | if len(lst) > 3: # add the last part of the number␊ |
514 | self.status += '.' + ('%02d' % int(lst[3]))␊ |
515 | self.readableStatus += '.' + lst[3]␊ |
516 | else:␊ |
517 | self.status += '.00'␊ |
518 | elif lst[0] == 'TranslatorEnglish':␊ |
519 | # Obsolete or Based on English.␊ |
520 | if self.classId[-2:] == 'En':␊ |
521 | self.readableStatus = 'English based'␊ |
522 | self.status = 'En'␊ |
523 | else:␊ |
524 | self.readableStatus = 'obsolete'␊ |
525 | self.status = '0.0.00'␊ |
526 | ␊ |
527 | # Check whether status was set, or set 'strange'.␊ |
528 | if self.status == None: ␊ |
529 | self.status = 'strange'␊ |
530 | if not self.readableStatus: ␊ |
531 | self.readableStatus = 'strange'␊ |
532 | ␊ |
533 | # Extract the name of the language and the readable form.␊ |
534 | self.lang = self.classId[10:] # without 'Translator'␊ |
535 | if self.lang == 'Brazilian':␊ |
536 | self.langReadable = 'Brazilian Portuguese'␊ |
537 | elif self.lang == 'Chinesetraditional':␊ |
538 | self.langReadable = 'Chinese Traditional'␊ |
539 | else:␊ |
540 | self.langReadable = self.lang␊ |
541 | ␊ |
542 | ␊ |
543 | def __unexpectedToken(self, status, tokenId, tokenLineNo):␊ |
544 | """Reports unexpected token and quits with exit code 1."""␊ |
545 | ␊ |
546 | import inspect␊ |
547 | calledFrom = inspect.stack()[1][3]␊ |
548 | msg = "\a\nUnexpected token '%s' on the line %d in '%s'.\n"␊ |
549 | msg = msg % (tokenId, tokenLineNo, self.fname)␊ |
550 | msg += 'status = %d in %s()\n' % (status, calledFrom)␊ |
551 | sys.stderr.write(msg)␊ |
552 | sys.exit(1)␊ |
553 | ␊ |
554 | ␊ |
555 | def collectPureVirtualPrototypes(self):␊ |
556 | """Returns dictionary 'unified prototype' -> 'full prototype'.␊ |
557 | ␊ |
558 | The method is expected to be called only for the translator.h. It␊ |
559 | extracts only the pure virtual method and build the dictionary where␊ |
560 | key is the unified prototype without argument identifiers."""␊ |
561 | ␊ |
562 | # Prepare empty dictionary that will be returned.␊ |
563 | resultDic = {}␊ |
564 | ␊ |
565 | # Start the token generator which parses the class source file.␊ |
566 | tokenIterator = self.__tokenGenerator()␊ |
567 | ␊ |
568 | # Collect the class and the base class identifiers.␊ |
569 | self.__collectClassInfo(tokenIterator)␊ |
570 | assert(self.classId == 'Translator')␊ |
571 | ␊ |
572 | # Let's collect readable form of the public virtual pure method␊ |
573 | # prototypes in the readable form -- as defined in translator.h.␊ |
574 | # Let's collect also unified form of the same prototype that omits␊ |
575 | # everything that can be omitted, namely 'virtual' and argument␊ |
576 | # identifiers.␊ |
577 | prototype = '' # readable prototype (with everything)␊ |
578 | uniPrototype = '' # unified prototype (without arg. identifiers)␊ |
579 | ␊ |
580 | # Collect the pure virtual method prototypes. Stop on the closing␊ |
581 | # curly brace followed by the semicolon (end of class).␊ |
582 | status = 0␊ |
583 | curlyCnt = 0 # counter for the level of curly braces␊ |
584 | ␊ |
585 | # Loop until the final state 777 is reached. The errors are processed␊ |
586 | # immediately. In this implementation, it always quits the application.␊ |
587 | while status != 777: ␊ |
588 | ␊ |
589 | # Get the next token.␊ |
590 | tokenId, tokenStr, tokenLineNo = tokenIterator.next()␊ |
591 | ␊ |
592 | if status == 0: # waiting for 'public:'␊ |
593 | if tokenId == 'public':␊ |
594 | status = 1␊ |
595 | ␊ |
596 | elif status == 1: # colon after the 'public'␊ |
597 | if tokenId == 'colon':␊ |
598 | status = 2␊ |
599 | else:␊ |
600 | self.__unexpectedToken(status, tokenId, tokenLineNo)␊ |
601 | ␊ |
602 | elif status == 2: # waiting for 'virtual'␊ |
603 | if tokenId == 'virtual':␊ |
604 | prototype = tokenStr # but not to unified prototype␊ |
605 | status = 3␊ |
606 | elif tokenId == 'comment':␊ |
607 | pass␊ |
608 | elif tokenId == 'rcurly':␊ |
609 | status = 11 # expected end of class␊ |
610 | else:␊ |
611 | self.__unexpectedToken(status, tokenId, tokenLineNo)␊ |
612 | ␊ |
613 | elif status == 3: # return type of the method expected␊ |
614 | if tokenId == 'id':␊ |
615 | prototype += ' ' + tokenStr␊ |
616 | uniPrototype = tokenStr # start collecting the unified prototype␊ |
617 | status = 4␊ |
618 | ␉␉elif tokenId == 'tilde':␊ |
619 | status = 4␊ |
620 | else:␊ |
621 | self.__unexpectedToken(status, tokenId, tokenLineNo)␊ |
622 | ␊ |
623 | elif status == 4: # method identifier expected␊ |
624 | if tokenId == 'id':␊ |
625 | prototype += ' ' + tokenStr␊ |
626 | uniPrototype += ' ' + tokenStr␊ |
627 | status = 5␊ |
628 | else:␊ |
629 | self.__unexpectedToken(status, tokenId, tokenLineNo)␊ |
630 | ␊ |
631 | elif status == 5: # left bracket of the argument list expected␊ |
632 | if tokenId == 'lpar':␊ |
633 | prototype += tokenStr␊ |
634 | uniPrototype += tokenStr␊ |
635 | status = 6␊ |
636 | else:␊ |
637 | self.__unexpectedToken(status, tokenId, tokenLineNo)␊ |
638 | ␊ |
639 | elif status == 6: # collecting arguments of the method␊ |
640 | if tokenId == 'rpar':␊ |
641 | prototype += tokenStr␊ |
642 | uniPrototype += tokenStr␊ |
643 | status = 7␊ |
644 | elif tokenId == 'const':␊ |
645 | prototype += tokenStr␊ |
646 | uniPrototype += tokenStr␊ |
647 | status = 12␊ |
648 | elif tokenId == 'id': # type identifier␊ |
649 | prototype += tokenStr␊ |
650 | uniPrototype += tokenStr␊ |
651 | status = 13␊ |
652 | else:␊ |
653 | self.__unexpectedToken(status, tokenId, tokenLineNo)␊ |
654 | ␊ |
655 | elif status == 7: # assignment expected or left curly brace␊ |
656 | if tokenId == 'assign':␊ |
657 | status = 8␊ |
658 | elif tokenId == 'lcurly':␊ |
659 | curlyCnt = 1 # method body entered ␊ |
660 | status = 10␊ |
661 | else:␊ |
662 | self.__unexpectedToken(status, tokenId, tokenLineNo)␊ |
663 | ␊ |
664 | elif status == 8: # zero expected␊ |
665 | if tokenId == 'num' and tokenStr == '0':␊ |
666 | status = 9␊ |
667 | else:␊ |
668 | self.__unexpectedToken(status, tokenId, tokenLineNo)␊ |
669 | ␊ |
670 | elif status == 9: # after semicolon, produce the dic item␊ |
671 | if tokenId == 'semic':␊ |
672 | assert(not resultDic.has_key(uniPrototype))␊ |
673 | resultDic[uniPrototype] = prototype␊ |
674 | status = 2␊ |
675 | else:␊ |
676 | self.__unexpectedToken(status, tokenId, tokenLineNo)␊ |
677 | ␊ |
678 | elif status == 10: # consuming the body of the method␊ |
679 | if tokenId == 'rcurly':␊ |
680 | curlyCnt -= 1␊ |
681 | if curlyCnt == 0:␊ |
682 | status = 2 # body consumed␊ |
683 | elif tokenId == 'lcurly':␊ |
684 | curlyCnt += 1␊ |
685 | ␊ |
686 | elif status == 11: # probably the end of class␊ |
687 | if tokenId == 'semic':␊ |
688 | status = 777␊ |
689 | else:␊ |
690 | self.__unexpectedToken(status, tokenId, tokenLineNo)␊ |
691 | ␊ |
692 | elif status == 12: # type id for argument expected␊ |
693 | if tokenId == 'id':␊ |
694 | prototype += ' ' + tokenStr␊ |
695 | uniPrototype += ' ' + tokenStr␊ |
696 | status = 13␊ |
697 | else:␊ |
698 | self.__unexpectedToken(status, tokenId, tokenLineNo)␊ |
699 | ␊ |
700 | elif status == 13: # namespace qualification or * or & expected␊ |
701 | if tokenId == 'colon': # was namespace id␊ |
702 | prototype += tokenStr␊ |
703 | uniPrototype += tokenStr␊ |
704 | status = 14␊ |
705 | elif tokenId == 'star' or tokenId == 'amp': # pointer or reference␊ |
706 | prototype += ' ' + tokenStr␊ |
707 | uniPrototype += ' ' + tokenStr␊ |
708 | status = 16␊ |
709 | elif tokenId == 'id': # argument identifier␊ |
710 | prototype += ' ' + tokenStr␊ |
711 | # don't put this into unified prototype␊ |
712 | status = 17␊ |
713 | else:␊ |
714 | self.__unexpectedToken(status, tokenId, tokenLineNo)␊ |
715 | ␊ |
716 | elif status == 14: # second colon for namespace:: expected␊ |
717 | if tokenId == 'colon': ␊ |
718 | prototype += tokenStr␊ |
719 | uniPrototype += tokenStr␊ |
720 | status = 15␊ |
721 | else:␊ |
722 | self.__unexpectedToken(status, tokenId, tokenLineNo)␊ |
723 | ␊ |
724 | elif status == 15: # type after namespace:: expected␊ |
725 | if tokenId == 'id': ␊ |
726 | prototype += tokenStr␊ |
727 | uniPrototype += tokenStr␊ |
728 | status = 13␊ |
729 | else:␊ |
730 | self.__unexpectedToken(status, tokenId, tokenLineNo)␊ |
731 | ␊ |
732 | elif status == 16: # argument identifier expected␊ |
733 | if tokenId == 'id': ␊ |
734 | prototype += ' ' + tokenStr␊ |
735 | # don't put this into unified prototype␊ |
736 | status = 17␊ |
737 | else:␊ |
738 | self.__unexpectedToken(status, tokenId, tokenLineNo)␊ |
739 | ␊ |
740 | elif status == 17: # comma or ')' after argument identifier expected␊ |
741 | if tokenId == 'comma': ␊ |
742 | prototype += ', '␊ |
743 | uniPrototype += ', '␊ |
744 | status = 6␊ |
745 | elif tokenId == 'rpar':␊ |
746 | prototype += tokenStr␊ |
747 | uniPrototype += tokenStr␊ |
748 | status = 7␊ |
749 | else:␊ |
750 | self.__unexpectedToken(status, tokenId, tokenLineNo)␊ |
751 | ␊ |
752 | # Eat the rest of the source to cause closing the file.␊ |
753 | while tokenId != 'eof':␊ |
754 | tokenId, tokenStr, tokenLineNo = tokenIterator.next()␊ |
755 | ␊ |
756 | # Return the resulting dictionary with 'uniPrototype -> prototype'.␊ |
757 | return resultDic␊ |
758 | ␊ |
759 | ␊ |
760 | def __collectPublicMethodPrototypes(self, tokenIterator): ␊ |
761 | """Collects prototypes of public methods and fills self.prototypeDic.␊ |
762 | ␊ |
763 | The dictionary is filled by items: uniPrototype -> prototype. ␊ |
764 | The method is expected to be called only for TranslatorXxxx classes,␊ |
765 | i.e. for the classes that implement translation to some language.␊ |
766 | It assumes that the openning curly brace of the class was already␊ |
767 | consumed. The source is consumed until the end of the class. ␊ |
768 | The caller should consume the source until the eof to cause closing ␊ |
769 | the source file."""␊ |
770 | ␊ |
771 | assert(self.classId != 'Translator')␊ |
772 | assert(self.baseClassId != None)␊ |
773 | ␊ |
774 | # The following finite automaton slightly differs from the one␊ |
775 | # inside self.collectPureVirtualPrototypes(). It produces the␊ |
776 | # dictionary item just after consuming the body of the method␊ |
777 | # (transition from from state 10 to state 2). It also does not allow␊ |
778 | # definitions of public pure virtual methods, except for␊ |
779 | # TranslatorAdapterBase (states 8 and 9). Argument identifier inside␊ |
780 | # method argument lists can be omitted or commented.␊ |
781 | #␊ |
782 | # Let's collect readable form of all public method prototypes in ␊ |
783 | # the readable form -- as defined in the source file.␊ |
784 | # Let's collect also unified form of the same prototype that omits␊ |
785 | # everything that can be omitted, namely 'virtual' and argument␊ |
786 | # identifiers.␊ |
787 | prototype = '' # readable prototype (with everything)␊ |
788 | uniPrototype = '' # unified prototype (without arg. identifiers)␊ |
789 | warning = '' # warning message -- if something special detected␊ |
790 | methodId = None # processed method id␊ |
791 | ␊ |
792 | # Collect the method prototypes. Stop on the closing␊ |
793 | # curly brace followed by the semicolon (end of class).␊ |
794 | status = 0␊ |
795 | curlyCnt = 0 # counter for the level of curly braces␊ |
796 | ␊ |
797 | # Loop until the final state 777 is reached. The errors are processed␊ |
798 | # immediately. In this implementation, it always quits the application.␊ |
799 | while status != 777: ␊ |
800 | ␊ |
801 | # Get the next token.␊ |
802 | tokenId, tokenStr, tokenLineNo = tokenIterator.next()␊ |
803 | ␊ |
804 | if status == 0: # waiting for 'public:'␊ |
805 | if tokenId == 'public':␊ |
806 | status = 1␊ |
807 | elif tokenId == 'eof': # non-public things until the eof␊ |
808 | status = 777␊ |
809 | ␊ |
810 | elif status == 1: # colon after the 'public'␊ |
811 | if tokenId == 'colon':␊ |
812 | status = 2␊ |
813 | else:␊ |
814 | self.__unexpectedToken(status, tokenId, tokenLineNo)␊ |
815 | ␊ |
816 | elif status == 2: # waiting for 'virtual' (can be omitted)␊ |
817 | if tokenId == 'virtual':␊ |
818 | prototype = tokenStr # but not to unified prototype␊ |
819 | status = 3␊ |
820 | elif tokenId == 'id': # 'virtual' was omitted␊ |
821 | prototype = tokenStr␊ |
822 | uniPrototype = tokenStr # start collecting the unified prototype␊ |
823 | status = 4␊ |
824 | elif tokenId == 'comment':␊ |
825 | pass␊ |
826 | elif tokenId == 'protected' or tokenId == 'private':␊ |
827 | status = 0␊ |
828 | elif tokenId == 'rcurly':␊ |
829 | status = 11 # expected end of class␊ |
830 | else:␊ |
831 | self.__unexpectedToken(status, tokenId, tokenLineNo)␊ |
832 | ␊ |
833 | elif status == 3: # return type of the method expected␊ |
834 | if tokenId == 'id':␊ |
835 | prototype += ' ' + tokenStr␊ |
836 | uniPrototype = tokenStr # start collecting the unified prototype␊ |
837 | status = 4␊ |
838 | else:␊ |
839 | self.__unexpectedToken(status, tokenId, tokenLineNo)␊ |
840 | ␊ |
841 | elif status == 4: # method identifier expected␊ |
842 | if tokenId == 'id':␊ |
843 | prototype += ' ' + tokenStr␊ |
844 | uniPrototype += ' ' + tokenStr␊ |
845 | methodId = tokenStr # for reporting␊ |
846 | status = 5␊ |
847 | else:␊ |
848 | self.__unexpectedToken(status, tokenId, tokenLineNo)␊ |
849 | ␊ |
850 | elif status == 5: # left bracket of the argument list expected␊ |
851 | if tokenId == 'lpar':␊ |
852 | prototype += tokenStr␊ |
853 | uniPrototype += tokenStr␊ |
854 | status = 6␊ |
855 | else:␊ |
856 | self.__unexpectedToken(status, tokenId, tokenLineNo)␊ |
857 | ␊ |
858 | elif status == 6: # collecting arguments of the method␊ |
859 | if tokenId == 'rpar':␊ |
860 | prototype += tokenStr␊ |
861 | uniPrototype += tokenStr␊ |
862 | status = 7␊ |
863 | elif tokenId == 'const':␊ |
864 | prototype += tokenStr␊ |
865 | uniPrototype += tokenStr␊ |
866 | status = 12␊ |
867 | elif tokenId == 'id': # type identifier␊ |
868 | prototype += tokenStr␊ |
869 | uniPrototype += tokenStr␊ |
870 | status = 13␊ |
871 | else:␊ |
872 | self.__unexpectedToken(status, tokenId, tokenLineNo)␊ |
873 | ␊ |
874 | elif status == 7: # left curly brace expected ␊ |
875 | if tokenId == 'lcurly':␊ |
876 | curlyCnt = 1 # method body entered ␊ |
877 | status = 10␊ |
878 | elif tokenId == 'comment':␊ |
879 | pass␊ |
880 | elif tokenId == 'assign': # allowed only for TranslatorAdapterBase␊ |
881 | assert(self.classId == 'TranslatorAdapterBase')␊ |
882 | status = 8␊ |
883 | else:␊ |
884 | self.__unexpectedToken(status, tokenId, tokenLineNo)␊ |
885 | ␊ |
886 | elif status == 8: # zero expected (TranslatorAdapterBase)␊ |
887 | assert(self.classId == 'TranslatorAdapterBase')␊ |
888 | if tokenId == 'num' and tokenStr == '0':␊ |
889 | status = 9␊ |
890 | else:␊ |
891 | self.__unexpectedToken(status, tokenId, tokenLineNo)␊ |
892 | ␊ |
893 | elif status == 9: # after semicolon (TranslatorAdapterBase)␊ |
894 | assert(self.classId == 'TranslatorAdapterBase')␊ |
895 | if tokenId == 'semic':␊ |
896 | status = 2␊ |
897 | else:␊ |
898 | self.__unexpectedToken(status, tokenId, tokenLineNo)␊ |
899 | ␊ |
900 | elif status == 10: # consuming the body of the method, then dic item␊ |
901 | if tokenId == 'rcurly':␊ |
902 | curlyCnt -= 1␊ |
903 | if curlyCnt == 0:␊ |
904 | # Insert new dictionary item.␊ |
905 | assert(not self.prototypeDic.has_key(uniPrototype))␊ |
906 | self.prototypeDic[uniPrototype] = prototype␊ |
907 | status = 2 # body consumed␊ |
908 | methodId = None # outside of any method␊ |
909 | elif tokenId == 'lcurly':␊ |
910 | curlyCnt += 1␊ |
911 | ␊ |
912 | # Warn in special case. ␊ |
913 | elif methodId == 'trLegendDocs' and tokenId == 'string' \␊ |
914 | and tokenStr.find('MAX_DOT_GRAPH_HEIGHT') >= 0:␊ |
915 | self.txtMAX_DOT_GRAPH_HEIGHT_flag = True ␊ |
916 | ␊ |
917 | ␊ |
918 | elif status == 11: # probably the end of class␊ |
919 | if tokenId == 'semic':␊ |
920 | status = 777␊ |
921 | else:␊ |
922 | self.__unexpectedToken(status, tokenId, tokenLineNo)␊ |
923 | ␊ |
924 | elif status == 12: # type id for argument expected␊ |
925 | if tokenId == 'id':␊ |
926 | prototype += ' ' + tokenStr␊ |
927 | uniPrototype += ' ' + tokenStr␊ |
928 | status = 13␊ |
929 | else:␊ |
930 | self.__unexpectedToken(status, tokenId, tokenLineNo)␊ |
931 | ␊ |
932 | elif status == 13: # :: or * or & or id or ) expected␊ |
933 | if tokenId == 'colon': # was namespace id␊ |
934 | prototype += tokenStr␊ |
935 | uniPrototype += tokenStr␊ |
936 | status = 14␊ |
937 | elif tokenId == 'star' or tokenId == 'amp': # pointer or reference␊ |
938 | prototype += ' ' + tokenStr␊ |
939 | uniPrototype += ' ' + tokenStr␊ |
940 | status = 16␊ |
941 | elif tokenId == 'id': # argument identifier␊ |
942 | prototype += ' ' + tokenStr␊ |
943 | # don't put this into unified prototype␊ |
944 | status = 17␊ |
945 | elif tokenId == 'comment': # probably commented-out identifier␊ |
946 | prototype += tokenStr␊ |
947 | elif tokenId == 'rpar':␊ |
948 | prototype += tokenStr␊ |
949 | uniPrototype += tokenStr␊ |
950 | status = 7␊ |
951 | elif tokenId == 'comma': ␊ |
952 | prototype += ', '␊ |
953 | uniPrototype += ', '␊ |
954 | status = 6␊ |
955 | else:␊ |
956 | self.__unexpectedToken(status, tokenId, tokenLineNo)␊ |
957 | ␊ |
958 | elif status == 14: # second colon for namespace:: expected␊ |
959 | if tokenId == 'colon': ␊ |
960 | prototype += tokenStr␊ |
961 | uniPrototype += tokenStr␊ |
962 | status = 15␊ |
963 | else:␊ |
964 | self.__unexpectedToken(status, tokenId, tokenLineNo)␊ |
965 | ␊ |
966 | elif status == 15: # type after namespace:: expected␊ |
967 | if tokenId == 'id': ␊ |
968 | prototype += tokenStr␊ |
969 | uniPrototype += tokenStr␊ |
970 | status = 13␊ |
971 | else:␊ |
972 | self.__unexpectedToken(status, tokenId, tokenLineNo)␊ |
973 | ␊ |
974 | elif status == 16: # argument identifier or ) expected␊ |
975 | if tokenId == 'id': ␊ |
976 | prototype += ' ' + tokenStr␊ |
977 | # don't put this into unified prototype␊ |
978 | status = 17␊ |
979 | elif tokenId == 'rpar':␊ |
980 | prototype += tokenStr␊ |
981 | uniPrototype += tokenStr␊ |
982 | status = 7␊ |
983 | elif tokenId == 'comment':␊ |
984 | prototype += tokenStr␊ |
985 | else:␊ |
986 | self.__unexpectedToken(status, tokenId, tokenLineNo)␊ |
987 | ␊ |
988 | elif status == 17: # comma or ')' after argument identifier expected␊ |
989 | if tokenId == 'comma': ␊ |
990 | prototype += ', '␊ |
991 | uniPrototype += ', '␊ |
992 | status = 6␊ |
993 | elif tokenId == 'rpar':␊ |
994 | prototype += tokenStr␊ |
995 | uniPrototype += tokenStr␊ |
996 | status = 7␊ |
997 | else:␊ |
998 | self.__unexpectedToken(status, tokenId, tokenLineNo)␊ |
999 | ␊ |
1000 | ␊ |
1001 | ␊ |
1002 | def collectAdapterPrototypes(self):␊ |
1003 | """Returns the dictionary of prototypes implemented by adapters.␊ |
1004 | ␊ |
1005 | It is created to process the translator_adapter.h. The returned ␊ |
1006 | dictionary has the form: unifiedPrototype -> (version, classId)␊ |
1007 | thus by looking for the prototype, we get the information what is ␊ |
1008 | the newest (least adapting) adapter that is sufficient for ␊ |
1009 | implementing the method.""" ␊ |
1010 | ␊ |
1011 | # Start the token generator which parses the class source file.␊ |
1012 | assert(os.path.split(self.fname)[1] == 'translator_adapter.h')␊ |
1013 | tokenIterator = self.__tokenGenerator()␊ |
1014 | ␊ |
1015 | # Get the references to the involved dictionaries.␊ |
1016 | reqDic = self.manager.requiredMethodsDic␊ |
1017 | ␊ |
1018 | # Create the empty dictionary that will be returned.␊ |
1019 | adaptDic = {}␊ |
1020 | ␊ |
1021 | ␊ |
1022 | # Loop through the source of the adapter file until no other adapter␊ |
1023 | # class is found.␊ |
1024 | while True:␊ |
1025 | try:␊ |
1026 | # Collect the class and the base class identifiers.␊ |
1027 | self.__collectClassInfo(tokenIterator)␊ |
1028 | ␊ |
1029 | # Extract the comparable version of the adapter class.␊ |
1030 | # Note: The self.status as set by self.__collectClassInfo()␊ |
1031 | # contains similar version, but is related to the base class,␊ |
1032 | # not to the class itself.␊ |
1033 | lst = self.classId.split('_')␊ |
1034 | version = ''␊ |
1035 | if lst[0] == 'TranslatorAdapter': # TranslatorAdapterBase otherwise␊ |
1036 | version = lst[1] + '.' + lst[2]␊ |
1037 | if len(lst) > 3: # add the last part of the number␊ |
1038 | version += '.' + ('%02d' % int(lst[3]))␊ |
1039 | else:␊ |
1040 | version += '.00'␊ |
1041 | ␊ |
1042 | # Collect the prototypes of implemented public methods.␊ |
1043 | self.__collectPublicMethodPrototypes(tokenIterator)␊ |
1044 | ␊ |
1045 | # For the required methods, update the dictionary of methods ␊ |
1046 | # implemented by the adapter.␊ |
1047 | for protoUni in self.prototypeDic:␊ |
1048 | if reqDic.has_key(protoUni):␊ |
1049 | # This required method will be marked as implemented␊ |
1050 | # by this adapter class. This implementation assumes ␊ |
1051 | # that newer adapters do not reimplement any required␊ |
1052 | # methods already implemented by older adapters.␊ |
1053 | assert(not adaptDic.has_key(protoUni))␊ |
1054 | adaptDic[protoUni] = (version, self.classId)␊ |
1055 | ␊ |
1056 | # Clear the dictionary object and the information related␊ |
1057 | # to the class as the next adapter class is to be processed.␊ |
1058 | self.prototypeDic.clear()␊ |
1059 | self.classId = None␊ |
1060 | self.baseClassId = None␊ |
1061 | ␊ |
1062 | except StopIteration:␊ |
1063 | break␊ |
1064 | ␊ |
1065 | # Return the result dictionary.␊ |
1066 | return adaptDic ␊ |
1067 | ␊ |
1068 | ␊ |
1069 | def processing(self):␊ |
1070 | """Processing of the source file -- only for TranslatorXxxx classes.""" ␊ |
1071 | ␊ |
1072 | # Start the token generator which parses the class source file.␊ |
1073 | tokenIterator = self.__tokenGenerator()␊ |
1074 | ␊ |
1075 | # Collect the class and the base class identifiers.␊ |
1076 | self.__collectClassInfo(tokenIterator)␊ |
1077 | assert(self.classId != 'Translator')␊ |
1078 | assert(self.classId[:17] != 'TranslatorAdapter')␊ |
1079 | ␊ |
1080 | # Collect the prototypes of implemented public methods.␊ |
1081 | self.__collectPublicMethodPrototypes(tokenIterator)␊ |
1082 | ␊ |
1083 | # Eat the rest of the source to cause closing the file.␊ |
1084 | while True:␊ |
1085 | try:␊ |
1086 | t = tokenIterator.next()␊ |
1087 | except StopIteration:␊ |
1088 | break␊ |
1089 | ␊ |
1090 | # Shorthands for the used dictionaries.␊ |
1091 | reqDic = self.manager.requiredMethodsDic␊ |
1092 | adaptDic = self.manager.adaptMethodsDic␊ |
1093 | myDic = self.prototypeDic␊ |
1094 | ␊ |
1095 | # Build the list of obsolete methods.␊ |
1096 | self.obsoleteMethods = []␊ |
1097 | for p in myDic:␊ |
1098 | if not reqDic.has_key(p):␊ |
1099 | self.obsoleteMethods.append(p)␊ |
1100 | ␊ |
1101 | # Build the list of missing methods and the list of implemented␊ |
1102 | # required methods.␊ |
1103 | self.missingMethods = []␊ |
1104 | self.implementedMethods = []␊ |
1105 | for p in reqDic:␊ |
1106 | if myDic.has_key(p):␊ |
1107 | self.implementedMethods.append(p)␊ |
1108 | else:␊ |
1109 | self.missingMethods.append(p)␊ |
1110 | ␊ |
1111 | # Set the least important note first if the translator is decoded.␊ |
1112 | # If yes, then it means that the implementation should be switched␊ |
1113 | # to UTF-8 later (suggestion).␊ |
1114 | self.isDecodedTranslator = self.classId in self.manager.decodedTranslators␊ |
1115 | if self.isDecodedTranslator:␊ |
1116 | self.note = 'Reimplementation using UTF-8 suggested.'␊ |
1117 | ␊ |
1118 | # Check whether adapter must be used or suggest the newest one.␊ |
1119 | # Change the status and set the note accordingly.␊ |
1120 | if self.baseClassId != 'Translator':␊ |
1121 | if not self.missingMethods: ␊ |
1122 | self.note = 'Change the base class to Translator.'␊ |
1123 | self.status = ''␊ |
1124 | self.readableStatus = 'up-to-date'␊ |
1125 | elif self.baseClassId != 'TranslatorEnglish':␊ |
1126 | # The translator uses some of the adapters.␊ |
1127 | # Look at the missing methods and check what adapter ␊ |
1128 | # implements them. Remember the one with the lowest version.␊ |
1129 | adaptMinVersion = '9.9.99'␊ |
1130 | adaptMinClass = 'TranslatorAdapter_9_9_99'␊ |
1131 | for uniProto in self.missingMethods:␊ |
1132 | if adaptDic.has_key(uniProto):␊ |
1133 | version, cls = adaptDic[uniProto]␊ |
1134 | if version < adaptMinVersion:␊ |
1135 | adaptMinVersion = version␊ |
1136 | adaptMinClass = cls␊ |
1137 | ␊ |
1138 | # Test against the current status -- preserve the self.status.␊ |
1139 | # Possibly, the translator implements enough methods to ␊ |
1140 | # use some newer adapter.␊ |
1141 | status = self.status␊ |
1142 | ␊ |
1143 | # If the version of the used adapter is smaller than␊ |
1144 | # the required, set the note and update the status as if␊ |
1145 | # the newer adapter was used.␊ |
1146 | if adaptMinVersion > status:␊ |
1147 | self.note = 'Change the base class to %s.' % adaptMinClass␊ |
1148 | self.status = adaptMinVersion␊ |
1149 | self.adaptMinClass = adaptMinClass␊ |
1150 | self.readableStatus = adaptMinVersion # simplified␊ |
1151 | ␊ |
1152 | # If everything seems OK, some explicit warning flags still could ␊ |
1153 | # be set. ␊ |
1154 | if not self.note and self.status == '' and \␊ |
1155 | (self.translateMeFlag or self.txtMAX_DOT_GRAPH_HEIGHT_flag):␊ |
1156 | self.note = '' ␊ |
1157 | if self.translateMeFlag:␊ |
1158 | self.note += 'The "%s" found in a comment.' % self.translateMeText␊ |
1159 | if self.note != '':␊ |
1160 | self.note += '\n\t\t' ␊ |
1161 | if self.txtMAX_DOT_GRAPH_HEIGHT_flag:␊ |
1162 | self.note += 'The MAX_DOT_GRAPH_HEIGHT found in trLegendDocs()'␊ |
1163 | ␊ |
1164 | # If everything seems OK, but there are obsolete methods, set ␊ |
1165 | # the note to clean-up source. This note will be used only when␊ |
1166 | # the previous code did not set another note (priority).␊ |
1167 | if not self.note and self.status == '' and self.obsoleteMethods:␊ |
1168 | self.note = 'Remove the obsolete methods (never used).'␊ |
1169 | ␊ |
1170 | ␊ |
1171 | def report(self, fout):␊ |
1172 | """Returns the report part for the source as a multiline string.␊ |
1173 | ␊ |
1174 | No output for up-to-date translators without problem."""␊ |
1175 | ␊ |
1176 | # If there is nothing to report, return immediately.␊ |
1177 | if self.status == '' and not self.note:␊ |
1178 | return␊ |
1179 | ␊ |
1180 | # Report the number of not implemented methods.␊ |
1181 | fout.write('\n\n\n')␊ |
1182 | fout.write(self.classId + ' (' + self.baseClassId + ')')␊ |
1183 | percentImplemented = 100 # init␊ |
1184 | allNum = len(self.manager.requiredMethodsDic) ␊ |
1185 | if self.missingMethods:␊ |
1186 | num = len(self.missingMethods)␊ |
1187 | percentImplemented = 100 * (allNum - num) / allNum␊ |
1188 | fout.write(' %d' % num)␊ |
1189 | fout.write(' method')␊ |
1190 | if num > 1:␊ |
1191 | fout.write('s')␊ |
1192 | fout.write(' to implement (%d %%)' % (100 * num / allNum))␊ |
1193 | fout.write('\n' + '-' * len(self.classId))␊ |
1194 | ␊ |
1195 | # Write the info about the implemented required methods.␊ |
1196 | fout.write('\n\n Implements %d' % len(self.implementedMethods))␊ |
1197 | fout.write(' of the required methods (%d %%).' % percentImplemented)␊ |
1198 | ␊ |
1199 | # Report the missing method, but only when it is not English-based␊ |
1200 | # translator.␊ |
1201 | if self.missingMethods and self.status != 'En':␊ |
1202 | fout.write('\n\n Missing methods (should be implemented):\n')␊ |
1203 | reqDic = self.manager.requiredMethodsDic␊ |
1204 | for p in self.missingMethods:␊ |
1205 | fout.write('\n ' + reqDic[p])␊ |
1206 | ␊ |
1207 | # Always report obsolete methods.␊ |
1208 | if self.obsoleteMethods:␊ |
1209 | fout.write('\n\n Obsolete methods (should be removed, never used):\n')␊ |
1210 | myDic = self.prototypeDic␊ |
1211 | for p in self.obsoleteMethods:␊ |
1212 | fout.write('\n ' + myDic[p])␊ |
1213 | ␊ |
1214 | # For English-based translator, report the implemented methods.␊ |
1215 | if self.status == 'En' and self.implementedMethods:␊ |
1216 | fout.write('\n\n This English-based translator implements ')␊ |
1217 | fout.write('the following methods:\n')␊ |
1218 | reqDic = self.manager.requiredMethodsDic␊ |
1219 | for p in self.implementedMethods:␊ |
1220 | fout.write('\n ' + reqDic[p])␊ |
1221 | ␊ |
1222 | ␊ |
1223 | def getmtime(self):␊ |
1224 | """Returns the last modification time of the source file."""␊ |
1225 | assert(os.path.isfile(self.fname))␊ |
1226 | return os.path.getmtime(self.fname)␊ |
1227 | ␊ |
1228 | ␊ |
1229 | class TrManager:␊ |
1230 | """Collects basic info and builds subordinate Transl objects."""␊ |
1231 | ␊ |
1232 | def __init__(self):␊ |
1233 | """Determines paths, creates and initializes structures.␊ |
1234 | ␊ |
1235 | The arguments of the script may explicitly say what languages should ␊ |
1236 | be processed. Write the two letter identifications that are used␊ |
1237 | for composing the source filenames, so...␊ |
1238 | ␊ |
1239 | python translator.py cz␊ |
1240 | ␊ |
1241 | this will process only translator_cz.h source.␊ |
1242 | """␊ |
1243 | ␊ |
1244 | # Determine the path to the script and its name.␊ |
1245 | self.script = os.path.abspath(sys.argv[0]) ␊ |
1246 | self.script_path, self.script_name = os.path.split(self.script) ␊ |
1247 | self.script_path = os.path.abspath(self.script_path)␊ |
1248 | ␊ |
1249 | # Determine the absolute path to the Doxygen's root subdirectory.␊ |
1250 | # If DOXYGEN environment variable is not found, the directory is␊ |
1251 | # determined from the path of the script.␊ |
1252 | doxy_default = os.path.join(self.script_path, '..') ␊ |
1253 | self.doxy_path = os.path.abspath(os.getenv('DOXYGEN', doxy_default))␊ |
1254 | ␊ |
1255 | # Get the explicit arguments of the script.␊ |
1256 | self.script_argLst = sys.argv[1:]␊ |
1257 | ␊ |
1258 | # Build the path names based on the Doxygen's root knowledge.␊ |
1259 | self.doc_path = os.path.join(self.doxy_path, 'doc')␊ |
1260 | self.src_path = os.path.join(self.doxy_path, 'src')␊ |
1261 | ␊ |
1262 | # Create the empty dictionary for Transl object identitied by the␊ |
1263 | # class identifier of the translator. ␊ |
1264 | self.__translDic = {}␊ |
1265 | ␊ |
1266 | # Create the None dictionary of required methods. The key is the␊ |
1267 | # unified prototype, the value is the full prototype. Set inside␊ |
1268 | # the self.__build().␊ |
1269 | self.requiredMethodsDic = None␊ |
1270 | ␊ |
1271 | # Create the empty dictionary that says what method is implemented␊ |
1272 | # by what adapter.␊ |
1273 | self.adaptMethodsDic = {}␊ |
1274 | ␊ |
1275 | # The last modification time will capture the modification of this␊ |
1276 | # script, of the translator.h, of the translator_adapter.h (see the ␊ |
1277 | # self.__build() for the last two) of all the translator_xx.h files␊ |
1278 | # and of the template for generating the documentation. So, this␊ |
1279 | # time can be compared with modification time of the generated ␊ |
1280 | # documentation to decide, whether the doc should be re-generated.␊ |
1281 | self.lastModificationTime = os.path.getmtime(self.script)␊ |
1282 | ␊ |
1283 | # Set the names of the translator report text file, of the template␊ |
1284 | # for generating "Internationalization" document, for the generated␊ |
1285 | # file itself, and for the maintainers list.␊ |
1286 | self.translatorReportFileName = 'translator_report.txt'␊ |
1287 | self.maintainersFileName = 'maintainers.txt'␊ |
1288 | self.languageTplFileName = 'language.tpl'␊ |
1289 | self.languageDocFileName = 'language.doc'␊ |
1290 | ␊ |
1291 | # The information about the maintainers will be stored ␊ |
1292 | # in the dictionary with the following name.␊ |
1293 | self.__maintainersDic = None␊ |
1294 | ␊ |
1295 | # Define the other used structures and variables for information.␊ |
1296 | self.langLst = None # including English based␊ |
1297 | self.supportedLangReadableStr = None # coupled En-based as a note␊ |
1298 | self.numLang = None # excluding coupled En-based␊ |
1299 | self.doxVersion = None # Doxygen version␊ |
1300 | ␊ |
1301 | # Capture the knowledge about translators that are not implemented␊ |
1302 | # to use UTF-8 internally.␊ |
1303 | self.decodedTranslators = self.getDecodedTranslators()␊ |
1304 | ␊ |
1305 | # Build objects where each one is responsible for one translator.␊ |
1306 | self.__build()␊ |
1307 | ␊ |
1308 | ␊ |
1309 | def getDecodedTranslators(self):␊ |
1310 | """Parses language.cpp to find what translators do not use UTF-8 yet"""␊ |
1311 | decodedTranslators = []␊ |
1312 | ␊ |
1313 | # Regular expression to detect the lines like␊ |
1314 | # theTranslator=new TranslatorDecoder(new TranslatorSwedish);␊ |
1315 | rex = re.compile(r'^\s*theTranslator\s*=\s*new\s+.*$')␊ |
1316 | ␊ |
1317 | # Regular expression to get the (optional) TranslatorDecoder and TranslatorXXX␊ |
1318 | rex2 = re.compile(r'\bTranslator\w+')␊ |
1319 | ␊ |
1320 | # Parse the lines in the specific source code.␊ |
1321 | f = open(os.path.join(self.src_path, 'language.cpp'), 'rU')␊ |
1322 | for line in f:␊ |
1323 | if rex.match(line):␊ |
1324 | lst = rex2.findall(line)␊ |
1325 | if lst[0] == 'TranslatorDecoder':␊ |
1326 | decodedTranslators.append(lst[1])␊ |
1327 | f.close()␊ |
1328 | ␊ |
1329 | # Display warning when all translator implementations were converted ␊ |
1330 | # to UTF-8.␊ |
1331 | if len(decodedTranslators) == 0:␊ |
1332 | print 'This script should be updated. All translators do use UTF-8'␊ |
1333 | print 'internally. The TranslatorDecoder adapter should be removed'␊ |
1334 | print 'from the code and its usage should not be checked any more.'␊ |
1335 | ␊ |
1336 | return decodedTranslators␊ |
1337 | ␊ |
1338 | ␊ |
1339 | def __build(self):␊ |
1340 | """Find the translator files and build the objects for translators."""␊ |
1341 | ␊ |
1342 | # The translator.h must exist (the Transl object will check it),␊ |
1343 | # create the object for it and let it build the dictionary of␊ |
1344 | # required methods.␊ |
1345 | tr = Transl(os.path.join(self.src_path, 'translator.h'), self)␊ |
1346 | self.requiredMethodsDic = tr.collectPureVirtualPrototypes()␊ |
1347 | tim = tr.getmtime()␊ |
1348 | if tim > self.lastModificationTime:␊ |
1349 | self.lastModificationTime = tim␊ |
1350 | ␊ |
1351 | # The translator_adapter.h must exist (the Transl object will check it),␊ |
1352 | # create the object for it and store the reference in the dictionary.␊ |
1353 | tr = Transl(os.path.join(self.src_path, 'translator_adapter.h'), self)␊ |
1354 | self.adaptMethodsDic = tr.collectAdapterPrototypes()␊ |
1355 | tim = tr.getmtime()␊ |
1356 | if tim > self.lastModificationTime:␊ |
1357 | self.lastModificationTime = tim␊ |
1358 | ␊ |
1359 | # Create the list of the filenames with language translator sources.␊ |
1360 | # If the explicit arguments of the script were typed, process only␊ |
1361 | # those files.␊ |
1362 | if self.script_argLst:␊ |
1363 | lst = ['translator_' + x + '.h' for x in self.script_argLst]␊ |
1364 | for fname in lst:␊ |
1365 | if not os.path.isfile(os.path.join(self.src_path, fname)):␊ |
1366 | sys.stderr.write("\a\nFile '%s' not found!\n" % fname)␊ |
1367 | sys.exit(1)␊ |
1368 | else: ␊ |
1369 | lst = os.listdir(self.src_path)␊ |
1370 | lst = filter(lambda x: x[:11] == 'translator_'␊ |
1371 | and x[-2:] == '.h'␊ |
1372 | and x != 'translator_adapter.h', lst)␊ |
1373 | ␊ |
1374 | # Build the object for the translator_xx.h files, and process the ␊ |
1375 | # content of the file. Then insert the object to the dictionary␊ |
1376 | # accessed via classId.␊ |
1377 | for fname in lst:␊ |
1378 | fullname = os.path.join(self.src_path, fname)␊ |
1379 | tr = Transl(fullname, self)␊ |
1380 | tr.processing()␊ |
1381 | assert(tr.classId != 'Translator')␊ |
1382 | self.__translDic[tr.classId] = tr␊ |
1383 | ␊ |
1384 | # Extract the global information of the processed info. ␊ |
1385 | self.__extractProcessedInfo()␊ |
1386 | ␊ |
1387 | ␊ |
1388 | def __extractProcessedInfo(self):␊ |
1389 | """Build lists and strings of the processed info.""" ␊ |
1390 | ␊ |
1391 | # Build the auxiliary list with strings compound of the status,␊ |
1392 | # readable form of the language, and classId.␊ |
1393 | statLst = []␊ |
1394 | for obj in self.__translDic.values():␊ |
1395 | assert(obj.classId != 'Translator')␊ |
1396 | s = obj.status + '|' + obj.langReadable + '|' + obj.classId␊ |
1397 | statLst.append(s)␊ |
1398 | ␊ |
1399 | # Sort the list and extract the object identifiers (classId's) for␊ |
1400 | # the up-to-date translators and English-based translators.␊ |
1401 | statLst.sort()␊ |
1402 | self.upToDateIdLst = [x.split('|')[2] for x in statLst if x[0] == '|']␊ |
1403 | self.EnBasedIdLst = [x.split('|')[2] for x in statLst if x[:2] == 'En']␊ |
1404 | ␊ |
1405 | # Reverse the list and extract the TranslatorAdapter based translators.␊ |
1406 | statLst.reverse()␊ |
1407 | self.adaptIdLst = [x.split('|')[2] for x in statLst if x[0].isdigit()]␊ |
1408 | ␊ |
1409 | # Build the list of tuples that contain (langReadable, obj). ␊ |
1410 | # Sort it by readable name.␊ |
1411 | self.langLst = []␊ |
1412 | for obj in self.__translDic.values():␊ |
1413 | self.langLst.append((obj.langReadable, obj))␊ |
1414 | self.langLst.sort(lambda a, b: cmp(a[0], b[0]))␊ |
1415 | ␊ |
1416 | # Create the list with readable language names. If the language has␊ |
1417 | # also the English-based version, modify the item by appending␊ |
1418 | # the note. Number of the supported languages is equal to the length␊ |
1419 | # of the list.␊ |
1420 | langReadableLst = []␊ |
1421 | for name, obj in self.langLst:␊ |
1422 | if obj.status == 'En': continue␊ |
1423 | ␊ |
1424 | # Append the 'En' to the classId to possibly obtain the classId␊ |
1425 | # of the English-based object. If the object exists, modify the␊ |
1426 | # name for the readable list of supported languages.␊ |
1427 | classIdEn = obj.classId + 'En'␊ |
1428 | if self.__translDic.has_key(classIdEn):␊ |
1429 | name += ' (+En)'␊ |
1430 | ␊ |
1431 | # Append the result name of the language, possibly with note.␊ |
1432 | langReadableLst.append(name)␊ |
1433 | ␊ |
1434 | # Create the multiline string of readable language names, ␊ |
1435 | # with punctuation, wrapped to paragraph.␊ |
1436 | if len(langReadableLst) == 1:␊ |
1437 | s = langReadableLst[0]␊ |
1438 | elif len(langReadableLst) == 2:␊ |
1439 | s = ' and '.join(langReadableLst) ␊ |
1440 | else: ␊ |
1441 | s = ', '.join(langReadableLst[:-1]) + ', and ' ␊ |
1442 | s += langReadableLst[-1]␊ |
1443 | ␊ |
1444 | self.supportedLangReadableStr = fill(s + '.') ␊ |
1445 | ␊ |
1446 | # Find the number of the supported languages. The English based␊ |
1447 | # languages are not counted if the non-English based also exists.␊ |
1448 | self.numLang = len(self.langLst)␊ |
1449 | for name, obj in self.langLst:␊ |
1450 | if obj.status == 'En':␊ |
1451 | classId = obj.classId[:-2]␊ |
1452 | if self.__translDic.has_key(classId):␊ |
1453 | self.numLang -= 1 # the couple will be counted as one␊ |
1454 | ␊ |
1455 | # Extract the version of Doxygen.␊ |
1456 | f = open(os.path.join(self.doxy_path, 'VERSION'))␊ |
1457 | self.doxVersion = f.readline().strip()␊ |
1458 | f.close()␊ |
1459 | ␊ |
1460 | # Update the last modification time.␊ |
1461 | for tr in self.__translDic.values():␊ |
1462 | tim = tr.getmtime()␊ |
1463 | if tim > self.lastModificationTime:␊ |
1464 | self.lastModificationTime = tim␊ |
1465 | ␊ |
1466 | ␊ |
1467 | def __getNoTrSourceFilesLst(self):␊ |
1468 | """Returns the list of sources to be checked.␊ |
1469 | ␊ |
1470 | All .cpp files and also .h files that do not declare or define␊ |
1471 | the translator methods are included in the list. The file names ␊ |
1472 | are searched in doxygen/src directory.␊ |
1473 | """ ␊ |
1474 | files = []␊ |
1475 | for item in os.listdir(self.src_path):␊ |
1476 | # Split the bare name to get the extension.␊ |
1477 | name, ext = os.path.splitext(item)␊ |
1478 | ext = ext.lower()␊ |
1479 | ␊ |
1480 | # Include only .cpp and .h files (case independent) and exclude␊ |
1481 | # the files where the checked identifiers are defined.␊ |
1482 | if ext == '.cpp' or (ext == '.h' and name.find('translator') == -1):␊ |
1483 | fname = os.path.join(self.src_path, item)␊ |
1484 | assert os.path.isfile(fname) # assumes no directory with the ext␊ |
1485 | files.append(fname) # full name␊ |
1486 | return files␊ |
1487 | ␊ |
1488 | ␊ |
1489 | def __removeUsedInFiles(self, fname, dic):␊ |
1490 | """Removes items for method identifiers that are found in fname.␊ |
1491 | ␊ |
1492 | The method reads the content of the file as one string and searches␊ |
1493 | for all identifiers from dic. The identifiers that were found in␊ |
1494 | the file are removed from the dictionary.␊ |
1495 | ␊ |
1496 | Note: If more files is to be checked, the files where most items are␊ |
1497 | probably used should be checked first and the resulting reduced ␊ |
1498 | dictionary should be used for checking the next files (speed up).␊ |
1499 | """␊ |
1500 | lst_in = dic.keys() # identifiers to be searched for␊ |
1501 | ␊ |
1502 | # Read content of the file as one string.␊ |
1503 | assert os.path.isfile(fname)␊ |
1504 | f = open(fname)␊ |
1505 | cont = f.read()␊ |
1506 | f.close()␊ |
1507 | ␊ |
1508 | # Remove the items for identifiers that were found in the file.␊ |
1509 | while lst_in:␊ |
1510 | item = lst_in.pop(0)␊ |
1511 | if cont.find(item) != -1:␊ |
1512 | del dic[item]␊ |
1513 | ␊ |
1514 | ␊ |
1515 | def __checkForNotUsedTrMethods(self):␊ |
1516 | """Returns the dictionary of not used translator methods.␊ |
1517 | ␊ |
1518 | The method can be called only after self.requiredMethodsDic has been␊ |
1519 | built. The stripped prototypes are the values, the method identifiers␊ |
1520 | are the keys.␊ |
1521 | """␊ |
1522 | # Build the dictionary of the required method prototypes with␊ |
1523 | # method identifiers used as keys.␊ |
1524 | trdic = {}␊ |
1525 | for prototype in self.requiredMethodsDic.keys():␊ |
1526 | ri = prototype.split('(')[0]␊ |
1527 | identifier = ri.split()[1].strip()␊ |
1528 | trdic[identifier] = prototype␊ |
1529 | ␊ |
1530 | # Build the list of source files where translator method identifiers␊ |
1531 | # can be used.␊ |
1532 | files = self.__getNoTrSourceFilesLst()␊ |
1533 | ␊ |
1534 | # Loop through the files and reduce the dictionary of id -> proto.␊ |
1535 | for fname in files:␊ |
1536 | self.__removeUsedInFiles(fname, trdic)␊ |
1537 | ␊ |
1538 | # Return the dictionary of not used translator methods.␊ |
1539 | return trdic␊ |
1540 | ␊ |
1541 | ␊ |
1542 | def __emails(self, classId):␊ |
1543 | """Returns the list of maintainer emails.␊ |
1544 | ␊ |
1545 | The method returns the list of e-mail adresses for the translator␊ |
1546 | class, but only the addresses that were not marked as [xxx]."""␊ |
1547 | lst = []␊ |
1548 | for m in self.__maintainersDic[classId]:␊ |
1549 | if not m[1].startswith('['):␊ |
1550 | email = m[1]␊ |
1551 | email = email.replace(' at ', '@') # Unmangle the mangled e-mail ␊ |
1552 | email = email.replace(' dot ', '.')␊ |
1553 | lst.append(email)␊ |
1554 | return lst␊ |
1555 | ␊ |
1556 | ␊ |
1557 | def generateTranslatorReport(self):␊ |
1558 | """Generates the translator report."""␊ |
1559 | ␊ |
1560 | output = os.path.join(self.doc_path, self.translatorReportFileName)␊ |
1561 | ␊ |
1562 | # Open the textual report file for the output.␊ |
1563 | f = open(output, 'w')␊ |
1564 | ␊ |
1565 | # Output the information about the version.␊ |
1566 | f.write('(' + self.doxVersion + ')\n\n')␊ |
1567 | ␊ |
1568 | # Output the information about the number of the supported languages␊ |
1569 | # and the list of the languages, or only the note about the explicitly␊ |
1570 | # given languages to process.␊ |
1571 | if self.script_argLst:␊ |
1572 | f.write('The report was generated for the following, explicitly')␊ |
1573 | f.write(' identified languages:\n\n')␊ |
1574 | f.write(self.supportedLangReadableStr + '\n\n')␊ |
1575 | else:␊ |
1576 | f.write('Doxygen supports the following ')␊ |
1577 | f.write(str(self.numLang))␊ |
1578 | f.write(' languages (sorted alphabetically):\n\n')␊ |
1579 | f.write(self.supportedLangReadableStr + '\n\n')␊ |
1580 | ␊ |
1581 | # Write the summary about the status of language translators (how ␊ |
1582 | # many translators) are up-to-date, etc.␊ |
1583 | s = 'Of them, %d translators are up-to-date, ' % len(self.upToDateIdLst)␊ |
1584 | s += '%d translators are based on some adapter class, ' % len(self.adaptIdLst) ␊ |
1585 | s += 'and %d are English based.' % len(self.EnBasedIdLst)␊ |
1586 | f.write(fill(s) + '\n\n')␊ |
1587 | ␊ |
1588 | # The e-mail addresses of the maintainers will be collected to ␊ |
1589 | # the auxiliary file in the order of translator classes listed␊ |
1590 | # in the translator report.␊ |
1591 | fmail = open('mailto.txt', 'w')␊ |
1592 | ␊ |
1593 | # Write the list of up-to-date translator classes. ␊ |
1594 | if self.upToDateIdLst:␊ |
1595 | s = '''The following translator classes are up-to-date (sorted␊ |
1596 | alphabetically). This means that they derive from the␊ |
1597 | Translator class and they implement all %d of the required␊ |
1598 | methods. Anyway, there still may be some details listed even ␊ |
1599 | for them:'''␊ |
1600 | s = s % len(self.requiredMethodsDic) ␊ |
1601 | f.write('-' * 70 + '\n')␊ |
1602 | f.write(fill(s) + '\n\n')␊ |
1603 | ␊ |
1604 | mailtoLst = []␊ |
1605 | for x in self.upToDateIdLst:␊ |
1606 | obj = self.__translDic[x]␊ |
1607 | f.write(' ' + obj.classId)␊ |
1608 | if obj.note:␊ |
1609 | f.write(' -- ' + obj.note)␊ |
1610 | f.write('\n')␊ |
1611 | mailtoLst.extend(self.__emails(obj.classId))␊ |
1612 | ␊ |
1613 | fmail.write('up-to-date\n')␊ |
1614 | fmail.write('; '.join(mailtoLst))␊ |
1615 | ␊ |
1616 | # Write the list of the adapter based classes. The very obsolete␊ |
1617 | # translators that derive from TranslatorEnglish are included.␊ |
1618 | if self.adaptIdLst:␊ |
1619 | s = '''The following translator classes need some maintenance ␊ |
1620 | (the most obsolete at the end). The other info shows the␊ |
1621 | estimation of Doxygen version when the class was last␊ |
1622 | updated and number of methods that must be implemented to␊ |
1623 | become up-to-date:'''␊ |
1624 | f.write('\n' + '-' * 70 + '\n')␊ |
1625 | f.write(fill(s) + '\n\n')␊ |
1626 | ␊ |
1627 | # Find also whether some adapter classes may be removed.␊ |
1628 | adaptMinVersion = '9.9.99'␊ |
1629 | ␊ |
1630 | mailtoLst = []␊ |
1631 | numRequired = len(self.requiredMethodsDic)␊ |
1632 | for x in self.adaptIdLst:␊ |
1633 | obj = self.__translDic[x]␊ |
1634 | f.write(' %-30s' % obj.classId)␊ |
1635 | f.write(' %-6s' % obj.readableStatus)␊ |
1636 | numimpl = len(obj.missingMethods)␊ |
1637 | pluralS = ''␊ |
1638 | if numimpl > 1: pluralS = 's'␊ |
1639 | percent = 100 * numimpl / numRequired␊ |
1640 | f.write('\t%2d method%s to implement (%d %%)' % (␊ |
1641 | numimpl, pluralS, percent))␊ |
1642 | if obj.note:␊ |
1643 | f.write('\n\tNote: ' + obj.note + '\n')␊ |
1644 | f.write('\n')␊ |
1645 | mailtoLst.extend(self.__emails(obj.classId)) # to maintainer␊ |
1646 | ␊ |
1647 | # Check the level of required adapter classes.␊ |
1648 | if obj.status != '0.0.00' and obj.status < adaptMinVersion:␊ |
1649 | adaptMinVersion = obj.status␊ |
1650 | ␊ |
1651 | fmail.write('\n\ntranslator based\n')␊ |
1652 | fmail.write('; '.join(mailtoLst))␊ |
1653 | ␊ |
1654 | # Set the note if some old translator adapters are not needed ␊ |
1655 | # any more. Do it only when the script is called without arguments,␊ |
1656 | # i.e. all languages were checked against the needed translator ␊ |
1657 | # adapters. ␊ |
1658 | if not self.script_argLst:␊ |
1659 | to_remove = {}␊ |
1660 | for version, adaptClassId in self.adaptMethodsDic.values():␊ |
1661 | if version < adaptMinVersion:␊ |
1662 | to_remove[adaptClassId] = True␊ |
1663 | ␊ |
1664 | if to_remove:␊ |
1665 | lst = to_remove.keys()␊ |
1666 | lst.sort()␊ |
1667 | plural = len(lst) > 1␊ |
1668 | note = 'Note: The adapter class'␊ |
1669 | if plural: note += 'es'␊ |
1670 | note += ' ' + ', '.join(lst) ␊ |
1671 | if not plural: ␊ |
1672 | note += ' is'␊ |
1673 | else:␊ |
1674 | note += ' are'␊ |
1675 | note += ' not used and can be removed.'␊ |
1676 | f.write('\n' + fill(note) + '\n')␊ |
1677 | ␊ |
1678 | # Write the list of the English-based classes.␊ |
1679 | if self.EnBasedIdLst:␊ |
1680 | s = '''The following translator classes derive directly from the ␊ |
1681 | TranslatorEnglish. The class identifier has the suffix 'En' ␊ |
1682 | that says that this is intentional. Usually, there is also ␊ |
1683 | a non-English based version of the translator for ␊ |
1684 | the language:'''␊ |
1685 | f.write('\n' + '-' * 70 + '\n')␊ |
1686 | f.write(fill(s) + '\n\n')␊ |
1687 | ␊ |
1688 | for x in self.EnBasedIdLst:␊ |
1689 | obj = self.__translDic[x]␊ |
1690 | f.write(' ' + obj.classId)␊ |
1691 | f.write('\timplements %d methods' % len(obj.implementedMethods))␊ |
1692 | if obj.note:␊ |
1693 | f.write(' -- ' + obj.note)␊ |
1694 | f.write('\n')␊ |
1695 | ␊ |
1696 | # Check for not used translator methods and generate warning if found.␊ |
1697 | # The check is rather time consuming, so it is not done when report␊ |
1698 | # is restricted to explicitly given language identifiers.␊ |
1699 | if not self.script_argLst:␊ |
1700 | dic = self.__checkForNotUsedTrMethods()␊ |
1701 | if dic:␊ |
1702 | s = '''WARNING: The following translator methods are declared␊ |
1703 | in the Translator class but their identifiers do not appear ␊ |
1704 | in source files. The situation should be checked. The .cpp ␊ |
1705 | files and .h files excluding the '*translator*' files ␊ |
1706 | in doxygen/src directory were simply searched for occurence ␊ |
1707 | of the method identifiers:'''␊ |
1708 | f.write('\n' + '=' * 70 + '\n')␊ |
1709 | f.write(fill(s) + '\n\n')␊ |
1710 | ␊ |
1711 | keys = dic.keys()␊ |
1712 | keys.sort()␊ |
1713 | for key in keys:␊ |
1714 | f.write(' ' + dic[key] + '\n')␊ |
1715 | f.write('\n')␊ |
1716 | ␊ |
1717 | # Write the details for the translators.␊ |
1718 | f.write('\n' + '=' * 70)␊ |
1719 | f.write('\nDetails for translators (classes sorted alphabetically):\n')␊ |
1720 | ␊ |
1721 | cls = self.__translDic.keys()␊ |
1722 | cls.sort()␊ |
1723 | ␊ |
1724 | for c in cls:␊ |
1725 | obj = self.__translDic[c]␊ |
1726 | assert(obj.classId != 'Translator') ␊ |
1727 | obj.report(f)␊ |
1728 | ␊ |
1729 | # Close the report file and the auxiliary file with e-mails. ␊ |
1730 | f.close()␊ |
1731 | fmail.close()␊ |
1732 | ␊ |
1733 | ␊ |
1734 | def __loadMaintainers(self):␊ |
1735 | """Load and process the file with the maintainers.␊ |
1736 | ␊ |
1737 | Fills the dictionary classId -> [(name, e-mail), ...]."""␊ |
1738 | ␊ |
1739 | fname = os.path.join(self.doc_path, self.maintainersFileName)␊ |
1740 | ␊ |
1741 | # Include the maintainers file to the group of files checked with␊ |
1742 | # respect to the modification time.␊ |
1743 | tim = os.path.getmtime(fname)␊ |
1744 | if tim > self.lastModificationTime:␊ |
1745 | self.lastModificationTime = tim␊ |
1746 | ␊ |
1747 | # Process the content of the maintainers file.␊ |
1748 | f = codecs.open(fname, 'r', 'utf-8')␊ |
1749 | inside = False # inside the record for the language␊ |
1750 | lineReady = True␊ |
1751 | classId = None␊ |
1752 | maintainersLst = None␊ |
1753 | self.__maintainersDic = {}␊ |
1754 | while lineReady:␊ |
1755 | line = f.readline() # next line␊ |
1756 | lineReady = line != '' # when eof, then line == ''␊ |
1757 | ␊ |
1758 | line = line.strip() # eof should also behave as separator␊ |
1759 | if line != u'' and line[0] == u'%': # skip the comment line␊ |
1760 | continue ␊ |
1761 | ␊ |
1762 | if not inside: # if outside of the record␊ |
1763 | if line != u'': # should be language identifier␊ |
1764 | classId = line␊ |
1765 | maintainersLst = []␊ |
1766 | inside = True␊ |
1767 | # Otherwise skip empty line that do not act as separator.␊ |
1768 | ␊ |
1769 | else: # if inside the record ␊ |
1770 | if line == u'': # separator found␊ |
1771 | inside = False␊ |
1772 | else:␊ |
1773 | # If it is the first maintainer, create the empty list.␊ |
1774 | if not self.__maintainersDic.has_key(classId):␊ |
1775 | self.__maintainersDic[classId] = []␊ |
1776 | ␊ |
1777 | # Split the information about the maintainer and append␊ |
1778 | # the tuple. The address may be prefixed '[unreachable]'␊ |
1779 | # or whatever '[xxx]'. This will be processed later.␊ |
1780 | lst = line.split(u':', 1)␊ |
1781 | assert(len(lst) == 2)␊ |
1782 | t = (lst[0].strip(), lst[1].strip())␊ |
1783 | self.__maintainersDic[classId].append(t)␊ |
1784 | f.close()␊ |
1785 | ␊ |
1786 | ␊ |
1787 | def generateLanguageDoc(self):␊ |
1788 | """Checks the modtime of files and generates language.doc."""␊ |
1789 | self.__loadMaintainers()␊ |
1790 | ␊ |
1791 | # Check the last modification time of the template file. It is the␊ |
1792 | # last file from the group that decide whether the documentation␊ |
1793 | # should or should not be generated.␊ |
1794 | fTplName = os.path.join(self.doc_path, self.languageTplFileName)␊ |
1795 | tim = os.path.getmtime(fTplName)␊ |
1796 | if tim > self.lastModificationTime:␊ |
1797 | self.lastModificationTime = tim␊ |
1798 | ␊ |
1799 | # If the generated documentation exists and is newer than any of ␊ |
1800 | # the source files from the group, do not generate it and quit ␊ |
1801 | # quietly.␊ |
1802 | fDocName = os.path.join(self.doc_path, self.languageDocFileName)␊ |
1803 | if os.path.isfile(fDocName): ␊ |
1804 | if os.path.getmtime(fDocName) > self.lastModificationTime:␊ |
1805 | return␊ |
1806 | ␊ |
1807 | # The document or does not exist or is older than some of the␊ |
1808 | # sources. It must be generated again.␊ |
1809 | #␊ |
1810 | # Read the template of the documentation, and remove the first␊ |
1811 | # attention lines.␊ |
1812 | f = codecs.open(fTplName, 'r', 'utf-8')␊ |
1813 | doctpl = f.read()␊ |
1814 | f.close()␊ |
1815 | ␊ |
1816 | pos = doctpl.find(u'/***')␊ |
1817 | assert pos != -1␊ |
1818 | doctpl = doctpl[pos:]␊ |
1819 | ␊ |
1820 | # Fill the tplDic by symbols that will be inserted into the ␊ |
1821 | # document template.␊ |
1822 | tplDic = {}␊ |
1823 | ␊ |
1824 | s = u'Do not edit this file. It was generated by the %s script.' % self.script_name␊ |
1825 | tplDic['editnote'] = s␊ |
1826 | ␊ |
1827 | tplDic['doxVersion'] = self.doxVersion␊ |
1828 | tplDic['supportedLangReadableStr'] = self.supportedLangReadableStr ␊ |
1829 | tplDic['translatorReportFileName'] = self.translatorReportFileName␊ |
1830 | ␊ |
1831 | ahref = u'<a href="../doc/' + self.translatorReportFileName␊ |
1832 | ahref += u'"\n><code>doxygen/doc/' + self.translatorReportFileName␊ |
1833 | ahref += u'</code></a>'␊ |
1834 | tplDic['translatorReportLink'] = ahref␊ |
1835 | tplDic['numLangStr'] = str(self.numLang)␊ |
1836 | ␊ |
1837 | # Define templates for HTML table parts of the documentation.␊ |
1838 | htmlTableTpl = u'''\␊ |
1839 | \\htmlonly␊ |
1840 | <table align="center" cellspacing="0" cellpadding="0" border="0">␊ |
1841 | <tr bgcolor="#000000">␊ |
1842 | <td>␊ |
1843 | <table cellspacing="1" cellpadding="2" border="0">␊ |
1844 | <tr bgcolor="#4040c0">␊ |
1845 | <td ><b><font size="+1" color="#ffffff"> Language </font></b></td>␊ |
1846 | <td ><b><font size="+1" color="#ffffff"> Maintainer </font></b></td>␊ |
1847 | <td ><b><font size="+1" color="#ffffff"> Contact address </font>␊ |
1848 | <font size="-2" color="#ffffff">(replace the at and dot)</font></b></td>␊ |
1849 | <td ><b><font size="+1" color="#ffffff"> Status </font></b></td>␊ |
1850 | </tr>␊ |
1851 | <!-- table content begin -->␊ |
1852 | %s␊ |
1853 | <!-- table content end -->␊ |
1854 | </table>␊ |
1855 | </td>␊ |
1856 | </tr>␊ |
1857 | </table>␊ |
1858 | \\endhtmlonly␊ |
1859 | '''␊ |
1860 | htmlTableTpl = dedent(htmlTableTpl) ␊ |
1861 | htmlTrTpl = u'\n <tr bgcolor="#ffffff">%s\n </tr>'␊ |
1862 | htmlTdTpl = u'\n <td>%s</td>'␊ |
1863 | ␊ |
1864 | # Loop through transl objects in the order of sorted readable names␊ |
1865 | # and add generate the content of the HTML table.␊ |
1866 | trlst = []␊ |
1867 | for name, obj in self.langLst:␊ |
1868 | # Fill the table data elements for one row. The first element␊ |
1869 | # contains the readable name of the language.␊ |
1870 | lst = [ htmlTdTpl % obj.langReadable ]␊ |
1871 | ␊ |
1872 | # The next two elements contain the list of maintainers ␊ |
1873 | # and the list of their mangled e-mails. For English-based␊ |
1874 | # translators that are coupled with the non-English based, ␊ |
1875 | # insert the 'see' note.␊ |
1876 | mm = None # init -- maintainer␊ |
1877 | ee = None # init -- e-mail address␊ |
1878 | if obj.status == 'En':␊ |
1879 | # Check whether there is the coupled non-English.␊ |
1880 | classId = obj.classId[:-2]␊ |
1881 | if classId in self.__translDic:␊ |
1882 | lang = self.__translDic[classId].langReadable␊ |
1883 | mm = u'see the %s language' % lang␊ |
1884 | ee = u' '␊ |
1885 | ␊ |
1886 | if not mm and obj.classId in self.__maintainersDic:␊ |
1887 | # Build a string of names separated by the HTML break element.␊ |
1888 | # Special notes used instead of names are highlighted.␊ |
1889 | lm = [] ␊ |
1890 | for maintainer in self.__maintainersDic[obj.classId]:␊ |
1891 | name = maintainer[0]␊ |
1892 | if name.startswith(u'--'):␊ |
1893 | name = u'<span style="color: red; background-color: yellow">'\␊ |
1894 | + name + u'</span>'␊ |
1895 | lm.append(name)␊ |
1896 | mm = u'<br/>'.join(lm)␊ |
1897 | ␊ |
1898 | # The marked adresses (they start with the mark '[unreachable]', ␊ |
1899 | # '[resigned]', whatever '[xxx]') will not be displayed at all.␊ |
1900 | # Only the mark will be used instead.␊ |
1901 | rexMark = re.compile(ur'(?P<mark>\[.*?\])')␊ |
1902 | le = []␊ |
1903 | for maintainer in self.__maintainersDic[obj.classId]:␊ |
1904 | address = maintainer[1]␊ |
1905 | m = rexMark.search(address)␊ |
1906 | if m is not None:␊ |
1907 | address = u'<span style="color: brown">'\␊ |
1908 | + m.group(u'mark') + u'</span>'␊ |
1909 | le.append(address) ␊ |
1910 | ee = u'<br/>'.join(le)␊ |
1911 | ␊ |
1912 | # Append the maintainer and e-mail elements.␊ |
1913 | lst.append(htmlTdTpl % mm)␊ |
1914 | lst.append(htmlTdTpl % ee)␊ |
1915 | ␊ |
1916 | # The last element contains the readable form of the status.␊ |
1917 | lst.append(htmlTdTpl % obj.readableStatus)␊ |
1918 | ␊ |
1919 | # Join the table data to one table row. ␊ |
1920 | trlst.append(htmlTrTpl % (''.join(lst)))␊ |
1921 | ␊ |
1922 | # Join the table rows and insert into the template.␊ |
1923 | htmlTable = htmlTableTpl % (''.join(trlst))␊ |
1924 | ␊ |
1925 | # Define templates for LaTeX table parts of the documentation.␊ |
1926 | latexTableTpl = ur'''␊ |
1927 | \latexonly␊ |
1928 | \footnotesize␊ |
1929 | \begin{longtable}{|l|l|l|l|}␊ |
1930 | \hline ␊ |
1931 | {\bf Language} & {\bf Maintainer} & {\bf Contact address} & {\bf Status} \\␊ |
1932 | \hline␊ |
1933 | %s␊ |
1934 | \hline␊ |
1935 | \end{longtable}␊ |
1936 | \normalsize␊ |
1937 | \endlatexonly␊ |
1938 | '''␊ |
1939 | latexTableTpl = dedent(latexTableTpl) ␊ |
1940 | latexLineTpl = u'\n' + r' %s & %s & {\tt\tiny %s} & %s \\'␊ |
1941 | ␊ |
1942 | # Loop through transl objects in the order of sorted readable names␊ |
1943 | # and add generate the content of the LaTeX table.␊ |
1944 | trlst = []␊ |
1945 | for name, obj in self.langLst:␊ |
1946 | # For LaTeX, more maintainers for the same language are␊ |
1947 | # placed on separate rows in the table. The line separator␊ |
1948 | # in the table is placed explicitly above the first␊ |
1949 | # maintainer. Prepare the arguments for the LaTeX row template.␊ |
1950 | maintainers = []␊ |
1951 | if self.__maintainersDic.has_key(obj.classId):␊ |
1952 | maintainers = self.__maintainersDic[obj.classId]␊ |
1953 | ␊ |
1954 | lang = obj.langReadable␊ |
1955 | maintainer = None # init␊ |
1956 | email = None # init␊ |
1957 | if obj.status == 'En':␊ |
1958 | # Check whether there is the coupled non-English.␊ |
1959 | classId = obj.classId[:-2]␊ |
1960 | if classId in self.__translDic:␊ |
1961 | langNE = self.__translDic[classId].langReadable␊ |
1962 | maintainer = u'see the %s language' % langNE␊ |
1963 | email = u'~'␊ |
1964 | ␊ |
1965 | if not maintainer and (obj.classId in self.__maintainersDic):␊ |
1966 | lm = [ m[0] for m in self.__maintainersDic[obj.classId] ] ␊ |
1967 | maintainer = maintainers[0][0]␊ |
1968 | email = maintainers[0][1]␊ |
1969 | ␊ |
1970 | status = obj.readableStatus␊ |
1971 | ␊ |
1972 | # Use the template to produce the line of the table and insert␊ |
1973 | # the hline plus the constructed line into the table content.␊ |
1974 | # The underscore character must be escaped.␊ |
1975 | trlst.append(u'\n \\hline')␊ |
1976 | s = latexLineTpl % (lang, maintainer, email, status)␊ |
1977 | s = s.replace(u'_', u'\\_')␊ |
1978 | trlst.append(s)␊ |
1979 | ␊ |
1980 | # List the other maintainers for the language. Do not set␊ |
1981 | # lang and status for them.␊ |
1982 | lang = u'~'␊ |
1983 | status = u'~'␊ |
1984 | for m in maintainers[1:]:␊ |
1985 | maintainer = m[0]␊ |
1986 | email = m[1]␊ |
1987 | s = latexLineTpl % (lang, maintainer, email, status)␊ |
1988 | s = s.replace(u'_', u'\\_')␊ |
1989 | trlst.append(s)␊ |
1990 | ␊ |
1991 | # Join the table lines and insert into the template.␊ |
1992 | latexTable = latexTableTpl % (u''.join(trlst))␊ |
1993 | ␊ |
1994 | # Put the HTML and LaTeX parts together and define the dic item.␊ |
1995 | tplDic['informationTable'] = htmlTable + u'\n' + latexTable ␊ |
1996 | ␊ |
1997 | # Insert the symbols into the document template and write it down.␊ |
1998 | f = codecs.open(fDocName, 'w', 'utf-8')␊ |
1999 | f.write(doctpl % tplDic)␊ |
2000 | f.close()␊ |
2001 | ␊ |
2002 | if __name__ == '__main__':␊ |
2003 | ␊ |
2004 | # Create the manager, build the transl objects, and parse the related␊ |
2005 | # sources.␊ |
2006 | trMan = TrManager()␊ |
2007 | ␊ |
2008 | # Generate the language.doc.␊ |
2009 | trMan.generateLanguageDoc()␊ |
2010 | ␊ |
2011 | # Generate the translator report.␊ |
2012 | trMan.generateTranslatorReport()␊ |
2013 |