symbian-qemu-0.9.1-12/python-win32-2.6.1/lib/formatter.py
changeset 1 2fb8b9db1c86
equal deleted inserted replaced
0:ffa851df0825 1:2fb8b9db1c86
       
     1 """Generic output formatting.
       
     2 
       
     3 Formatter objects transform an abstract flow of formatting events into
       
     4 specific output events on writer objects. Formatters manage several stack
       
     5 structures to allow various properties of a writer object to be changed and
       
     6 restored; writers need not be able to handle relative changes nor any sort
       
     7 of ``change back'' operation. Specific writer properties which may be
       
     8 controlled via formatter objects are horizontal alignment, font, and left
       
     9 margin indentations. A mechanism is provided which supports providing
       
    10 arbitrary, non-exclusive style settings to a writer as well. Additional
       
    11 interfaces facilitate formatting events which are not reversible, such as
       
    12 paragraph separation.
       
    13 
       
    14 Writer objects encapsulate device interfaces. Abstract devices, such as
       
    15 file formats, are supported as well as physical devices. The provided
       
    16 implementations all work with abstract devices. The interface makes
       
    17 available mechanisms for setting the properties which formatter objects
       
    18 manage and inserting data into the output.
       
    19 """
       
    20 
       
    21 import sys
       
    22 
       
    23 
       
    24 AS_IS = None
       
    25 
       
    26 
       
    27 class NullFormatter:
       
    28     """A formatter which does nothing.
       
    29 
       
    30     If the writer parameter is omitted, a NullWriter instance is created.
       
    31     No methods of the writer are called by NullFormatter instances.
       
    32 
       
    33     Implementations should inherit from this class if implementing a writer
       
    34     interface but don't need to inherit any implementation.
       
    35 
       
    36     """
       
    37 
       
    38     def __init__(self, writer=None):
       
    39         if writer is None:
       
    40             writer = NullWriter()
       
    41         self.writer = writer
       
    42     def end_paragraph(self, blankline): pass
       
    43     def add_line_break(self): pass
       
    44     def add_hor_rule(self, *args, **kw): pass
       
    45     def add_label_data(self, format, counter, blankline=None): pass
       
    46     def add_flowing_data(self, data): pass
       
    47     def add_literal_data(self, data): pass
       
    48     def flush_softspace(self): pass
       
    49     def push_alignment(self, align): pass
       
    50     def pop_alignment(self): pass
       
    51     def push_font(self, x): pass
       
    52     def pop_font(self): pass
       
    53     def push_margin(self, margin): pass
       
    54     def pop_margin(self): pass
       
    55     def set_spacing(self, spacing): pass
       
    56     def push_style(self, *styles): pass
       
    57     def pop_style(self, n=1): pass
       
    58     def assert_line_data(self, flag=1): pass
       
    59 
       
    60 
       
    61 class AbstractFormatter:
       
    62     """The standard formatter.
       
    63 
       
    64     This implementation has demonstrated wide applicability to many writers,
       
    65     and may be used directly in most circumstances.  It has been used to
       
    66     implement a full-featured World Wide Web browser.
       
    67 
       
    68     """
       
    69 
       
    70     #  Space handling policy:  blank spaces at the boundary between elements
       
    71     #  are handled by the outermost context.  "Literal" data is not checked
       
    72     #  to determine context, so spaces in literal data are handled directly
       
    73     #  in all circumstances.
       
    74 
       
    75     def __init__(self, writer):
       
    76         self.writer = writer            # Output device
       
    77         self.align = None               # Current alignment
       
    78         self.align_stack = []           # Alignment stack
       
    79         self.font_stack = []            # Font state
       
    80         self.margin_stack = []          # Margin state
       
    81         self.spacing = None             # Vertical spacing state
       
    82         self.style_stack = []           # Other state, e.g. color
       
    83         self.nospace = 1                # Should leading space be suppressed
       
    84         self.softspace = 0              # Should a space be inserted
       
    85         self.para_end = 1               # Just ended a paragraph
       
    86         self.parskip = 0                # Skipped space between paragraphs?
       
    87         self.hard_break = 1             # Have a hard break
       
    88         self.have_label = 0
       
    89 
       
    90     def end_paragraph(self, blankline):
       
    91         if not self.hard_break:
       
    92             self.writer.send_line_break()
       
    93             self.have_label = 0
       
    94         if self.parskip < blankline and not self.have_label:
       
    95             self.writer.send_paragraph(blankline - self.parskip)
       
    96             self.parskip = blankline
       
    97             self.have_label = 0
       
    98         self.hard_break = self.nospace = self.para_end = 1
       
    99         self.softspace = 0
       
   100 
       
   101     def add_line_break(self):
       
   102         if not (self.hard_break or self.para_end):
       
   103             self.writer.send_line_break()
       
   104             self.have_label = self.parskip = 0
       
   105         self.hard_break = self.nospace = 1
       
   106         self.softspace = 0
       
   107 
       
   108     def add_hor_rule(self, *args, **kw):
       
   109         if not self.hard_break:
       
   110             self.writer.send_line_break()
       
   111         self.writer.send_hor_rule(*args, **kw)
       
   112         self.hard_break = self.nospace = 1
       
   113         self.have_label = self.para_end = self.softspace = self.parskip = 0
       
   114 
       
   115     def add_label_data(self, format, counter, blankline = None):
       
   116         if self.have_label or not self.hard_break:
       
   117             self.writer.send_line_break()
       
   118         if not self.para_end:
       
   119             self.writer.send_paragraph((blankline and 1) or 0)
       
   120         if isinstance(format, str):
       
   121             self.writer.send_label_data(self.format_counter(format, counter))
       
   122         else:
       
   123             self.writer.send_label_data(format)
       
   124         self.nospace = self.have_label = self.hard_break = self.para_end = 1
       
   125         self.softspace = self.parskip = 0
       
   126 
       
   127     def format_counter(self, format, counter):
       
   128         label = ''
       
   129         for c in format:
       
   130             if c == '1':
       
   131                 label = label + ('%d' % counter)
       
   132             elif c in 'aA':
       
   133                 if counter > 0:
       
   134                     label = label + self.format_letter(c, counter)
       
   135             elif c in 'iI':
       
   136                 if counter > 0:
       
   137                     label = label + self.format_roman(c, counter)
       
   138             else:
       
   139                 label = label + c
       
   140         return label
       
   141 
       
   142     def format_letter(self, case, counter):
       
   143         label = ''
       
   144         while counter > 0:
       
   145             counter, x = divmod(counter-1, 26)
       
   146             # This makes a strong assumption that lowercase letters
       
   147             # and uppercase letters form two contiguous blocks, with
       
   148             # letters in order!
       
   149             s = chr(ord(case) + x)
       
   150             label = s + label
       
   151         return label
       
   152 
       
   153     def format_roman(self, case, counter):
       
   154         ones = ['i', 'x', 'c', 'm']
       
   155         fives = ['v', 'l', 'd']
       
   156         label, index = '', 0
       
   157         # This will die of IndexError when counter is too big
       
   158         while counter > 0:
       
   159             counter, x = divmod(counter, 10)
       
   160             if x == 9:
       
   161                 label = ones[index] + ones[index+1] + label
       
   162             elif x == 4:
       
   163                 label = ones[index] + fives[index] + label
       
   164             else:
       
   165                 if x >= 5:
       
   166                     s = fives[index]
       
   167                     x = x-5
       
   168                 else:
       
   169                     s = ''
       
   170                 s = s + ones[index]*x
       
   171                 label = s + label
       
   172             index = index + 1
       
   173         if case == 'I':
       
   174             return label.upper()
       
   175         return label
       
   176 
       
   177     def add_flowing_data(self, data):
       
   178         if not data: return
       
   179         prespace = data[:1].isspace()
       
   180         postspace = data[-1:].isspace()
       
   181         data = " ".join(data.split())
       
   182         if self.nospace and not data:
       
   183             return
       
   184         elif prespace or self.softspace:
       
   185             if not data:
       
   186                 if not self.nospace:
       
   187                     self.softspace = 1
       
   188                     self.parskip = 0
       
   189                 return
       
   190             if not self.nospace:
       
   191                 data = ' ' + data
       
   192         self.hard_break = self.nospace = self.para_end = \
       
   193                           self.parskip = self.have_label = 0
       
   194         self.softspace = postspace
       
   195         self.writer.send_flowing_data(data)
       
   196 
       
   197     def add_literal_data(self, data):
       
   198         if not data: return
       
   199         if self.softspace:
       
   200             self.writer.send_flowing_data(" ")
       
   201         self.hard_break = data[-1:] == '\n'
       
   202         self.nospace = self.para_end = self.softspace = \
       
   203                        self.parskip = self.have_label = 0
       
   204         self.writer.send_literal_data(data)
       
   205 
       
   206     def flush_softspace(self):
       
   207         if self.softspace:
       
   208             self.hard_break = self.para_end = self.parskip = \
       
   209                               self.have_label = self.softspace = 0
       
   210             self.nospace = 1
       
   211             self.writer.send_flowing_data(' ')
       
   212 
       
   213     def push_alignment(self, align):
       
   214         if align and align != self.align:
       
   215             self.writer.new_alignment(align)
       
   216             self.align = align
       
   217             self.align_stack.append(align)
       
   218         else:
       
   219             self.align_stack.append(self.align)
       
   220 
       
   221     def pop_alignment(self):
       
   222         if self.align_stack:
       
   223             del self.align_stack[-1]
       
   224         if self.align_stack:
       
   225             self.align = align = self.align_stack[-1]
       
   226             self.writer.new_alignment(align)
       
   227         else:
       
   228             self.align = None
       
   229             self.writer.new_alignment(None)
       
   230 
       
   231     def push_font(self, (size, i, b, tt)):
       
   232         if self.softspace:
       
   233             self.hard_break = self.para_end = self.softspace = 0
       
   234             self.nospace = 1
       
   235             self.writer.send_flowing_data(' ')
       
   236         if self.font_stack:
       
   237             csize, ci, cb, ctt = self.font_stack[-1]
       
   238             if size is AS_IS: size = csize
       
   239             if i is AS_IS: i = ci
       
   240             if b is AS_IS: b = cb
       
   241             if tt is AS_IS: tt = ctt
       
   242         font = (size, i, b, tt)
       
   243         self.font_stack.append(font)
       
   244         self.writer.new_font(font)
       
   245 
       
   246     def pop_font(self):
       
   247         if self.font_stack:
       
   248             del self.font_stack[-1]
       
   249         if self.font_stack:
       
   250             font = self.font_stack[-1]
       
   251         else:
       
   252             font = None
       
   253         self.writer.new_font(font)
       
   254 
       
   255     def push_margin(self, margin):
       
   256         self.margin_stack.append(margin)
       
   257         fstack = filter(None, self.margin_stack)
       
   258         if not margin and fstack:
       
   259             margin = fstack[-1]
       
   260         self.writer.new_margin(margin, len(fstack))
       
   261 
       
   262     def pop_margin(self):
       
   263         if self.margin_stack:
       
   264             del self.margin_stack[-1]
       
   265         fstack = filter(None, self.margin_stack)
       
   266         if fstack:
       
   267             margin = fstack[-1]
       
   268         else:
       
   269             margin = None
       
   270         self.writer.new_margin(margin, len(fstack))
       
   271 
       
   272     def set_spacing(self, spacing):
       
   273         self.spacing = spacing
       
   274         self.writer.new_spacing(spacing)
       
   275 
       
   276     def push_style(self, *styles):
       
   277         if self.softspace:
       
   278             self.hard_break = self.para_end = self.softspace = 0
       
   279             self.nospace = 1
       
   280             self.writer.send_flowing_data(' ')
       
   281         for style in styles:
       
   282             self.style_stack.append(style)
       
   283         self.writer.new_styles(tuple(self.style_stack))
       
   284 
       
   285     def pop_style(self, n=1):
       
   286         del self.style_stack[-n:]
       
   287         self.writer.new_styles(tuple(self.style_stack))
       
   288 
       
   289     def assert_line_data(self, flag=1):
       
   290         self.nospace = self.hard_break = not flag
       
   291         self.para_end = self.parskip = self.have_label = 0
       
   292 
       
   293 
       
   294 class NullWriter:
       
   295     """Minimal writer interface to use in testing & inheritance.
       
   296 
       
   297     A writer which only provides the interface definition; no actions are
       
   298     taken on any methods.  This should be the base class for all writers
       
   299     which do not need to inherit any implementation methods.
       
   300 
       
   301     """
       
   302     def __init__(self): pass
       
   303     def flush(self): pass
       
   304     def new_alignment(self, align): pass
       
   305     def new_font(self, font): pass
       
   306     def new_margin(self, margin, level): pass
       
   307     def new_spacing(self, spacing): pass
       
   308     def new_styles(self, styles): pass
       
   309     def send_paragraph(self, blankline): pass
       
   310     def send_line_break(self): pass
       
   311     def send_hor_rule(self, *args, **kw): pass
       
   312     def send_label_data(self, data): pass
       
   313     def send_flowing_data(self, data): pass
       
   314     def send_literal_data(self, data): pass
       
   315 
       
   316 
       
   317 class AbstractWriter(NullWriter):
       
   318     """A writer which can be used in debugging formatters, but not much else.
       
   319 
       
   320     Each method simply announces itself by printing its name and
       
   321     arguments on standard output.
       
   322 
       
   323     """
       
   324 
       
   325     def new_alignment(self, align):
       
   326         print "new_alignment(%r)" % (align,)
       
   327 
       
   328     def new_font(self, font):
       
   329         print "new_font(%r)" % (font,)
       
   330 
       
   331     def new_margin(self, margin, level):
       
   332         print "new_margin(%r, %d)" % (margin, level)
       
   333 
       
   334     def new_spacing(self, spacing):
       
   335         print "new_spacing(%r)" % (spacing,)
       
   336 
       
   337     def new_styles(self, styles):
       
   338         print "new_styles(%r)" % (styles,)
       
   339 
       
   340     def send_paragraph(self, blankline):
       
   341         print "send_paragraph(%r)" % (blankline,)
       
   342 
       
   343     def send_line_break(self):
       
   344         print "send_line_break()"
       
   345 
       
   346     def send_hor_rule(self, *args, **kw):
       
   347         print "send_hor_rule()"
       
   348 
       
   349     def send_label_data(self, data):
       
   350         print "send_label_data(%r)" % (data,)
       
   351 
       
   352     def send_flowing_data(self, data):
       
   353         print "send_flowing_data(%r)" % (data,)
       
   354 
       
   355     def send_literal_data(self, data):
       
   356         print "send_literal_data(%r)" % (data,)
       
   357 
       
   358 
       
   359 class DumbWriter(NullWriter):
       
   360     """Simple writer class which writes output on the file object passed in
       
   361     as the file parameter or, if file is omitted, on standard output.  The
       
   362     output is simply word-wrapped to the number of columns specified by
       
   363     the maxcol parameter.  This class is suitable for reflowing a sequence
       
   364     of paragraphs.
       
   365 
       
   366     """
       
   367 
       
   368     def __init__(self, file=None, maxcol=72):
       
   369         self.file = file or sys.stdout
       
   370         self.maxcol = maxcol
       
   371         NullWriter.__init__(self)
       
   372         self.reset()
       
   373 
       
   374     def reset(self):
       
   375         self.col = 0
       
   376         self.atbreak = 0
       
   377 
       
   378     def send_paragraph(self, blankline):
       
   379         self.file.write('\n'*blankline)
       
   380         self.col = 0
       
   381         self.atbreak = 0
       
   382 
       
   383     def send_line_break(self):
       
   384         self.file.write('\n')
       
   385         self.col = 0
       
   386         self.atbreak = 0
       
   387 
       
   388     def send_hor_rule(self, *args, **kw):
       
   389         self.file.write('\n')
       
   390         self.file.write('-'*self.maxcol)
       
   391         self.file.write('\n')
       
   392         self.col = 0
       
   393         self.atbreak = 0
       
   394 
       
   395     def send_literal_data(self, data):
       
   396         self.file.write(data)
       
   397         i = data.rfind('\n')
       
   398         if i >= 0:
       
   399             self.col = 0
       
   400             data = data[i+1:]
       
   401         data = data.expandtabs()
       
   402         self.col = self.col + len(data)
       
   403         self.atbreak = 0
       
   404 
       
   405     def send_flowing_data(self, data):
       
   406         if not data: return
       
   407         atbreak = self.atbreak or data[0].isspace()
       
   408         col = self.col
       
   409         maxcol = self.maxcol
       
   410         write = self.file.write
       
   411         for word in data.split():
       
   412             if atbreak:
       
   413                 if col + len(word) >= maxcol:
       
   414                     write('\n')
       
   415                     col = 0
       
   416                 else:
       
   417                     write(' ')
       
   418                     col = col + 1
       
   419             write(word)
       
   420             col = col + len(word)
       
   421             atbreak = 1
       
   422         self.col = col
       
   423         self.atbreak = data[-1].isspace()
       
   424 
       
   425 
       
   426 def test(file = None):
       
   427     w = DumbWriter()
       
   428     f = AbstractFormatter(w)
       
   429     if file is not None:
       
   430         fp = open(file)
       
   431     elif sys.argv[1:]:
       
   432         fp = open(sys.argv[1])
       
   433     else:
       
   434         fp = sys.stdin
       
   435     for line in fp:
       
   436         if line == '\n':
       
   437             f.end_paragraph(1)
       
   438         else:
       
   439             f.add_flowing_data(line)
       
   440     f.end_paragraph(0)
       
   441 
       
   442 
       
   443 if __name__ == '__main__':
       
   444     test()