Blame doc/translator.py

Packit 1c1d7e
"""Script to generate reports on translator classes from Doxygen sources.
Packit 1c1d7e

Packit 1c1d7e
  The main purpose of the script is to extract the information from sources
Packit 1c1d7e
  related to internationalization (the translator classes). It uses the
Packit 1c1d7e
  information to generate documentation (language.doc,
Packit 1c1d7e
  translator_report.txt) from templates (language.tpl, maintainers.txt).
Packit 1c1d7e

Packit 1c1d7e
  Simply run the script without parameters to get the reports and
Packit 1c1d7e
  documentation for all supported languages. If you want to generate the
Packit 1c1d7e
  translator report only for some languages, pass their codes as arguments
Packit 1c1d7e
  to the script. In that case, the language.doc will not be generated.
Packit 1c1d7e
  Example:
Packit 1c1d7e

Packit 1c1d7e
    python translator.py en nl cz
Packit 1c1d7e

Packit 1c1d7e
  Originally, the script was written in Perl and was known as translator.pl.
Packit 1c1d7e
  The last Perl version was dated 2002/05/21 (plus some later corrections)
Packit 1c1d7e

Packit 1c1d7e
                                         Petr Prikryl (prikryl at atlas dot cz)
Packit 1c1d7e

Packit 1c1d7e
  History:
Packit 1c1d7e
  --------
Packit 1c1d7e
  2002/05/21 - This was the last Perl version.
Packit 1c1d7e
  2003/05/16 - List of language marks can be passed as arguments.
Packit 1c1d7e
  2004/01/24 - Total reimplementation started: classes TrManager, and Transl.
Packit 1c1d7e
  2004/02/05 - First version that produces translator report. No language.doc yet.
Packit 1c1d7e
  2004/02/10 - First fully functional version that generates both the translator
Packit 1c1d7e
               report and the documentation. It is a bit slower than the
Packit 1c1d7e
               Perl version, but is much less tricky and much more flexible.
Packit 1c1d7e
               It also solves some problems that were not solved by the Perl
Packit 1c1d7e
               version. The translator report content should be more useful
Packit 1c1d7e
               for developers.
Packit 1c1d7e
  2004/02/11 - Some tuning-up to provide more useful information.
Packit 1c1d7e
  2004/04/16 - Added new tokens to the tokenizer (to remove some warnings).
Packit 1c1d7e
  2004/05/25 - Added from __future__ import generators not to force Python 2.3.
Packit 1c1d7e
  2004/06/03 - Removed dependency on textwrap module.
Packit 1c1d7e
  2004/07/07 - Fixed the bug in the fill() function.
Packit 1c1d7e
  2004/07/21 - Better e-mail mangling for HTML part of language.doc.
Packit 1c1d7e
             - Plural not used for reporting a single missing method.
Packit 1c1d7e
             - Removal of not used translator adapters is suggested only
Packit 1c1d7e
               when the report is not restricted to selected languages
Packit 1c1d7e
               explicitly via script arguments.
Packit 1c1d7e
  2004/07/26 - Better reporting of not-needed adapters.
Packit 1c1d7e
  2004/10/04 - Reporting of not called translator methods added.
Packit 1c1d7e
  2004/10/05 - Modified to check only doxygen/src sources for the previous report.
Packit 1c1d7e
  2005/02/28 - Slight modification to generate "mailto.txt" auxiliary file.
Packit 1c1d7e
  2005/08/15 - Doxygen's root directory determined primarily from DOXYGEN
Packit 1c1d7e
               environment variable. When not found, then relatively to the script.
Packit 1c1d7e
  2007/03/20 - The "translate me!" searched in comments and reported if found.
Packit 1c1d7e
  2008/06/09 - Warning when the MAX_DOT_GRAPH_HEIGHT is still part of trLegendDocs().
Packit 1c1d7e
  2009/05/09 - Changed HTML output to fit it with XHTML DTD
Packit 1c1d7e
  2009/09/02 - Added percentage info to the report (implemented / to be implemented).
Packit 1c1d7e
  2010/02/09 - Added checking/suggestion 'Reimplementation using UTF-8 suggested.
Packit 1c1d7e
  2010/03/03 - Added [unreachable] prefix used in maintainers.txt.
Packit 1c1d7e
  2010/05/28 - BOM skipped; minor code cleaning.
Packit 1c1d7e
  2010/05/31 - e-mail mangled already in maintainers.txt
Packit 1c1d7e
  2010/08/20 - maintainers.txt to UTF-8, related processing of unicode strings
Packit 1c1d7e
             - [any mark] introduced instead of [unreachable] only
Packit 1c1d7e
             - marks highlighted in HTML
Packit 1c1d7e
  2010/08/30 - Highlighting in what will be the table in langhowto.html modified.
Packit 1c1d7e
  2010/09/27 - The underscore in \latexonly part of the generated language.doc
Packit 1c1d7e
               was prefixed by backslash (was LaTeX related error).
Packit 1c1d7e
  2013/02/19 - Better diagnostics when translator_xx.h is too crippled.
Packit 1c1d7e
  2013/06/25 - TranslatorDecoder checks removed after removing the class.
Packit 1c1d7e
  2013/09/04 - Coloured status in langhowto. *ALMOST up-to-date* category
Packit 1c1d7e
               of translators introduced.
Packit 1c1d7e
  2014/06/16 - unified for Python 2.6+ and 3.0+
Packit 1c1d7e
  """
Packit 1c1d7e

Packit 1c1d7e
from __future__ import print_function
Packit 1c1d7e

Packit 1c1d7e
import os
Packit 1c1d7e
import platform
Packit 1c1d7e
import re
Packit 1c1d7e
import sys
Packit 1c1d7e
import textwrap
Packit 1c1d7e

Packit 1c1d7e

Packit 1c1d7e
def xopen(fname, mode='r', encoding='utf-8-sig'):
Packit 1c1d7e
    '''Unified file opening for Python 2 an Python 3.
Packit 1c1d7e

Packit 1c1d7e
    Python 2 does not have the encoding argument. Python 3 has one, and
Packit 1c1d7e
    the default 'utf-8-sig' is used (skips the BOM automatically).
Packit 1c1d7e
    '''
Packit 1c1d7e

Packit 1c1d7e
    if sys.version_info[0] == 2:
Packit 1c1d7e
        return open(fname, mode=mode) # Python 2 without encoding
Packit 1c1d7e
    else:
Packit 1c1d7e
        return open(fname, mode=mode, encoding=encoding) # Python 3 with encoding
Packit 1c1d7e

Packit 1c1d7e

Packit 1c1d7e
def fill(s):
Packit 1c1d7e
    """Returns string formatted to the wrapped paragraph multiline string.
Packit 1c1d7e

Packit 1c1d7e
    Replaces whitespaces by one space and then uses he textwrap.fill()."""
Packit 1c1d7e

Packit 1c1d7e
    # Replace all whitespace by spaces, remove whitespaces that are not
Packit 1c1d7e
    # necessary, strip the left and right whitespaces, and break the string
Packit 1c1d7e
    # to list of words.
Packit 1c1d7e
    rexWS = re.compile(r'\s+')
Packit 1c1d7e
    lst = rexWS.sub(' ', s).strip().split()
Packit 1c1d7e

Packit 1c1d7e
    # If the list is not empty, put the words together and form the lines
Packit 1c1d7e
    # of maximum 70 characters. Build the list of lines.
Packit 1c1d7e
    lines = []
Packit 1c1d7e
    if lst:
Packit 1c1d7e
        line = lst.pop(0)   # no separation space in front of the first word
Packit 1c1d7e
        for word in lst:
Packit 1c1d7e
            if len(line) + len(word) < 70:
Packit 1c1d7e
                line += ' ' + word
Packit 1c1d7e
            else:
Packit 1c1d7e
                lines.append(line)  # another full line formed
Packit 1c1d7e
                line = word         # next line started
Packit 1c1d7e
        lines.append(line)          # the last line
Packit 1c1d7e
    return '\n'.join(lines)
Packit 1c1d7e

Packit 1c1d7e

Packit 1c1d7e
class Transl:
Packit 1c1d7e
    """One instance is build for each translator.
Packit 1c1d7e

Packit 1c1d7e
    The abbreviation of the source file--part after 'translator_'--is used as
Packit 1c1d7e
    the identification of the object. The empty string is used for the
Packit 1c1d7e
    abstract Translator class from translator.h. The other information is
Packit 1c1d7e
    extracted from inside the source file."""
Packit 1c1d7e

Packit 1c1d7e
    def __init__(self, fname, manager):
Packit 1c1d7e
        """Bind to the manager and initialize."""
Packit 1c1d7e

Packit 1c1d7e
        # Store the filename and the reference to the manager object.
Packit 1c1d7e
        self.fname = fname
Packit 1c1d7e
        self.manager = manager
Packit 1c1d7e

Packit 1c1d7e
        # The instance is responsible for loading the source file, so it checks
Packit 1c1d7e
        # for its existence and quits if something goes wrong.
Packit 1c1d7e
        if not os.path.isfile(fname):
Packit 1c1d7e
            sys.stderr.write("\a\nFile '%s' not found!\n" % fname)
Packit 1c1d7e
            sys.exit(1)
Packit 1c1d7e

Packit 1c1d7e
        # Initialize the other collected information.
Packit 1c1d7e
        self.classId = None
Packit 1c1d7e
        self.baseClassId = None
Packit 1c1d7e
        self.readableStatus = None   # 'up-to-date', '1.2.3', '1.3', etc.
Packit 1c1d7e
        self.status = None           # '', '1.2.03', '1.3.00', etc.
Packit 1c1d7e
        self.lang = None             # like 'Brasilian'
Packit 1c1d7e
        self.langReadable = None     # like 'Brasilian Portuguese'
Packit 1c1d7e
        self.note = None             # like 'should be cleaned up'
Packit 1c1d7e
        self.prototypeDic = {}       # uniPrototype -> prototype
Packit 1c1d7e
        self.translateMeText = 'translate me!'
Packit 1c1d7e
        self.translateMeFlag = False # comments with "translate me!" found
Packit 1c1d7e
        self.txtMAX_DOT_GRAPH_HEIGHT_flag = False # found in string in trLegendDocs()
Packit 1c1d7e
        self.obsoleteMethods = None  # list of prototypes to be removed
Packit 1c1d7e
        self.missingMethods = None   # list of prototypes to be implemented
Packit 1c1d7e
        self.implementedMethods = None  # list of implemented required methods
Packit 1c1d7e
        self.adaptMinClass = None    # The newest adapter class that can be used
Packit 1c1d7e

Packit 1c1d7e
    def __tokenGenerator(self):
Packit 1c1d7e
        """Generator that reads the file and yields tokens as 4-tuples.
Packit 1c1d7e

Packit 1c1d7e
        The tokens have the form (tokenId, tokenString, lineNo). The
Packit 1c1d7e
        last returned token has the form ('eof', None, None). When trying
Packit 1c1d7e
        to access next token after that, the exception would be raised."""
Packit 1c1d7e

Packit 1c1d7e
        # Set the dictionary for recognizing tokenId for keywords, separators
Packit 1c1d7e
        # and the similar categories. The key is the string to be recognized,
Packit 1c1d7e
        # the value says its token identification.
Packit 1c1d7e
        tokenDic = { 'class':     'class',
Packit 1c1d7e
                     'const':     'const',
Packit 1c1d7e
                     'public':    'public',
Packit 1c1d7e
                     'protected': 'protected',
Packit 1c1d7e
                     'private':   'private',
Packit 1c1d7e
                     'static':    'static',
Packit 1c1d7e
                     'virtual':   'virtual',
Packit 1c1d7e
                     ':':         'colon',
Packit 1c1d7e
                     ';':         'semic',
Packit 1c1d7e
                     ',':         'comma',
Packit 1c1d7e
                     '[':         'lsqbra',
Packit 1c1d7e
                     ']':         'rsqbra',
Packit 1c1d7e
                     '(':         'lpar',
Packit 1c1d7e
                     ')':         'rpar',
Packit 1c1d7e
                     '{':         'lcurly',
Packit 1c1d7e
                     '}':         'rcurly',
Packit 1c1d7e
                     '=':         'assign',
Packit 1c1d7e
                     '*':         'star',
Packit 1c1d7e
                     '&':         'amp',
Packit 1c1d7e
                     '+':         'plus',
Packit 1c1d7e
                     '-':         'minus',
Packit 1c1d7e
                     '!':         'excl',
Packit 1c1d7e
                     '?':         'qmark',
Packit 1c1d7e
                     '<':         'lt',
Packit 1c1d7e
                     '>':         'gt',
Packit 1c1d7e
                     "'":         'quot',
Packit 1c1d7e
                     '"':         'dquot',
Packit 1c1d7e
                     '.':         'dot',
Packit 1c1d7e
                     '%':         'perc',
Packit 1c1d7e
                     '~':         'tilde',
Packit 1c1d7e
                     '^':         'caret',
Packit 1c1d7e
                   }
Packit 1c1d7e

Packit 1c1d7e
        # Regular expression for recognizing identifiers.
Packit 1c1d7e
        rexId = re.compile(r'^[a-zA-Z]\w*$')
Packit 1c1d7e

Packit 1c1d7e
        # Open the file for reading and extracting tokens until the eof.
Packit 1c1d7e
        # Initialize the finite automaton.
Packit 1c1d7e
        f = xopen(self.fname)
Packit 1c1d7e
        lineNo = 0
Packit 1c1d7e
        line = ''         # init -- see the pos initialization below
Packit 1c1d7e
        linelen = 0       # init
Packit 1c1d7e
        pos = 100         # init -- pos after the end of line
Packit 1c1d7e
        status = 0
Packit 1c1d7e

Packit 1c1d7e
        tokenId = None    # init
Packit 1c1d7e
        tokenStr = ''     # init -- the characters will be appended.
Packit 1c1d7e
        tokenLineNo = 0
Packit 1c1d7e

Packit 1c1d7e
        while status != 777:
Packit 1c1d7e

Packit 1c1d7e
            # Get the next character. Read next line first, if necessary.
Packit 1c1d7e
            if pos < linelen:
Packit 1c1d7e
                c = line[pos]
Packit 1c1d7e
            else:
Packit 1c1d7e
                lineNo += 1
Packit 1c1d7e
                line = f.readline()
Packit 1c1d7e
                linelen = len(line)
Packit 1c1d7e
                pos = 0
Packit 1c1d7e
                if line == '':         # eof
Packit 1c1d7e
                    status = 777
Packit 1c1d7e
                else:
Packit 1c1d7e
                    c = line[pos]
Packit 1c1d7e

Packit 1c1d7e
            # Consume the character based on the status
Packit 1c1d7e

Packit 1c1d7e
            if status == 0:     # basic status
Packit 1c1d7e

Packit 1c1d7e
                # This is the initial status. If tokenId is set, yield the
Packit 1c1d7e
                # token here and only here (except when eof is found).
Packit 1c1d7e
                # Initialize the token variables after the yield.
Packit 1c1d7e
                if tokenId:
Packit 1c1d7e
                    # If it is an unknown item, it can still be recognized
Packit 1c1d7e
                    # here. Keywords and separators are the example.
Packit 1c1d7e
                    if tokenId == 'unknown':
Packit 1c1d7e
                        if tokenStr in tokenDic:
Packit 1c1d7e
                            tokenId = tokenDic[tokenStr]
Packit 1c1d7e
                        elif tokenStr.isdigit():
Packit 1c1d7e
                            tokenId = 'num'
Packit 1c1d7e
                        elif rexId.match(tokenStr):
Packit 1c1d7e
                            tokenId = 'id'
Packit 1c1d7e
                        else:
Packit 1c1d7e
                            msg = '\aWarning: unknown token "' + tokenStr + '"'
Packit 1c1d7e
                            msg += '\tfound on line %d' % tokenLineNo
Packit 1c1d7e
                            msg += ' in "' + self.fname + '".\n'
Packit 1c1d7e
                            sys.stderr.write(msg)
Packit 1c1d7e

Packit 1c1d7e
                    yield (tokenId, tokenStr, tokenLineNo)
Packit 1c1d7e

Packit 1c1d7e
                    # If it is a comment that contains the self.translateMeText
Packit 1c1d7e
                    # string, set the flag -- the situation will be reported.
Packit 1c1d7e
                    if tokenId == 'comment' and tokenStr.find(self.translateMeText) >= 0:
Packit 1c1d7e
                        self.translateMeFlag = True
Packit 1c1d7e

Packit 1c1d7e
                    tokenId = None
Packit 1c1d7e
                    tokenStr = ''
Packit 1c1d7e
                    tokenLineNo = 0
Packit 1c1d7e

Packit 1c1d7e
                # Now process the character. When we just skip it (spaces),
Packit 1c1d7e
                # stay in this status. All characters that will be part of
Packit 1c1d7e
                # some token cause moving to the specific status. And only
Packit 1c1d7e
                # when moving to the status == 0 (or the final state 777),
Packit 1c1d7e
                # the token is yielded. With respect to that the automaton
Packit 1c1d7e
                # behaves as Moore's one (output bound to status). When
Packit 1c1d7e
                # collecting tokens, the automaton is the Mealy's one
Packit 1c1d7e
                # (actions bound to transitions).
Packit 1c1d7e
                if c.isspace():
Packit 1c1d7e
                    pass                 # just skip whitespace characters
Packit 1c1d7e
                elif c == '/':           # Possibly comment starts here, but
Packit 1c1d7e
                    tokenId = 'unknown'  # it could be only a slash in code.
Packit 1c1d7e
                    tokenStr = c
Packit 1c1d7e
                    tokenLineNo = lineNo
Packit 1c1d7e
                    status = 1
Packit 1c1d7e
                elif c == '#':
Packit 1c1d7e
                    tokenId = 'preproc'  # preprocessor directive
Packit 1c1d7e
                    tokenStr = c
Packit 1c1d7e
                    tokenLineNo = lineNo
Packit 1c1d7e
                    status = 5
Packit 1c1d7e
                elif c == '"':           # string starts here
Packit 1c1d7e
                    tokenId = 'string'
Packit 1c1d7e
                    tokenStr = c
Packit 1c1d7e
                    tokenLineNo = lineNo
Packit 1c1d7e
                    status = 6
Packit 1c1d7e
                elif c == "'":           # char literal starts here
Packit 1c1d7e
                    tokenId = 'charlit'
Packit 1c1d7e
                    tokenStr = c
Packit 1c1d7e
                    tokenLineNo = lineNo
Packit 1c1d7e
                    status = 8
Packit 1c1d7e
                elif c in tokenDic:  # known one-char token
Packit 1c1d7e
                    tokenId = tokenDic[c]
Packit 1c1d7e
                    tokenStr = c
Packit 1c1d7e
                    tokenLineNo = lineNo
Packit 1c1d7e
                    # stay in this state to yield token immediately
Packit 1c1d7e
                else:
Packit 1c1d7e
                    tokenId = 'unknown'  # totally unknown
Packit 1c1d7e
                    tokenStr = c
Packit 1c1d7e
                    tokenLineNo = lineNo
Packit 1c1d7e
                    status = 333
Packit 1c1d7e

Packit 1c1d7e
                pos += 1                 # move position in any case
Packit 1c1d7e

Packit 1c1d7e
            elif status == 1:            # possibly a comment
Packit 1c1d7e
                if c == '/':             # ... definitely the C++ comment
Packit 1c1d7e
                    tokenId = 'comment'
Packit 1c1d7e
                    tokenStr += c
Packit 1c1d7e
                    pos += 1
Packit 1c1d7e
                    status = 2
Packit 1c1d7e
                elif c == '*':           # ... definitely the C comment
Packit 1c1d7e
                    tokenId = 'comment'
Packit 1c1d7e
                    tokenStr += c
Packit 1c1d7e
                    pos += 1
Packit 1c1d7e
                    status = 3
Packit 1c1d7e
                else:
Packit 1c1d7e
                    status = 0           # unrecognized, don't move pos
Packit 1c1d7e

Packit 1c1d7e
            elif status == 2:            # inside the C++ comment
Packit 1c1d7e
                if c == '\n':            # the end of C++ comment
Packit 1c1d7e
                    status = 0           # yield the token
Packit 1c1d7e
                else:
Packit 1c1d7e
                    tokenStr += c        # collect the C++ comment
Packit 1c1d7e
                pos += 1
Packit 1c1d7e

Packit 1c1d7e
            elif status == 3:            # inside the C comment
Packit 1c1d7e
                if c == '*':             # possibly the end of the C comment
Packit 1c1d7e
                    tokenStr += c
Packit 1c1d7e
                    status = 4
Packit 1c1d7e
                else:
Packit 1c1d7e
                    tokenStr += c        # collect the C comment
Packit 1c1d7e
                pos += 1
Packit 1c1d7e

Packit 1c1d7e
            elif status == 4:            # possibly the end of the C comment
Packit 1c1d7e
                if c == '/':             # definitely the end of the C comment
Packit 1c1d7e
                    tokenStr += c
Packit 1c1d7e
                    status = 0           # yield the token
Packit 1c1d7e
                elif c == '*':           # more stars inside the comment
Packit 1c1d7e
                    tokenStr += c
Packit 1c1d7e
                else:
Packit 1c1d7e
                    tokenStr += c        # this cannot be the end of comment
Packit 1c1d7e
                    status = 3
Packit 1c1d7e
                pos += 1
Packit 1c1d7e

Packit 1c1d7e
            elif status == 5:            # inside the preprocessor directive
Packit 1c1d7e
                if c == '\n':            # the end of the preproc. command
Packit 1c1d7e
                    status = 0           # yield the token
Packit 1c1d7e
                else:
Packit 1c1d7e
                    tokenStr += c        # collect the preproc
Packit 1c1d7e
                pos += 1
Packit 1c1d7e

Packit 1c1d7e
            elif status == 6:            # inside the string
Packit 1c1d7e
                if c == '\\':            # escaped char inside the string
Packit 1c1d7e
                    tokenStr += c
Packit 1c1d7e
                    status = 7
Packit 1c1d7e
                elif c == '"':           # end of the string
Packit 1c1d7e
                    tokenStr += c
Packit 1c1d7e
                    status = 0
Packit 1c1d7e
                else:
Packit 1c1d7e
                    tokenStr += c        # collect the chars of the string
Packit 1c1d7e
                pos += 1
Packit 1c1d7e

Packit 1c1d7e
            elif status == 7:            # escaped char inside the string
Packit 1c1d7e
                tokenStr += c            # collect the char of the string
Packit 1c1d7e
                status = 6
Packit 1c1d7e
                pos += 1
Packit 1c1d7e

Packit 1c1d7e
            elif status == 8:            # inside the char literal
Packit 1c1d7e
                tokenStr += c            # collect the char of the literal
Packit 1c1d7e
                status = 9
Packit 1c1d7e
                pos += 1
Packit 1c1d7e

Packit 1c1d7e
            elif status == 9:            # end of char literal expected
Packit 1c1d7e
                if c == "'":             # ... and found
Packit 1c1d7e
                    tokenStr += c
Packit 1c1d7e
                    status = 0
Packit 1c1d7e
                    pos += 1
Packit 1c1d7e
                else:
Packit 1c1d7e
                    tokenId = 'error'    # end of literal was expected
Packit 1c1d7e
                    tokenStr += c
Packit 1c1d7e
                    status = 0
Packit 1c1d7e

Packit 1c1d7e
            elif status == 333:          # start of the unknown token
Packit 1c1d7e
                if c.isspace():
Packit 1c1d7e
                    pos += 1
Packit 1c1d7e
                    status = 0           # tokenId may be determined later
Packit 1c1d7e
                elif c in tokenDic:  # separator, don't move pos
Packit 1c1d7e
                    status = 0
Packit 1c1d7e
                else:
Packit 1c1d7e
                    tokenStr += c        # collect
Packit 1c1d7e
                    pos += 1
Packit 1c1d7e

Packit 1c1d7e
        # We should have finished in the final status. If some token
Packit 1c1d7e
        # have been extracted, yield it first.
Packit 1c1d7e
        assert(status == 777)
Packit 1c1d7e
        if tokenId:
Packit 1c1d7e
            yield (tokenId, tokenStr, tokenLineNo)
Packit 1c1d7e
            tokenId = None
Packit 1c1d7e
            tokenStr = ''
Packit 1c1d7e
            tokenLineNo = 0
Packit 1c1d7e

Packit 1c1d7e
        # The file content is processed. Close the file. Then always yield
Packit 1c1d7e
        # the eof token.
Packit 1c1d7e
        f.close()
Packit 1c1d7e
        yield ('eof', None, None)
Packit 1c1d7e

Packit 1c1d7e

Packit 1c1d7e
    def __collectClassInfo(self, tokenIterator):
Packit 1c1d7e
        """Collect the information about the class and base class.
Packit 1c1d7e

Packit 1c1d7e
        The tokens including the opening left curly brace of the class are
Packit 1c1d7e
        consumed."""
Packit 1c1d7e

Packit 1c1d7e
        status = 0  # initial state
Packit 1c1d7e

Packit 1c1d7e
        while status != 777:   # final state
Packit 1c1d7e

Packit 1c1d7e
            # Always assume that the previous tokens were processed. Get
Packit 1c1d7e
            # the next one.
Packit 1c1d7e
            tokenId, tokenStr, tokenLineNo = next(tokenIterator)
Packit 1c1d7e

Packit 1c1d7e
            # Process the token and never return back.
Packit 1c1d7e
            if status == 0:    # waiting for the 'class' keyword.
Packit 1c1d7e
                if tokenId == 'class':
Packit 1c1d7e
                    status = 1
Packit 1c1d7e

Packit 1c1d7e
            elif status == 1:  # expecting the class identification
Packit 1c1d7e
                if tokenId == 'id':
Packit 1c1d7e
                    self.classId = tokenStr
Packit 1c1d7e
                    status = 2
Packit 1c1d7e
                else:
Packit 1c1d7e
                    self.__unexpectedToken(status, tokenId, tokenLineNo)
Packit 1c1d7e

Packit 1c1d7e
            elif status == 2:  # expecting the curly brace or base class info
Packit 1c1d7e
                if tokenId == 'lcurly':
Packit 1c1d7e
                    status = 777        # correctly finished
Packit 1c1d7e
                elif tokenId == 'colon':
Packit 1c1d7e
                    status = 3
Packit 1c1d7e
                else:
Packit 1c1d7e
                    self.__unexpectedToken(status, tokenId, tokenLineNo)
Packit 1c1d7e

Packit 1c1d7e
            elif status == 3:  # expecting the 'public' in front of base class id
Packit 1c1d7e
                if tokenId == 'public':
Packit 1c1d7e
                    status = 4
Packit 1c1d7e
                else:
Packit 1c1d7e
                    self.__unexpectedToken(status, tokenId, tokenLineNo)
Packit 1c1d7e

Packit 1c1d7e
            elif status == 4:  # expecting the base class id
Packit 1c1d7e
                if tokenId == 'id':
Packit 1c1d7e
                    self.baseClassId = tokenStr
Packit 1c1d7e
                    status = 5
Packit 1c1d7e
                else:
Packit 1c1d7e
                    self.__unexpectedToken(status, tokenId, tokenLineNo)
Packit 1c1d7e

Packit 1c1d7e
            elif status == 5:  # expecting the curly brace and quitting
Packit 1c1d7e
                if tokenId == 'lcurly':
Packit 1c1d7e
                    status = 777        # correctly finished
Packit 1c1d7e
                elif tokenId == 'comment':
Packit 1c1d7e
                    pass
Packit 1c1d7e
                else:
Packit 1c1d7e
                    self.__unexpectedToken(status, tokenId, tokenLineNo)
Packit 1c1d7e

Packit 1c1d7e
        # Extract the status of the TranslatorXxxx class. The readable form
Packit 1c1d7e
        # will be used in reports the status form is a string that can be
Packit 1c1d7e
        # compared lexically (unified length, padding with zeros, etc.).
Packit 1c1d7e
        if self.baseClassId:
Packit 1c1d7e
            lst = self.baseClassId.split('_')
Packit 1c1d7e
            if lst[0] == 'Translator':
Packit 1c1d7e
                self.readableStatus = 'up-to-date'
Packit 1c1d7e
                self.status = ''
Packit 1c1d7e
            elif lst[0] == 'TranslatorAdapter':
Packit 1c1d7e
                self.status = lst[1] + '.' + lst[2]
Packit 1c1d7e
                self.readableStatus = self.status
Packit 1c1d7e
                if len(lst) > 3:        # add the last part of the number
Packit 1c1d7e
                    self.status += '.' + ('%02d' % int(lst[3]))
Packit 1c1d7e
                    self.readableStatus += '.' + lst[3]
Packit 1c1d7e
                else:
Packit 1c1d7e
                    self.status += '.00'
Packit 1c1d7e
            elif lst[0] == 'TranslatorEnglish':
Packit 1c1d7e
                # Obsolete or Based on English.
Packit 1c1d7e
                if self.classId[-2:] == 'En':
Packit 1c1d7e
                    self.readableStatus = 'English based'
Packit 1c1d7e
                    self.status = 'En'
Packit 1c1d7e
                else:
Packit 1c1d7e
                    self.readableStatus = 'obsolete'
Packit 1c1d7e
                    self.status = '0.0.00'
Packit 1c1d7e

Packit 1c1d7e
            # Check whether status was set, or set 'strange'.
Packit 1c1d7e
            if self.status == None:
Packit 1c1d7e
                self.status = 'strange'
Packit 1c1d7e
            if not self.readableStatus:
Packit 1c1d7e
                self.readableStatus = 'strange'
Packit 1c1d7e

Packit 1c1d7e
            # Extract the name of the language and the readable form.
Packit 1c1d7e
            self.lang = self.classId[10:]  # without 'Translator'
Packit 1c1d7e
            if self.lang == 'Brazilian':
Packit 1c1d7e
                self.langReadable = 'Brazilian Portuguese'
Packit 1c1d7e
            elif self.lang == 'Chinesetraditional':
Packit 1c1d7e
                self.langReadable = 'Chinese Traditional'
Packit 1c1d7e
            else:
Packit 1c1d7e
                self.langReadable = self.lang
Packit 1c1d7e

Packit 1c1d7e

Packit 1c1d7e
    def __unexpectedToken(self, status, tokenId, tokenLineNo):
Packit 1c1d7e
        """Reports unexpected token and quits with exit code 1."""
Packit 1c1d7e

Packit 1c1d7e
        import inspect
Packit 1c1d7e
        calledFrom = inspect.stack()[1][3]
Packit 1c1d7e
        msg = "\a\nUnexpected token '%s' on the line %d in '%s'.\n"
Packit 1c1d7e
        msg = msg % (tokenId, tokenLineNo, self.fname)
Packit 1c1d7e
        msg += 'status = %d in %s()\n' % (status, calledFrom)
Packit 1c1d7e
        sys.stderr.write(msg)
Packit 1c1d7e
        sys.exit(1)
Packit 1c1d7e

Packit 1c1d7e

Packit 1c1d7e
    def collectPureVirtualPrototypes(self):
Packit 1c1d7e
        """Returns dictionary 'unified prototype' -> 'full prototype'.
Packit 1c1d7e

Packit 1c1d7e
        The method is expected to be called only for the translator.h. It
Packit 1c1d7e
        extracts only the pure virtual method and build the dictionary where
Packit 1c1d7e
        key is the unified prototype without argument identifiers."""
Packit 1c1d7e

Packit 1c1d7e
        # Prepare empty dictionary that will be returned.
Packit 1c1d7e
        resultDic = {}
Packit 1c1d7e

Packit 1c1d7e
        # Start the token generator which parses the class source file.
Packit 1c1d7e
        tokenIterator = self.__tokenGenerator()
Packit 1c1d7e

Packit 1c1d7e
        # Collect the class and the base class identifiers.
Packit 1c1d7e
        self.__collectClassInfo(tokenIterator)
Packit 1c1d7e
        assert(self.classId == 'Translator')
Packit 1c1d7e

Packit 1c1d7e
        # Let's collect readable form of the public virtual pure method
Packit 1c1d7e
        # prototypes in the readable form -- as defined in translator.h.
Packit 1c1d7e
        # Let's collect also unified form of the same prototype that omits
Packit 1c1d7e
        # everything that can be omitted, namely 'virtual' and argument
Packit 1c1d7e
        # identifiers.
Packit 1c1d7e
        prototype = ''    # readable prototype (with everything)
Packit 1c1d7e
        uniPrototype = '' # unified prototype (without arg. identifiers)
Packit 1c1d7e

Packit 1c1d7e
        # Collect the pure virtual method prototypes. Stop on the closing
Packit 1c1d7e
        # curly brace followed by the semicolon (end of class).
Packit 1c1d7e
        status = 0
Packit 1c1d7e
        curlyCnt = 0      # counter for the level of curly braces
Packit 1c1d7e

Packit 1c1d7e
        # Loop until the final state 777 is reached. The errors are processed
Packit 1c1d7e
        # immediately. In this implementation, it always quits the application.
Packit 1c1d7e
        while status != 777:
Packit 1c1d7e

Packit 1c1d7e
            # Get the next token.
Packit 1c1d7e
            tokenId, tokenStr, tokenLineNo = next(tokenIterator)
Packit 1c1d7e

Packit 1c1d7e
            if status == 0:      # waiting for 'public:'
Packit 1c1d7e
                if tokenId == 'public':
Packit 1c1d7e
                    status = 1
Packit 1c1d7e

Packit 1c1d7e
            elif status == 1:    # colon after the 'public'
Packit 1c1d7e
                if tokenId == 'colon':
Packit 1c1d7e
                    status = 2
Packit 1c1d7e
                else:
Packit 1c1d7e
                    self.__unexpectedToken(status, tokenId, tokenLineNo)
Packit 1c1d7e

Packit 1c1d7e
            elif status == 2:    # waiting for 'virtual'
Packit 1c1d7e
                if tokenId == 'virtual':
Packit 1c1d7e
                    prototype = tokenStr  # but not to unified prototype
Packit 1c1d7e
                    status = 3
Packit 1c1d7e
                elif tokenId == 'comment':
Packit 1c1d7e
                    pass
Packit 1c1d7e
                elif tokenId == 'rcurly':
Packit 1c1d7e
                    status = 11         # expected end of class
Packit 1c1d7e
                else:
Packit 1c1d7e
                    self.__unexpectedToken(status, tokenId, tokenLineNo)
Packit 1c1d7e

Packit 1c1d7e
            elif status == 3:    # return type of the method expected
Packit 1c1d7e
                if tokenId == 'id':
Packit 1c1d7e
                    prototype += ' ' + tokenStr
Packit 1c1d7e
                    uniPrototype = tokenStr  # start collecting the unified prototype
Packit 1c1d7e
                    status = 4
Packit 1c1d7e
                elif tokenId == 'tilde':
Packit 1c1d7e
                    status = 4
Packit 1c1d7e
                else:
Packit 1c1d7e
                    self.__unexpectedToken(status, tokenId, tokenLineNo)
Packit 1c1d7e

Packit 1c1d7e
            elif status == 4:    # method identifier expected
Packit 1c1d7e
                if tokenId == 'id':
Packit 1c1d7e
                    prototype += ' ' + tokenStr
Packit 1c1d7e
                    uniPrototype += ' ' + tokenStr
Packit 1c1d7e
                    status = 5
Packit 1c1d7e
                else:
Packit 1c1d7e
                    self.__unexpectedToken(status, tokenId, tokenLineNo)
Packit 1c1d7e

Packit 1c1d7e
            elif status == 5:    # left bracket of the argument list expected
Packit 1c1d7e
                if tokenId == 'lpar':
Packit 1c1d7e
                    prototype += tokenStr
Packit 1c1d7e
                    uniPrototype += tokenStr
Packit 1c1d7e
                    status = 6
Packit 1c1d7e
                else:
Packit 1c1d7e
                    self.__unexpectedToken(status, tokenId, tokenLineNo)
Packit 1c1d7e

Packit 1c1d7e
            elif status == 6:    # collecting arguments of the method
Packit 1c1d7e
                if tokenId == 'rpar':
Packit 1c1d7e
                    prototype += tokenStr
Packit 1c1d7e
                    uniPrototype += tokenStr
Packit 1c1d7e
                    status = 7
Packit 1c1d7e
                elif tokenId == 'const':
Packit 1c1d7e
                    prototype += tokenStr
Packit 1c1d7e
                    uniPrototype += tokenStr
Packit 1c1d7e
                    status = 12
Packit 1c1d7e
                elif tokenId == 'id':           # type identifier
Packit 1c1d7e
                    prototype += tokenStr
Packit 1c1d7e
                    uniPrototype += tokenStr
Packit 1c1d7e
                    status = 13
Packit 1c1d7e
                else:
Packit 1c1d7e
                    self.__unexpectedToken(status, tokenId, tokenLineNo)
Packit 1c1d7e

Packit 1c1d7e
            elif status == 7:    # assignment expected or left curly brace
Packit 1c1d7e
                if tokenId == 'assign':
Packit 1c1d7e
                    status = 8
Packit 1c1d7e
                elif tokenId == 'lcurly':
Packit 1c1d7e
                    curlyCnt = 1      # method body entered
Packit 1c1d7e
                    status = 10
Packit 1c1d7e
                else:
Packit 1c1d7e
                    self.__unexpectedToken(status, tokenId, tokenLineNo)
Packit 1c1d7e

Packit 1c1d7e
            elif status == 8:    # zero expected
Packit 1c1d7e
                if tokenId == 'num' and tokenStr == '0':
Packit 1c1d7e
                    status = 9
Packit 1c1d7e
                else:
Packit 1c1d7e
                    self.__unexpectedToken(status, tokenId, tokenLineNo)
Packit 1c1d7e

Packit 1c1d7e
            elif status == 9:    # after semicolon, produce the dic item
Packit 1c1d7e
                if tokenId == 'semic':
Packit 1c1d7e
                    assert(uniPrototype not in resultDic)
Packit 1c1d7e
                    resultDic[uniPrototype] = prototype
Packit 1c1d7e
                    status = 2
Packit 1c1d7e
                else:
Packit 1c1d7e
                    self.__unexpectedToken(status, tokenId, tokenLineNo)
Packit 1c1d7e

Packit 1c1d7e
            elif status == 10:   # consuming the body of the method
Packit 1c1d7e
                if tokenId == 'rcurly':
Packit 1c1d7e
                    curlyCnt -= 1
Packit 1c1d7e
                    if curlyCnt == 0:
Packit 1c1d7e
                        status = 2     # body consumed
Packit 1c1d7e
                elif tokenId == 'lcurly':
Packit 1c1d7e
                    curlyCnt += 1
Packit 1c1d7e

Packit 1c1d7e
            elif status == 11:   # probably the end of class
Packit 1c1d7e
                if tokenId == 'semic':
Packit 1c1d7e
                    status = 777
Packit 1c1d7e
                else:
Packit 1c1d7e
                    self.__unexpectedToken(status, tokenId, tokenLineNo)
Packit 1c1d7e

Packit 1c1d7e
            elif status == 12:   # type id for argument expected
Packit 1c1d7e
                if tokenId == 'id':
Packit 1c1d7e
                    prototype += ' ' + tokenStr
Packit 1c1d7e
                    uniPrototype += ' ' + tokenStr
Packit 1c1d7e
                    status = 13
Packit 1c1d7e
                else:
Packit 1c1d7e
                    self.__unexpectedToken(status, tokenId, tokenLineNo)
Packit 1c1d7e

Packit 1c1d7e
            elif status == 13:   # namespace qualification or * or & expected
Packit 1c1d7e
                if tokenId == 'colon':        # was namespace id
Packit 1c1d7e
                    prototype += tokenStr
Packit 1c1d7e
                    uniPrototype += tokenStr
Packit 1c1d7e
                    status = 14
Packit 1c1d7e
                elif tokenId == 'star' or tokenId == 'amp':  # pointer or reference
Packit 1c1d7e
                    prototype += ' ' + tokenStr
Packit 1c1d7e
                    uniPrototype += ' ' + tokenStr
Packit 1c1d7e
                    status = 16
Packit 1c1d7e
                elif tokenId == 'id':         # argument identifier
Packit 1c1d7e
                    prototype += ' ' + tokenStr
Packit 1c1d7e
                    # don't put this into unified prototype
Packit 1c1d7e
                    status = 17
Packit 1c1d7e
                else:
Packit 1c1d7e
                    self.__unexpectedToken(status, tokenId, tokenLineNo)
Packit 1c1d7e

Packit 1c1d7e
            elif status == 14:   # second colon for namespace:: expected
Packit 1c1d7e
                if tokenId == 'colon':
Packit 1c1d7e
                    prototype += tokenStr
Packit 1c1d7e
                    uniPrototype += tokenStr
Packit 1c1d7e
                    status = 15
Packit 1c1d7e
                else:
Packit 1c1d7e
                    self.__unexpectedToken(status, tokenId, tokenLineNo)
Packit 1c1d7e

Packit 1c1d7e
            elif status == 15:   # type after namespace:: expected
Packit 1c1d7e
                if tokenId == 'id':
Packit 1c1d7e
                    prototype += tokenStr
Packit 1c1d7e
                    uniPrototype += tokenStr
Packit 1c1d7e
                    status = 13
Packit 1c1d7e
                else:
Packit 1c1d7e
                    self.__unexpectedToken(status, tokenId, tokenLineNo)
Packit 1c1d7e

Packit 1c1d7e
            elif status == 16:   # argument identifier expected
Packit 1c1d7e
                if tokenId == 'id':
Packit 1c1d7e
                    prototype += ' ' + tokenStr
Packit 1c1d7e
                    # don't put this into unified prototype
Packit 1c1d7e
                    status = 17
Packit 1c1d7e
                else:
Packit 1c1d7e
                    self.__unexpectedToken(status, tokenId, tokenLineNo)
Packit 1c1d7e

Packit 1c1d7e
            elif status == 17:   # comma or ')' after argument identifier expected
Packit 1c1d7e
                if tokenId == 'comma':
Packit 1c1d7e
                    prototype += ', '
Packit 1c1d7e
                    uniPrototype += ', '
Packit 1c1d7e
                    status = 6
Packit 1c1d7e
                elif tokenId == 'rpar':
Packit 1c1d7e
                    prototype += tokenStr
Packit 1c1d7e
                    uniPrototype += tokenStr
Packit 1c1d7e
                    status = 7
Packit 1c1d7e
                else:
Packit 1c1d7e
                    self.__unexpectedToken(status, tokenId, tokenLineNo)
Packit 1c1d7e

Packit 1c1d7e
        # Eat the rest of the source to cause closing the file.
Packit 1c1d7e
        while tokenId != 'eof':
Packit 1c1d7e
            tokenId, tokenStr, tokenLineNo = next(tokenIterator)
Packit 1c1d7e

Packit 1c1d7e
        # Return the resulting dictionary with 'uniPrototype -> prototype'.
Packit 1c1d7e
        return resultDic
Packit 1c1d7e

Packit 1c1d7e

Packit 1c1d7e
    def __collectPublicMethodPrototypes(self, tokenIterator):
Packit 1c1d7e
        """Collects prototypes of public methods and fills self.prototypeDic.
Packit 1c1d7e

Packit 1c1d7e
        The dictionary is filled by items: uniPrototype -> prototype.
Packit 1c1d7e
        The method is expected to be called only for TranslatorXxxx classes,
Packit 1c1d7e
        i.e. for the classes that implement translation to some language.
Packit 1c1d7e
        It assumes that the opening curly brace of the class was already
Packit 1c1d7e
        consumed. The source is consumed until the end of the class.
Packit 1c1d7e
        The caller should consume the source until the eof to cause closing
Packit 1c1d7e
        the source file."""
Packit 1c1d7e

Packit 1c1d7e
        assert(self.classId != 'Translator')
Packit 1c1d7e
        assert(self.baseClassId != None)
Packit 1c1d7e

Packit 1c1d7e
        # The following finite automaton slightly differs from the one
Packit 1c1d7e
        # inside self.collectPureVirtualPrototypes(). It produces the
Packit 1c1d7e
        # dictionary item just after consuming the body of the method
Packit 1c1d7e
        # (transition from state 10 to state 2). It also does not allow
Packit 1c1d7e
        # definitions of public pure virtual methods, except for
Packit 1c1d7e
        # TranslatorAdapterBase (states 8 and 9). Argument identifier inside
Packit 1c1d7e
        # method argument lists can be omitted or commented.
Packit 1c1d7e
        #
Packit 1c1d7e
        # Let's collect readable form of all public method prototypes in
Packit 1c1d7e
        # the readable form -- as defined in the source file.
Packit 1c1d7e
        # Let's collect also unified form of the same prototype that omits
Packit 1c1d7e
        # everything that can be omitted, namely 'virtual' and argument
Packit 1c1d7e
        # identifiers.
Packit 1c1d7e
        prototype = ''    # readable prototype (with everything)
Packit 1c1d7e
        uniPrototype = '' # unified prototype (without arg. identifiers)
Packit 1c1d7e
        warning = ''      # warning message -- if something special detected
Packit 1c1d7e
        methodId = None   # processed method id
Packit 1c1d7e

Packit 1c1d7e
        # Collect the method prototypes. Stop on the closing
Packit 1c1d7e
        # curly brace followed by the semicolon (end of class).
Packit 1c1d7e
        status = 0
Packit 1c1d7e
        curlyCnt = 0      # counter for the level of curly braces
Packit 1c1d7e

Packit 1c1d7e
        # Loop until the final state 777 is reached. The errors are processed
Packit 1c1d7e
        # immediately. In this implementation, it always quits the application.
Packit 1c1d7e
        while status != 777:
Packit 1c1d7e

Packit 1c1d7e
            # Get the next token.
Packit 1c1d7e
            tokenId, tokenStr, tokenLineNo = next(tokenIterator)
Packit 1c1d7e

Packit 1c1d7e
            if status == 0:      # waiting for 'public:'
Packit 1c1d7e
                if tokenId == 'public':
Packit 1c1d7e
                    status = 1
Packit 1c1d7e
                elif tokenId == 'eof':  # non-public things until the eof
Packit 1c1d7e
                    status = 777
Packit 1c1d7e

Packit 1c1d7e
            elif status == 1:    # colon after the 'public'
Packit 1c1d7e
                if tokenId == 'colon':
Packit 1c1d7e
                    status = 2
Packit 1c1d7e
                else:
Packit 1c1d7e
                    self.__unexpectedToken(status, tokenId, tokenLineNo)
Packit 1c1d7e

Packit 1c1d7e
            elif status == 2:    # waiting for 'virtual' (can be omitted)
Packit 1c1d7e
                if tokenId == 'virtual':
Packit 1c1d7e
                    prototype = tokenStr  # but not to unified prototype
Packit 1c1d7e
                    status = 3
Packit 1c1d7e
                elif tokenId == 'id':     # 'virtual' was omitted
Packit 1c1d7e
                    prototype = tokenStr
Packit 1c1d7e
                    uniPrototype = tokenStr  # start collecting the unified prototype
Packit 1c1d7e
                    status = 4
Packit 1c1d7e
                elif tokenId == 'comment':
Packit 1c1d7e
                    pass
Packit 1c1d7e
                elif tokenId == 'protected' or tokenId == 'private':
Packit 1c1d7e
                    status = 0
Packit 1c1d7e
                elif tokenId == 'rcurly':
Packit 1c1d7e
                    status = 11         # expected end of class
Packit 1c1d7e
                else:
Packit 1c1d7e
                    self.__unexpectedToken(status, tokenId, tokenLineNo)
Packit 1c1d7e

Packit 1c1d7e
            elif status == 3:    # return type of the method expected
Packit 1c1d7e
                if tokenId == 'id':
Packit 1c1d7e
                    prototype += ' ' + tokenStr
Packit 1c1d7e
                    uniPrototype = tokenStr  # start collecting the unified prototype
Packit 1c1d7e
                    status = 4
Packit 1c1d7e
                else:
Packit 1c1d7e
                    self.__unexpectedToken(status, tokenId, tokenLineNo)
Packit 1c1d7e

Packit 1c1d7e
            elif status == 4:    # method identifier expected
Packit 1c1d7e
                if tokenId == 'id':
Packit 1c1d7e
                    prototype += ' ' + tokenStr
Packit 1c1d7e
                    uniPrototype += ' ' + tokenStr
Packit 1c1d7e
                    methodId = tokenStr    # for reporting
Packit 1c1d7e
                    status = 5
Packit 1c1d7e
                else:
Packit 1c1d7e
                    self.__unexpectedToken(status, tokenId, tokenLineNo)
Packit 1c1d7e

Packit 1c1d7e
            elif status == 5:    # left bracket of the argument list expected
Packit 1c1d7e
                if tokenId == 'lpar':
Packit 1c1d7e
                    prototype += tokenStr
Packit 1c1d7e
                    uniPrototype += tokenStr
Packit 1c1d7e
                    status = 6
Packit 1c1d7e
                else:
Packit 1c1d7e
                    self.__unexpectedToken(status, tokenId, tokenLineNo)
Packit 1c1d7e

Packit 1c1d7e
            elif status == 6:    # collecting arguments of the method
Packit 1c1d7e
                if tokenId == 'rpar':
Packit 1c1d7e
                    prototype += tokenStr
Packit 1c1d7e
                    uniPrototype += tokenStr
Packit 1c1d7e
                    status = 7
Packit 1c1d7e
                elif tokenId == 'const':
Packit 1c1d7e
                    prototype += tokenStr
Packit 1c1d7e
                    uniPrototype += tokenStr
Packit 1c1d7e
                    status = 12
Packit 1c1d7e
                elif tokenId == 'id':           # type identifier
Packit 1c1d7e
                    prototype += tokenStr
Packit 1c1d7e
                    uniPrototype += tokenStr
Packit 1c1d7e
                    status = 13
Packit 1c1d7e
                else:
Packit 1c1d7e
                    self.__unexpectedToken(status, tokenId, tokenLineNo)
Packit 1c1d7e

Packit 1c1d7e
            elif status == 7:    # left curly brace expected
Packit 1c1d7e
                if tokenId == 'lcurly':
Packit 1c1d7e
                    curlyCnt = 1      # method body entered
Packit 1c1d7e
                    status = 10
Packit 1c1d7e
                elif tokenId == 'comment':
Packit 1c1d7e
                    pass
Packit 1c1d7e
                elif tokenId == 'assign': # allowed only for TranslatorAdapterBase
Packit 1c1d7e
                    assert(self.classId == 'TranslatorAdapterBase')
Packit 1c1d7e
                    status = 8
Packit 1c1d7e
                else:
Packit 1c1d7e
                    self.__unexpectedToken(status, tokenId, tokenLineNo)
Packit 1c1d7e

Packit 1c1d7e
            elif status == 8:    # zero expected (TranslatorAdapterBase)
Packit 1c1d7e
                assert(self.classId == 'TranslatorAdapterBase')
Packit 1c1d7e
                if tokenId == 'num' and tokenStr == '0':
Packit 1c1d7e
                    status = 9
Packit 1c1d7e
                else:
Packit 1c1d7e
                    self.__unexpectedToken(status, tokenId, tokenLineNo)
Packit 1c1d7e

Packit 1c1d7e
            elif status == 9:    # after semicolon (TranslatorAdapterBase)
Packit 1c1d7e
                assert(self.classId == 'TranslatorAdapterBase')
Packit 1c1d7e
                if tokenId == 'semic':
Packit 1c1d7e
                    status = 2
Packit 1c1d7e
                else:
Packit 1c1d7e
                    self.__unexpectedToken(status, tokenId, tokenLineNo)
Packit 1c1d7e

Packit 1c1d7e
            elif status == 10:   # consuming the body of the method, then dic item
Packit 1c1d7e
                if tokenId == 'rcurly':
Packit 1c1d7e
                    curlyCnt -= 1
Packit 1c1d7e
                    if curlyCnt == 0:
Packit 1c1d7e
                        # Check for possible copy/paste error when name
Packit 1c1d7e
                        # of the method was not corrected (i.e. the same
Packit 1c1d7e
                        # name already exists).
Packit 1c1d7e
                        if uniPrototype in self.prototypeDic:
Packit 1c1d7e
                            msg = "'%s' prototype found again (duplicity)\n"
Packit 1c1d7e
                            msg += "in '%s'.\n" % self.fname
Packit 1c1d7e
                            msg = msg % uniPrototype
Packit 1c1d7e
                            sys.stderr.write(msg)
Packit 1c1d7e
                            assert False
Packit 1c1d7e

Packit 1c1d7e
                        assert(uniPrototype not in self.prototypeDic)
Packit 1c1d7e
                        # Insert new dictionary item.
Packit 1c1d7e
                        self.prototypeDic[uniPrototype] = prototype
Packit 1c1d7e
                        status = 2      # body consumed
Packit 1c1d7e
                        methodId = None # outside of any method
Packit 1c1d7e
                elif tokenId == 'lcurly':
Packit 1c1d7e
                    curlyCnt += 1
Packit 1c1d7e

Packit 1c1d7e
                # Warn in special case.
Packit 1c1d7e
                elif methodId == 'trLegendDocs' and tokenId == 'string' \
Packit 1c1d7e
                    and tokenStr.find('MAX_DOT_GRAPH_HEIGHT') >= 0:
Packit 1c1d7e
                        self.txtMAX_DOT_GRAPH_HEIGHT_flag = True
Packit 1c1d7e

Packit 1c1d7e

Packit 1c1d7e
            elif status == 11:   # probably the end of class
Packit 1c1d7e
                if tokenId == 'semic':
Packit 1c1d7e
                    status = 777
Packit 1c1d7e
                else:
Packit 1c1d7e
                    self.__unexpectedToken(status, tokenId, tokenLineNo)
Packit 1c1d7e

Packit 1c1d7e
            elif status == 12:   # type id for argument expected
Packit 1c1d7e
                if tokenId == 'id':
Packit 1c1d7e
                    prototype += ' ' + tokenStr
Packit 1c1d7e
                    uniPrototype += ' ' + tokenStr
Packit 1c1d7e
                    status = 13
Packit 1c1d7e
                else:
Packit 1c1d7e
                    self.__unexpectedToken(status, tokenId, tokenLineNo)
Packit 1c1d7e

Packit 1c1d7e
            elif status == 13:   # :: or * or & or id or ) expected
Packit 1c1d7e
                if tokenId == 'colon':        # was namespace id
Packit 1c1d7e
                    prototype += tokenStr
Packit 1c1d7e
                    uniPrototype += tokenStr
Packit 1c1d7e
                    status = 14
Packit 1c1d7e
                elif tokenId == 'star' or tokenId == 'amp':  # pointer or reference
Packit 1c1d7e
                    prototype += ' ' + tokenStr
Packit 1c1d7e
                    uniPrototype += ' ' + tokenStr
Packit 1c1d7e
                    status = 16
Packit 1c1d7e
                elif tokenId == 'id':         # argument identifier
Packit 1c1d7e
                    prototype += ' ' + tokenStr
Packit 1c1d7e
                    # don't put this into unified prototype
Packit 1c1d7e
                    status = 17
Packit 1c1d7e
                elif tokenId == 'comment':    # probably commented-out identifier
Packit 1c1d7e
                    prototype += tokenStr
Packit 1c1d7e
                elif tokenId == 'rpar':
Packit 1c1d7e
                    prototype += tokenStr
Packit 1c1d7e
                    uniPrototype += tokenStr
Packit 1c1d7e
                    status = 7
Packit 1c1d7e
                elif tokenId == 'comma':
Packit 1c1d7e
                    prototype += ', '
Packit 1c1d7e
                    uniPrototype += ', '
Packit 1c1d7e
                    status = 6
Packit 1c1d7e
                else:
Packit 1c1d7e
                    self.__unexpectedToken(status, tokenId, tokenLineNo)
Packit 1c1d7e

Packit 1c1d7e
            elif status == 14:   # second colon for namespace:: expected
Packit 1c1d7e
                if tokenId == 'colon':
Packit 1c1d7e
                    prototype += tokenStr
Packit 1c1d7e
                    uniPrototype += tokenStr
Packit 1c1d7e
                    status = 15
Packit 1c1d7e
                else:
Packit 1c1d7e
                    self.__unexpectedToken(status, tokenId, tokenLineNo)
Packit 1c1d7e

Packit 1c1d7e
            elif status == 15:   # type after namespace:: expected
Packit 1c1d7e
                if tokenId == 'id':
Packit 1c1d7e
                    prototype += tokenStr
Packit 1c1d7e
                    uniPrototype += tokenStr
Packit 1c1d7e
                    status = 13
Packit 1c1d7e
                else:
Packit 1c1d7e
                    self.__unexpectedToken(status, tokenId, tokenLineNo)
Packit 1c1d7e

Packit 1c1d7e
            elif status == 16:   # argument identifier or ) expected
Packit 1c1d7e
                if tokenId == 'id':
Packit 1c1d7e
                    prototype += ' ' + tokenStr
Packit 1c1d7e
                    # don't put this into unified prototype
Packit 1c1d7e
                    status = 17
Packit 1c1d7e
                elif tokenId == 'rpar':
Packit 1c1d7e
                    prototype += tokenStr
Packit 1c1d7e
                    uniPrototype += tokenStr
Packit 1c1d7e
                    status = 7
Packit 1c1d7e
                elif tokenId == 'comment':
Packit 1c1d7e
                    prototype += tokenStr
Packit 1c1d7e
                else:
Packit 1c1d7e
                    self.__unexpectedToken(status, tokenId, tokenLineNo)
Packit 1c1d7e

Packit 1c1d7e
            elif status == 17:   # comma or ')' after argument identifier expected
Packit 1c1d7e
                if tokenId == 'comma':
Packit 1c1d7e
                    prototype += ', '
Packit 1c1d7e
                    uniPrototype += ', '
Packit 1c1d7e
                    status = 6
Packit 1c1d7e
                elif tokenId == 'rpar':
Packit 1c1d7e
                    prototype += tokenStr
Packit 1c1d7e
                    uniPrototype += tokenStr
Packit 1c1d7e
                    status = 7
Packit 1c1d7e
                else:
Packit 1c1d7e
                    self.__unexpectedToken(status, tokenId, tokenLineNo)
Packit 1c1d7e

Packit 1c1d7e

Packit 1c1d7e

Packit 1c1d7e
    def collectAdapterPrototypes(self):
Packit 1c1d7e
        """Returns the dictionary of prototypes implemented by adapters.
Packit 1c1d7e

Packit 1c1d7e
        It is created to process the translator_adapter.h. The returned
Packit 1c1d7e
        dictionary has the form: unifiedPrototype -> (version, classId)
Packit 1c1d7e
        thus by looking for the prototype, we get the information what is
Packit 1c1d7e
        the newest (least adapting) adapter that is sufficient for
Packit 1c1d7e
        implementing the method."""
Packit 1c1d7e

Packit 1c1d7e
        # Start the token generator which parses the class source file.
Packit 1c1d7e
        assert(os.path.split(self.fname)[1] == 'translator_adapter.h')
Packit 1c1d7e
        tokenIterator = self.__tokenGenerator()
Packit 1c1d7e

Packit 1c1d7e
        # Get the references to the involved dictionaries.
Packit 1c1d7e
        reqDic = self.manager.requiredMethodsDic
Packit 1c1d7e

Packit 1c1d7e
        # Create the empty dictionary that will be returned.
Packit 1c1d7e
        adaptDic = {}
Packit 1c1d7e

Packit 1c1d7e

Packit 1c1d7e
        # Loop through the source of the adapter file until no other adapter
Packit 1c1d7e
        # class is found.
Packit 1c1d7e
        while True:
Packit 1c1d7e
            try:
Packit 1c1d7e
                # Collect the class and the base class identifiers.
Packit 1c1d7e
                self.__collectClassInfo(tokenIterator)
Packit 1c1d7e

Packit 1c1d7e
                # Extract the comparable version of the adapter class.
Packit 1c1d7e
                # Note: The self.status as set by self.__collectClassInfo()
Packit 1c1d7e
                # contains similar version, but is related to the base class,
Packit 1c1d7e
                # not to the class itself.
Packit 1c1d7e
                lst = self.classId.split('_')
Packit 1c1d7e
                version = ''
Packit 1c1d7e
                if lst[0] == 'TranslatorAdapter': # TranslatorAdapterBase otherwise
Packit 1c1d7e
                    version = lst[1] + '.' + lst[2]
Packit 1c1d7e
                    if len(lst) > 3:        # add the last part of the number
Packit 1c1d7e
                        version += '.' + ('%02d' % int(lst[3]))
Packit 1c1d7e
                    else:
Packit 1c1d7e
                        version += '.00'
Packit 1c1d7e

Packit 1c1d7e
                # Collect the prototypes of implemented public methods.
Packit 1c1d7e
                self.__collectPublicMethodPrototypes(tokenIterator)
Packit 1c1d7e

Packit 1c1d7e
                # For the required methods, update the dictionary of methods
Packit 1c1d7e
                # implemented by the adapter.
Packit 1c1d7e
                for protoUni in self.prototypeDic:
Packit 1c1d7e
                    if protoUni in reqDic:
Packit 1c1d7e
                        # This required method will be marked as implemented
Packit 1c1d7e
                        # by this adapter class. This implementation assumes
Packit 1c1d7e
                        # that newer adapters do not reimplement any required
Packit 1c1d7e
                        # methods already implemented by older adapters.
Packit 1c1d7e
                        assert(protoUni not in adaptDic)
Packit 1c1d7e
                        adaptDic[protoUni] = (version, self.classId)
Packit 1c1d7e

Packit 1c1d7e
                # Clear the dictionary object and the information related
Packit 1c1d7e
                # to the class as the next adapter class is to be processed.
Packit 1c1d7e
                self.prototypeDic.clear()
Packit 1c1d7e
                self.classId = None
Packit 1c1d7e
                self.baseClassId = None
Packit 1c1d7e

Packit 1c1d7e
            except StopIteration:
Packit 1c1d7e
                break
Packit 1c1d7e

Packit 1c1d7e
        # Return the result dictionary.
Packit 1c1d7e
        return adaptDic
Packit 1c1d7e

Packit 1c1d7e

Packit 1c1d7e
    def processing(self):
Packit 1c1d7e
        """Processing of the source file -- only for TranslatorXxxx classes."""
Packit 1c1d7e

Packit 1c1d7e
        # Start the token generator which parses the class source file.
Packit 1c1d7e
        tokenIterator = self.__tokenGenerator()
Packit 1c1d7e

Packit 1c1d7e
        # Collect the class and the base class identifiers.
Packit 1c1d7e
        self.__collectClassInfo(tokenIterator)
Packit 1c1d7e
        assert(self.classId != 'Translator')
Packit 1c1d7e
        assert(self.classId[:17] != 'TranslatorAdapter')
Packit 1c1d7e

Packit 1c1d7e
        # Collect the prototypes of implemented public methods.
Packit 1c1d7e
        self.__collectPublicMethodPrototypes(tokenIterator)
Packit 1c1d7e

Packit 1c1d7e
        # Eat the rest of the source to cause closing the file.
Packit 1c1d7e
        while True:
Packit 1c1d7e
            try:
Packit 1c1d7e
                t = next(tokenIterator)
Packit 1c1d7e
            except StopIteration:
Packit 1c1d7e
                break
Packit 1c1d7e

Packit 1c1d7e
        # Shorthands for the used dictionaries.
Packit 1c1d7e
        reqDic = self.manager.requiredMethodsDic
Packit 1c1d7e
        adaptDic = self.manager.adaptMethodsDic
Packit 1c1d7e
        myDic = self.prototypeDic
Packit 1c1d7e

Packit 1c1d7e
        # Build the list of obsolete methods.
Packit 1c1d7e
        self.obsoleteMethods = []
Packit 1c1d7e
        for p in myDic:
Packit 1c1d7e
            if p not in reqDic:
Packit 1c1d7e
                self.obsoleteMethods.append(p)
Packit 1c1d7e
        self.obsoleteMethods.sort()
Packit 1c1d7e

Packit 1c1d7e
        # Build the list of missing methods and the list of implemented
Packit 1c1d7e
        # required methods.
Packit 1c1d7e
        self.missingMethods = []
Packit 1c1d7e
        self.implementedMethods = []
Packit 1c1d7e
        for p in reqDic:
Packit 1c1d7e
            if p in myDic:
Packit 1c1d7e
                self.implementedMethods.append(p)
Packit 1c1d7e
            else:
Packit 1c1d7e
                self.missingMethods.append(p)
Packit 1c1d7e
        self.missingMethods.sort()
Packit 1c1d7e
        self.implementedMethods.sort()
Packit 1c1d7e

Packit 1c1d7e
        # Check whether adapter must be used or suggest the newest one.
Packit 1c1d7e
        # Change the status and set the note accordingly.
Packit 1c1d7e
        if self.baseClassId != 'Translator':
Packit 1c1d7e
            if not self.missingMethods:
Packit 1c1d7e
                self.note = 'Change the base class to Translator.'
Packit 1c1d7e
                self.status = ''
Packit 1c1d7e
                self.readableStatus = 'almost up-to-date'
Packit 1c1d7e
            elif self.baseClassId != 'TranslatorEnglish':
Packit 1c1d7e
                # The translator uses some of the adapters.
Packit 1c1d7e
                # Look at the missing methods and check what adapter
Packit 1c1d7e
                # implements them. Remember the one with the lowest version.
Packit 1c1d7e
                adaptMinVersion = '9.9.99'
Packit 1c1d7e
                adaptMinClass = 'TranslatorAdapter_9_9_99'
Packit 1c1d7e
                for uniProto in self.missingMethods:
Packit 1c1d7e
                    if uniProto in adaptDic:
Packit 1c1d7e
                        version, cls = adaptDic[uniProto]
Packit 1c1d7e
                        if version < adaptMinVersion:
Packit 1c1d7e
                            adaptMinVersion = version
Packit 1c1d7e
                            adaptMinClass = cls
Packit 1c1d7e

Packit 1c1d7e
                # Test against the current status -- preserve the self.status.
Packit 1c1d7e
                # Possibly, the translator implements enough methods to
Packit 1c1d7e
                # use some newer adapter.
Packit 1c1d7e
                status = self.status
Packit 1c1d7e

Packit 1c1d7e
                # If the version of the used adapter is smaller than
Packit 1c1d7e
                # the required, set the note and update the status as if
Packit 1c1d7e
                # the newer adapter was used.
Packit 1c1d7e
                if adaptMinVersion > status:
Packit 1c1d7e
                    self.note = 'Change the base class to %s.' % adaptMinClass
Packit 1c1d7e
                    self.status = adaptMinVersion
Packit 1c1d7e
                    self.adaptMinClass = adaptMinClass
Packit 1c1d7e
                    self.readableStatus = adaptMinVersion # simplified
Packit 1c1d7e

Packit 1c1d7e
        # If everything seems OK, some explicit warning flags still could
Packit 1c1d7e
        # be set.
Packit 1c1d7e
        if not self.note and self.status == '' and \
Packit 1c1d7e
           (self.translateMeFlag or self.txtMAX_DOT_GRAPH_HEIGHT_flag):
Packit 1c1d7e
           self.note = ''
Packit 1c1d7e
           if self.translateMeFlag:
Packit 1c1d7e
               self.note += 'The "%s" found in a comment.' % self.translateMeText
Packit 1c1d7e
           if self.note != '':
Packit 1c1d7e
               self.note += '\n\t\t'
Packit 1c1d7e
           if self.txtMAX_DOT_GRAPH_HEIGHT_flag:
Packit 1c1d7e
               self.note += 'The MAX_DOT_GRAPH_HEIGHT found in trLegendDocs()'
Packit 1c1d7e

Packit 1c1d7e
        # If everything seems OK, but there are obsolete methods, set
Packit 1c1d7e
        # the note to clean-up source. This note will be used only when
Packit 1c1d7e
        # the previous code did not set another note (priority).
Packit 1c1d7e
        if not self.note and self.status == '' and self.obsoleteMethods:
Packit 1c1d7e
            self.note = 'Remove the obsolete methods (never used).'
Packit 1c1d7e

Packit 1c1d7e
        # If there is at least some note but the status suggests it is
Packit 1c1d7e
        # otherwise up-to-date, mark is as ALMOST up-to-date.
Packit 1c1d7e
        if self.note and self.status == '':
Packit 1c1d7e
            self.readableStatus = 'almost up-to-date'
Packit 1c1d7e

Packit 1c1d7e

Packit 1c1d7e
    def report(self, fout):
Packit 1c1d7e
        """Returns the report part for the source as a multiline string.
Packit 1c1d7e

Packit 1c1d7e
        No output for up-to-date translators without problem."""
Packit 1c1d7e

Packit 1c1d7e
        # If there is nothing to report, return immediately.
Packit 1c1d7e
        if self.status == '' and not self.note:
Packit 1c1d7e
            return
Packit 1c1d7e

Packit 1c1d7e
        # Report the number of not implemented methods.
Packit 1c1d7e
        fout.write('\n\n\n')
Packit 1c1d7e
        fout.write(self.classId + '   (' + self.baseClassId + ')')
Packit 1c1d7e
        percentImplemented = 100    # init
Packit 1c1d7e
        allNum = len(self.manager.requiredMethodsDic)
Packit 1c1d7e
        if self.missingMethods:
Packit 1c1d7e
            num = len(self.missingMethods)
Packit 1c1d7e
            percentImplemented = 100 * (allNum - num) / allNum
Packit 1c1d7e
            fout.write('  %d' % num)
Packit 1c1d7e
            fout.write(' method')
Packit 1c1d7e
            if num > 1:
Packit 1c1d7e
                fout.write('s')
Packit 1c1d7e
            fout.write(' to implement (%d %%)' % (100 * num / allNum))
Packit 1c1d7e
        fout.write('\n' + '-' * len(self.classId))
Packit 1c1d7e

Packit 1c1d7e
        # Write the info about the implemented required methods.
Packit 1c1d7e
        fout.write('\n\n  Implements %d' % len(self.implementedMethods))
Packit 1c1d7e
        fout.write(' of the required methods (%d %%).' % percentImplemented)
Packit 1c1d7e

Packit 1c1d7e
        # Report the missing method, but only when it is not English-based
Packit 1c1d7e
        # translator.
Packit 1c1d7e
        if self.missingMethods and self.status != 'En':
Packit 1c1d7e
            fout.write('\n\n  Missing methods (should be implemented):\n')
Packit 1c1d7e
            reqDic = self.manager.requiredMethodsDic
Packit 1c1d7e
            for p in self.missingMethods:
Packit 1c1d7e
                fout.write('\n    ' + reqDic[p])
Packit 1c1d7e

Packit 1c1d7e
        # Always report obsolete methods.
Packit 1c1d7e
        if self.obsoleteMethods:
Packit 1c1d7e
            fout.write('\n\n  Obsolete methods (should be removed, never used):\n')
Packit 1c1d7e
            myDic = self.prototypeDic
Packit 1c1d7e
            for p in self.obsoleteMethods:
Packit 1c1d7e
                fout.write('\n    ' + myDic[p])
Packit 1c1d7e

Packit 1c1d7e
        # For English-based translator, report the implemented methods.
Packit 1c1d7e
        if self.status == 'En' and self.implementedMethods:
Packit 1c1d7e
            fout.write('\n\n  This English-based translator implements ')
Packit 1c1d7e
            fout.write('the following methods:\n')
Packit 1c1d7e
            reqDic = self.manager.requiredMethodsDic
Packit 1c1d7e
            for p in self.implementedMethods:
Packit 1c1d7e
                fout.write('\n    ' + reqDic[p])
Packit 1c1d7e

Packit 1c1d7e

Packit 1c1d7e
    def getmtime(self):
Packit 1c1d7e
        """Returns the last modification time of the source file."""
Packit 1c1d7e
        assert(os.path.isfile(self.fname))
Packit 1c1d7e
        return os.path.getmtime(self.fname)
Packit 1c1d7e

Packit 1c1d7e

Packit 1c1d7e
class TrManager:
Packit 1c1d7e
    """Collects basic info and builds subordinate Transl objects."""
Packit 1c1d7e

Packit 1c1d7e
    def __init__(self):
Packit 1c1d7e
        """Determines paths, creates and initializes structures.
Packit 1c1d7e

Packit 1c1d7e
        The arguments of the script may explicitly say what languages should
Packit 1c1d7e
        be processed. Write the two letter identifications that are used
Packit 1c1d7e
        for composing the source filenames, so...
Packit 1c1d7e

Packit 1c1d7e
            python translator.py cz
Packit 1c1d7e

Packit 1c1d7e
        this will process only translator_cz.h source.
Packit 1c1d7e
        """
Packit 1c1d7e

Packit 1c1d7e
        # Determine the path to the script and its name.
Packit 1c1d7e
        self.script = os.path.abspath(sys.argv[0])
Packit 1c1d7e
        self.script_path, self.script_name = os.path.split(self.script)
Packit 1c1d7e
        self.script_path = os.path.abspath(self.script_path)
Packit 1c1d7e

Packit 1c1d7e
        # Determine the absolute path to the Doxygen's root subdirectory.
Packit 1c1d7e
        # If DOXYGEN environment variable is not found, the directory is
Packit 1c1d7e
        # determined from the path of the script.
Packit 1c1d7e
        doxy_default = os.path.join(self.script_path, '..')
Packit 1c1d7e
        self.doxy_path = os.path.abspath(os.getenv('DOXYGEN', doxy_default))
Packit 1c1d7e

Packit 1c1d7e
        # Get the explicit arguments of the script.
Packit 1c1d7e
        self.script_argLst = sys.argv[1:]
Packit 1c1d7e

Packit 1c1d7e
        # Build the path names based on the Doxygen's root knowledge.
Packit 1c1d7e
        self.doc_path = os.path.join(self.doxy_path, 'doc')
Packit 1c1d7e
        self.src_path = os.path.join(self.doxy_path, 'src')
Packit 1c1d7e

Packit 1c1d7e
        # Create the empty dictionary for Transl object identified by the
Packit 1c1d7e
        # class identifier of the translator.
Packit 1c1d7e
        self.__translDic = {}
Packit 1c1d7e

Packit 1c1d7e
        # Create the None dictionary of required methods. The key is the
Packit 1c1d7e
        # unified prototype, the value is the full prototype. Set inside
Packit 1c1d7e
        # the self.__build().
Packit 1c1d7e
        self.requiredMethodsDic = None
Packit 1c1d7e

Packit 1c1d7e
        # Create the empty dictionary that says what method is implemented
Packit 1c1d7e
        # by what adapter.
Packit 1c1d7e
        self.adaptMethodsDic = {}
Packit 1c1d7e

Packit 1c1d7e
        # The last modification time will capture the modification of this
Packit 1c1d7e
        # script, of the translator.h, of the translator_adapter.h (see the
Packit 1c1d7e
        # self.__build() for the last two) of all the translator_xx.h files
Packit 1c1d7e
        # and of the template for generating the documentation. So, this
Packit 1c1d7e
        # time can be compared with modification time of the generated
Packit 1c1d7e
        # documentation to decide, whether the doc should be re-generated.
Packit 1c1d7e
        self.lastModificationTime = os.path.getmtime(self.script)
Packit 1c1d7e

Packit 1c1d7e
        # Set the names of the translator report text file, of the template
Packit 1c1d7e
        # for generating "Internationalization" document, for the generated
Packit 1c1d7e
        # file itself, and for the maintainers list.
Packit 1c1d7e
        self.translatorReportFileName = 'translator_report.txt'
Packit 1c1d7e
        self.maintainersFileName = 'maintainers.txt'
Packit 1c1d7e
        self.languageTplFileName = 'language.tpl'
Packit 1c1d7e
        self.languageDocFileName = 'language.doc'
Packit 1c1d7e

Packit 1c1d7e
        # The information about the maintainers will be stored
Packit 1c1d7e
        # in the dictionary with the following name.
Packit 1c1d7e
        self.__maintainersDic = None
Packit 1c1d7e

Packit 1c1d7e
        # Define the other used structures and variables for information.
Packit 1c1d7e
        self.langLst = None                   # including English based
Packit 1c1d7e
        self.supportedLangReadableStr = None  # coupled En-based as a note
Packit 1c1d7e
        self.numLang = None                   # excluding coupled En-based
Packit 1c1d7e
        self.doxVersion = None                # Doxygen version
Packit 1c1d7e

Packit 1c1d7e
        # Build objects where each one is responsible for one translator.
Packit 1c1d7e
        self.__build()
Packit 1c1d7e

Packit 1c1d7e

Packit 1c1d7e
    def __build(self):
Packit 1c1d7e
        """Find the translator files and build the objects for translators."""
Packit 1c1d7e

Packit 1c1d7e
        # The translator.h must exist (the Transl object will check it),
Packit 1c1d7e
        # create the object for it and let it build the dictionary of
Packit 1c1d7e
        # required methods.
Packit 1c1d7e
        tr = Transl(os.path.join(self.src_path, 'translator.h'), self)
Packit 1c1d7e
        self.requiredMethodsDic = tr.collectPureVirtualPrototypes()
Packit 1c1d7e
        tim = tr.getmtime()
Packit 1c1d7e
        if tim > self.lastModificationTime:
Packit 1c1d7e
            self.lastModificationTime = tim
Packit 1c1d7e

Packit 1c1d7e
        # The translator_adapter.h must exist (the Transl object will check it),
Packit 1c1d7e
        # create the object for it and store the reference in the dictionary.
Packit 1c1d7e
        tr = Transl(os.path.join(self.src_path, 'translator_adapter.h'), self)
Packit 1c1d7e
        self.adaptMethodsDic = tr.collectAdapterPrototypes()
Packit 1c1d7e
        tim = tr.getmtime()
Packit 1c1d7e
        if tim > self.lastModificationTime:
Packit 1c1d7e
            self.lastModificationTime = tim
Packit 1c1d7e

Packit 1c1d7e
        # Create the list of the filenames with language translator sources.
Packit 1c1d7e
        # If the explicit arguments of the script were typed, process only
Packit 1c1d7e
        # those files.
Packit 1c1d7e
        if self.script_argLst:
Packit 1c1d7e
            lst = ['translator_' + x + '.h' for x in self.script_argLst]
Packit 1c1d7e
            for fname in lst:
Packit 1c1d7e
                if not os.path.isfile(os.path.join(self.src_path, fname)):
Packit 1c1d7e
                    sys.stderr.write("\a\nFile '%s' not found!\n" % fname)
Packit 1c1d7e
                    sys.exit(1)
Packit 1c1d7e
        else:
Packit 1c1d7e
            lst = os.listdir(self.src_path)
Packit 1c1d7e
            lst = [x for x in lst if x[:11] == 'translator_'
Packit 1c1d7e
                                   and x[-2:] == '.h'
Packit 1c1d7e
                                   and x != 'translator_adapter.h']
Packit 1c1d7e

Packit 1c1d7e
        # Build the object for the translator_xx.h files, and process the
Packit 1c1d7e
        # content of the file. Then insert the object to the dictionary
Packit 1c1d7e
        # accessed via classId.
Packit 1c1d7e
        for fname in lst:
Packit 1c1d7e
            fullname = os.path.join(self.src_path, fname)
Packit 1c1d7e
            tr = Transl(fullname, self)
Packit 1c1d7e
            tr.processing()
Packit 1c1d7e
            assert(tr.classId != 'Translator')
Packit 1c1d7e
            self.__translDic[tr.classId] = tr
Packit 1c1d7e

Packit 1c1d7e
        # Extract the global information of the processed info.
Packit 1c1d7e
        self.__extractProcessedInfo()
Packit 1c1d7e

Packit 1c1d7e

Packit 1c1d7e
    def __extractProcessedInfo(self):
Packit 1c1d7e
        """Build lists and strings of the processed info."""
Packit 1c1d7e

Packit 1c1d7e
        # Build the auxiliary list with strings compound of the status,
Packit 1c1d7e
        # readable form of the language, and classId.
Packit 1c1d7e
        statLst = []
Packit 1c1d7e
        for obj in list(self.__translDic.values()):
Packit 1c1d7e
            assert(obj.classId != 'Translator')
Packit 1c1d7e
            s = obj.status + '|' + obj.langReadable + '|' + obj.classId
Packit 1c1d7e
            statLst.append(s)
Packit 1c1d7e

Packit 1c1d7e
        # Sort the list and extract the object identifiers (classId's) for
Packit 1c1d7e
        # the up-to-date translators and English-based translators.
Packit 1c1d7e
        statLst.sort()
Packit 1c1d7e
        self.upToDateIdLst = [x.split('|')[2] for x in statLst if x[0] == '|']
Packit 1c1d7e
        self.EnBasedIdLst = [x.split('|')[2] for x in statLst if x[:2] == 'En']
Packit 1c1d7e

Packit 1c1d7e
        # Reverse the list and extract the TranslatorAdapter based translators.
Packit 1c1d7e
        statLst.reverse()
Packit 1c1d7e
        self.adaptIdLst = [x.split('|')[2] for x in statLst if x[0].isdigit()]
Packit 1c1d7e

Packit 1c1d7e
        # Build the list of tuples that contain (langReadable, obj).
Packit 1c1d7e
        # Sort it by readable name.
Packit 1c1d7e
        self.langLst = []
Packit 1c1d7e
        for obj in list(self.__translDic.values()):
Packit 1c1d7e
            self.langLst.append((obj.langReadable, obj))
Packit 1c1d7e

Packit 1c1d7e
        self.langLst.sort(key=lambda x: x[0])
Packit 1c1d7e

Packit 1c1d7e
        # Create the list with readable language names. If the language has
Packit 1c1d7e
        # also the English-based version, modify the item by appending
Packit 1c1d7e
        # the note. Number of the supported languages is equal to the length
Packit 1c1d7e
        # of the list.
Packit 1c1d7e
        langReadableLst = []
Packit 1c1d7e
        for name, obj in self.langLst:
Packit 1c1d7e
            if obj.status == 'En': continue
Packit 1c1d7e

Packit 1c1d7e
            # Append the 'En' to the classId to possibly obtain the classId
Packit 1c1d7e
            # of the English-based object. If the object exists, modify the
Packit 1c1d7e
            # name for the readable list of supported languages.
Packit 1c1d7e
            classIdEn = obj.classId + 'En'
Packit 1c1d7e
            if classIdEn in self.__translDic:
Packit 1c1d7e
                name += ' (+En)'
Packit 1c1d7e

Packit 1c1d7e
            # Append the result name of the language, possibly with note.
Packit 1c1d7e
            langReadableLst.append(name)
Packit 1c1d7e

Packit 1c1d7e
        # Create the multiline string of readable language names,
Packit 1c1d7e
        # with punctuation, wrapped to paragraph.
Packit 1c1d7e
        if len(langReadableLst) == 1:
Packit 1c1d7e
            s = langReadableLst[0]
Packit 1c1d7e
        elif len(langReadableLst) == 2:
Packit 1c1d7e
            s = ' and '.join(langReadableLst)
Packit 1c1d7e
        else:
Packit 1c1d7e
            s = ', '.join(langReadableLst[:-1]) + ', and '
Packit 1c1d7e
            s += langReadableLst[-1]
Packit 1c1d7e

Packit 1c1d7e
        self.supportedLangReadableStr = fill(s + '.')
Packit 1c1d7e

Packit 1c1d7e
        # Find the number of the supported languages. The English based
Packit 1c1d7e
        # languages are not counted if the non-English based also exists.
Packit 1c1d7e
        self.numLang = len(self.langLst)
Packit 1c1d7e
        for name, obj in self.langLst:
Packit 1c1d7e
            if obj.status == 'En':
Packit 1c1d7e
                classId = obj.classId[:-2]
Packit 1c1d7e
                if classId in self.__translDic:
Packit 1c1d7e
                    self.numLang -= 1    # the couple will be counted as one
Packit 1c1d7e

Packit 1c1d7e
        # Extract the version of Doxygen.
Packit 1c1d7e
        f = xopen(os.path.join(self.doxy_path, 'VERSION'))
Packit 1c1d7e
        self.doxVersion = f.readline().strip()
Packit 1c1d7e
        f.close()
Packit 1c1d7e

Packit 1c1d7e
        # Update the last modification time.
Packit 1c1d7e
        for tr in list(self.__translDic.values()):
Packit 1c1d7e
            tim = tr.getmtime()
Packit 1c1d7e
            if tim > self.lastModificationTime:
Packit 1c1d7e
                self.lastModificationTime = tim
Packit 1c1d7e

Packit 1c1d7e

Packit 1c1d7e
    def __getNoTrSourceFilesLst(self):
Packit 1c1d7e
        """Returns the list of sources to be checked.
Packit 1c1d7e

Packit 1c1d7e
        All .cpp files and also .h files that do not declare or define
Packit 1c1d7e
        the translator methods are included in the list. The file names
Packit 1c1d7e
        are searched in doxygen/src directory.
Packit 1c1d7e
        """
Packit 1c1d7e
        files = []
Packit 1c1d7e
        for item in os.listdir(self.src_path):
Packit 1c1d7e
            # Split the bare name to get the extension.
Packit 1c1d7e
            name, ext = os.path.splitext(item)
Packit 1c1d7e
            ext = ext.lower()
Packit 1c1d7e

Packit 1c1d7e
            # Include only .cpp and .h files (case independent) and exclude
Packit 1c1d7e
            # the files where the checked identifiers are defined.
Packit 1c1d7e
            if ext == '.cpp' or (ext == '.h' and name.find('translator') == -1):
Packit 1c1d7e
                fname = os.path.join(self.src_path, item)
Packit 1c1d7e
                assert os.path.isfile(fname) # assumes no directory with the ext
Packit 1c1d7e
                files.append(fname)          # full name
Packit 1c1d7e
        return files
Packit 1c1d7e

Packit 1c1d7e

Packit 1c1d7e
    def __removeUsedInFiles(self, fname, dic):
Packit 1c1d7e
        """Removes items for method identifiers that are found in fname.
Packit 1c1d7e

Packit 1c1d7e
        The method reads the content of the file as one string and searches
Packit 1c1d7e
        for all identifiers from dic. The identifiers that were found in
Packit 1c1d7e
        the file are removed from the dictionary.
Packit 1c1d7e

Packit 1c1d7e
        Note: If more files is to be checked, the files where most items are
Packit 1c1d7e
        probably used should be checked first and the resulting reduced
Packit 1c1d7e
        dictionary should be used for checking the next files (speed up).
Packit 1c1d7e
        """
Packit 1c1d7e
        lst_in = list(dic.keys())   # identifiers to be searched for
Packit 1c1d7e

Packit 1c1d7e
        # Read content of the file as one string.
Packit 1c1d7e
        assert os.path.isfile(fname)
Packit 1c1d7e
        f = xopen(fname)
Packit 1c1d7e
        cont = f.read()
Packit 1c1d7e
        f.close()
Packit 1c1d7e

Packit 1c1d7e
        # Remove the items for identifiers that were found in the file.
Packit 1c1d7e
        while lst_in:
Packit 1c1d7e
            item = lst_in.pop(0)
Packit 1c1d7e
            if cont.find(item) != -1:
Packit 1c1d7e
                del dic[item]
Packit 1c1d7e

Packit 1c1d7e

Packit 1c1d7e
    def __checkForNotUsedTrMethods(self):
Packit 1c1d7e
        """Returns the dictionary of not used translator methods.
Packit 1c1d7e

Packit 1c1d7e
        The method can be called only after self.requiredMethodsDic has been
Packit 1c1d7e
        built. The stripped prototypes are the values, the method identifiers
Packit 1c1d7e
        are the keys.
Packit 1c1d7e
        """
Packit 1c1d7e
        # Build the dictionary of the required method prototypes with
Packit 1c1d7e
        # method identifiers used as keys.
Packit 1c1d7e
        trdic = {}
Packit 1c1d7e
        for prototype in list(self.requiredMethodsDic.keys()):
Packit 1c1d7e
            ri = prototype.split('(')[0]
Packit 1c1d7e
            identifier = ri.split()[1].strip()
Packit 1c1d7e
            trdic[identifier] = prototype
Packit 1c1d7e

Packit 1c1d7e
        # Build the list of source files where translator method identifiers
Packit 1c1d7e
        # can be used.
Packit 1c1d7e
        files = self.__getNoTrSourceFilesLst()
Packit 1c1d7e

Packit 1c1d7e
        # Loop through the files and reduce the dictionary of id -> proto.
Packit 1c1d7e
        for fname in files:
Packit 1c1d7e
            self.__removeUsedInFiles(fname, trdic)
Packit 1c1d7e

Packit 1c1d7e
        # Return the dictionary of not used translator methods.
Packit 1c1d7e
        return trdic
Packit 1c1d7e

Packit 1c1d7e

Packit 1c1d7e
    def __emails(self, classId):
Packit 1c1d7e
        """Returns the list of maintainer emails.
Packit 1c1d7e

Packit 1c1d7e
        The method returns the list of e-mail addresses for the translator
Packit 1c1d7e
        class, but only the addresses that were not marked as [xxx]."""
Packit 1c1d7e
        lst = []
Packit 1c1d7e
        for m in self.__maintainersDic[classId]:
Packit 1c1d7e
            if not m[1].startswith('['):
Packit 1c1d7e
                email = m[1]
Packit 1c1d7e
                email = email.replace(' at ', '@') # Unmangle the mangled e-mail
Packit 1c1d7e
                email = email.replace(' dot ', '.')
Packit 1c1d7e
                lst.append(email)
Packit 1c1d7e
        return lst
Packit 1c1d7e

Packit 1c1d7e

Packit 1c1d7e
    def getBgcolorByReadableStatus(self, readableStatus):
Packit 1c1d7e
        if readableStatus == 'up-to-date':
Packit 1c1d7e
            color = '#ccffcc'    # green
Packit 1c1d7e
        elif readableStatus.startswith('almost'):
Packit 1c1d7e
            color = '#ffffff'    # white
Packit 1c1d7e
        elif readableStatus.startswith('English'):
Packit 1c1d7e
            color = '#ccffcc'    # green
Packit 1c1d7e
        elif readableStatus.startswith('1.8'):
Packit 1c1d7e
            color = '#ffffcc'    # yellow
Packit 1c1d7e
        elif readableStatus.startswith('1.7'):
Packit 1c1d7e
            color = '#ffcccc'    # pink
Packit 1c1d7e
        elif readableStatus.startswith('1.6'):
Packit 1c1d7e
            color = '#ffcccc'    # pink
Packit 1c1d7e
        else:
Packit 1c1d7e
            color = '#ff5555'    # red
Packit 1c1d7e
        return color
Packit 1c1d7e

Packit 1c1d7e

Packit 1c1d7e
    def generateTranslatorReport(self):
Packit 1c1d7e
        """Generates the translator report."""
Packit 1c1d7e

Packit 1c1d7e
        output = os.path.join(self.doc_path, self.translatorReportFileName)
Packit 1c1d7e

Packit 1c1d7e
        # Open the textual report file for the output.
Packit 1c1d7e
        f = xopen(output, 'w')
Packit 1c1d7e

Packit 1c1d7e
        # Output the information about the version.
Packit 1c1d7e
        f.write('(' + self.doxVersion + ')\n\n')
Packit 1c1d7e

Packit 1c1d7e
        # Output the information about the number of the supported languages
Packit 1c1d7e
        # and the list of the languages, or only the note about the explicitly
Packit 1c1d7e
        # given languages to process.
Packit 1c1d7e
        if self.script_argLst:
Packit 1c1d7e
            f.write('The report was generated for the following, explicitly')
Packit 1c1d7e
            f.write(' identified languages:\n\n')
Packit 1c1d7e
            f.write(self.supportedLangReadableStr + '\n\n')
Packit 1c1d7e
        else:
Packit 1c1d7e
            f.write('Doxygen supports the following ')
Packit 1c1d7e
            f.write(str(self.numLang))
Packit 1c1d7e
            f.write(' languages (sorted alphabetically):\n\n')
Packit 1c1d7e
            f.write(self.supportedLangReadableStr + '\n\n')
Packit 1c1d7e

Packit 1c1d7e
            # Write the summary about the status of language translators (how
Packit 1c1d7e
            # many translators) are up-to-date, etc.
Packit 1c1d7e
            s = 'Of them, %d translators are up-to-date, ' % len(self.upToDateIdLst)
Packit 1c1d7e
            s += '%d translators are based on some adapter class, ' % len(self.adaptIdLst)
Packit 1c1d7e
            s += 'and %d are English based.' % len(self.EnBasedIdLst)
Packit 1c1d7e
            f.write(fill(s) + '\n\n')
Packit 1c1d7e

Packit 1c1d7e
        # The e-mail addresses of the maintainers will be collected to
Packit 1c1d7e
        # the auxiliary file in the order of translator classes listed
Packit 1c1d7e
        # in the translator report.
Packit 1c1d7e
        fmail = xopen('mailto.txt', 'w')
Packit 1c1d7e

Packit 1c1d7e
        # Write the list of "up-to-date" translator classes.
Packit 1c1d7e
        if self.upToDateIdLst:
Packit 1c1d7e
            s = '''The following translator classes are up-to-date (sorted
Packit 1c1d7e
                alphabetically). This means that they derive from the
Packit 1c1d7e
                Translator class, they implement all %d of the required
Packit 1c1d7e
                methods, and even minor problems were not spotted by the script:'''
Packit 1c1d7e
            s = s % len(self.requiredMethodsDic)
Packit 1c1d7e
            f.write('-' * 70 + '\n')
Packit 1c1d7e
            f.write(fill(s) + '\n\n')
Packit 1c1d7e

Packit 1c1d7e
            mailtoLst = []
Packit 1c1d7e
            for x in self.upToDateIdLst:
Packit 1c1d7e
                obj = self.__translDic[x]
Packit 1c1d7e
                if obj.note is None:
Packit 1c1d7e
                    f.write('  ' + obj.classId + '\n')
Packit 1c1d7e
                    mailtoLst.extend(self.__emails(obj.classId))
Packit 1c1d7e

Packit 1c1d7e
            fmail.write('up-to-date\n')
Packit 1c1d7e
            fmail.write('; '.join(mailtoLst))
Packit 1c1d7e

Packit 1c1d7e

Packit 1c1d7e
            # Write separately the list of "ALMOST up-to-date" translator classes.
Packit 1c1d7e
            s = '''The following translator classes are ALMOST up-to-date (sorted
Packit 1c1d7e
                alphabetically). This means that they derive from the
Packit 1c1d7e
                Translator class, but there still may be some minor problems
Packit 1c1d7e
                listed for them:'''
Packit 1c1d7e
            f.write('\n' + ('-' * 70) + '\n')
Packit 1c1d7e
            f.write(fill(s) + '\n\n')
Packit 1c1d7e
            mailtoLst = []
Packit 1c1d7e
            for x in self.upToDateIdLst:
Packit 1c1d7e
                obj = self.__translDic[x]
Packit 1c1d7e
                if obj.note is not None:
Packit 1c1d7e
                    f.write('  ' + obj.classId + '\t-- ' + obj.note + '\n')
Packit 1c1d7e
                    mailtoLst.extend(self.__emails(obj.classId))
Packit 1c1d7e

Packit 1c1d7e
            fmail.write('\n\nalmost up-to-date\n')
Packit 1c1d7e
            fmail.write('; '.join(mailtoLst))
Packit 1c1d7e

Packit 1c1d7e
        # Write the list of the adapter based classes. The very obsolete
Packit 1c1d7e
        # translators that derive from TranslatorEnglish are included.
Packit 1c1d7e
        if self.adaptIdLst:
Packit 1c1d7e
            s = '''The following translator classes need maintenance
Packit 1c1d7e
                (the most obsolete at the end). The other info shows the
Packit 1c1d7e
                estimation of Doxygen version when the class was last
Packit 1c1d7e
                updated and number of methods that must be implemented to
Packit 1c1d7e
                become up-to-date:'''
Packit 1c1d7e
            f.write('\n' + '-' * 70 + '\n')
Packit 1c1d7e
            f.write(fill(s) + '\n\n')
Packit 1c1d7e

Packit 1c1d7e
            # Find also whether some adapter classes may be removed.
Packit 1c1d7e
            adaptMinVersion = '9.9.99'
Packit 1c1d7e

Packit 1c1d7e
            mailtoLst = []
Packit 1c1d7e
            numRequired = len(self.requiredMethodsDic)
Packit 1c1d7e
            for x in self.adaptIdLst:
Packit 1c1d7e
                obj = self.__translDic[x]
Packit 1c1d7e
                f.write('  %-30s' % obj.classId)
Packit 1c1d7e
                f.write('  %-6s' % obj.readableStatus)
Packit 1c1d7e
                numimpl = len(obj.missingMethods)
Packit 1c1d7e
                pluralS = ''
Packit 1c1d7e
                if numimpl > 1: pluralS = 's'
Packit 1c1d7e
                percent = 100 * numimpl / numRequired
Packit 1c1d7e
                f.write('\t%2d method%s to implement (%d %%)' % (
Packit 1c1d7e
                        numimpl, pluralS, percent))
Packit 1c1d7e
                if obj.note:
Packit 1c1d7e
                    f.write('\n\tNote: ' + obj.note + '\n')
Packit 1c1d7e
                f.write('\n')
Packit 1c1d7e
                mailtoLst.extend(self.__emails(obj.classId)) # to maintainer
Packit 1c1d7e

Packit 1c1d7e
                # Check the level of required adapter classes.
Packit 1c1d7e
                if obj.status != '0.0.00' and obj.status < adaptMinVersion:
Packit 1c1d7e
                    adaptMinVersion = obj.status
Packit 1c1d7e

Packit 1c1d7e
            fmail.write('\n\ntranslator based\n')
Packit 1c1d7e
            fmail.write('; '.join(mailtoLst))
Packit 1c1d7e

Packit 1c1d7e
            # Set the note if some old translator adapters are not needed
Packit 1c1d7e
            # any more. Do it only when the script is called without arguments,
Packit 1c1d7e
            # i.e. all languages were checked against the needed translator
Packit 1c1d7e
            # adapters.
Packit 1c1d7e
            if not self.script_argLst:
Packit 1c1d7e
                to_remove = {}
Packit 1c1d7e
                for version, adaptClassId in list(self.adaptMethodsDic.values()):
Packit 1c1d7e
                    if version < adaptMinVersion:
Packit 1c1d7e
                        to_remove[adaptClassId] = True
Packit 1c1d7e

Packit 1c1d7e
                if to_remove:
Packit 1c1d7e
                    lst = list(to_remove.keys())
Packit 1c1d7e
                    lst.sort()
Packit 1c1d7e
                    plural = len(lst) > 1
Packit 1c1d7e
                    note = 'Note: The adapter class'
Packit 1c1d7e
                    if plural: note += 'es'
Packit 1c1d7e
                    note += ' ' + ', '.join(lst)
Packit 1c1d7e
                    if not plural:
Packit 1c1d7e
                        note += ' is'
Packit 1c1d7e
                    else:
Packit 1c1d7e
                        note += ' are'
Packit 1c1d7e
                    note += ' not used and can be removed.'
Packit 1c1d7e
                    f.write('\n' + fill(note) + '\n')
Packit 1c1d7e

Packit 1c1d7e
        # Write the list of the English-based classes.
Packit 1c1d7e
        if self.EnBasedIdLst:
Packit 1c1d7e
            s = '''The following translator classes derive directly from the
Packit 1c1d7e
                TranslatorEnglish. The class identifier has the suffix 'En'
Packit 1c1d7e
                that says that this is intentional. Usually, there is also
Packit 1c1d7e
                a non-English based version of the translator for
Packit 1c1d7e
                the language:'''
Packit 1c1d7e
            f.write('\n' + '-' * 70 + '\n')
Packit 1c1d7e
            f.write(fill(s) + '\n\n')
Packit 1c1d7e

Packit 1c1d7e
            for x in self.EnBasedIdLst:
Packit 1c1d7e
                obj = self.__translDic[x]
Packit 1c1d7e
                f.write('  ' + obj.classId)
Packit 1c1d7e
                f.write('\timplements %d methods' % len(obj.implementedMethods))
Packit 1c1d7e
                if obj.note:
Packit 1c1d7e
                    f.write(' -- ' + obj.note)
Packit 1c1d7e
                f.write('\n')
Packit 1c1d7e

Packit 1c1d7e
        # Check for not used translator methods and generate warning if found.
Packit 1c1d7e
        # The check is rather time consuming, so it is not done when report
Packit 1c1d7e
        # is restricted to explicitly given language identifiers.
Packit 1c1d7e
        if not self.script_argLst:
Packit 1c1d7e
            dic = self.__checkForNotUsedTrMethods()
Packit 1c1d7e
            if dic:
Packit 1c1d7e
                s = '''WARNING: The following translator methods are declared
Packit 1c1d7e
                    in the Translator class but their identifiers do not appear
Packit 1c1d7e
                    in source files. The situation should be checked. The .cpp
Packit 1c1d7e
                    files and .h files excluding the '*translator*' files
Packit 1c1d7e
                    in doxygen/src directory were simply searched for occurrence
Packit 1c1d7e
                    of the method identifiers:'''
Packit 1c1d7e
                f.write('\n' + '=' * 70 + '\n')
Packit 1c1d7e
                f.write(fill(s) + '\n\n')
Packit 1c1d7e

Packit 1c1d7e
                keys = list(dic.keys())
Packit 1c1d7e
                keys.sort()
Packit 1c1d7e
                for key in keys:
Packit 1c1d7e
                    f.write('  ' + dic[key] + '\n')
Packit 1c1d7e
                f.write('\n')
Packit 1c1d7e

Packit 1c1d7e
        # Write the details for the translators.
Packit 1c1d7e
        f.write('\n' + '=' * 70)
Packit 1c1d7e
        f.write('\nDetails for translators (classes sorted alphabetically):\n')
Packit 1c1d7e

Packit 1c1d7e
        cls = list(self.__translDic.keys())
Packit 1c1d7e
        cls.sort()
Packit 1c1d7e

Packit 1c1d7e
        for c in cls:
Packit 1c1d7e
            obj = self.__translDic[c]
Packit 1c1d7e
            assert(obj.classId != 'Translator')
Packit 1c1d7e
            obj.report(f)
Packit 1c1d7e

Packit 1c1d7e
        # Close the report file and the auxiliary file with e-mails.
Packit 1c1d7e
        f.close()
Packit 1c1d7e
        fmail.close()
Packit 1c1d7e

Packit 1c1d7e

Packit 1c1d7e
    def __loadMaintainers(self):
Packit 1c1d7e
        """Load and process the file with the maintainers.
Packit 1c1d7e

Packit 1c1d7e
        Fills the dictionary classId -> [(name, e-mail), ...]."""
Packit 1c1d7e

Packit 1c1d7e
        fname = os.path.join(self.doc_path, self.maintainersFileName)
Packit 1c1d7e

Packit 1c1d7e
        # Include the maintainers file to the group of files checked with
Packit 1c1d7e
        # respect to the modification time.
Packit 1c1d7e
        tim = os.path.getmtime(fname)
Packit 1c1d7e
        if tim > self.lastModificationTime:
Packit 1c1d7e
            self.lastModificationTime = tim
Packit 1c1d7e

Packit 1c1d7e
        # Process the content of the maintainers file.
Packit 1c1d7e
        f = xopen(fname)
Packit 1c1d7e
        inside = False  # inside the record for the language
Packit 1c1d7e
        lineReady = True
Packit 1c1d7e
        classId = None
Packit 1c1d7e
        maintainersLst = None
Packit 1c1d7e
        self.__maintainersDic = {}
Packit 1c1d7e
        while lineReady:
Packit 1c1d7e
            line = f.readline()            # next line
Packit 1c1d7e
            lineReady = line != ''         # when eof, then line == ''
Packit 1c1d7e

Packit 1c1d7e
            line = line.strip()            # eof should also behave as separator
Packit 1c1d7e
            if line != '' and line[0] == '%':    # skip the comment line
Packit 1c1d7e
                continue
Packit 1c1d7e

Packit 1c1d7e
            if not inside:                 # if outside of the record
Packit 1c1d7e
                if line != '':            # should be language identifier
Packit 1c1d7e
                    classId = line
Packit 1c1d7e
                    maintainersLst = []
Packit 1c1d7e
                    inside = True
Packit 1c1d7e
                # Otherwise skip empty line that do not act as separator.
Packit 1c1d7e

Packit 1c1d7e
            else:                          # if inside the record
Packit 1c1d7e
                if line == '':            # separator found
Packit 1c1d7e
                    inside = False
Packit 1c1d7e
                else:
Packit 1c1d7e
                    # If it is the first maintainer, create the empty list.
Packit 1c1d7e
                    if classId not in self.__maintainersDic:
Packit 1c1d7e
                        self.__maintainersDic[classId] = []
Packit 1c1d7e

Packit 1c1d7e
                    # Split the information about the maintainer and append
Packit 1c1d7e
                    # the tuple. The address may be prefixed '[unreachable]'
Packit 1c1d7e
                    # or whatever '[xxx]'. This will be processed later.
Packit 1c1d7e
                    lst = line.split(':', 1)
Packit 1c1d7e
                    assert(len(lst) == 2)
Packit 1c1d7e
                    t = (lst[0].strip(), lst[1].strip())
Packit 1c1d7e
                    self.__maintainersDic[classId].append(t)
Packit 1c1d7e
        f.close()
Packit 1c1d7e

Packit 1c1d7e

Packit 1c1d7e
    def generateLanguageDoc(self):
Packit 1c1d7e
        """Checks the modtime of files and generates language.doc."""
Packit 1c1d7e
        self.__loadMaintainers()
Packit 1c1d7e

Packit 1c1d7e
        # Check the last modification time of the template file. It is the
Packit 1c1d7e
        # last file from the group that decide whether the documentation
Packit 1c1d7e
        # should or should not be generated.
Packit 1c1d7e
        fTplName = os.path.join(self.doc_path, self.languageTplFileName)
Packit 1c1d7e
        tim = os.path.getmtime(fTplName)
Packit 1c1d7e
        if tim > self.lastModificationTime:
Packit 1c1d7e
            self.lastModificationTime = tim
Packit 1c1d7e

Packit 1c1d7e
        # If the generated documentation exists and is newer than any of
Packit 1c1d7e
        # the source files from the group, do not generate it and quit
Packit 1c1d7e
        # quietly.
Packit 1c1d7e
        fDocName = os.path.join(self.doc_path, self.languageDocFileName)
Packit 1c1d7e
        if os.path.isfile(fDocName):
Packit 1c1d7e
            if os.path.getmtime(fDocName) > self.lastModificationTime:
Packit 1c1d7e
                return
Packit 1c1d7e

Packit 1c1d7e
        # The document or does not exist or is older than some of the
Packit 1c1d7e
        # sources. It must be generated again.
Packit 1c1d7e
        #
Packit 1c1d7e
        # Read the template of the documentation, and remove the first
Packit 1c1d7e
        # attention lines.
Packit 1c1d7e
        f = xopen(fTplName)
Packit 1c1d7e
        doctpl = f.read()
Packit 1c1d7e
        f.close()
Packit 1c1d7e

Packit 1c1d7e
        pos = doctpl.find('/***')
Packit 1c1d7e
        assert pos != -1
Packit 1c1d7e
        doctpl = doctpl[pos:]
Packit 1c1d7e

Packit 1c1d7e
        # Fill the tplDic by symbols that will be inserted into the
Packit 1c1d7e
        # document template.
Packit 1c1d7e
        tplDic = {}
Packit 1c1d7e

Packit 1c1d7e
        s = ('Do not edit this file. It was generated by the %s script.\n' +\
Packit 1c1d7e
             ' * Edit the %s and %s files instead.') % (
Packit 1c1d7e
             self.script_name, self.languageTplFileName, self.maintainersFileName)
Packit 1c1d7e
        tplDic['editnote'] = s
Packit 1c1d7e

Packit 1c1d7e
        tplDic['doxVersion'] = self.doxVersion
Packit 1c1d7e
        tplDic['supportedLangReadableStr'] = self.supportedLangReadableStr
Packit 1c1d7e
        tplDic['translatorReportFileName'] = self.translatorReportFileName
Packit 1c1d7e

Packit 1c1d7e
        ahref = '
Packit 1c1d7e
        ahref += '"\n>doxygen/doc/'  + self.translatorReportFileName
Packit 1c1d7e
        ahref += ''
Packit 1c1d7e
        tplDic['translatorReportLink'] = ahref
Packit 1c1d7e
        tplDic['numLangStr'] = str(self.numLang)
Packit 1c1d7e

Packit 1c1d7e
        # Define templates for HTML table parts of the documentation.
Packit 1c1d7e
        htmlTableTpl = '''\
Packit 1c1d7e
            \\htmlonly
Packit 1c1d7e
			

Packit 1c1d7e
            
Packit 1c1d7e
            
Packit 1c1d7e
            
Packit 1c1d7e
              
Packit 1c1d7e
              
Packit 1c1d7e
              <font size="+1" color="#ffffff"> Language </font>
Packit 1c1d7e
              <font size="+1" color="#ffffff"> Maintainer </font>
Packit 1c1d7e
              <font size="+1" color="#ffffff"> Contact address </font>
Packit 1c1d7e
                      <font size="-2" color="#ffffff">(replace the at and dot)</font>
Packit 1c1d7e
              <font size="+1" color="#ffffff"> Status </font>
Packit 1c1d7e
              
Packit 1c1d7e
              
Packit 1c1d7e
            %s
Packit 1c1d7e
              
Packit 1c1d7e
              
Packit 1c1d7e
            
Packit 1c1d7e
            
Packit 1c1d7e
            
Packit 1c1d7e
			

Packit 1c1d7e
            \\endhtmlonly
Packit 1c1d7e
            '''
Packit 1c1d7e
        htmlTableTpl = textwrap.dedent(htmlTableTpl)
Packit 1c1d7e
        htmlTrTpl = '\n  %s\n  '
Packit 1c1d7e
        htmlTdTpl = '\n    %s'
Packit 1c1d7e
        htmlTdStatusColorTpl = '\n    %s'
Packit 1c1d7e

Packit 1c1d7e
        # Loop through transl objects in the order of sorted readable names
Packit 1c1d7e
        # and add generate the content of the HTML table.
Packit 1c1d7e
        trlst = []
Packit 1c1d7e
        for name, obj in self.langLst:
Packit 1c1d7e
            # Fill the table data elements for one row. The first element
Packit 1c1d7e
            # contains the readable name of the language. Only the oldest
Packit 1c1d7e
            # translator are colour marked in the language column. Less
Packit 1c1d7e
            # "heavy" color is used (when compared with the Status column).
Packit 1c1d7e
            if obj.readableStatus.startswith('1.4'):
Packit 1c1d7e
                bkcolor = self.getBgcolorByReadableStatus('1.4')
Packit 1c1d7e
            else:
Packit 1c1d7e
                bkcolor = '#ffffff'
Packit 1c1d7e

Packit 1c1d7e
            lst = [ htmlTdStatusColorTpl % (bkcolor, obj.langReadable) ]
Packit 1c1d7e

Packit 1c1d7e
            # The next two elements contain the list of maintainers
Packit 1c1d7e
            # and the list of their mangled e-mails. For English-based
Packit 1c1d7e
            # translators that are coupled with the non-English based,
Packit 1c1d7e
            # insert the 'see' note.
Packit 1c1d7e
            mm = None  # init -- maintainer
Packit 1c1d7e
            ee = None  # init -- e-mail address
Packit 1c1d7e
            if obj.status == 'En':
Packit 1c1d7e
                # Check whether there is the coupled non-English.
Packit 1c1d7e
                classId = obj.classId[:-2]
Packit 1c1d7e
                if classId in self.__translDic:
Packit 1c1d7e
                    lang = self.__translDic[classId].langReadable
Packit 1c1d7e
                    mm = 'see the %s language' % lang
Packit 1c1d7e
                    ee = ' '
Packit 1c1d7e

Packit 1c1d7e
            if not mm and obj.classId in self.__maintainersDic:
Packit 1c1d7e
                # Build a string of names separated by the HTML break element.
Packit 1c1d7e
                # Special notes used instead of names are highlighted.
Packit 1c1d7e
                lm = []
Packit 1c1d7e
                for maintainer in self.__maintainersDic[obj.classId]:
Packit 1c1d7e
                    name = maintainer[0]
Packit 1c1d7e
                    if name.startswith('--'):
Packit 1c1d7e
                        name = ''\
Packit 1c1d7e
                               + name + ''
Packit 1c1d7e
                    lm.append(name)
Packit 1c1d7e
                mm = '
'.join(lm)
Packit 1c1d7e

Packit 1c1d7e
                # The marked addresses (they start with the mark '[unreachable]',
Packit 1c1d7e
                # '[resigned]', whatever '[xxx]') will not be displayed at all.
Packit 1c1d7e
                # Only the mark will be used instead.
Packit 1c1d7e
                rexMark = re.compile('(?P<mark>\\[.*?\\])')
Packit 1c1d7e
                le = []
Packit 1c1d7e
                for maintainer in self.__maintainersDic[obj.classId]:
Packit 1c1d7e
                    address = maintainer[1]
Packit 1c1d7e
                    m = rexMark.search(address)
Packit 1c1d7e
                    if m is not None:
Packit 1c1d7e
                        address = ''\
Packit 1c1d7e
                                  + m.group('mark') + ''
Packit 1c1d7e
                    le.append(address)
Packit 1c1d7e
                ee = '
'.join(le)
Packit 1c1d7e

Packit 1c1d7e
            # Append the maintainer and e-mail elements.
Packit 1c1d7e
            lst.append(htmlTdTpl % mm)
Packit 1c1d7e
            lst.append(htmlTdTpl % ee)
Packit 1c1d7e

Packit 1c1d7e
            # The last element contains the readable form of the status.
Packit 1c1d7e
            bgcolor = self.getBgcolorByReadableStatus(obj.readableStatus)
Packit 1c1d7e
            lst.append(htmlTdStatusColorTpl % (bgcolor, obj.readableStatus))
Packit 1c1d7e

Packit 1c1d7e
            # Join the table data to one table row.
Packit 1c1d7e
            trlst.append(htmlTrTpl % (''.join(lst)))
Packit 1c1d7e

Packit 1c1d7e
        # Join the table rows and insert into the template.
Packit 1c1d7e
        htmlTable = htmlTableTpl % (''.join(trlst))
Packit 1c1d7e

Packit 1c1d7e
        # Define templates for LaTeX table parts of the documentation.
Packit 1c1d7e
        latexTableTpl = r'''
Packit 1c1d7e
            \latexonly
Packit 1c1d7e
            \footnotesize
Packit 1c1d7e
            \begin{longtable}{|l|l|l|l|}
Packit 1c1d7e
              \hline
Packit 1c1d7e
              {\bf Language} & {\bf Maintainer} & {\bf Contact address} & {\bf Status} \\
Packit 1c1d7e
              \hline
Packit 1c1d7e
            %s
Packit 1c1d7e
              \hline
Packit 1c1d7e
            \end{longtable}
Packit 1c1d7e
            \normalsize
Packit 1c1d7e
            \endlatexonly
Packit 1c1d7e
            '''
Packit 1c1d7e
        latexTableTpl = textwrap.dedent(latexTableTpl)
Packit 1c1d7e
        latexLineTpl = '\n' + r'  %s & %s & {\tt\tiny %s} & %s \\'
Packit 1c1d7e

Packit 1c1d7e
        # Loop through transl objects in the order of sorted readable names
Packit 1c1d7e
        # and add generate the content of the LaTeX table.
Packit 1c1d7e
        trlst = []
Packit 1c1d7e
        for name, obj in self.langLst:
Packit 1c1d7e
            # For LaTeX, more maintainers for the same language are
Packit 1c1d7e
            # placed on separate rows in the table.  The line separator
Packit 1c1d7e
            # in the table is placed explicitly above the first
Packit 1c1d7e
            # maintainer. Prepare the arguments for the LaTeX row template.
Packit 1c1d7e
            maintainers = []
Packit 1c1d7e
            if obj.classId in self.__maintainersDic:
Packit 1c1d7e
                maintainers = self.__maintainersDic[obj.classId]
Packit 1c1d7e

Packit 1c1d7e
            lang = obj.langReadable
Packit 1c1d7e
            maintainer = None  # init
Packit 1c1d7e
            email = None       # init
Packit 1c1d7e
            if obj.status == 'En':
Packit 1c1d7e
                # Check whether there is the coupled non-English.
Packit 1c1d7e
                classId = obj.classId[:-2]
Packit 1c1d7e
                if classId in self.__translDic:
Packit 1c1d7e
                    langNE = self.__translDic[classId].langReadable
Packit 1c1d7e
                    maintainer = 'see the %s language' % langNE
Packit 1c1d7e
                    email = '~'
Packit 1c1d7e

Packit 1c1d7e
            if not maintainer and (obj.classId in self.__maintainersDic):
Packit 1c1d7e
                lm = [ m[0] for m in self.__maintainersDic[obj.classId] ]
Packit 1c1d7e
                maintainer = maintainers[0][0]
Packit 1c1d7e
                email = maintainers[0][1]
Packit 1c1d7e

Packit 1c1d7e
            status = obj.readableStatus
Packit 1c1d7e

Packit 1c1d7e
            # Use the template to produce the line of the table and insert
Packit 1c1d7e
            # the hline plus the constructed line into the table content.
Packit 1c1d7e
            # The underscore character must be escaped.
Packit 1c1d7e
            trlst.append('\n  \\hline')
Packit 1c1d7e
            s = latexLineTpl % (lang, maintainer, email, status)
Packit 1c1d7e
            s = s.replace('_', '\\_')
Packit 1c1d7e
            trlst.append(s)
Packit 1c1d7e

Packit 1c1d7e
            # List the other maintainers for the language. Do not set
Packit 1c1d7e
            # lang and status for them.
Packit 1c1d7e
            lang = '~'
Packit 1c1d7e
            status = '~'
Packit 1c1d7e
            for m in maintainers[1:]:
Packit 1c1d7e
                maintainer = m[0]
Packit 1c1d7e
                email = m[1]
Packit 1c1d7e
                s = latexLineTpl % (lang, maintainer, email, status)
Packit 1c1d7e
                s = s.replace('_', '\\_')
Packit 1c1d7e
                trlst.append(s)
Packit 1c1d7e

Packit 1c1d7e
        # Join the table lines and insert into the template.
Packit 1c1d7e
        latexTable = latexTableTpl % (''.join(trlst))
Packit 1c1d7e

Packit 1c1d7e
        # Put the HTML and LaTeX parts together and define the dic item.
Packit 1c1d7e
        tplDic['informationTable'] = htmlTable + '\n' + latexTable
Packit 1c1d7e

Packit 1c1d7e
        # Insert the symbols into the document template and write it down.
Packit 1c1d7e
        f = xopen(fDocName, 'w')
Packit 1c1d7e
        f.write(doctpl % tplDic)
Packit 1c1d7e
        f.close()
Packit 1c1d7e

Packit 1c1d7e
if __name__ == '__main__':
Packit 1c1d7e

Packit 1c1d7e
    # The Python 2.6+ or 3.3+ is required.
Packit 1c1d7e
    major = sys.version_info[0]
Packit 1c1d7e
    minor = sys.version_info[1]
Packit 1c1d7e
    if (major == 2 and minor < 6) or (major == 3 and minor < 0):
Packit 1c1d7e
        print('Python 2.6+ or Python 3.0+ are required for the script')
Packit 1c1d7e
        sys.exit(1)
Packit 1c1d7e

Packit 1c1d7e
    # The translator manager builds the Transl objects, parses the related
Packit 1c1d7e
    # sources, and keeps them in memory.
Packit 1c1d7e
    trMan = TrManager()
Packit 1c1d7e

Packit 1c1d7e
    # Process the Transl objects and generate the output files.
Packit 1c1d7e
    trMan.generateLanguageDoc()
Packit 1c1d7e
    trMan.generateTranslatorReport()