root/galaxy-central/eggs/docutils-0.4-py2.6.egg/docutils/nodes.py

リビジョン 3, 57.5 KB (コミッタ: kohda, 14 年 前)

Install Unix tools  http://hannonlab.cshl.edu/galaxy_unix_tools/galaxy.html

行番号 
1# Author: David Goodger
2# Contact: goodger@users.sourceforge.net
3# Revision: $Revision: 4242 $
4# Date: $Date: 2006-01-06 00:28:53 +0100 (Fri, 06 Jan 2006) $
5# Copyright: This module has been placed in the public domain.
6
7"""
8Docutils document tree element class library.
9
10Classes in CamelCase are abstract base classes or auxiliary classes. The one
11exception is `Text`, for a text (PCDATA) node; uppercase is used to
12differentiate from element classes.  Classes in lower_case_with_underscores
13are element classes, matching the XML element generic identifiers in the DTD_.
14
15The position of each node (the level at which it can occur) is significant and
16is represented by abstract base classes (`Root`, `Structural`, `Body`,
17`Inline`, etc.).  Certain transformations will be easier because we can use
18``isinstance(node, base_class)`` to determine the position of the node in the
19hierarchy.
20
21.. _DTD: http://docutils.sourceforge.net/docs/ref/docutils.dtd
22"""
23
24__docformat__ = 'reStructuredText'
25
26import sys
27import os
28import re
29import warnings
30from types import IntType, SliceType, StringType, UnicodeType, \
31     TupleType, ListType, ClassType
32from UserString import UserString
33
34
35# ==============================
36#  Functional Node Base Classes
37# ==============================
38
39class Node:
40
41    """Abstract base class of nodes in a document tree."""
42
43    parent = None
44    """Back-reference to the Node immediately containing this Node."""
45
46    document = None
47    """The `document` node at the root of the tree containing this Node."""
48
49    source = None
50    """Path or description of the input source which generated this Node."""
51
52    line = None
53    """The line number (1-based) of the beginning of this Node in `source`."""
54
55    def __nonzero__(self):
56        """
57        Node instances are always true, even if they're empty.  A node is more
58        than a simple container.  Its boolean "truth" does not depend on
59        having one or more subnodes in the doctree.
60
61        Use `len()` to check node length.  Use `None` to represent a boolean
62        false value.
63        """
64        return 1
65
66    def asdom(self, dom=None):
67        """Return a DOM **fragment** representation of this Node."""
68        if dom is None:
69            import xml.dom.minidom as dom
70        domroot = dom.Document()
71        return self._dom_node(domroot)
72
73    def pformat(self, indent='    ', level=0):
74        """
75        Return an indented pseudo-XML representation, for test purposes.
76
77        Override in subclasses.
78        """
79        raise NotImplementedError
80
81    def copy(self):
82        """Return a copy of self."""
83        raise NotImplementedError
84
85    def deepcopy(self):
86        """Return a deep copy of self (also copying children)."""
87        raise NotImplementedError
88
89    def setup_child(self, child):
90        child.parent = self
91        if self.document:
92            child.document = self.document
93            if child.source is None:
94                child.source = self.document.current_source
95            if child.line is None:
96                child.line = self.document.current_line
97
98    def walk(self, visitor):
99        """
100        Traverse a tree of `Node` objects, calling the
101        `dispatch_visit()` method of `visitor` when entering each
102        node.  (The `walkabout()` method is similar, except it also
103        calls the `dispatch_departure()` method before exiting each
104        node.)
105
106        This tree traversal supports limited in-place tree
107        modifications.  Replacing one node with one or more nodes is
108        OK, as is removing an element.  However, if the node removed
109        or replaced occurs after the current node, the old node will
110        still be traversed, and any new nodes will not.
111
112        Within ``visit`` methods (and ``depart`` methods for
113        `walkabout()`), `TreePruningException` subclasses may be raised
114        (`SkipChildren`, `SkipSiblings`, `SkipNode`, `SkipDeparture`).
115
116        Parameter `visitor`: A `NodeVisitor` object, containing a
117        ``visit`` implementation for each `Node` subclass encountered.
118        """
119        visitor.document.reporter.debug(
120            'docutils.nodes.Node.walk calling dispatch_visit for %s'
121            % self.__class__.__name__)
122        try:
123            visitor.dispatch_visit(self)
124        except (SkipChildren, SkipNode):
125            return
126        except SkipDeparture:           # not applicable; ignore
127            pass
128        children = self.children
129        try:
130            for child in children[:]:
131                child.walk(visitor)
132        except SkipSiblings:
133            pass
134
135    def walkabout(self, visitor):
136        """
137        Perform a tree traversal similarly to `Node.walk()` (which
138        see), except also call the `dispatch_departure()` method
139        before exiting each node.
140
141        Parameter `visitor`: A `NodeVisitor` object, containing a
142        ``visit`` and ``depart`` implementation for each `Node`
143        subclass encountered.
144        """
145        call_depart = 1
146        visitor.document.reporter.debug(
147            'docutils.nodes.Node.walkabout calling dispatch_visit for %s'
148            % self.__class__.__name__)
149        try:
150            try:
151                visitor.dispatch_visit(self)
152            except SkipNode:
153                return
154            except SkipDeparture:
155                call_depart = 0
156            children = self.children
157            try:
158                for child in children[:]:
159                    child.walkabout(visitor)
160            except SkipSiblings:
161                pass
162        except SkipChildren:
163            pass
164        if call_depart:
165            visitor.document.reporter.debug(
166                'docutils.nodes.Node.walkabout calling dispatch_departure '
167                'for %s' % self.__class__.__name__)
168            visitor.dispatch_departure(self)
169
170    def traverse(self, condition=None,
171                 include_self=1, descend=1, siblings=0, ascend=0):
172        """
173        Return an iterable containing
174
175        * self (if include_self is true)
176        * all descendants in tree traversal order (if descend is true)
177        * all siblings (if siblings is true) and their descendants (if
178          also descend is true)
179        * the siblings of the parent (if ascend is true) and their
180          descendants (if also descend is true), and so on
181
182        If `condition` is not None, the iterable contains only nodes
183        for which ``condition(node)`` is true.  If `condition` is a
184        node class ``cls``, it is equivalent to a function consisting
185        of ``return isinstance(node, cls)``.
186
187        If ascend is true, assume siblings to be true as well.
188
189        For example, given the following tree::
190
191            <paragraph>
192                <emphasis>      <--- emphasis.traverse() and
193                    <strong>    <--- strong.traverse() are called.
194                        Foo
195                    Bar
196                <reference name="Baz" refid="baz">
197                    Baz
198
199        Then list(emphasis.traverse()) equals ::
200
201            [<emphasis>, <strong>, <#text: Foo>, <#text: Bar>]
202
203        and list(strong.traverse(ascend=1)) equals ::
204
205            [<strong>, <#text: Foo>, <#text: Bar>, <reference>, <#text: Baz>]
206        """
207        r = []
208        if ascend:
209            siblings=1
210        if isinstance(condition, ClassType):
211            node_class = condition
212            def condition(node, node_class=node_class):
213                return isinstance(node, node_class)
214        if include_self and (condition is None or condition(self)):
215            r.append(self)
216        if descend and len(self.children):
217            for child in self:
218                r.extend(child.traverse(
219                    include_self=1, descend=1, siblings=0, ascend=0,
220                    condition=condition))
221        if siblings or ascend:
222            node = self
223            while node.parent:
224                index = node.parent.index(node)
225                for sibling in node.parent[index+1:]:
226                    r.extend(sibling.traverse(include_self=1, descend=descend,
227                                              siblings=0, ascend=0,
228                                              condition=condition))
229                if not ascend:
230                    break
231                else:
232                    node = node.parent
233        return r
234
235    def next_node(self, condition=None,
236                  include_self=0, descend=1, siblings=0, ascend=0):
237        """
238        Return the first node in the iterable returned by traverse(),
239        or None if the iterable is empty.
240
241        Parameter list is the same as of traverse.  Note that
242        include_self defaults to 0, though.
243        """
244        iterable = self.traverse(condition=condition,
245                                 include_self=include_self, descend=descend,
246                                 siblings=siblings, ascend=ascend)
247        try:
248            return iterable[0]
249        except IndexError:
250            return None
251
252class Text(Node, UserString):
253
254    """
255    Instances are terminal nodes (leaves) containing text only; no child
256    nodes or attributes.  Initialize by passing a string to the constructor.
257    Access the text itself with the `astext` method.
258    """
259
260    tagname = '#text'
261
262    children = ()
263    """Text nodes have no children, and cannot have children."""
264
265    def __init__(self, data, rawsource=''):
266        UserString.__init__(self, data)
267
268        self.rawsource = rawsource
269        """The raw text from which this element was constructed."""
270
271    def __repr__(self):
272        data = repr(self.data)
273        if len(data) > 70:
274            data = repr(self.data[:64] + ' ...')
275        return '<%s: %s>' % (self.tagname, data)
276
277    def __len__(self):
278        return len(self.data)
279
280    def shortrepr(self):
281        data = repr(self.data)
282        if len(data) > 20:
283            data = repr(self.data[:16] + ' ...')
284        return '<%s: %s>' % (self.tagname, data)
285
286    def _dom_node(self, domroot):
287        return domroot.createTextNode(self.data)
288
289    def astext(self):
290        return self.data
291
292    def copy(self):
293        return self.__class__(self.data)
294
295    def deepcopy(self):
296        return self.copy()
297
298    def pformat(self, indent='    ', level=0):
299        result = []
300        indent = indent * level
301        for line in self.data.splitlines():
302            result.append(indent + line + '\n')
303        return ''.join(result)
304
305
306class Element(Node):
307
308    """
309    `Element` is the superclass to all specific elements.
310
311    Elements contain attributes and child nodes.  Elements emulate
312    dictionaries for attributes, indexing by attribute name (a string).  To
313    set the attribute 'att' to 'value', do::
314
315        element['att'] = 'value'
316
317    There are two special attributes: 'ids' and 'names'.  Both are
318    lists of unique identifiers, and names serve as human interfaces
319    to IDs.  Names are case- and whitespace-normalized (see the
320    fully_normalize_name() function), and IDs conform to the regular
321    expression ``[a-z](-?[a-z0-9]+)*`` (see the make_id() function).
322
323    Elements also emulate lists for child nodes (element nodes and/or text
324    nodes), indexing by integer.  To get the first child node, use::
325
326        element[0]
327
328    Elements may be constructed using the ``+=`` operator.  To add one new
329    child node to element, do::
330
331        element += node
332
333    This is equivalent to ``element.append(node)``.
334
335    To add a list of multiple child nodes at once, use the same ``+=``
336    operator::
337
338        element += [node1, node2]
339
340    This is equivalent to ``element.extend([node1, node2])``.
341    """
342
343    list_attributes = ('ids', 'classes', 'names', 'dupnames', 'backrefs')
344    """List attributes, automatically initialized to empty lists for
345    all nodes."""
346
347    tagname = None
348    """The element generic identifier. If None, it is set as an instance
349    attribute to the name of the class."""
350
351    child_text_separator = '\n\n'
352    """Separator for child nodes, used by `astext()` method."""
353
354    def __init__(self, rawsource='', *children, **attributes):
355        self.rawsource = rawsource
356        """The raw text from which this element was constructed."""
357
358        self.children = []
359        """List of child nodes (elements and/or `Text`)."""
360
361        self.extend(children)           # maintain parent info
362
363        self.attributes = {}
364        """Dictionary of attribute {name: value}."""
365
366        # Initialize list attributes.
367        for att in self.list_attributes:
368            self.attributes[att] = []
369
370        for att, value in attributes.items():
371            att = att.lower()
372            if att in self.list_attributes:
373                # mutable list; make a copy for this node
374                self.attributes[att] = value[:]
375            else:
376                self.attributes[att] = value
377
378        if self.tagname is None:
379            self.tagname = self.__class__.__name__
380
381    def _dom_node(self, domroot):
382        element = domroot.createElement(self.tagname)
383        for attribute, value in self.attlist():
384            if isinstance(value, ListType):
385                value = ' '.join([serial_escape('%s' % v) for v in value])
386            element.setAttribute(attribute, '%s' % value)
387        for child in self.children:
388            element.appendChild(child._dom_node(domroot))
389        return element
390
391    def __repr__(self):
392        data = ''
393        for c in self.children:
394            data += c.shortrepr()
395            if len(data) > 60:
396                data = data[:56] + ' ...'
397                break
398        if self['names']:
399            return '<%s "%s": %s>' % (self.__class__.__name__,
400                                      '; '.join(self['names']), data)
401        else:
402            return '<%s: %s>' % (self.__class__.__name__, data)
403
404    def shortrepr(self):
405        if self['names']:
406            return '<%s "%s"...>' % (self.__class__.__name__,
407                                     '; '.join(self['names']))
408        else:
409            return '<%s...>' % self.tagname
410
411    def __str__(self):
412        return self.__unicode__().encode('raw_unicode_escape')
413
414    def __unicode__(self):
415        if self.children:
416            return u'%s%s%s' % (self.starttag(),
417                                 ''.join([str(c) for c in self.children]),
418                                 self.endtag())
419        else:
420            return self.emptytag()
421
422    def starttag(self):
423        parts = [self.tagname]
424        for name, value in self.attlist():
425            if value is None:           # boolean attribute
426                parts.append(name)
427            elif isinstance(value, ListType):
428                values = [serial_escape('%s' % v) for v in value]
429                parts.append('%s="%s"' % (name, ' '.join(values)))
430            else:
431                parts.append('%s="%s"' % (name, value))
432        return '<%s>' % ' '.join(parts)
433
434    def endtag(self):
435        return '</%s>' % self.tagname
436
437    def emptytag(self):
438        return u'<%s/>' % ' '.join([self.tagname] +
439                                    ['%s="%s"' % (n, v)
440                                     for n, v in self.attlist()])
441
442    def __len__(self):
443        return len(self.children)
444
445    def __getitem__(self, key):
446        if isinstance(key, UnicodeType) or isinstance(key, StringType):
447            return self.attributes[key]
448        elif isinstance(key, IntType):
449            return self.children[key]
450        elif isinstance(key, SliceType):
451            assert key.step in (None, 1), 'cannot handle slice with stride'
452            return self.children[key.start:key.stop]
453        else:
454            raise TypeError, ('element index must be an integer, a slice, or '
455                              'an attribute name string')
456
457    def __setitem__(self, key, item):
458        if isinstance(key, UnicodeType) or isinstance(key, StringType):
459            self.attributes[str(key)] = item
460        elif isinstance(key, IntType):
461            self.setup_child(item)
462            self.children[key] = item
463        elif isinstance(key, SliceType):
464            assert key.step in (None, 1), 'cannot handle slice with stride'
465            for node in item:
466                self.setup_child(node)
467            self.children[key.start:key.stop] = item
468        else:
469            raise TypeError, ('element index must be an integer, a slice, or '
470                              'an attribute name string')
471
472    def __delitem__(self, key):
473        if isinstance(key, UnicodeType) or isinstance(key, StringType):
474            del self.attributes[key]
475        elif isinstance(key, IntType):
476            del self.children[key]
477        elif isinstance(key, SliceType):
478            assert key.step in (None, 1), 'cannot handle slice with stride'
479            del self.children[key.start:key.stop]
480        else:
481            raise TypeError, ('element index must be an integer, a simple '
482                              'slice, or an attribute name string')
483
484    def __add__(self, other):
485        return self.children + other
486
487    def __radd__(self, other):
488        return other + self.children
489
490    def __iadd__(self, other):
491        """Append a node or a list of nodes to `self.children`."""
492        if isinstance(other, Node):
493            self.append(other)
494        elif other is not None:
495            self.extend(other)
496        return self
497
498    def astext(self):
499        return self.child_text_separator.join(
500              [child.astext() for child in self.children])
501
502    def non_default_attributes(self):
503        atts = {}
504        for key, value in self.attributes.items():
505            if self.is_not_default(key):
506                atts[key] = value
507        return atts
508
509    def attlist(self):
510        attlist = self.non_default_attributes().items()
511        attlist.sort()
512        return attlist
513
514    def get(self, key, failobj=None):
515        return self.attributes.get(key, failobj)
516
517    def hasattr(self, attr):
518        return self.attributes.has_key(attr)
519
520    def delattr(self, attr):
521        if self.attributes.has_key(attr):
522            del self.attributes[attr]
523
524    def setdefault(self, key, failobj=None):
525        return self.attributes.setdefault(key, failobj)
526
527    has_key = hasattr
528
529    def append(self, item):
530        self.setup_child(item)
531        self.children.append(item)
532
533    def extend(self, item):
534        for node in item:
535            self.append(node)
536
537    def insert(self, index, item):
538        if isinstance(item, Node):
539            self.setup_child(item)
540            self.children.insert(index, item)
541        elif item is not None:
542            self[index:index] = item
543
544    def pop(self, i=-1):
545        return self.children.pop(i)
546
547    def remove(self, item):
548        self.children.remove(item)
549
550    def index(self, item):
551        return self.children.index(item)
552
553    def is_not_default(self, key):
554        if self[key] == [] and key in self.list_attributes:
555            return 0
556        else:
557            return 1
558
559    def update_basic_atts(self, dict):
560        """
561        Update basic attributes ('ids', 'names', 'classes',
562        'dupnames', but not 'source') from node or dictionary `dict`.
563        """
564        if isinstance(dict, Node):
565            dict = dict.attributes
566        for att in ('ids', 'classes', 'names', 'dupnames'):
567            for value in dict.get(att, []):
568                if not value in self[att]:
569                    self[att].append(value)
570
571    def clear(self):
572        self.children = []
573
574    def replace(self, old, new):
575        """Replace one child `Node` with another child or children."""
576        index = self.index(old)
577        if isinstance(new, Node):
578            self.setup_child(new)
579            self[index] = new
580        elif new is not None:
581            self[index:index+1] = new
582
583    def replace_self(self, new):
584        """
585        Replace `self` node with `new`, where `new` is a node or a
586        list of nodes.
587        """
588        update = new
589        if not isinstance(new, Node):
590            # `new` is a list; update first child.
591            try:
592                update = new[0]
593            except IndexError:
594                update = None
595        if isinstance(update, Element):
596            update.update_basic_atts(self)
597        else:
598            # `update` is a Text node or `new` is an empty list.
599            # Assert that we aren't losing any attributes.
600            for att in ('ids', 'names', 'classes', 'dupnames'):
601                assert not self[att], \
602                       'Losing "%s" attribute: %s' % (att, self[att])
603        self.parent.replace(self, new)
604
605    def first_child_matching_class(self, childclass, start=0, end=sys.maxint):
606        """
607        Return the index of the first child whose class exactly matches.
608
609        Parameters:
610
611        - `childclass`: A `Node` subclass to search for, or a tuple of `Node`
612          classes. If a tuple, any of the classes may match.
613        - `start`: Initial index to check.
614        - `end`: Initial index to *not* check.
615        """
616        if not isinstance(childclass, TupleType):
617            childclass = (childclass,)
618        for index in range(start, min(len(self), end)):
619            for c in childclass:
620                if isinstance(self[index], c):
621                    return index
622        return None
623
624    def first_child_not_matching_class(self, childclass, start=0,
625                                       end=sys.maxint):
626        """
627        Return the index of the first child whose class does *not* match.
628
629        Parameters:
630
631        - `childclass`: A `Node` subclass to skip, or a tuple of `Node`
632          classes. If a tuple, none of the classes may match.
633        - `start`: Initial index to check.
634        - `end`: Initial index to *not* check.
635        """
636        if not isinstance(childclass, TupleType):
637            childclass = (childclass,)
638        for index in range(start, min(len(self), end)):
639            for c in childclass:
640                if isinstance(self.children[index], c):
641                    break
642            else:
643                return index
644        return None
645
646    def pformat(self, indent='    ', level=0):
647        return ''.join(['%s%s\n' % (indent * level, self.starttag())] +
648                       [child.pformat(indent, level+1)
649                        for child in self.children])
650
651    def copy(self):
652        return self.__class__(**self.attributes)
653
654    def deepcopy(self):
655        copy = self.copy()
656        copy.extend([child.deepcopy() for child in self.children])
657        return copy
658
659    def set_class(self, name):
660        """Add a new class to the "classes" attribute."""
661        warnings.warn('docutils.nodes.Element.set_class deprecated; '
662                      "append to Element['classes'] list attribute directly",
663                      DeprecationWarning, stacklevel=2)
664        assert ' ' not in name
665        self['classes'].append(name.lower())
666
667    def note_referenced_by(self, name=None, id=None):
668        """Note that this Element has been referenced by its name
669        `name` or id `id`."""
670        self.referenced = 1
671        # Element.expect_referenced_by_* dictionaries map names or ids
672        # to nodes whose ``referenced`` attribute is set to true as
673        # soon as this node is referenced by the given name or id.
674        # Needed for target propagation.
675        by_name = getattr(self, 'expect_referenced_by_name', {}).get(name)
676        by_id = getattr(self, 'expect_referenced_by_id', {}).get(id)
677        if by_name:
678            assert name is not None
679            by_name.referenced = 1
680        if by_id:
681            assert id is not None
682            by_id.referenced = 1
683
684
685class TextElement(Element):
686
687    """
688    An element which directly contains text.
689
690    Its children are all `Text` or `Inline` subclass nodes.  You can
691    check whether an element's context is inline simply by checking whether
692    its immediate parent is a `TextElement` instance (including subclasses).
693    This is handy for nodes like `image` that can appear both inline and as
694    standalone body elements.
695
696    If passing children to `__init__()`, make sure to set `text` to
697    ``''`` or some other suitable value.
698    """
699
700    child_text_separator = ''
701    """Separator for child nodes, used by `astext()` method."""
702
703    def __init__(self, rawsource='', text='', *children, **attributes):
704        if text != '':
705            textnode = Text(text)
706            Element.__init__(self, rawsource, textnode, *children,
707                              **attributes)
708        else:
709            Element.__init__(self, rawsource, *children, **attributes)
710
711
712class FixedTextElement(TextElement):
713
714    """An element which directly contains preformatted text."""
715
716    def __init__(self, rawsource='', text='', *children, **attributes):
717        TextElement.__init__(self, rawsource, text, *children, **attributes)
718        self.attributes['xml:space'] = 'preserve'
719
720
721# ========
722#  Mixins
723# ========
724
725class Resolvable:
726
727    resolved = 0
728
729
730class BackLinkable:
731
732    def add_backref(self, refid):
733        self['backrefs'].append(refid)
734
735
736# ====================
737#  Element Categories
738# ====================
739
740class Root: pass
741
742class Titular: pass
743
744class PreBibliographic:
745    """Category of Node which may occur before Bibliographic Nodes."""
746
747class Bibliographic: pass
748
749class Decorative(PreBibliographic): pass
750
751class Structural: pass
752
753class Body: pass
754
755class General(Body): pass
756
757class Sequential(Body):
758    """List-like elements."""
759
760class Admonition(Body): pass
761
762class Special(Body):
763    """Special internal body elements."""
764
765class Invisible(PreBibliographic):
766    """Internal elements that don't appear in output."""
767
768class Part: pass
769
770class Inline: pass
771
772class Referential(Resolvable): pass
773
774
775class Targetable(Resolvable):
776
777    referenced = 0
778
779    indirect_reference_name = None
780    """Holds the whitespace_normalized_name (contains mixed case) of a target.
781    Required for MoinMoin/reST compatibility."""
782
783
784class Labeled:
785    """Contains a `label` as its first element."""
786
787
788# ==============
789#  Root Element
790# ==============
791
792class document(Root, Structural, Element):
793
794    """
795    The document root element.
796
797    Do not instantiate this class directly; use
798    `docutils.utils.new_document()` instead.
799    """
800
801    def __init__(self, settings, reporter, *args, **kwargs):
802        Element.__init__(self, *args, **kwargs)
803
804        self.current_source = None
805        """Path to or description of the input source being processed."""
806
807        self.current_line = None
808        """Line number (1-based) of `current_source`."""
809
810        self.settings = settings
811        """Runtime settings data record."""
812
813        self.reporter = reporter
814        """System message generator."""
815
816        self.indirect_targets = []
817        """List of indirect target nodes."""
818
819        self.substitution_defs = {}
820        """Mapping of substitution names to substitution_definition nodes."""
821
822        self.substitution_names = {}
823        """Mapping of case-normalized substitution names to case-sensitive
824        names."""
825
826        self.refnames = {}
827        """Mapping of names to lists of referencing nodes."""
828
829        self.refids = {}
830        """Mapping of ids to lists of referencing nodes."""
831
832        self.nameids = {}
833        """Mapping of names to unique id's."""
834
835        self.nametypes = {}
836        """Mapping of names to hyperlink type (boolean: True => explicit,
837        False => implicit."""
838
839        self.ids = {}
840        """Mapping of ids to nodes."""
841
842        self.footnote_refs = {}
843        """Mapping of footnote labels to lists of footnote_reference nodes."""
844
845        self.citation_refs = {}
846        """Mapping of citation labels to lists of citation_reference nodes."""
847
848        self.autofootnotes = []
849        """List of auto-numbered footnote nodes."""
850
851        self.autofootnote_refs = []
852        """List of auto-numbered footnote_reference nodes."""
853
854        self.symbol_footnotes = []
855        """List of symbol footnote nodes."""
856
857        self.symbol_footnote_refs = []
858        """List of symbol footnote_reference nodes."""
859
860        self.footnotes = []
861        """List of manually-numbered footnote nodes."""
862
863        self.citations = []
864        """List of citation nodes."""
865
866        self.autofootnote_start = 1
867        """Initial auto-numbered footnote number."""
868
869        self.symbol_footnote_start = 0
870        """Initial symbol footnote symbol index."""
871
872        self.id_start = 1
873        """Initial ID number."""
874
875        self.parse_messages = []
876        """System messages generated while parsing."""
877
878        self.transform_messages = []
879        """System messages generated while applying transforms."""
880
881        import docutils.transforms
882        self.transformer = docutils.transforms.Transformer(self)
883        """Storage for transforms to be applied to this document."""
884
885        self.decoration = None
886        """Document's `decoration` node."""
887
888        self.document = self
889
890    def asdom(self, dom=None):
891        """Return a DOM representation of this document."""
892        if dom is None:
893            import xml.dom.minidom as dom
894        domroot = dom.Document()
895        domroot.appendChild(self._dom_node(domroot))
896        return domroot
897
898    def set_id(self, node, msgnode=None):
899        for id in node['ids']:
900            if self.ids.has_key(id) and self.ids[id] is not node:
901                msg = self.reporter.severe('Duplicate ID: "%s".' % id)
902                if msgnode != None:
903                    msgnode += msg
904        if not node['ids']:
905            for name in node['names']:
906                id = self.settings.id_prefix + make_id(name)
907                if id and not self.ids.has_key(id):
908                    break
909            else:
910                id = ''
911                while not id or self.ids.has_key(id):
912                    id = (self.settings.id_prefix +
913                          self.settings.auto_id_prefix + str(self.id_start))
914                    self.id_start += 1
915            node['ids'].append(id)
916        self.ids[id] = node
917        return id
918
919    def set_name_id_map(self, node, id, msgnode=None, explicit=None):
920        """
921        `self.nameids` maps names to IDs, while `self.nametypes` maps names to
922        booleans representing hyperlink type (True==explicit,
923        False==implicit).  This method updates the mappings.
924
925        The following state transition table shows how `self.nameids` ("ids")
926        and `self.nametypes` ("types") change with new input (a call to this
927        method), and what actions are performed ("implicit"-type system
928        messages are INFO/1, and "explicit"-type system messages are ERROR/3):
929
930        ====  =====  ========  ========  =======  ====  =====  =====
931         Old State    Input          Action        New State   Notes
932        -----------  --------  -----------------  -----------  -----
933        ids   types  new type  sys.msg.  dupname  ids   types
934        ====  =====  ========  ========  =======  ====  =====  =====
935        -     -      explicit  -         -        new   True
936        -     -      implicit  -         -        new   False
937        None  False  explicit  -         -        new   True
938        old   False  explicit  implicit  old      new   True
939        None  True   explicit  explicit  new      None  True
940        old   True   explicit  explicit  new,old  None  True   [#]_
941        None  False  implicit  implicit  new      None  False
942        old   False  implicit  implicit  new,old  None  False
943        None  True   implicit  implicit  new      None  True
944        old   True   implicit  implicit  new      old   True
945        ====  =====  ========  ========  =======  ====  =====  =====
946
947        .. [#] Do not clear the name-to-id map or invalidate the old target if
948           both old and new targets are external and refer to identical URIs.
949           The new target is invalidated regardless.
950        """
951        for name in node['names']:
952            if self.nameids.has_key(name):
953                self.set_duplicate_name_id(node, id, name, msgnode, explicit)
954            else:
955                self.nameids[name] = id
956                self.nametypes[name] = explicit
957
958    def set_duplicate_name_id(self, node, id, name, msgnode, explicit):
959        old_id = self.nameids[name]
960        old_explicit = self.nametypes[name]
961        self.nametypes[name] = old_explicit or explicit
962        if explicit:
963            if old_explicit:
964                level = 2
965                if old_id is not None:
966                    old_node = self.ids[old_id]
967                    if node.has_key('refuri'):
968                        refuri = node['refuri']
969                        if old_node['names'] \
970                               and old_node.has_key('refuri') \
971                               and old_node['refuri'] == refuri:
972                            level = 1   # just inform if refuri's identical
973                    if level > 1:
974                        dupname(old_node, name)
975                        self.nameids[name] = None
976                msg = self.reporter.system_message(
977                    level, 'Duplicate explicit target name: "%s".' % name,
978                    backrefs=[id], base_node=node)
979                if msgnode != None:
980                    msgnode += msg
981                dupname(node, name)
982            else:
983                self.nameids[name] = id
984                if old_id is not None:
985                    old_node = self.ids[old_id]
986                    dupname(old_node, name)
987        else:
988            if old_id is not None and not old_explicit:
989                self.nameids[name] = None
990                old_node = self.ids[old_id]
991                dupname(old_node, name)
992            dupname(node, name)
993        if not explicit or (not old_explicit and old_id is not None):
994            msg = self.reporter.info(
995                'Duplicate implicit target name: "%s".' % name,
996                backrefs=[id], base_node=node)
997            if msgnode != None:
998                msgnode += msg
999
1000    def has_name(self, name):
1001        return self.nameids.has_key(name)
1002
1003    # "note" here is an imperative verb: "take note of".
1004    def note_implicit_target(self, target, msgnode=None):
1005        id = self.set_id(target, msgnode)
1006        self.set_name_id_map(target, id, msgnode, explicit=None)
1007
1008    def note_explicit_target(self, target, msgnode=None):
1009        id = self.set_id(target, msgnode)
1010        self.set_name_id_map(target, id, msgnode, explicit=1)
1011
1012    def note_refname(self, node):
1013        self.refnames.setdefault(node['refname'], []).append(node)
1014
1015    def note_refid(self, node):
1016        self.refids.setdefault(node['refid'], []).append(node)
1017
1018    def note_indirect_target(self, target):
1019        self.indirect_targets.append(target)
1020        if target['names']:
1021            self.note_refname(target)
1022
1023    def note_anonymous_target(self, target):
1024        self.set_id(target)
1025
1026    def note_autofootnote(self, footnote):
1027        self.set_id(footnote)
1028        self.autofootnotes.append(footnote)
1029
1030    def note_autofootnote_ref(self, ref):
1031        self.set_id(ref)
1032        self.autofootnote_refs.append(ref)
1033
1034    def note_symbol_footnote(self, footnote):
1035        self.set_id(footnote)
1036        self.symbol_footnotes.append(footnote)
1037
1038    def note_symbol_footnote_ref(self, ref):
1039        self.set_id(ref)
1040        self.symbol_footnote_refs.append(ref)
1041
1042    def note_footnote(self, footnote):
1043        self.set_id(footnote)
1044        self.footnotes.append(footnote)
1045
1046    def note_footnote_ref(self, ref):
1047        self.set_id(ref)
1048        self.footnote_refs.setdefault(ref['refname'], []).append(ref)
1049        self.note_refname(ref)
1050
1051    def note_citation(self, citation):
1052        self.citations.append(citation)
1053
1054    def note_citation_ref(self, ref):
1055        self.set_id(ref)
1056        self.citation_refs.setdefault(ref['refname'], []).append(ref)
1057        self.note_refname(ref)
1058
1059    def note_substitution_def(self, subdef, def_name, msgnode=None):
1060        name = whitespace_normalize_name(def_name)
1061        if self.substitution_defs.has_key(name):
1062            msg = self.reporter.error(
1063                  'Duplicate substitution definition name: "%s".' % name,
1064                  base_node=subdef)
1065            if msgnode != None:
1066                msgnode += msg
1067            oldnode = self.substitution_defs[name]
1068            dupname(oldnode, name)
1069        # keep only the last definition:
1070        self.substitution_defs[name] = subdef
1071        # case-insensitive mapping:
1072        self.substitution_names[fully_normalize_name(name)] = name
1073
1074    def note_substitution_ref(self, subref, refname):
1075        subref['refname'] = whitespace_normalize_name(refname)
1076
1077    def note_pending(self, pending, priority=None):
1078        self.transformer.add_pending(pending, priority)
1079
1080    def note_parse_message(self, message):
1081        self.parse_messages.append(message)
1082
1083    def note_transform_message(self, message):
1084        self.transform_messages.append(message)
1085
1086    def note_source(self, source, offset):
1087        self.current_source = source
1088        if offset is None:
1089            self.current_line = offset
1090        else:
1091            self.current_line = offset + 1
1092
1093    def copy(self):
1094        return self.__class__(self.settings, self.reporter,
1095                              **self.attributes)
1096
1097    def get_decoration(self):
1098        if not self.decoration:
1099            self.decoration = decoration()
1100            index = self.first_child_not_matching_class(Titular)
1101            if index is None:
1102                self.append(self.decoration)
1103            else:
1104                self.insert(index, self.decoration)
1105        return self.decoration
1106
1107
1108# ================
1109#  Title Elements
1110# ================
1111
1112class title(Titular, PreBibliographic, TextElement): pass
1113class subtitle(Titular, PreBibliographic, TextElement): pass
1114class rubric(Titular, TextElement): pass
1115
1116
1117# ========================
1118#  Bibliographic Elements
1119# ========================
1120
1121class docinfo(Bibliographic, Element): pass
1122class author(Bibliographic, TextElement): pass
1123class authors(Bibliographic, Element): pass
1124class organization(Bibliographic, TextElement): pass
1125class address(Bibliographic, FixedTextElement): pass
1126class contact(Bibliographic, TextElement): pass
1127class version(Bibliographic, TextElement): pass
1128class revision(Bibliographic, TextElement): pass
1129class status(Bibliographic, TextElement): pass
1130class date(Bibliographic, TextElement): pass
1131class copyright(Bibliographic, TextElement): pass
1132
1133
1134# =====================
1135#  Decorative Elements
1136# =====================
1137
1138class decoration(Decorative, Element):
1139
1140    def get_header(self):
1141        if not len(self.children) or not isinstance(self.children[0], header):
1142            self.insert(0, header())
1143        return self.children[0]
1144
1145    def get_footer(self):
1146        if not len(self.children) or not isinstance(self.children[-1], footer):
1147            self.append(footer())
1148        return self.children[-1]
1149
1150
1151class header(Decorative, Element): pass
1152class footer(Decorative, Element): pass
1153
1154
1155# =====================
1156#  Structural Elements
1157# =====================
1158
1159class section(Structural, Element): pass
1160
1161
1162class topic(Structural, Element):
1163
1164    """
1165    Topics are terminal, "leaf" mini-sections, like block quotes with titles,
1166    or textual figures.  A topic is just like a section, except that it has no
1167    subsections, and it doesn't have to conform to section placement rules.
1168
1169    Topics are allowed wherever body elements (list, table, etc.) are allowed,
1170    but only at the top level of a section or document.  Topics cannot nest
1171    inside topics, sidebars, or body elements; you can't have a topic inside a
1172    table, list, block quote, etc.
1173    """
1174
1175
1176class sidebar(Structural, Element):
1177
1178    """
1179    Sidebars are like miniature, parallel documents that occur inside other
1180    documents, providing related or reference material.  A sidebar is
1181    typically offset by a border and "floats" to the side of the page; the
1182    document's main text may flow around it.  Sidebars can also be likened to
1183    super-footnotes; their content is outside of the flow of the document's
1184    main text.
1185
1186    Sidebars are allowed wherever body elements (list, table, etc.) are
1187    allowed, but only at the top level of a section or document.  Sidebars
1188    cannot nest inside sidebars, topics, or body elements; you can't have a
1189    sidebar inside a table, list, block quote, etc.
1190    """
1191
1192
1193class transition(Structural, Element): pass
1194
1195
1196# ===============
1197#  Body Elements
1198# ===============
1199
1200class paragraph(General, TextElement): pass
1201class compound(General, Element): pass
1202class container(General, Element): pass
1203class bullet_list(Sequential, Element): pass
1204class enumerated_list(Sequential, Element): pass
1205class list_item(Part, Element): pass
1206class definition_list(Sequential, Element): pass
1207class definition_list_item(Part, Element): pass
1208class term(Part, TextElement): pass
1209class classifier(Part, TextElement): pass
1210class definition(Part, Element): pass
1211class field_list(Sequential, Element): pass
1212class field(Part, Element): pass
1213class field_name(Part, TextElement): pass
1214class field_body(Part, Element): pass
1215
1216
1217class option(Part, Element):
1218
1219    child_text_separator = ''
1220
1221
1222class option_argument(Part, TextElement):
1223
1224    def astext(self):
1225        return self.get('delimiter', ' ') + TextElement.astext(self)
1226
1227
1228class option_group(Part, Element):
1229
1230    child_text_separator = ', '
1231
1232
1233class option_list(Sequential, Element): pass
1234
1235
1236class option_list_item(Part, Element):
1237
1238    child_text_separator = '  '
1239
1240
1241class option_string(Part, TextElement): pass
1242class description(Part, Element): pass
1243class literal_block(General, FixedTextElement): pass
1244class doctest_block(General, FixedTextElement): pass
1245class line_block(General, Element): pass
1246
1247
1248class line(Part, TextElement):
1249
1250    indent = None
1251
1252
1253class block_quote(General, Element): pass
1254class attribution(Part, TextElement): pass
1255class attention(Admonition, Element): pass
1256class caution(Admonition, Element): pass
1257class danger(Admonition, Element): pass
1258class error(Admonition, Element): pass
1259class important(Admonition, Element): pass
1260class note(Admonition, Element): pass
1261class tip(Admonition, Element): pass
1262class hint(Admonition, Element): pass
1263class warning(Admonition, Element): pass
1264class admonition(Admonition, Element): pass
1265class comment(Special, Invisible, FixedTextElement): pass
1266class substitution_definition(Special, Invisible, TextElement): pass
1267class target(Special, Invisible, Inline, TextElement, Targetable): pass
1268class footnote(General, BackLinkable, Element, Labeled, Targetable): pass
1269class citation(General, BackLinkable, Element, Labeled, Targetable): pass
1270class label(Part, TextElement): pass
1271class figure(General, Element): pass
1272class caption(Part, TextElement): pass
1273class legend(Part, Element): pass
1274class table(General, Element): pass
1275class tgroup(Part, Element): pass
1276class colspec(Part, Element): pass
1277class thead(Part, Element): pass
1278class tbody(Part, Element): pass
1279class row(Part, Element): pass
1280class entry(Part, Element): pass
1281
1282
1283class system_message(Special, BackLinkable, PreBibliographic, Element):
1284
1285    """
1286    System message element.
1287
1288    Do not instantiate this class directly; use
1289    ``document.reporter.info/warning/error/severe()`` instead.
1290    """
1291
1292    def __init__(self, message=None, *children, **attributes):
1293        if message:
1294            p = paragraph('', message)
1295            children = (p,) + children
1296        try:
1297            Element.__init__(self, '', *children, **attributes)
1298        except:
1299            print 'system_message: children=%r' % (children,)
1300            raise
1301
1302    def astext(self):
1303        line = self.get('line', '')
1304        return u'%s:%s: (%s/%s) %s' % (self['source'], line, self['type'],
1305                                       self['level'], Element.astext(self))
1306
1307
1308class pending(Special, Invisible, Element):
1309
1310    """
1311    The "pending" element is used to encapsulate a pending operation: the
1312    operation (transform), the point at which to apply it, and any data it
1313    requires.  Only the pending operation's location within the document is
1314    stored in the public document tree (by the "pending" object itself); the
1315    operation and its data are stored in the "pending" object's internal
1316    instance attributes.
1317
1318    For example, say you want a table of contents in your reStructuredText
1319    document.  The easiest way to specify where to put it is from within the
1320    document, with a directive::
1321
1322        .. contents::
1323
1324    But the "contents" directive can't do its work until the entire document
1325    has been parsed and possibly transformed to some extent.  So the directive
1326    code leaves a placeholder behind that will trigger the second phase of its
1327    processing, something like this::
1328
1329        <pending ...public attributes...> + internal attributes
1330
1331    Use `document.note_pending()` so that the
1332    `docutils.transforms.Transformer` stage of processing can run all pending
1333    transforms.
1334    """
1335
1336    def __init__(self, transform, details=None,
1337                 rawsource='', *children, **attributes):
1338        Element.__init__(self, rawsource, *children, **attributes)
1339
1340        self.transform = transform
1341        """The `docutils.transforms.Transform` class implementing the pending
1342        operation."""
1343
1344        self.details = details or {}
1345        """Detail data (dictionary) required by the pending operation."""
1346
1347    def pformat(self, indent='    ', level=0):
1348        internals = [
1349              '.. internal attributes:',
1350              '     .transform: %s.%s' % (self.transform.__module__,
1351                                          self.transform.__name__),
1352              '     .details:']
1353        details = self.details.items()
1354        details.sort()
1355        for key, value in details:
1356            if isinstance(value, Node):
1357                internals.append('%7s%s:' % ('', key))
1358                internals.extend(['%9s%s' % ('', line)
1359                                  for line in value.pformat().splitlines()])
1360            elif value and isinstance(value, ListType) \
1361                  and isinstance(value[0], Node):
1362                internals.append('%7s%s:' % ('', key))
1363                for v in value:
1364                    internals.extend(['%9s%s' % ('', line)
1365                                      for line in v.pformat().splitlines()])
1366            else:
1367                internals.append('%7s%s: %r' % ('', key, value))
1368        return (Element.pformat(self, indent, level)
1369                + ''.join([('    %s%s\n' % (indent * level, line))
1370                           for line in internals]))
1371
1372    def copy(self):
1373        return self.__class__(self.transform, self.details, self.rawsource,
1374                              **self.attributes)
1375
1376
1377class raw(Special, Inline, PreBibliographic, FixedTextElement):
1378
1379    """
1380    Raw data that is to be passed untouched to the Writer.
1381    """
1382
1383    pass
1384
1385
1386# =================
1387#  Inline Elements
1388# =================
1389
1390class emphasis(Inline, TextElement): pass
1391class strong(Inline, TextElement): pass
1392class literal(Inline, TextElement): pass
1393class reference(General, Inline, Referential, TextElement): pass
1394class footnote_reference(Inline, Referential, TextElement): pass
1395class citation_reference(Inline, Referential, TextElement): pass
1396class substitution_reference(Inline, TextElement): pass
1397class title_reference(Inline, TextElement): pass
1398class abbreviation(Inline, TextElement): pass
1399class acronym(Inline, TextElement): pass
1400class superscript(Inline, TextElement): pass
1401class subscript(Inline, TextElement): pass
1402
1403
1404class image(General, Inline, Element):
1405
1406    def astext(self):
1407        return self.get('alt', '')
1408
1409
1410class inline(Inline, TextElement): pass
1411class problematic(Inline, TextElement): pass
1412class generated(Inline, TextElement): pass
1413
1414
1415# ========================================
1416#  Auxiliary Classes, Functions, and Data
1417# ========================================
1418
1419node_class_names = """
1420    Text
1421    abbreviation acronym address admonition attention attribution author
1422        authors
1423    block_quote bullet_list
1424    caption caution citation citation_reference classifier colspec comment
1425        compound contact container copyright
1426    danger date decoration definition definition_list definition_list_item
1427        description docinfo doctest_block document
1428    emphasis entry enumerated_list error
1429    field field_body field_list field_name figure footer
1430        footnote footnote_reference
1431    generated
1432    header hint
1433    image important inline
1434    label legend line line_block list_item literal literal_block
1435    note
1436    option option_argument option_group option_list option_list_item
1437        option_string organization
1438    paragraph pending problematic
1439    raw reference revision row rubric
1440    section sidebar status strong subscript substitution_definition
1441        substitution_reference subtitle superscript system_message
1442    table target tbody term tgroup thead tip title title_reference topic
1443        transition
1444    version
1445    warning""".split()
1446"""A list of names of all concrete Node subclasses."""
1447
1448
1449class NodeVisitor:
1450
1451    """
1452    "Visitor" pattern [GoF95]_ abstract superclass implementation for
1453    document tree traversals.
1454
1455    Each node class has corresponding methods, doing nothing by
1456    default; override individual methods for specific and useful
1457    behaviour.  The `dispatch_visit()` method is called by
1458    `Node.walk()` upon entering a node.  `Node.walkabout()` also calls
1459    the `dispatch_departure()` method before exiting a node.
1460
1461    The dispatch methods call "``visit_`` + node class name" or
1462    "``depart_`` + node class name", resp.
1463
1464    This is a base class for visitors whose ``visit_...`` & ``depart_...``
1465    methods should be implemented for *all* node types encountered (such as
1466    for `docutils.writers.Writer` subclasses).  Unimplemented methods will
1467    raise exceptions.
1468
1469    For sparse traversals, where only certain node types are of interest,
1470    subclass `SparseNodeVisitor` instead.  When (mostly or entirely) uniform
1471    processing is desired, subclass `GenericNodeVisitor`.
1472
1473    .. [GoF95] Gamma, Helm, Johnson, Vlissides. *Design Patterns: Elements of
1474       Reusable Object-Oriented Software*. Addison-Wesley, Reading, MA, USA,
1475       1995.
1476    """
1477
1478    optional = ()
1479    """
1480    Tuple containing node class names (as strings).
1481
1482    No exception will be raised if writers do not implement visit
1483    or departure functions for these node classes.
1484
1485    Used to ensure transitional compatibility with existing 3rd-party writers.
1486    """
1487
1488    def __init__(self, document):
1489        self.document = document
1490
1491    def dispatch_visit(self, node):
1492        """
1493        Call self."``visit_`` + node class name" with `node` as
1494        parameter.  If the ``visit_...`` method does not exist, call
1495        self.unknown_visit.
1496        """
1497        node_name = node.__class__.__name__
1498        method = getattr(self, 'visit_' + node_name, self.unknown_visit)
1499        self.document.reporter.debug(
1500            'docutils.nodes.NodeVisitor.dispatch_visit calling %s for %s'
1501            % (method.__name__, node_name))
1502        return method(node)
1503
1504    def dispatch_departure(self, node):
1505        """
1506        Call self."``depart_`` + node class name" with `node` as
1507        parameter.  If the ``depart_...`` method does not exist, call
1508        self.unknown_departure.
1509        """
1510        node_name = node.__class__.__name__
1511        method = getattr(self, 'depart_' + node_name, self.unknown_departure)
1512        self.document.reporter.debug(
1513            'docutils.nodes.NodeVisitor.dispatch_departure calling %s for %s'
1514            % (method.__name__, node_name))
1515        return method(node)
1516
1517    def unknown_visit(self, node):
1518        """
1519        Called when entering unknown `Node` types.
1520
1521        Raise an exception unless overridden.
1522        """
1523        if  (node.document.settings.strict_visitor
1524             or node.__class__.__name__ not in self.optional):
1525            raise NotImplementedError(
1526                '%s visiting unknown node type: %s'
1527                % (self.__class__, node.__class__.__name__))
1528
1529    def unknown_departure(self, node):
1530        """
1531        Called before exiting unknown `Node` types.
1532
1533        Raise exception unless overridden.
1534        """
1535        if  (node.document.settings.strict_visitor
1536             or node.__class__.__name__ not in self.optional):
1537            raise NotImplementedError(
1538                '%s departing unknown node type: %s'
1539                % (self.__class__, node.__class__.__name__))
1540
1541
1542class SparseNodeVisitor(NodeVisitor):
1543
1544    """
1545    Base class for sparse traversals, where only certain node types are of
1546    interest.  When ``visit_...`` & ``depart_...`` methods should be
1547    implemented for *all* node types (such as for `docutils.writers.Writer`
1548    subclasses), subclass `NodeVisitor` instead.
1549    """
1550
1551
1552class GenericNodeVisitor(NodeVisitor):
1553
1554    """
1555    Generic "Visitor" abstract superclass, for simple traversals.
1556
1557    Unless overridden, each ``visit_...`` method calls `default_visit()`, and
1558    each ``depart_...`` method (when using `Node.walkabout()`) calls
1559    `default_departure()`. `default_visit()` (and `default_departure()`) must
1560    be overridden in subclasses.
1561
1562    Define fully generic visitors by overriding `default_visit()` (and
1563    `default_departure()`) only. Define semi-generic visitors by overriding
1564    individual ``visit_...()`` (and ``depart_...()``) methods also.
1565
1566    `NodeVisitor.unknown_visit()` (`NodeVisitor.unknown_departure()`) should
1567    be overridden for default behavior.
1568    """
1569
1570    def default_visit(self, node):
1571        """Override for generic, uniform traversals."""
1572        raise NotImplementedError
1573
1574    def default_departure(self, node):
1575        """Override for generic, uniform traversals."""
1576        raise NotImplementedError
1577
1578def _call_default_visit(self, node):
1579    self.default_visit(node)
1580
1581def _call_default_departure(self, node):
1582    self.default_departure(node)
1583
1584def _nop(self, node):
1585    pass
1586
1587def _add_node_class_names(names):
1588    """Save typing with dynamic assignments:"""
1589    for _name in names:
1590        setattr(GenericNodeVisitor, "visit_" + _name, _call_default_visit)
1591        setattr(GenericNodeVisitor, "depart_" + _name, _call_default_departure)
1592        setattr(SparseNodeVisitor, 'visit_' + _name, _nop)
1593        setattr(SparseNodeVisitor, 'depart_' + _name, _nop)
1594
1595_add_node_class_names(node_class_names)
1596
1597
1598class TreeCopyVisitor(GenericNodeVisitor):
1599
1600    """
1601    Make a complete copy of a tree or branch, including element attributes.
1602    """
1603
1604    def __init__(self, document):
1605        GenericNodeVisitor.__init__(self, document)
1606        self.parent_stack = []
1607        self.parent = []
1608
1609    def get_tree_copy(self):
1610        return self.parent[0]
1611
1612    def default_visit(self, node):
1613        """Copy the current node, and make it the new acting parent."""
1614        newnode = node.copy()
1615        self.parent.append(newnode)
1616        self.parent_stack.append(self.parent)
1617        self.parent = newnode
1618
1619    def default_departure(self, node):
1620        """Restore the previous acting parent."""
1621        self.parent = self.parent_stack.pop()
1622
1623
1624class TreePruningException(Exception):
1625
1626    """
1627    Base class for `NodeVisitor`-related tree pruning exceptions.
1628
1629    Raise subclasses from within ``visit_...`` or ``depart_...`` methods
1630    called from `Node.walk()` and `Node.walkabout()` tree traversals to prune
1631    the tree traversed.
1632    """
1633
1634    pass
1635
1636
1637class SkipChildren(TreePruningException):
1638
1639    """
1640    Do not visit any children of the current node.  The current node's
1641    siblings and ``depart_...`` method are not affected.
1642    """
1643
1644    pass
1645
1646
1647class SkipSiblings(TreePruningException):
1648
1649    """
1650    Do not visit any more siblings (to the right) of the current node.  The
1651    current node's children and its ``depart_...`` method are not affected.
1652    """
1653
1654    pass
1655
1656
1657class SkipNode(TreePruningException):
1658
1659    """
1660    Do not visit the current node's children, and do not call the current
1661    node's ``depart_...`` method.
1662    """
1663
1664    pass
1665
1666
1667class SkipDeparture(TreePruningException):
1668
1669    """
1670    Do not call the current node's ``depart_...`` method.  The current node's
1671    children and siblings are not affected.
1672    """
1673
1674    pass
1675
1676
1677class NodeFound(TreePruningException):
1678
1679    """
1680    Raise to indicate that the target of a search has been found.  This
1681    exception must be caught by the client; it is not caught by the traversal
1682    code.
1683    """
1684
1685    pass
1686
1687
1688def make_id(string):
1689    """
1690    Convert `string` into an identifier and return it.
1691
1692    Docutils identifiers will conform to the regular expression
1693    ``[a-z](-?[a-z0-9]+)*``.  For CSS compatibility, identifiers (the "class"
1694    and "id" attributes) should have no underscores, colons, or periods.
1695    Hyphens may be used.
1696
1697    - The `HTML 4.01 spec`_ defines identifiers based on SGML tokens:
1698
1699          ID and NAME tokens must begin with a letter ([A-Za-z]) and may be
1700          followed by any number of letters, digits ([0-9]), hyphens ("-"),
1701          underscores ("_"), colons (":"), and periods (".").
1702
1703    - However the `CSS1 spec`_ defines identifiers based on the "name" token,
1704      a tighter interpretation ("flex" tokenizer notation; "latin1" and
1705      "escape" 8-bit characters have been replaced with entities)::
1706
1707          unicode     \\[0-9a-f]{1,4}
1708          latin1      [&iexcl;-&yuml;]
1709          escape      {unicode}|\\[ -~&iexcl;-&yuml;]
1710          nmchar      [-a-z0-9]|{latin1}|{escape}
1711          name        {nmchar}+
1712
1713    The CSS1 "nmchar" rule does not include underscores ("_"), colons (":"),
1714    or periods ("."), therefore "class" and "id" attributes should not contain
1715    these characters. They should be replaced with hyphens ("-"). Combined
1716    with HTML's requirements (the first character must be a letter; no
1717    "unicode", "latin1", or "escape" characters), this results in the
1718    ``[a-z](-?[a-z0-9]+)*`` pattern.
1719
1720    .. _HTML 4.01 spec: http://www.w3.org/TR/html401
1721    .. _CSS1 spec: http://www.w3.org/TR/REC-CSS1
1722    """
1723    id = _non_id_chars.sub('-', ' '.join(string.lower().split()))
1724    id = _non_id_at_ends.sub('', id)
1725    return str(id)
1726
1727_non_id_chars = re.compile('[^a-z0-9]+')
1728_non_id_at_ends = re.compile('^[-0-9]+|-+$')
1729
1730def dupname(node, name):
1731    node['dupnames'].append(name)
1732    node['names'].remove(name)
1733    # Assume that this method is referenced, even though it isn't; we
1734    # don't want to throw unnecessary system_messages.
1735    node.referenced = 1
1736
1737def fully_normalize_name(name):
1738    """Return a case- and whitespace-normalized name."""
1739    return ' '.join(name.lower().split())
1740
1741def whitespace_normalize_name(name):
1742    """Return a whitespace-normalized name."""
1743    return ' '.join(name.split())
1744
1745def serial_escape(value):
1746    """Escape string values that are elements of a list, for serialization."""
1747    return value.replace('\\', r'\\').replace(' ', r'\ ')
1748
1749#
1750#
1751# Local Variables:
1752# indent-tabs-mode: nil
1753# sentence-end-double-space: t
1754# fill-column: 78
1755# End:
Note: リポジトリブラウザについてのヘルプは TracBrowser を参照してください。