Back to index

moin  1.9.0~rc2
dom_xml.py
Go to the documentation of this file.
00001 # -*- coding: iso-8859-1 -*-
00002 """
00003     MoinMoin - DOM XML Formatter
00004 
00005     @copyright: 2000-2004 by Juergen Hermann <jh@web.de>
00006     @license: GNU GPL, see COPYING for details.
00007 """
00008 
00009 line_anchors = True
00010 
00011 from xml.dom import minidom
00012 from MoinMoin import wikiutil
00013 from MoinMoin.formatter import FormatterBase
00014 
00015 #def print_dom(element, indent=''):
00016 #    print indent + element.tagName
00017 #    for child in element.get
00018 
00019 class Formatter(FormatterBase):
00020     """ This defines the output interface used all over the rest of the code.
00021 
00022         Note that no other means should be used to generate _content_ output,
00023         while navigational elements (HTML page header/footer) and the like
00024         can be printed directly without violating output abstraction.
00025     """
00026     hardspace = ' '
00027 
00028     # those tags will be automatically reopened if they were auto-closed due to
00029     # an enclosing tag being closed:
00030     format_tags = ['b', 'em', 'highlight', 'sup', 'sub', 'strike', 'code', 'u']
00031 
00032     # those tags want a <p> around them:
00033     need_p = [] # format_tags[:]
00034     need_p.extend(['ol', 'a', 'pagelink', 'interwiki', 'macro']) # XXX add more tags
00035 
00036     # those tags inhibit auto-generation of a <p> after them:
00037     no_p_after = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'ol', 'ul', 'pre',
00038                   'small', 'big', 'table', 'td', 'tr', 'dt',
00039                   'codearea', 'codeline', 'codetoken',
00040                   'sysmesg']
00041 
00042     # if key tag is opened, auto-close all tags in value list if they are open
00043     close_on_open = {
00044         'h1': ['p'],
00045         'li': ['li'],
00046         'p': ['p'],
00047         #'pre': ['p'],
00048         }
00049 
00050     for i in xrange(2, 7):
00051         close_on_open['h%i' % i] = close_on_open['h1']
00052 
00053     # if key tag is closed, auto-close all tags in value list if they are open
00054     close_on_close = {
00055         'table': ['td', 'tr'],
00056         'td': ['tr'], # XXX WTF? this doesn't look correct.
00057         'tr': ['td'],
00058         'ol': ['li'],
00059         'ul': ['li'],
00060         }
00061 
00062     # FIXME - this overrides the values defined above
00063     close_on_open = {}
00064     close_on_close = {}
00065 
00066     def __init__(self, request, **kw):
00067         FormatterBase.__init__(self, request, **kw)
00068 
00069         self.document = minidom.Document()
00070         self.document.documentElement = self.document.createElement('xml')
00071         self.position = self.document.documentElement
00072         self.tag_stack = [('xml', {})]
00073 
00074     def setPage(self, page):
00075         self.page = page
00076 
00077     def _open_tag(self, tag, **attrs):
00078         """ low level function: opens tag right now
00079 
00080         @param tag: tag name, string
00081         @param attrs: attributes keywords, ascii or unicode
00082         """
00083         if tag == 'p':
00084             FormatterBase.paragraph(self, 1)
00085         self.tag_stack.append((tag, attrs))
00086         node = self.document.createElement(tag)
00087         for name, value in attrs.items():
00088             if value:
00089                 node.setAttribute(name, unicode(value))
00090         self.position.appendChild(node)
00091         self.position = node
00092         return ''
00093 
00094     def _close_tag(self, tag):
00095         """ low level function: closes tag right now
00096             must be the last opened tag!!!
00097         """
00098         if tag == 'p':
00099             FormatterBase.paragraph(self, 0)
00100         if self.tag_stack[-1][0] != tag:
00101             raise ValueError, "closing of <%s> expected, but <%s> closed" % (self.tag_stack[-1][0], tag)
00102         self.position = self.position.parentNode
00103         return self.tag_stack.pop()
00104 
00105     def _add_tag(self, tag, **attrs):
00106         """ low level function: insert self closing tag right now
00107             does check_p
00108         """
00109         self._check_p(tag)
00110         node = self.document.createElement(tag)
00111         for name, value in attrs.items():
00112             if value:
00113                 node.setAttribute(name, str(value))
00114         self.position.appendChild(node)
00115         return ''
00116 
00117     def _text(self, text):
00118         if text.strip():
00119             self._check_p()
00120             self.position.appendChild(self.document.createTextNode(text))
00121         return ''
00122 
00123     def _set_tag(self, tag, on, **attrs):
00124         if on:
00125             close_on_open = self.close_on_open.get(tag, [])
00126             tags_to_reopen = []
00127             while True:
00128                 last_tag = self.tag_stack[-1][0]
00129                 if last_tag in close_on_open:
00130                     self._close_tag(last_tag)
00131                 elif last_tag in self.format_tags:
00132                     tags_to_reopen.append(self._close_tag(last_tag))
00133                 else:
00134                     break
00135             # XXX check if enclosing tag is ok
00136 
00137             if tag in self.need_p:
00138                 self._check_p(tag)
00139 
00140             self._open_tag(tag, **attrs)
00141             tags_to_reopen.reverse()
00142             for tag_name, args in tags_to_reopen:
00143                 self._open_tag(tag_name, **args)
00144         else:
00145             tags_to_reopen = []
00146             close_on_close = self.close_on_close.get(tag, [])
00147             # walk up
00148             while self.tag_stack:
00149                 # collect format tags
00150                 last_tag = self.tag_stack[-1][0]
00151                 if last_tag == tag:
00152                     break
00153                 elif last_tag in close_on_close:
00154                     self._close_tag(last_tag)
00155                 elif last_tag in self.format_tags:
00156                     tags_to_reopen.append(self._close_tag(last_tag))
00157                 else:
00158                     self.request.write("tag_stack: %r\n" % self.tag_stack)
00159                     self.request.write(self.document.documentElement.toprettyxml(" "))
00160                     raise ValueError, "closing of <%s> expected, but <%s> closed" % (last_tag, tag)
00161             self._close_tag(tag)
00162             tags_to_reopen.reverse()
00163             for tag_name, args in tags_to_reopen:
00164                 self._open_tag(tag_name, **args)
00165         return ''
00166 
00167     def _check_p(self, opening_tag=None):
00168         if (opening_tag is not None) and (opening_tag not in self.need_p):
00169             return
00170         for tag in self.tag_stack:
00171             if tag[0] in self.no_p_after:
00172                 return
00173         if self.in_p:
00174             return
00175         self._open_tag('p', type=str(opening_tag))
00176 
00177     def sysmsg(self, on, **kw):
00178         """ Emit a system message (embed it into the page).
00179 
00180             Normally used to indicate disabled options, or invalid markup.
00181         """
00182         return self._set_tag('sysmesg', on, **kw)
00183 
00184     def startDocument(self, pagename):
00185         return ''
00186 
00187     def endDocument(self):
00188         #return self.document.documentElement.toxml()
00189         return self.document.documentElement.toprettyxml("  ")
00190 
00191     def lang(self, on, lang_name):
00192         return self._set_tag('lang', on, value=lang_name)
00193 
00194     def pagelink(self, on, pagename='', page=None, **kw):
00195         FormatterBase.pagelink(self, pagename, page, **kw)
00196         if not pagename and page is not None:
00197             pagename = page.page_name
00198         kw['pagename'] = pagename
00199         return self._set_tag('pagelink', on, **kw)
00200 
00201     def interwikilink(self, on, interwiki='', pagename='', **kw):
00202         kw['wiki'] = interwiki
00203         kw['pagename'] = pagename
00204         return self._set_tag('interwiki', on, **kw)
00205 
00206     def macro(self, macro_obj, name, args, markup=None):
00207         # call the macro
00208         return self._add_tag('macro', name=name, args=(args or ''))
00209 
00210     def parser(self, parser_name, lines):
00211         """ parser_name MUST be valid!
00212             writes out the result instead of returning it!
00213         """
00214         node = self.document.createElement('parser')
00215         node.setAttribute('name', parser_name)
00216         node.appendChild(self.document.createTextNode('\n'.join(lines)))
00217         return (self._set_tag('parser', True, name=parser_name) +
00218                 self.text('\n'.join(lines)) +
00219                 self._set_tag('parser', False))
00220 
00221     def url(self, on, url='', css=None, **kw):
00222         kw['href'] = str(url)
00223         if css:
00224             kw['class'] = str(css)
00225         return self._set_tag('a', on, **kw)
00226 
00227     def attachment_link(self, on, url=None, **kw):
00228         kw['href'] = url
00229         kw['type'] = 'link'
00230         if on:
00231             return self._set_tag('attachment', 1, **kw)
00232         else:
00233             return self._set_tag('attachment', 0, **kw)
00234 
00235     def attachment_image(self, url, **kw):
00236         kw['href'] = url
00237         kw['type'] = 'image'
00238         return self._add_tag('attachment', **kw)
00239 
00240     def attachment_drawing(self, url, **kw):
00241         kw['href'] = url
00242         kw['type'] = 'drawing'
00243         return self._add_tag('attachment', **kw)
00244 
00245     def attachment_inlined(self, url, **kw):
00246         kw['href'] = url
00247         kw['type'] = 'inline'
00248         return self._add_tag('attachment', **kw)
00249 
00250     def rule(self, size=0, **kw):
00251         return self._add_tag('hr', size=str(size))
00252 
00253     def icon(self, type):
00254         return self._add_tag('icon', type=type)
00255 
00256     def smiley(self, type):
00257         return self._add_tag('smiley', type=type)
00258 
00259     def strong(self, on, **kw):
00260         return self._set_tag('b', on)
00261 
00262     def emphasis(self, on, **kw):
00263         return self._set_tag('em', on)
00264 
00265     def highlight(self, on, **kw):
00266         return self._set_tag('highlight', on)
00267 
00268     def number_list(self, on, type=None, start=None, **kw):
00269         return self._set_tag('ol', on, type=type, start=start)
00270 
00271     def bullet_list(self, on, **kw):
00272         return self._set_tag('ul', on)
00273 
00274     def listitem(self, on, **kw):
00275         return self._set_tag('li', on)
00276 
00277     def sup(self, on, **kw):
00278         return self._set_tag('sup', on)
00279 
00280     def sub(self, on, **kw):
00281         return self._set_tag('sub', on)
00282 
00283     def strike(self, on, **kw):
00284         return self._set_tag('strike', on)
00285 
00286     def code(self, on, **kw):
00287         return self._set_tag('code', on)
00288 
00289     def preformatted(self, on, **kw):
00290         self.in_pre = on != 0
00291         return self._set_tag('pre', on)
00292 
00293     def paragraph(self, on, **kw):
00294         FormatterBase.paragraph(self, on)
00295         return self._set_tag('p', on)
00296 
00297     def linebreak(self, preformatted=1):
00298         if self.tag_stack[-1][0] == 'pre':
00299             return self.text('\n')
00300         else:
00301             return self._add_tag('br')
00302 
00303     def heading(self, on, depth, **kw):
00304         return self._set_tag('h%d' %depth, on, **kw)
00305 
00306     def _check_attrs(self, attrs):
00307         result = {}
00308         for name, value in attrs.iteritems():
00309             result[str(name)] = value
00310         return result
00311 
00312     def table(self, on, attrs={}, **kw):
00313         return self._set_tag('table', on, **self._check_attrs(attrs))
00314 
00315     def table_row(self, on, attrs={}, **kw):
00316         return self._set_tag('tr', on, **self._check_attrs(attrs))
00317 
00318     def table_cell(self, on, attrs={}, **kw):
00319         return self._set_tag('td', on, **self._check_attrs(attrs))
00320 
00321     def anchordef(self, name):
00322         return self._add_tag('anchor', name=name)
00323 
00324     def anchorlink(self, on, name, **kw):
00325         return self.url(on, "#" + name, **kw)
00326 
00327     def line_anchordef(self, lineno):
00328         if line_anchors:
00329             return self.anchordef("line-%d" % lineno)
00330         else:
00331             return ''
00332 
00333     def underline(self, on, **kw):
00334         return self._set_tag('u', on)
00335 
00336     def definition_list(self, on, **kw):
00337         return self._set_tag('dl', on)
00338 
00339     def definition_term(self, on, compact=0, **kw):
00340         # XXX may be not correct
00341         # self._langAttr() missing
00342         if compact and on:
00343             return self._set_tag('dt compact', on)
00344         else:
00345             return self._set_tag('dt', on)
00346 
00347     def definition_desc(self, on, **kw):
00348         # self._langAttr() missing
00349         return self._set_tag('dd', on)
00350 
00351     def image(self, src=None, **kw):
00352         """ Take HTML <IMG> tag attributes in `attr`.
00353 
00354             Attribute names have to be lowercase!
00355         """
00356         if src:
00357             kw['src'] = src
00358         return self._add_tag('img', **kw)
00359 
00360     def transclusion(self, on, **kw):
00361         return self._set_tag('object', on, **kw)
00362 
00363     def transclusion_param(self, **kw):
00364         return self._add_tag('param', **kw)
00365 
00366     def escapedText(self, text, **kw):
00367         return wikiutil.escape(text)
00368 
00369     def small(self, on, **kw):
00370         return self._set_tag('small', on)
00371 
00372     def big(self, on, **kw):
00373         return self._set_tag('big', on)
00374 
00375     def code_area(self, on, code_id, code_type='code', show=0, start=-1, step=-1, msg=None):
00376         kw = {'id': code_id,
00377               'type': code_type,
00378               'show': show,
00379              }
00380         if start != -1:
00381             kw['start'] = start
00382         if step != -1:
00383             kw['step'] = step
00384         return self._set_tag('codearea', on, **kw)
00385 
00386     def code_line(self, on):
00387         return self._set_tag('codeline', on)
00388 
00389     def code_token(self, on, tok_type):
00390         return self._set_tag('codetoken', on, type=tok_type)
00391 
00392