root/livinglogic.python.xist/src/ll/xist/xsc.py @ 2713:caf8094a7c40

Revision 2713:caf8094a7c40, 114.1 KB (checked in by Walter Doerwald <walter@…>, 13 years ago)

Add tests for FindCSS (in test_xfind because FindCSS will eventually move
there).

Make node.walknode(elementclass) work.

Line 
1#! /usr/bin/env python
2# -*- coding: iso-8859-1 -*-
3
4## Copyright 1999-2007 by LivingLogic AG, Bayreuth/Germany.
5## Copyright 1999-2007 by Walter Dörwald
6##
7## All Rights Reserved
8##
9## See xist/__init__.py for the license
10
11
12"""
13This module contains all the central &xml; tree classes, the namespace classes,
14exception and warning classes and a few helper classes and functions.
15"""
16
17
18__version__ = "$Revision$".split()[1]
19# $Source$
20
21
22import sys, os, random, copy, warnings, cPickle, threading, weakref, itertools, types
23
24from ll import misc, url as url_
25
26try:
27     import astyle
28except ImportError:
29    astyle = None
30
31# IPython/ipipe support
32try:
33    import ipipe
34except ImportError:
35    ipipe = None
36
37
38local = threading.local()
39
40def getstack():
41    try:
42        stack = getattr(local, "ll.xist.xsc.nodes")
43    except AttributeError:
44        stack = []
45        setattr(local, "ll.xist.xsc.nodes", stack)
46    return stack
47
48
49xml_xmlns = "http://www.w3.org/XML/1998/namespace"
50
51
52###
53### helpers
54###
55
56def tonode(value):
57    """
58    <par>convert <arg>value</arg> to an &xist; <pyref class="Node"><class>Node</class></pyref>.</par>
59
60    <par>If <arg>value</arg> is a tuple or list, it will be (recursively) converted
61    to a <pyref class="Frag"><class>Frag</class></pyref>. Integers, strings, etc.
62    will be converted to a <pyref class="Text"><class>Text</class></pyref>.
63    If <arg>value</arg> is a <pyref class="Node"><class>Node</class></pyref> already,
64    it will be returned unchanged. In the case of <lit>None</lit> the &xist; Null
65    (<class>ll.xist.xsc.Null</class>) will be returned. If <arg>value</arg> is
66    iterable, a <class>Frag</class> will be generated from the items.
67    Anything else will issue a warning and will be ignored (by returning
68    <class>Null</class>).</par>
69    """
70    if isinstance(value, Node):
71        if isinstance(value, Attrs):
72            raise IllegalObjectError(value)
73        # we don't have to turn an Attr into a Frag, because this will be done once the Attr is put back into the tree
74        return value
75    elif isinstance(value, (basestring, int, long, float)):
76        return Text(value)
77    elif value is None:
78        return Null
79    elif isinstance(value, (list, tuple)):
80        return Frag(*value)
81    elif isinstance(value, url_.URL):
82        return Text(value)
83    else:
84        # Maybe it's an iterator/generator?
85        try:
86            return Frag(*list(value))
87        except TypeError:
88            pass
89    raise IllegalObjectError(value) # none of the above => bail out
90
91
92def add(*args, **kwargs):
93    """
94    <function>add</function> appends items in <arg>args</arg> and sets attributes
95    in <arg>kwargs</arg> in the currenly active node in the <lit>with</lit> stack.
96    """
97    getstack()[-1](*args, **kwargs)
98
99
100###
101### Magic constants for tree traversal
102###
103
104entercontent = misc.Const("entercontent")
105enterattrs = misc.Const("enterattrs")
106
107
108###
109### Common tree traversal filters
110###
111
112class FindType(object):
113    """
114    Tree traversal filter that finds nodes of a certain type on the first level
115    of the tree without decending further down.
116    """
117    def __init__(self, *types):
118        self.types = types
119
120    def __call__(self, path):
121        return (isinstance(path[-1], self.types), )
122
123
124class FindTypeAll(object):
125    """
126    Tree traversal filter that finds nodes of a certain type searching the
127    complete tree.
128    """
129    def __init__(self, *types):
130        self.types = types
131
132    def __call__(self, path):
133        return (isinstance(path[-1], self.types), entercontent)
134
135
136class FindTypeAllAttrs(object):
137    """
138    Tree traversal filter that finds nodes of a certain type searching the
139    complete tree (including attributes).
140    """
141    def __init__(self, *types):
142        self.types = types
143
144    def __call__(self, path):
145        return (isinstance(path[-1], self.types), entercontent, enterattrs)
146
147
148class FindTypeTop(object):
149    """
150    Tree traversal filter that finds nodes of a certain type searching the
151    complete tree, but traversal of the children of a node is skipped if this
152    node is of the specified type.
153    """
154    def __init__(self, *types):
155        self.types = types
156
157    def __call__(self, path):
158        if isinstance(path[-1], self.types):
159            return (True,)
160        else:
161            return (entercontent,)
162
163
164class FindVisitAll(object):
165    """
166    Base class for all filters that visit the complete tree (except attributes).
167    """
168    @misc.notimplemented
169    def match(self, path):
170        """
171        """
172
173    def __call__(self, path):
174        return (True, entercontent) if self.match(path) else (entercontent,)
175
176
177###
178### Conversion context
179###
180
181class Context(object):
182    """
183    <par>This is an empty class, that can be used by the
184    <pyref class="Node" method="convert"><method>convert</method></pyref>
185    method to hold element or namespace specific data during the convert call.
186    The method <pyref class="Converter" method="__getitem__"><method>Converter.__getitem__</method></pyref>
187    will return a unique instance of this class.</par>
188    """
189    __fullname__ = "Context"
190
191
192###
193### Exceptions and warnings
194###
195
196class Error(Exception):
197    """
198    Base class for all &xist; exceptions
199    """
200    pass
201
202
203class Warning(UserWarning):
204    """
205    Base class for all warning exceptions (i.e. those that won't
206    result in a program termination.)
207    """
208    pass
209
210
211class IllegalAttrValueWarning(Warning):
212    """
213    Warning that is issued when an attribute has an illegal value when parsing or publishing.
214    """
215
216    def __init__(self, attr):
217        self.attr = attr
218
219    def __str__(self):
220        attr = self.attr
221        return "Attribute value %r not allowed for %s" % (unicode(attr), attr._str(fullname=True, xml=False, decorate=False))
222
223
224class RequiredAttrMissingWarning(Warning):
225    """
226    Warning that is issued when a required attribute is missing during parsing or publishing.
227    """
228
229    def __init__(self, attrs, reqattrs):
230        self.attrs = attrs
231        self.reqattrs = reqattrs
232
233    def __str__(self):
234        return "Required attribute%s %s missing in %s" % (("s" if len(self.reqattrs)>1 else ""), ", ".join(attr.__class__.__name__ for attr in self.reqattrs), self.attrs._str(fullname=True, xml=False, decorate=False))
235
236
237class IllegalDTDChildWarning(Warning):
238    """
239    Warning that is issued when the <pyref module="ll.xist.parsers" class="HTMLParser"><class>HTMLParser</class></pyref>
240    detects an element that is not allowed inside its parent element according to the &html; &dtd;
241    """
242
243    def __init__(self, childname, parentname):
244        self.childname = childname
245        self.parentname = parentname
246
247    def __str__(self):
248        return "Element %s not allowed as a child of element %s" % (self.childname, self.parentname)
249
250
251class IllegalCloseTagWarning(Warning):
252    """
253    Warning that is issued when the <pyref module="ll.xist.parsers" class="HTMLParser"><class>HTMLParser</class></pyref>
254    finds an end tag that has no corresponding start tag.
255    """
256
257    def __init__(self, name):
258        self.name = name
259
260    def __str__(self):
261        return "Element %s has never been opened" % (self.name,)
262
263
264class PrefixNeededError(Error, ValueError):
265    """
266    Exception that is raised when something requires a prefix on publishing.
267    """
268    def __init__(self, xmlns):
269        self.xmlns = xmlns
270
271    def __str__(self):
272        return "namespace %s needs a prefix" % nsclark(self.xmlns)
273
274
275class IllegalPrefixError(Error, LookupError):
276    """
277    Exception that is raised when a namespace prefix is undefined.
278    """
279    def __init__(self, prefix):
280        self.prefix = prefix
281
282    def __str__(self):
283        return "namespace prefix %s is undefined" % (self.prefix,)
284
285
286class IllegalNamespaceError(Error, LookupError):
287    """
288    Exception that is raised when a namespace name is undefined
289    i.e. if there is no namespace with this name.
290    """
291    def __init__(self, name):
292        self.name = name
293
294    def __str__(self):
295        return "namespace name %s is undefined" % (self.name,)
296
297
298class IllegalElementError(Error, LookupError):
299    """
300    Exception that is raised, when an illegal element class is requested
301    """
302
303    def __init__(self, name, xmlns, xml=False):
304        self.name = name
305        self.xmlns = xmlns
306        self.xml = xml
307
308    def __str__(self):
309        xmlns = self.xmlns
310        if isinstance(xmlns, (list, tuple)):
311            if len(xmlns) > 1:
312                return "no element with %s name %s in namespaces %s" % (("Python", "XML")[self.xml], self.name, ", ".join(nsclark(xmlns) for xmlns in xmlns))
313            xmlns = xmlns[0]
314        return "no element with %s name %s%s" % (("Python", "XML")[self.xml], nsclark(xmlns), self.name)
315
316
317class IllegalProcInstError(Error, LookupError):
318    """
319    Exception that is raised, when an illegal procinst class is requested
320    """
321
322    def __init__(self, name, xml=False):
323        self.name = name
324        self.xml = xml
325
326    def __str__(self):
327        return "no procinst with %s name %s" % (("Python", "XML")[self.xml], self.name)
328
329
330class IllegalEntityError(Error, LookupError):
331    """
332    Exception that is raised, when an illegal entity class is requested
333    """
334
335    def __init__(self, name, xml=False):
336        self.name = name
337        self.xml = xml
338
339    def __str__(self):
340        return "no entity with %s name %s" % (("Python", "XML")[self.xml], self.name)
341
342
343class IllegalCharRefError(Error, LookupError):
344    """
345    Exception that is raised, when an illegal charref class is requested
346    """
347
348    def __init__(self, name, xml=False):
349        self.name = name
350        self.xml = xml
351
352    def __str__(self):
353        if isinstance(self.name, (int, long)):
354            return "no charref with codepoint %s" % self.name
355        return "no charref with %s name %s" % (("Python", "XML")[self.xml], self.name)
356
357
358class IllegalAttrError(Error, LookupError):
359    """
360    Exception that is raised, when an illegal attribute is requested.
361    """
362
363    def __init__(self, name, cls, xml=False):
364        self.name = name
365        self.cls = cls
366        self.xml = xml
367
368    def __str__(self):
369        if isinstance(self.name, basestring):
370            return "no local attribute with %s name %s in %r" % (("Python", "XML")[self.xml], self.name, self.cls)
371        elif self.name.xmlns is None:
372            return "no local attribute with class %r in %r" % (self.name, self.cls)
373        else:
374            return "no global attribute with class %r" % self.name
375
376
377class AmbiguousNodeError(Error, LookupError):
378    """
379    exception that is raised, when an node class is ambiguous (most commonly for processing instructions or entities)
380    """
381
382    type = "node"
383
384    def __init__(self, name, xml=False):
385        self.name = name
386        self.xml = xml
387
388    def __str__(self):
389        return "%s with %s name %s is ambigous" % (self.type, ("Python", "XML")[self.xml], self.name)
390
391
392class AmbiguousProcInstError(AmbiguousNodeError):
393    type = "procinst"
394
395
396class AmbiguousEntityError(AmbiguousNodeError):
397    type = "entity"
398
399
400class AmbiguousCharRefError(AmbiguousNodeError):
401    type = "charref"
402
403    def __str__(self):
404        if isinstance(self.name, (int, long)):
405            return "%s with codepoint %s is ambigous" % (self.type, self.name)
406        else:
407            return AmbiguousNodeError.__str__(self)
408
409
410class MultipleRootsError(Error):
411    def __str__(self):
412        return "can't add namespace attributes: XML tree has multiple roots"
413
414
415class ElementNestingError(Error):
416    """
417    Exception that is raised, when an element has an illegal nesting
418    (e.g. <lit>&lt;a&gt;&lt;b&gt;&lt;/a&gt;&lt;/b&gt;</lit>)
419    """
420
421    def __init__(self, expectedelement, foundelement):
422        self.expectedelement = expectedelement
423        self.foundelement = foundelement
424
425    def __str__(self):
426        return "mismatched element nesting (close tag for %s expected; close tag for %s found)" % (self.expectedelement._str(fullname=True, xml=False, decorate=True), self.foundelement._str(fullname=True, xml=False, decorate=True))
427
428
429class IllegalAttrNodeError(Error):
430    """
431    Exception that is raised, when something is found in an attribute that
432    doesn't belong there (e.g. an element or a comment).
433    """
434
435    def __init__(self, node):
436        self.node = node
437
438    def __str__(self):
439        return "illegal node of type %s found inside attribute" % self.node.__class__.__name__
440
441
442class NodeNotFoundError(Error):
443    """
444    Exception that is raised when <pyref module="ll.xist.xsc" class="Node" method="findfirst"><method>findfirst</method></pyref> fails.
445    """
446    def __str__(self):
447        return "no appropriate node found"
448
449
450class FileNotFoundWarning(Warning):
451    """
452    Warning that is issued, when a file can't be found.
453    """
454    def __init__(self, message, filename, exc):
455        Warning.__init__(self, message, filename, exc)
456        self.message = message
457        self.filename = filename
458        self.exc = exc
459
460    def __str__(self):
461        return "%s: %r not found (%s)" % (self.message, self.filename, self.exc)
462
463
464class IllegalObjectError(TypeError):
465    """
466    Exception that is raised when an &xist; constructor gets passed an unconvertable object.
467    """
468
469    def __init__(self, object):
470        self.object = object
471
472    def __str__(self):
473        return "can't convert object %r of type %s to an XIST node" % (self.object, type(self.object).__name__)
474
475
476class MalformedCharRefWarning(Warning):
477    """
478    Exception that is raised when a character reference is malformed (e.g. <lit>&amp;#foo;</lit>)
479    """
480
481    def __init__(self, name):
482        self.name = name
483
484    def __str__(self):
485        return "malformed character reference: &%s;" % self.name
486
487
488class IllegalCommentContentWarning(Warning):
489    """
490    Warning that is issued when there is an illegal comment, i.e. one
491    containing <lit>--</lit> or ending in <lit>-</lit>.
492    (This can only happen, when the comment is instantiated by the
493    program, not when parsed from an &xml; file.)
494    """
495
496    def __init__(self, comment):
497        self.comment = comment
498
499    def __str__(self):
500        return "comment with content %r is illegal, as it contains '--' or ends in '-'." % self.comment.content
501
502
503class IllegalProcInstFormatError(Error):
504    """
505    Exception that is raised, when there is an illegal processing instruction, i.e. one containing <lit>?&gt;</lit>.
506    (This can only happen, when the processing instruction is instantiated by the
507    program, not when parsed from an &xml; file.)
508    """
509
510    def __init__(self, procinst):
511        self.procinst = procinst
512
513    def __str__(self):
514        return "processing instruction with content %r is illegal, as it contains '?>'." % self.procinst.content
515
516
517class IllegalXMLDeclFormatError(Error):
518    """
519    Exception that is raised, when there is an illegal XML declaration,
520    i.e. there something wrong in <lit>&lt;?xml ...?&gt;</lit>.
521    (This can only happen, when the processing instruction is instantiated by the
522    program, not when parsed from an &xml; file.)
523    """
524
525    def __init__(self, procinst):
526        self.procinst = procinst
527
528    def __str__(self):
529        return "XML declaration with content %r is malformed." % self.procinst.content
530
531
532class ParseWarning(Warning):
533    """
534    General warning issued during parsing.
535    """
536
537
538class IllegalElementParseWarning(IllegalElementError, ParseWarning):
539    """
540    Warning about an illegal element that is issued during parsing.
541    """
542warnings.filterwarnings("error", category=IllegalElementParseWarning)
543
544
545class IllegalProcInstParseWarning(IllegalProcInstError, ParseWarning):
546    """
547    Warning about an illegal processing instruction that is issued during parsing.
548    """
549warnings.filterwarnings("error", category=IllegalProcInstParseWarning)
550
551
552class AmbiguousProcInstParseWarning(AmbiguousProcInstError, ParseWarning):
553    """
554    Warning about an ambigous processing instruction that is issued during parsing.
555    """
556warnings.filterwarnings("error", category=AmbiguousProcInstParseWarning)
557
558
559class IllegalEntityParseWarning(IllegalEntityError, ParseWarning):
560    """
561    Warning about an illegal entity that is issued during parsing.
562    """
563warnings.filterwarnings("error", category=IllegalEntityParseWarning)
564
565
566class AmbiguousEntityParseWarning(AmbiguousEntityError, ParseWarning):
567    """
568    Warning about an ambigous entity that is issued during parsing.
569    """
570warnings.filterwarnings("error", category=AmbiguousEntityParseWarning)
571
572
573class IllegalCharRefParseWarning(IllegalCharRefError, ParseWarning):
574    """
575    Warning about an illegal character references that is issued during parsing.
576    """
577warnings.filterwarnings("error", category=IllegalCharRefParseWarning)
578
579
580class AmbiguousCharRefParseWarning(AmbiguousCharRefError, ParseWarning):
581    """
582    Warning about an ambigous character references that is issued during parsing.
583    """
584warnings.filterwarnings("error", category=AmbiguousCharRefParseWarning)
585
586
587class IllegalAttrParseWarning(IllegalAttrError, ParseWarning):
588    """
589    Warning about an illegal attribute that is issued during parsing.
590    """
591warnings.filterwarnings("error", category=IllegalAttrParseWarning)
592
593
594class NodeOutsideContextError(Error):
595    """
596    Error that is raised, when a convert method can't find required context info.
597    """
598
599    def __init__(self, node, outerclass):
600        self.node = node
601        self.outerclass = outerclass
602
603    def __str__(self):
604        return "node %s outside of %s" % (self.node._str(fullname=True, xml=False, decorate=True), self.outerclass._str(fullname=True, xml=False, decorate=True))
605
606
607###
608### The DOM classes
609###
610
611import xfind
612
613class _Node_Meta(type, xfind.Operator):
614    def __new__(cls, name, bases, dict):
615        dict["__fullname__"] = name
616        if "register" not in dict:
617            dict["register"] = True
618        if "xmlname" not in dict:
619            dict["xmlname"] = name.rsplit(".", 1)[-1]
620        return type.__new__(cls, name, bases, dict)
621
622    def __repr__(self):
623        return "<class %s:%s at 0x%x>" % (self.__module__, self.__fullname__, id(self))
624
625    def xwalk(self, iterator):
626        for child in iterator:
627            if isinstance(child, (Frag, Element)):
628                for subchild in child:
629                    if isinstance(subchild, self):
630                        yield subchild
631
632
633class Node(object):
634    """
635    base class for nodes in the document tree. Derived classes must
636    overwrite <pyref method="convert"><method>convert</method></pyref>
637    and may overwrite <pyref method="publish"><method>publish</method></pyref>.
638    """
639    __metaclass__ = _Node_Meta
640
641    # location of this node in the XML file (will be hidden in derived classes, but is
642    # specified here, so that no special tests are required. In derived classes
643    # this will be set by the parser)
644    startloc = None
645    endloc = None
646
647    # Subclasses relevant for parsing (i.e. Element, ProcInst, Entity and CharRef)
648    # have an additional class attribute named register. This attribute may have three values:
649    # False: Don't register for parsing.
650    # True:  Use for parsing.
651    # If register is not set it defaults to True
652
653    Context = Context
654
655    def __repr__(self):
656        return "<%s:%s object at 0x%x>" % (self.__module__, self.__fullname__, id(self))
657
658    def __ne__(self, other):
659        return not self==other
660
661    xmlname = None
662    xmlns = None
663
664    @classmethod
665    def _strbase(cls, fullname, xml):
666        v = []
667        if fullname:
668            ns = cls.xmlns if xml else cls.__module__
669            if ns is not None:
670                v.append(ns)
671                v.append(":")
672        if xml:
673            name = cls.xmlname
674        elif fullname:
675            name = cls.__fullname__
676        else:
677            name = cls.__name__
678        v.append(name)
679        return "".join(v)
680
681    def __pos__(self):
682        getstack()[-1].append(self)
683
684    def clone(self):
685        """
686        return a clone of <self/>. Compared to <pyref method="deepcopy"><method>deepcopy</method></pyref> <method>clone</method>
687        will create multiple instances of objects that can be found in the tree more than once. <method>clone</method> can't
688        clone trees that contain cycles.
689        """
690        return self
691
692    def copy(self):
693        """
694        Return a shallow copy of <self/>.
695        """
696        return self.__copy__()
697
698    def __copy__(self):
699        return self
700
701    def deepcopy(self):
702        """
703        Return a deep copy of <self/>.
704        """
705        return self.__deepcopy__()
706
707    def __deepcopy__(self, memo=None):
708        return self
709
710    @misc.notimplemented
711    def present(self, presenter):
712        """
713        <par><method>present</method> is used as a central dispatch method for
714        the <pyref module="ll.xist.presenters">presenter classes</pyref>. Normally
715        it is not called by the user, but internally by the presenter. The user
716        should call <pyref method="repr"><method>repr</method></pyref>
717        instead.</par>
718        """
719        # Subclasses of Node implement this method by calling the appropriate present* method in the publisher (i.e. double dispatch)
720
721    def conv(self, converter=None, root=None, mode=None, stage=None, target=None, lang=None, function=None, makeaction=None, makeproject=None):
722        """
723        <par>Convenience method for calling <pyref method="convert"><method>convert</method></pyref>.</par>
724        <par><method>conv</method> will automatically set <lit><arg>converter</arg>.node</lit> to <self/> to remember the
725        <z>document root node</z> for which <method>conv</method> has been called, this means that you should not call
726        <method>conv</method> in any of the recursive calls, as you would loose this information. Call
727        <pyref method="convert"><method>convert</method></pyref> directly instead.</par>
728        """
729        if converter is None:
730            converter = converters.Converter(node=self, root=root, mode=mode, stage=stage, target=target, lang=lang, makeaction=makeaction, makeproject=makeproject)
731            return self.convert(converter)
732        else:
733            converter.push(node=self, root=root, mode=mode, stage=stage, target=target, lang=lang, makeaction=makeaction, makeproject=makeproject)
734            node = self.convert(converter)
735            converter.pop()
736            return node
737
738    @misc.notimplemented
739    def convert(self, converter):
740        """
741        <par>implementation of the conversion method. When you define your own
742        element classes you have to overwrite this method and implement the desired
743        conversion.</par>
744
745        <par>This method must return an instance of <class>Node</class>.
746        It may <em>not</em> change <self/>.</par>
747        """
748        pass
749
750    @misc.notimplemented
751    def __unicode__(self):
752        """
753        <par>Return the character content of <self/> as a unicode string.
754        This means that comments and processing instructions will be filtered out.
755        For elements you'll get the element content.</par>
756
757        <par><method>__unicode__</method> can be used everywhere where
758        a plain string representation of the node is required.</par>
759        """
760        pass
761
762    def __str__(self):
763        """
764        Return the character content of <self/> as a string (if possible, i.e.
765        there are no characters that are unencodable in the default encoding).
766        """
767        return str(unicode(self))
768
769    def __int__(self):
770        """
771        Convert the character content of <self/> to an <class>int</class>.
772        """
773        return int(unicode(self))
774
775    def __long__(self):
776        """
777        Convert the character content of <self/> to an <class>long</class>.
778        """
779        return long(unicode(self))
780
781    def asFloat(self, decimal=".", ignore=""):
782        """
783        <par>Convert the character content of <self/> to an <class>float</class>.
784        <arg>decimal</arg> specifies which decimal separator is used in the value
785        (e.g. <lit>"."</lit> (the default) or <lit>","</lit>).
786        <arg>ignore</arg> specifies which character will be ignored.</par>
787        """
788        s = unicode(self)
789        for c in ignore:
790            s = s.replace(c, u"")
791        if decimal != u".":
792            s = s.replace(decimal, u".")
793        return float(s)
794
795    def __float__(self):
796        """
797        Convert the character content of <self/> to an <class>float</class>.
798        """
799        return self.asFloat()
800
801    def __complex__(self):
802        """
803        Convert the character content of <self/> to an <class>complex</class>.
804        """
805        return complex(unicode(self))
806
807    def parsed(self, parser, start=None):
808        """
809        <par>This method will be called by the parser <arg>parser</arg> once after
810        <self/> is created by the parser and must return the node that is to be
811        put into the tree (in most cases this is <self/>, it's used e.g. by
812        <pyref class="URLAttr"><class>URLAttr</class></pyref> to incorporate
813        the base <pyref module="ll.url" class="URL"><class>URL</class></pyref>
814        into the attribute.</par>
815
816        <par>For elements <function>parsed</function> will be called twice:
817        Once at the beginning (i.e. before the content is parsed) with
818        <lit><arg>start</arg>==True</lit> and once at the end after parsing of
819        the content is finished <lit><arg>start</arg>==False</lit>. For the
820        second call the return value will be ignored.</par>
821        """
822        return self
823
824    def checkvalid(self):
825        """
826        <par>This method will be called when parsing or publishing to check
827        whether <self/> is valid.</par>
828
829        <par>If <self/> is found to be invalid a warning should be issued through
830        the <pyref module="warnings">Python warning framework</pyref>.</par>
831        """
832
833    @misc.notimplemented
834    def publish(self, publisher):
835        """
836        <par>Generate unicode strings for the node. <arg>publisher</arg> must be an instance of
837        <pyref module="ll.xist.publishers" class="Publisher"><class>ll.xist.publishers.Publisher</class></pyref>.</par>
838
839        <par>The encoding and xhtml specification are taken from the <arg>publisher</arg>.</par>
840        """
841
842    def bytes(self, base=None, publisher=None, **publishargs):
843        """
844        <par>A generator that will produce this node as a serialized byte string.</par>
845
846        <par>For the possible parameters see the
847        <pyref module="ll.xist.publishers" class="Publisher"><class>ll.xist.publishers.Publisher</class></pyref>
848        constructor.</par>
849        """
850        if publisher is None:
851            publisher = publishers.Publisher(**publishargs)
852
853        return publisher.publish(self, base) # return a generator-iterator
854
855    def asBytes(self, base=None, publisher=None, **publishargs):
856        """
857        <par>Return this node as a serialized byte string.</par>
858
859        <par>For the possible parameters see the
860        <pyref module="ll.xist.publishers" class="Publisher"><class>ll.xist.publishers.Publisher</class></pyref>
861        constructor.</par>
862        """
863        return "".join(self.bytes(base, publisher, **publishargs))
864
865    def asString(self, base=None, publisher=None, **publishargs):
866        """
867        <par>Return this node as a serialized unicode string.</par>
868
869        <par>For the possible parameters see the
870        <pyref module="ll.xist.publishers" class="Publisher"><class>ll.xist.publishers.Publisher</class></pyref>
871        constructor.</par>
872        """
873        if publisher is None:
874            publisher = publishers.Publisher(**publishargs)
875        encoding = publisher.encoding
876        result = "".join(publisher.publish(self, base))
877        return result.decode(encoding)
878
879    def write(self, stream, *args, **publishargs):
880        """
881        <par>Write <self/> to the file-like object <arg>stream</arg> (which must
882        provide a <method>write</method> method).</par>
883
884        <par>For the rest of the parameters see the
885        <pyref module="ll.xist.publishers" class="Publisher"><class>ll.xist.publishers.Publisher</class></pyref>
886        constructor.</par>
887        """
888        for part in self.bytes(*args, **publishargs):
889            stream.write(part)
890
891    def _walk(self, filter, path):
892        """
893        <par>Internal helper for <pyref method="walk"><method>walk</method></pyref>.</par>
894        """
895        if callable(filter):
896            found = filter(path)
897        else:
898            found = filter
899
900        for option in found:
901            if option is not entercontent and option is not enterattrs and option:
902                yield path
903
904    def walk(self, filter=(True, entercontent)):
905        """
906        <par>Return an iterator for traversing the tree rooted at <self/>.</par>
907
908        <par><arg>filter</arg> is used for specifying whether or not a node should
909        be yielded and when the children of this node should be traversed. If
910        <arg>filter</arg> is callable, it will be called for each node visited
911        during the traversal. A path (i.e. a list of all nodes from the root to
912        the current node) will be passed to the filter on each call and the
913        filter must return a sequence of <z>node handling options</z>.
914        If <arg>filter</arg> is not callable, it must be a sequence of node
915        handling options that will be used for all visited nodes.</par>
916
917        <par>Entries in this returned sequence can be the following:</par>
918
919        <dlist>
920        <term><lit>True</lit></term><item>This tells <method>walk</method> to
921        yield this node from the iterator.</item>
922        <term><lit>False</lit></term><item>Don't yield this node from the iterator.</item>
923        <term><lit>enterattrs</lit></term><item>This is a global constant in
924        <module>ll.xist.xsc</module> and tells <method>walk</method> to traverse
925        the attributes of this node (if it's an
926        <pyref class="Element"><class>Element</class></pyref>, otherwise this
927        option will be ignored).</item>
928        <term><lit>entercontent</lit></term><item>This is a global constant in
929        <module>ll.xist.xsc</module> and tells <method>walk</method> to traverse
930        the child nodes of this node (if it's an
931        <pyref class="Element"><class>Element</class></pyref>, otherwise this
932        option will be ignored).</item>
933        </dlist>
934
935        <par>These options will be executed in the order they are specified in the
936        sequence, so to get a top down traversal of a tree (without entering
937        attributes), the following call can be made:</par>
938
939        <prog>
940        <rep>node</rep>.walk((True, xsc.entercontent))
941        </prog>
942
943        <par>For a bottom up traversal the following call can be made:</par>
944
945        <prog>
946        <rep>node</rep>.walk((xsc.entercontent, True))
947        </prog>
948
949        <par>Each item produced by the iterator is a path list.
950        <method>walk</method> reuses this list, so you can't rely on the value
951        of the list being the same across calls to <method>next</method>.</par>
952        """
953        return self._walk(filter, [self])
954
955    def walknode(self, filter=(True, entercontent)):
956        """
957        Return an iterator for traversing the tree. <arg>filter</arg> works the
958        same as the <arg>filter</arg> argument for <pyref method="walk"><method>walk</method></pyref>.
959        The items produced by the iterator are the nodes themselves.
960        """
961        if isinstance(filter, type) and issubclass(filter, Node):
962            filter = FindTypeAll(filter)
963        def iterate(path):
964            for path in self._walk(filter, path):
965                yield path[-1]
966        return misc.Iterator(iterate([self]))
967
968    def walkpath(self, filter=(True, entercontent)):
969        """
970        Return an iterator for traversing the tree. <arg>filter</arg> works the
971        same as the <arg>filter</arg> argument for <pyref method="walk"><method>walk</method></pyref>.
972        The items produced by the iterator are copies of the path.
973        """
974        if isinstance(filter, type) and issubclass(filter, Node):
975            filter = FindTypeAll(filter)
976        def iterate(path):
977            for path in self._walk(filter, path):
978                yield path[:]
979        return misc.Iterator(iterate([self]))
980
981    def __div__(self, other):
982        return xfind.Expr(self, other)
983
984    def __floordiv__(self, other):
985        return xfind.Expr(self, xfind.all, other)
986
987    def compact(self):
988        """
989        Return a version of <self/>, where textnodes or character references that
990        contain only linefeeds are removed, i.e. potentially needless whitespace
991        is removed.
992        """
993        return self
994
995    def _decoratenode(self, node):
996        """
997        Decorate the <pyref class="Node"><class>Node</class></pyref>
998        <arg>node</arg> with the same location information as <self/>.
999        """
1000
1001        node.startloc = self.startloc
1002        node.endloc = self.endloc
1003        return node
1004
1005    def mapped(self, function, converter=None, **converterargs):
1006        """
1007        <par>Return the node mapped through the function <arg>function</arg>. This
1008        call works recursively (for <pyref class="Frag"><class>Frag</class></pyref>
1009        and <pyref class="Element"><class>Element</class></pyref>).</par>
1010
1011        <par>When you want an unmodified node you simply can return <self/>.
1012        <method>mapped</method> will make a copy of it and fill the content
1013        recursively. Note that element attributes will not be mapped. When you
1014        return a different node from <function>function</function> this node will
1015        be incorporated into the result as-is.
1016        """
1017        if converter is None:
1018            converter = converters.Converter(**converterargs)
1019        node = function(self, converter)
1020        assert isinstance(node, Node), "the mapped method returned the illegal object %r (type %r) when mapping %r" % (node, type(node), self)
1021        return node
1022
1023    def normalized(self):
1024        """
1025        <par>Return a normalized version of <self/>, which means that consecutive
1026        <pyref class="Text"><class>Text</class> nodes</pyref> are merged.</par>
1027        """
1028        return self
1029
1030    def __mul__(self, factor):
1031        """
1032        <par>Return a <pyref class="Frag"><class>Frag</class></pyref> with
1033        <arg>factor</arg> times the node as an entry. Note that the node will not
1034        be copied, i.e. it is a <z>shallow <method>__mul__</method></z>.</par>
1035        """
1036        return Frag(*factor*[self])
1037
1038    def __rmul__(self, factor):
1039        """
1040        <par>Return a <pyref class="Frag"><class>Frag</class></pyref> with
1041        <arg>factor</arg> times the node as an entry.</par>
1042        """
1043        return Frag(*[self]*factor)
1044
1045    def pretty(self, level=0, indent="\t"):
1046        """
1047        <par>Return a prettyfied version of <self/>, i.e. one with properly
1048        nested and indented tags (as far as possible). If an element has mixed
1049        content (i.e. <pyref class="Text"><class>Text</class></pyref> and
1050        non-<pyref class="Text"><class>Text</class></pyref> nodes) the content
1051        will be returned as is.</par>
1052
1053        <par>Note that whitespace will prevent pretty printing too, so
1054        you might want to call <pyref method="normalized"><method>normalized</method></pyref>
1055        and <pyref method="compact"><method>compact</method></pyref> before
1056        calling <method>pretty</method> to remove whitespace.</par>
1057        """
1058        if level:
1059            return Frag(indent*level, self)
1060        else:
1061            return self
1062
1063
1064###
1065### Helper functions for ipipe
1066###
1067
1068
1069if ipipe is not None:
1070    def _ipipe_type(node):
1071        "The type of the node"
1072        if node is Null:
1073            return "null"
1074        elif isinstance(node, Element):
1075            return "element"
1076        elif isinstance(node, ProcInst):
1077            return "procinst"
1078        elif isinstance(node, CharRef):
1079            return "charref"
1080        elif isinstance(node, Entity):
1081            return "entity"
1082        elif isinstance(node, Text):
1083            return "text"
1084        elif isinstance(node, Comment):
1085            return "comment"
1086        elif isinstance(node, DocType):
1087            return "doctype"
1088        elif isinstance(node, Attr):
1089            return "attr"
1090        elif isinstance(node, Frag):
1091            return "frag"
1092        return ipipe.noitem
1093    _ipipe_type.__xname__ = "type"
1094
1095
1096    def _ipipe_ns(node):
1097        "The namespace"
1098        return node.xmlns
1099    _ipipe_ns.__xname__ = "ns"
1100
1101
1102    def _ipipe_name(node):
1103        "The element/procinst/entity/attribute name of the node"
1104        if isinstance(node, (Element, ProcInst, Entity, Attr)):
1105            return "%s.%s" % (node.__class__.__module__, node.__fullname__)
1106        return ipipe.noitem
1107    _ipipe_name.__xname__ = "name"
1108
1109
1110    def _ipipe_childrencount(node):
1111        "The number of child nodes"
1112        if isinstance(node, Element):
1113            return len(node.content)
1114        elif isinstance(node, Frag):
1115            return len(node)
1116        return ipipe.noitem
1117    _ipipe_childrencount.__xname__ = "# children"
1118
1119
1120    def _ipipe_attrscount(node):
1121        "The number of attribute nodes"
1122        if isinstance(node, Element):
1123            return len(node.attrs)
1124        return ipipe.noitem
1125    _ipipe_attrscount.__xname__ = "# attrs"
1126
1127
1128    def _ipipe_content(node):
1129        "The text content"
1130        if isinstance(node, CharacterData):
1131            return unicode(node.content)
1132        elif isinstance(node, Attr):
1133            return unicode(node)
1134        return ipipe.noitem
1135    _ipipe_content.__xname__ = "content"
1136
1137
1138    @ipipe.xrepr.when_type(_Node_Meta)
1139    def repr_nodeclass(self, mode="default"):
1140        yield (astyle.style_type_type, "%s.%s" % (self.__module__, self.__fullname__))
1141
1142
1143    @ipipe.xattrs.when_type(Node)
1144    def xattrs_nodeclass(self, mode="default"):
1145        yield ipipe.AttributeDescriptor("startloc", doc="the locate in the XML file")
1146        yield ipipe.FunctionDescriptor(_ipipe_type)
1147        yield ipipe.AttributeDescriptor("xmlns", doc="the XML namespace of the node")
1148        yield ipipe.FunctionDescriptor(_ipipe_name, "name")
1149        yield ipipe.FunctionDescriptor(_ipipe_content, "content")
1150        if mode == "detail":
1151            yield ipipe.IterAttributeDescriptor("content", "the element content")
1152            yield ipipe.IterAttributeDescriptor("attrs", "the element attributes")
1153        else:
1154            yield ipipe.FunctionDescriptor(_ipipe_childrencount, "children")
1155            yield ipipe.FunctionDescriptor(_ipipe_attrscount, "attrs")
1156
1157
1158class CharacterData(Node):
1159    """
1160    <par>Base class for &xml; character data (<pyref class="Text"><class>Text</class></pyref>,
1161    <pyref class="ProcInst"><class>ProcInst</class></pyref>,
1162    <pyref class="Comment"><class>Comment</class></pyref> and
1163    <pyref class="DocType"><class>DocType</class></pyref>)</par>
1164
1165    <par>Provides nearly the same functionality as <class>UserString</class>,
1166    but omits a few methods.</par>
1167    """
1168    __slots__ = ("_content",)
1169
1170    def __init__(self, *content):
1171        self._content = u"".join(unicode(x) for x in content)
1172
1173    def __getstate__(self):
1174        return self._content
1175
1176    def __setstate__(self, content):
1177        self._content = content
1178
1179    class content(misc.propclass):
1180        """
1181        The text content of the node as a <class>unicode</class> object.
1182        """
1183        def __get__(self):
1184            return self._content
1185
1186    def __hash__(self):
1187        return self._content.__hash__()
1188
1189    def __eq__(self, other):
1190        return self.__class__ is other.__class__ and self._content==other._content
1191
1192    def __len__(self):
1193        return self._content.__len__()
1194
1195    def __getitem__(self, index):
1196        return self.__class__(self._content.__getitem__(index))
1197
1198    def __add__(self, other):
1199        return self.__class__(self._content + other)
1200
1201    def __radd__(self, other):
1202        return self.__class__(unicode(other) + self._content)
1203
1204    def __mul__(self, n):
1205        return self.__class__(n * self._content)
1206
1207    def __rmul__(self, n):
1208        return self.__class__(n * self._content)
1209
1210    def __getslice__(self, index1, index2):
1211        return self.__class__(self._content.__getslice__(index1, index2))
1212
1213    def capitalize(self):
1214        return self.__class__(self._content.capitalize())
1215
1216    def center(self, width):
1217        return self.__class__(self._content.center(width))
1218
1219    def count(self, sub, start=0, end=sys.maxint):
1220        return self._content.count(sub, start, end)
1221
1222    def endswith(self, suffix, start=0, end=sys.maxint):
1223        return self._content.endswith(suffix, start, end)
1224
1225    def index(self, sub, start=0, end=sys.maxint):
1226        return self._content.index(sub, start, end)
1227
1228    def isalpha(self):
1229        return self._content.isalpha()
1230
1231    def isalnum(self):
1232        return self._content.isalnum()
1233
1234    def isdecimal(self):
1235        return self._content.isdecimal()
1236
1237    def isdigit(self):
1238        return self._content.isdigit()
1239
1240    def islower(self):
1241        return self._content.islower()
1242
1243    def isnumeric(self):
1244        return self._content.isnumeric()
1245
1246    def isspace(self):
1247        return self._content.isspace()
1248
1249    def istitle(self):
1250        return self._content.istitle()
1251
1252    def isupper(self):
1253        return self._content.isupper()
1254
1255    def join(self, frag):
1256        return frag.withsep(self)
1257
1258    def ljust(self, width, fill=u" "):
1259        return self.__class__(self._content.ljust(width, fill))
1260
1261    def lower(self):
1262        return self.__class__(self._content.lower())
1263
1264    def lstrip(self, chars=None):
1265        return self.__class__(self._content.lstrip(chars))
1266
1267    def replace(self, old, new, maxsplit=-1):
1268        return self.__class__(self._content.replace(old, new, maxsplit))
1269
1270    def rjust(self, width, fill=u" "):
1271        return self.__class__(self._content.rjust(width, fill))
1272
1273    def rstrip(self, chars=None):
1274        return self.__class__(self._content.rstrip(chars))
1275
1276    def rfind(self, sub, start=0, end=sys.maxint):
1277        return self._content.rfind(sub, start, end)
1278
1279    def rindex(self, sub, start=0, end=sys.maxint):
1280        return self._content.rindex(sub, start, end)
1281
1282    def split(self, sep=None, maxsplit=-1):
1283        return Frag(self._content.split(sep, maxsplit))
1284
1285    def splitlines(self, keepends=0):
1286        return Frag(self._content.splitlines(keepends))
1287
1288    def startswith(self, prefix, start=0, end=sys.maxint):
1289        return self._content.startswith(prefix, start, end)
1290
1291    def strip(self, chars=None):
1292        return self.__class__(self._content.strip(chars))
1293
1294    def swapcase(self):
1295        return self.__class__(self._content.swapcase())
1296
1297    def title(self):
1298        return self.__class__(self._content.title())
1299
1300    def translate(self, table):
1301        return self.__class__(self._content.translate(table))
1302
1303    def upper(self):
1304        return self.__class__(self._content.upper())
1305
1306    def __repr__(self):
1307        if self.startloc is not None:
1308            loc = " (from %s)" % self.startloc
1309        else:
1310            loc = ""
1311        return "<%s.%s content=%r%s at 0x%x>" % (self.__class__.__module__, self.__fullname__, self.content, loc, id(self))
1312
1313
1314class Text(CharacterData):
1315    """
1316    <par>A text node. The characters <markup>&lt;</markup>, <markup>&gt;</markup>, <markup>&amp;</markup>
1317    (and <markup>"</markup> inside attributes) will be <z>escaped</z> with the
1318    appropriate character entities when this node is published.</par>
1319    """
1320
1321    def convert(self, converter):
1322        return self
1323
1324    def __unicode__(self):
1325        return self._content
1326
1327    def publish(self, publisher):
1328        yield publisher.encodetext(self._content)
1329
1330    def present(self, presenter):
1331        return presenter.presentText(self) # return a generator-iterator
1332
1333    def compact(self):
1334        if self.content.isspace():
1335            return Null
1336        else:
1337            return self
1338
1339    def pretty(self, level=0, indent="\t"):
1340        return self
1341
1342
1343class Frag(Node, list):
1344    """
1345    <par>A fragment contains a list of nodes and can be used for dynamically constructing content.
1346    The member <lit>content</lit> of an <pyref class="Element"><class>Element</class></pyref> is a <class>Frag</class>.</par>
1347    """
1348
1349    def __init__(self, *content):
1350        list.__init__(self)
1351        for child in content:
1352            child = tonode(child)
1353            if isinstance(child, Frag):
1354                list.extend(self, child)
1355            elif child is not Null:
1356                list.append(self, child)
1357
1358    def __enter__(self):
1359        stack = getstack()
1360        if stack:
1361            stack[-1].append(self)
1362        stack.append(self)
1363        return self
1364
1365    def __exit__(self, type, value, traceback):
1366        getstack().pop()
1367
1368    def __call__(self, *content):
1369        self.extend(content)
1370        return self
1371
1372    @classmethod
1373    def _str(cls, fullname=True, xml=True, decorate=True):
1374        s = cls._strbase(fullname=fullname, xml=xml)
1375        if decorate:
1376            s = "<%s>" % s
1377        return s
1378
1379    def _create(self):
1380        """
1381        <par>internal helper that is used to create an empty clone of <self/>.
1382        This is overwritten by <pyref class="Attr"><class>Attr</class></pyref>
1383        to insure that attributes don't get initialized with the default
1384        value when used in various methods that create new attributes.</par>
1385        """
1386        return self.__class__()
1387
1388    def clear(self):
1389        """
1390        Make <self/> empty.
1391        """
1392        del self[:]
1393
1394    def convert(self, converter):
1395        node = self._create()
1396        for child in self:
1397            convertedchild = child.convert(converter)
1398            assert isinstance(convertedchild, Node), "the convert method returned the illegal object %r (type %r) when converting %r" % (convertedchild, type(convertedchild), self)
1399            node.append(convertedchild)
1400        return self._decoratenode(node)
1401
1402    def clone(self):
1403        node = self._create()
1404        list.extend(node, (child.clone() for child in self))
1405        return self._decoratenode(node)
1406
1407    def __copy__(self):
1408        """
1409        helper for the <pyref module="copy"><module>copy</module></pyref> module.
1410        """
1411        node = self._create()
1412        list.extend(node, self)
1413        return self._decoratenode(node)
1414
1415    def __deepcopy__(self, memo=None):
1416        """
1417        helper for the <pyref module="copy"><module>copy</module></pyref> module.
1418        """
1419        node = self._create()
1420        if memo is None:
1421            memo = {}
1422        memo[id(self)] = node
1423        list.extend(node, (copy.deepcopy(child, memo) for child in self))
1424        return self._decoratenode(node)
1425
1426    def present(self, presenter):
1427        return presenter.presentFrag(self) # return a generator-iterator
1428
1429    def __unicode__(self):
1430        return u"".join(unicode(child) for child in self)
1431
1432    def __eq__(self, other):
1433        return self.__class__ is other.__class__ and list.__eq__(self, other)
1434
1435    def publish(self, publisher):
1436        for child in self:
1437            for part in child.publish(publisher):
1438                yield part
1439
1440    def __getitem__(self, index):
1441        """
1442        <par>Return the <arg>index</arg>'th node for the content of the fragment.
1443        If <arg>index</arg> is a list <method>__getitem__</method> will work
1444        recursively. If <arg>index</arg> is an empty list, <self/> will be returned.</par>
1445        """
1446        if isinstance(index, list):
1447            node = self
1448            for subindex in index:
1449                node = node[subindex]
1450            return node
1451        elif isinstance(index, type) and issubclass(index, Node):
1452            def iterate(self, index):
1453                for child in self:
1454                    if isinstance(child, index):
1455                        yield child
1456            return misc.Iterator(iterate(self, index))
1457        elif isinstance(index, slice):
1458            return self.__class__(list.__getitem__(self, index))
1459        else:
1460            return list.__getitem__(self, index)
1461
1462    def __setitem__(self, index, value):
1463        """
1464        <par>Allows you to replace the <arg>index</arg>'th content node of the fragment
1465        with the new value <arg>value</arg> (which will be converted to a node).
1466        If  <arg>index</arg> is a list <method>__setitem__</method> will be applied
1467        to the innermost index after traversing the rest of <arg>index</arg> recursively.
1468        If <arg>index</arg> is an empty list, an exception will be raised.</par>
1469        """
1470        if isinstance(index, list):
1471            if not index:
1472                raise ValueError("can't replace self")
1473            node = self
1474            for subindex in index[:-1]:
1475                node = node[subindex]
1476            node[index[-1]] = value
1477        else:
1478            value = Frag(value)
1479            if isinstance(index, slice):
1480                list.__setitem__(self, index, value)
1481            else:
1482                if index==-1:
1483                    l = len(self)
1484                    list.__setslice__(self, l-1, l, value)
1485                else:
1486                    list.__setslice__(self, index, index+1, value)
1487
1488    def __delitem__(self, index):
1489        """
1490        <par>Remove the <arg>index</arg>'th content node from the fragment.
1491        If <arg>index</arg> is a list, the innermost index will be deleted,
1492        after traversing the rest of <arg>index</arg> recursively.
1493        If <arg>index</arg> is an empty list, an exception will be raised.</par>
1494        """
1495        if isinstance(index, list):
1496            if not index:
1497                raise ValueError("can't delete self")
1498            node = self
1499            for subindex in index[:-1]:
1500                node = node[subindex]
1501            del node[index[-1]]
1502        else:
1503            list.__delitem__(self, index)
1504
1505    def __getslice__(self, index1, index2):
1506        """
1507        Returns slice of the content of the fragment.
1508        """
1509        node = self._create()
1510        list.extend(node, list.__getslice__(self, index1, index2))
1511        return node
1512
1513    def __setslice__(self, index1, index2, sequence):
1514        """
1515        Replace a slice of the content of the fragment
1516        """
1517        list.__setslice__(self, index1, index2, Frag(sequence))
1518
1519    # no need to implement __delslice__
1520
1521    def __mul__(self, factor):
1522        """
1523        Return a <pyref class="Frag"><class>Frag</class></pyref> with
1524        <arg>factor</arg> times the content of <self/>. Note that no copies of the
1525        content will be generated, so this is a <z>shallow <method>__mul__</method></z>.
1526        """
1527        node = self._create()
1528        list.extend(node, list.__mul__(self, factor))
1529        return node
1530
1531    __rmul__ = __mul__
1532
1533    def __iadd__(self, other):
1534        self.extend(other)
1535        return self
1536
1537    # no need to implement __len__ or __nonzero__
1538
1539    def append(self, *others):
1540        """
1541        <par>Append every item in <arg>others</arg> to <self/>.</par>
1542        """
1543        for other in others:
1544            other = tonode(other)
1545            if isinstance(other, Frag):
1546                list.extend(self, other)
1547            elif other is not Null:
1548                list.append(self, other)
1549
1550    def extend(self, items):
1551        """
1552        <par>Append all items from the sequence <arg>items</arg> to <self/>.</par>
1553        """
1554        self.append(items)
1555
1556    def insert(self, index, *others):
1557        """
1558        <par>Insert all items in <arg>others</arg> at the position <arg>index</arg>.
1559        (this is the same as <lit><self/>[<arg>index</arg>:<arg>index</arg>] = <arg>others</arg></lit>)
1560        """
1561        other = Frag(*others)
1562        list.__setslice__(self, index, index, other)
1563
1564    def _walk(self, filter, path):
1565        path.append(None)
1566        for child in self:
1567            path[-1] = child
1568            for result in child._walk(filter, path):
1569                yield result
1570        path.pop()
1571
1572    def compact(self):
1573        node = self._create()
1574        for child in self:
1575            compactedchild = child.compact()
1576            assert isinstance(compactedchild, Node), "the compact method returned the illegal object %r (type %r) when compacting %r" % (compactedchild, type(compactedchild), child)
1577            if compactedchild is not Null:
1578                list.append(node, compactedchild)
1579        return self._decoratenode(node)
1580
1581    def withsep(self, separator, clone=False):
1582        """
1583        <par>Return a version of <self/> with a separator node between the nodes of <self/>.</par>
1584
1585        <par>if <arg>clone</arg> is false one node will be inserted several times,
1586        if <arg>clone</arg> is true, clones of this node will be used.</par>
1587        """
1588        node = self._create()
1589        newseparator = tonode(separator)
1590        for child in self:
1591            if len(node):
1592                node.append(newseparator)
1593                if clone:
1594                    newseparator = newseparator.clone()
1595            node.append(child)
1596        return node
1597
1598    def sorted(self, cmp=None, key=None, reverse=False):
1599        """
1600        <par>Return a sorted version of the <self/>. <arg>cmp</arg>, <arg>key</arg>
1601        and <arg>reverse</arg> have to same meaning as for the builtin function
1602        <function>sorted</function>.
1603        """
1604        return self.__class__(sorted(self, cmp, key, reverse))
1605
1606    def reversed(self):
1607        """
1608        <par>Return a reversed version of the <self/>.</par>
1609        """
1610        node = list(self)
1611        node.reverse()
1612        return self.__class__(node)
1613
1614    def filtered(self, function):
1615        """
1616        <par>Return a filtered version of the <self/>, i.e. a copy of <self/>,
1617        where only content nodes for which <function>function</function> returns
1618        true will be copied.</par>
1619        """
1620        node = self._create()
1621        list.extend(node, (child for child in self if function(child)))
1622        return node
1623
1624    def shuffled(self):
1625        """
1626        <par>Return a shuffled version of <self/>, i.e. a copy of <self/> where
1627        the content nodes are randomly reshuffled.</par>
1628        """
1629        content = list(self)
1630        node = self._create()
1631        while content:
1632            index = random.randrange(len(content))
1633            list.append(node, content[index])
1634            del content[index]
1635        return node
1636
1637    def mapped(self, function, converter=None, **converterargs):
1638        if converter is None:
1639            converter = converters.Converter(**converterargs)
1640        node = function(self, converter)
1641        assert isinstance(node, Node), "the mapped method returned the illegal object %r (type %r) when mapping %r" % (node, type(node), self)
1642        if node is self:
1643            node = self._create()
1644            for child in self:
1645                node.append(child.mapped(function, converter))
1646        return node
1647
1648    def normalized(self):
1649        node = self._create()
1650        lasttypeOK = False
1651        for child in self:
1652            normalizedchild = child.normalized()
1653            thistypeOK = isinstance(normalizedchild, Text)
1654            if thistypeOK and lasttypeOK:
1655                node[-1] += normalizedchild
1656            else:
1657                list.append(node, normalizedchild)
1658            lasttypeOK = thistypeOK
1659        return node
1660
1661    def pretty(self, level=0, indent="\t"):
1662        node = self._create()
1663        for (i, child) in enumerate(self):
1664            if i:
1665                node.append("\n")
1666            node.append(child.pretty(level, indent))
1667        return node
1668
1669    def __repr__(self):
1670        l = len(self)
1671        if l==0:
1672            info = "no children"
1673        elif l==1:
1674            info = "1 child"
1675        else:
1676            info = "%d children" % l
1677        loc = " (from %s)" % self.startloc if self.startloc is not None else ""
1678        return "<%s.%s object (%s)%s at 0x%x>" % (self.__class__.__module__, self.__fullname__, info, loc, id(self))
1679
1680
1681class Comment(CharacterData):
1682    """
1683    An &xml; comment.
1684    """
1685
1686    def convert(self, converter):
1687        return self
1688
1689    def __unicode__(self):
1690        return u""
1691
1692    def present(self, presenter):
1693        return presenter.presentComment(self)  # return a generator-iterator
1694
1695    def publish(self, publisher):
1696        if publisher.inattr:
1697            raise IllegalAttrNodeError(self)
1698        content = self.content
1699        if u"--" in content or content.endswith(u"-"):
1700            warnings.warn(IllegalCommentContentWarning(self))
1701        yield publisher.encode(u"<!--")
1702        yield publisher.encode(content)
1703        yield publisher.encode(u"-->")
1704
1705
1706class _DocType_Meta(Node.__metaclass__):
1707    def __repr__(self):
1708        return "<doctype class %s:%s at 0x%x>" % (self.__module__, self.__fullname__, id(self))
1709
1710
1711class DocType(CharacterData):
1712    """
1713    An &xml; document type declaration.
1714    """
1715
1716    __metaclass__ = _DocType_Meta
1717
1718    def convert(self, converter):
1719        return self
1720
1721    def present(self, presenter):
1722        return presenter.presentDocType(self) # return a generator-iterator
1723
1724    def publish(self, publisher):
1725        if publisher.inattr:
1726            raise IllegalAttrNodeError(self)
1727        yield publisher.encode(u"<!DOCTYPE ")
1728        yield publisher.encode(self.content)
1729        yield publisher.encode(u">")
1730
1731    def __unicode__(self):
1732        return u""
1733
1734
1735class _ProcInst_Meta(Node.__metaclass__):
1736    def __new__(cls, name, bases, dict):
1737        self = super(_ProcInst_Meta, cls).__new__(cls, name, bases, dict)
1738        if dict.get("register") is not None: # check here as getpoolstack isn't defined yet
1739            getpoolstack()[-1].register(self)
1740        return self
1741
1742    def __repr__(self):
1743        return "<procinst class %s:%s at 0x%x>" % (self.__module__, self.__fullname__, id(self))
1744
1745
1746class ProcInst(CharacterData):
1747    """
1748    <par>Base class for processing instructions. This class is abstract.</par>
1749
1750    <par>Processing instructions for specific targets must
1751    be implemented as subclasses of <class>ProcInst</class>.</par>
1752    """
1753    __metaclass__ = _ProcInst_Meta
1754
1755    register = None
1756
1757    @classmethod
1758    def _str(cls, fullname=True, xml=True, decorate=True):
1759        s = cls._strbase(fullname=fullname, xml=xml)
1760        if decorate:
1761            s = "<%s>" % s
1762        return s
1763
1764    def convert(self, converter):
1765        return self
1766
1767    def present(self, presenter):
1768        return presenter.presentProcInst(self) # return a generator-iterator
1769
1770    def publish(self, publisher):
1771        content = self.content
1772        if u"?>" in content:
1773            raise IllegalProcInstFormatError(self)
1774        yield publisher.encode(u"<?")
1775        yield publisher.encode(self.xmlname)
1776        yield publisher.encode(u" ")
1777        yield publisher.encode(content)
1778        yield publisher.encode(u"?>")
1779
1780    def __unicode__(self):
1781        return u""
1782
1783    def __repr__(self):
1784        if self.startloc is not None:
1785            loc = " (from %s)" % self.startloc
1786        else:
1787            loc = ""
1788        return "<%s.%s procinst content=%r%s at 0x%x>" % (self.__class__.__module__, self.__fullname__, self.content, loc, id(self))
1789
1790
1791class Null(CharacterData):
1792    """
1793    node that does not contain anything.
1794    """
1795
1796    @classmethod
1797    def _str(cls, fullname=True, xml=True, decorate=True):
1798        s = cls._strbase(fullname=fullname, xml=xml)
1799        if decorate:
1800            s = "<%s>" % s
1801        return s
1802
1803    def convert(self, converter):
1804        return self
1805
1806    def publish(self, publisher):
1807        if False:
1808            yield ""
1809
1810    def present(self, presenter):
1811        return presenter.presentNull(self) # return a generator-iterator
1812
1813    def __unicode__(self):
1814        return u""
1815
1816    def __repr__(self):
1817        return "<null>"
1818
1819
1820Null = Null() # Singleton, the Python way
1821
1822
1823class _Attr_Meta(Frag.__metaclass__, xfind.Operator):
1824    def __new__(cls, name, bases, dict):
1825        # can be overwritten in subclasses, to specify that this attributes is required
1826        if "required" in dict:
1827            dict["required"] = bool(dict["required"])
1828        # convert the default to a Frag
1829        if "default" in dict:
1830            dict["default"] = Frag(dict["default"])
1831        # convert the entries in values to unicode
1832        if "values" in dict:
1833            values = dict["values"]
1834            if values is not None:
1835                dict["values"] = tuple(unicode(entry) for entry in values)
1836        self = super(_Attr_Meta, cls).__new__(cls, name, bases, dict)
1837        if self.xmlns is not None:
1838            getpoolstack()[-1].register(self)
1839        return self
1840
1841    def xwalk(self, iterator):
1842        for child in iterator:
1843            if isinstance(child, Element):
1844                for (attrname, attrvalue) in child.attrs.iteritems():
1845                    if isinstance(attrvalue, self):
1846                        yield attrvalue
1847
1848    def __repr__(self):
1849        return "<attribute class %s:%s at 0x%x>" % (self.__module__, self.__fullname__, id(self))
1850
1851
1852class Attr(Frag):
1853    """
1854    <par>Base class of all attribute classes.</par>
1855
1856    <par>The content of an attribute may be any other &xist; node. This is different from
1857    a normal &dom;, where only text and character references are allowed. The reason for
1858    this is to allow dynamic content (implemented as elements or processing instructions)
1859    to be put into attributes.</par>
1860
1861    <par>Of course, this dynamic content when finally converted to &html; will normally result in
1862    a fragment consisting only of text and character references. But note that it is allowed
1863    to have elements and processing instructions inside of attributes even when publishing.
1864    Processing instructions will be published as is and for elements their content will be
1865    published.</par>
1866    <example><title>Elements inside attributes</title>
1867    <tty>
1868    <prompt>&gt;&gt;&gt; </prompt><input>from ll.xist.ns import html, php</input>
1869    <prompt>&gt;&gt;&gt; </prompt><input>node = html.img(</input>
1870    <prompt>... </prompt><input>   src=php.php("echo 'eggs.gif'"),</input>
1871    <prompt>... </prompt><input>   alt=html.abbr(</input>
1872    <prompt>... </prompt><input>      "EGGS",</input>
1873    <prompt>... </prompt><input>      title="Extensible Graphics Generation System",</input>
1874    <prompt>... </prompt><input>      lang="en"</input>
1875    <prompt>... </prompt><input>   )</input>
1876    <prompt>... </prompt><input>)</input>
1877    &gt;&gt;&gt; print node.asBytes()
1878    &lt;img alt="EGGS" src="&lt;?php echo 'eggs.gif'?&gt;" /&gt;
1879    </tty>
1880    </example>
1881    """
1882    __metaclass__ = _Attr_Meta
1883    required = False
1884    default = None
1885    values = None
1886
1887    def isfancy(self):
1888        """
1889        <par>Return whether <self/> contains nodes other than
1890        <pyref class="Text"><class>Text</class></pyref>.</par>
1891        """
1892        for child in self:
1893            if not isinstance(child, Text):
1894                return True
1895        return False
1896
1897    @classmethod
1898    def _str(cls, fullname=True, xml=True, decorate=True):
1899        return cls._strbase(fullname=fullname, xml=xml)
1900
1901    def present(self, presenter):
1902        return presenter.presentAttr(self) # return a generator-iterator
1903
1904    def checkvalid(self):
1905        """
1906        <par>Check whether <self/> has an allowed value, i.e. one
1907        that is specified in the class attribute <lit>values</lit>.
1908        If the value is not allowed a warning will be issued through
1909        the Python warning framework.</par>
1910        <par>If <self/> is <pyref method="isfancy">isfancy</pyref>,
1911        no check will be done.</par>
1912        """
1913        values = self.__class__.values
1914        if len(self) and isinstance(values, tuple) and not self.isfancy():
1915            value = unicode(self)
1916            if value not in values:
1917                warnings.warn(IllegalAttrValueWarning(self))
1918
1919    def _walk(self, filter, path):
1920        if callable(filter):
1921            found = filter(path)
1922        else:
1923            found = filter
1924
1925        for option in found:
1926            if option is entercontent:
1927                for result in Frag._walk(self, filter, path):
1928                    yield result
1929            elif option is enterattrs:
1930                pass
1931            elif option:
1932                yield path
1933
1934    def _publishname(self, publisher):
1935        if self.xmlns is not None:
1936            prefix = publisher._ns2prefix.get(self.xmlns) if self.xmlns != xml_xmlns else u"xml"
1937            if prefix is not None:
1938                return u"%s:%s" % (prefix, self.xmlname)
1939        return self.xmlname
1940
1941    def _publishattrvalue(self, publisher):
1942        # Internal helper that is used to publish the attribute value
1943        # (can be overwritten in subclass (done by e.g. StyleAttr and URLAttr)
1944        return Frag.publish(self, publisher)
1945
1946    def publish(self, publisher):
1947        if publisher.validate:
1948            self.checkvalid()
1949        publisher.inattr += 1
1950        yield publisher.encode(self._publishname(publisher)) # publish the XML name, not the Python name
1951        yield publisher.encode(u"=\"")
1952        publisher.pushtextfilter(helpers.escapeattr)
1953        for part in self._publishattrvalue(publisher):
1954            yield part
1955        publisher.poptextfilter()
1956        yield publisher.encode(u"\"")
1957        publisher.inattr -= 1
1958
1959    def pretty(self, level=0, indent="\t"):
1960        return self.clone()
1961
1962    def __repr__(self):
1963        l = len(self)
1964        if l==0:
1965            info = u"no children"
1966        elif l==1:
1967            info = u"1 child"
1968        else:
1969            info = u"%d children" % l
1970        loc = " (from %s)" % self.startloc if self.startloc is not None else ""
1971        return "<%s.%s attr object (%s)%s at 0x%x>" % (self.__class__.__module__, self.__fullname__, info, loc, id(self))
1972
1973
1974class TextAttr(Attr):
1975    """
1976    <par>Attribute class that is used for normal text attributes.</par>
1977    """
1978
1979
1980class IDAttr(Attr):
1981    """
1982    <par>Attribute used for ids.</par>
1983    """
1984
1985
1986class NumberAttr(Attr):
1987    """
1988    <par>Attribute class that is used for when the attribute value may be any kind
1989    of number.</par>
1990    """
1991
1992
1993class IntAttr(NumberAttr):
1994    """
1995    <par>Attribute class that is used when the attribute value may be an
1996    integer.</par>
1997    """
1998
1999
2000class FloatAttr(NumberAttr):
2001    """
2002    <par>Attribute class that is used when the attribute value may be a
2003    floating point value.</par>
2004    """
2005
2006
2007class BoolAttr(Attr):
2008    """
2009    <par>Attribute class that is used for boolean attributes. When publishing
2010    the value will always be the attribute name, regardless of the real value.</par>
2011    """
2012
2013    # We can't simply overwrite _publishattrvalue(), because for xhtml==0 we don't output a "proper" attribute
2014    def publish(self, publisher):
2015        if publisher.validate:
2016            self.checkvalid()
2017        publisher.inattr += 1
2018        name = self._publishname(publisher)
2019        yield publisher.encode(name) # publish the XML name, not the Python name
2020        if publisher.xhtml>0:
2021            yield publisher.encode(u"=\"")
2022            publisher.pushtextfilter(helpers.escapeattr)
2023            yield publisher.encode(name)
2024            publisher.poptextfilter()
2025            yield publisher.encode(u"\"")
2026        publisher.inattr -= 1
2027
2028
2029class ColorAttr(Attr):
2030    """
2031    <par>Attribute class that is used for a color attributes.</par>
2032    """
2033
2034
2035class StyleAttr(Attr):
2036    """
2037    <par>Attribute class that is used for &css; style attributes.</par>
2038    """
2039
2040    def parsed(self, parser, start=None):
2041        if not self.isfancy():
2042            csshandler = cssparsers.ParseHandler(ignorecharset=True)
2043            value = csshandler.parseString(unicode(self), base=parser.base)
2044            return self.__class__(value)
2045        return self
2046
2047    def _publishattrvalue(self, publisher):
2048        if not self.isfancy():
2049            csshandler = cssparsers.PublishHandler(ignorecharset=True)
2050            value = csshandler.parseString(unicode(self), base=publisher.base)
2051            new = Frag(value)
2052            for part in new.publish(publisher):
2053                yield part
2054        else:
2055            for part in super(StyleAttr, self)._publishattrvalue(publisher):
2056                yield part
2057
2058    def urls(self, base=None):
2059        """
2060        <par>Return a list of all the <pyref module="ll.url" class="URL"><class>URL</class></pyref>s
2061        found in the style attribute.</par>
2062        """
2063        csshandler = cssparsers.CollectHandler(ignorecharset=True)
2064        csshandler.parseString(unicode(self), base=base)
2065        urls = csshandler.urls
2066        return urls
2067
2068
2069class URLAttr(Attr):
2070    """
2071    <par>Attribute class that is used for &url;s. See the module <pyref module="ll.url"><module>ll.url</module></pyref>
2072    for more information about &url; handling.</par>
2073    """
2074
2075    def parsed(self, parser, start=None):
2076        return self.__class__(utils.replaceInitialURL(self, lambda u: parser.base/u))
2077
2078    def _publishattrvalue(self, publisher):
2079        new = utils.replaceInitialURL(self, lambda u: u.relative(publisher.base))
2080        for part in new.publish(publisher):
2081            yield part
2082
2083    def asURL(self):
2084        """
2085        <par>Return <self/> as a <pyref module="ll.url" class="URL"><class>URL</class></pyref>
2086        instance (note that non-character content will be filtered out).</par>
2087        """
2088        return url_.URL(Attr.__unicode__(self))
2089
2090    def __unicode__(self):
2091        return self.asURL().url
2092
2093    def forInput(self, root=None):
2094        """
2095        <par>return a <pyref module="ll.url" class="URL"><class>URL</class></pyref> pointing
2096        to the real location of the referenced resource. <arg>root</arg> must be the
2097        root &url; relative to which <self/> will be interpreted and usually
2098        comes from the <lit>root</lit> attribute of the <arg>converter</arg> argument in
2099        <pyref class="Node" method="convert"><method>convert</method></pyref>.</par>
2100        """
2101        u = self.asURL()
2102        if u.scheme == "root":
2103            u.scheme = None
2104        u = url_.URL(root)/u
2105        return u
2106
2107    def imagesize(self, root=None):
2108        """
2109        Return the size of an image as a tuple.
2110        """
2111        return self.openread(root).imagesize
2112
2113    def contentlength(self, root=None):
2114        """
2115        Return the size of a file in bytes.
2116        """
2117        return self.openread(root).contentlength
2118
2119    def lastmodified(self, root=None):
2120        """
2121        returns the timestamp for the last modification to the file
2122        """
2123        return self.openread(root).lastmodified
2124
2125    def openread(self, root=None):
2126        """
2127        Return a <pyref module="ll.url" class="ReadResource"><class>ReadResource</class></pyref>
2128        for reading from the &url;.
2129        """
2130        return self.forInput(root).openread()
2131
2132    def openwrite(self, root=None):
2133        """
2134        Return a <pyref module="ll.url" class="WriteResource"><class>WriteResource</class></pyref>
2135        for writing to the &url;.
2136        """
2137        return self.forInput(root).openwrite()
2138
2139
2140class _Attrs_Meta(Node.__metaclass__):
2141    def __new__(cls, name, bases, dict):
2142        self = super(_Attrs_Meta, cls).__new__(cls, name, bases, dict)
2143        self._byxmlname = weakref.WeakValueDictionary() # map XML name to attribute class
2144        self._bypyname = weakref.WeakValueDictionary() # map Python name to attribute class
2145        self._defaultattrsxml = weakref.WeakValueDictionary() # map XML name to attribute class with default value
2146        self._defaultattrspy = weakref.WeakValueDictionary() # map Python name to attribute class with default value
2147
2148        # go through the attributes and register them in the cache
2149        for key in dir(self):
2150            value = getattr(self, key)
2151            if isinstance(value, type) and issubclass(value, Attr):
2152                self.add(value)
2153        return self
2154
2155    def __repr__(self):
2156        return "<attrs class %s:%s with %s attrs at 0x%x>" % (self.__module__, self.__fullname__, len(self._bypyname), id(self))
2157
2158    def __contains__(self, key):
2159        if isinstance(key, basestring):
2160            return key in self._bypyname
2161        if key.xmlns is not None:
2162            return True
2163        return self._bypyname.get(key.__name__, None) is key
2164
2165
2166class Attrs(Node, dict):
2167    """
2168    <par>An attribute map. Allowed entries are specified through nested subclasses
2169    of <pyref class="Attr"><class>Attr</class></pyref>.</par>
2170    """
2171    __metaclass__ = _Attrs_Meta
2172
2173    def __init__(self, _content=None, **attrs):
2174        dict.__init__(self)
2175        # set default attribute values
2176        for (key, value) in self._defaultattrspy.iteritems():
2177            self[key] = value.default.clone()
2178        # set attributes, this might overwrite (or delete) default attributes
2179        self.update(_content, **attrs)
2180
2181    def __eq__(self, other):
2182        return self.__class__ is other.__class__ and dict.__eq__(self, other)
2183
2184    @classmethod
2185    def _str(cls, fullname=True, xml=True, decorate=True):
2186        return cls._strbase(fullname=fullname, xml=xml)
2187
2188    @classmethod
2189    def add(cls, value):
2190        cls._byxmlname[value.xmlname] = value
2191        cls._bypyname[value.__name__] = value
2192        if value.default:
2193            cls._defaultattrsxml[value.xmlname] = value
2194            cls._defaultattrspy[value.__name__] = value
2195        # fix classname (but don't patch inherited attributes)
2196        if "." not in value.__fullname__:
2197            value.__fullname__ = "%s.%s" % (cls.__fullname__, value.__fullname__)
2198
2199    def _create(self):
2200        node = self.__class__() # "virtual" constructor
2201        node.clear()
2202        return node
2203
2204    def clone(self):
2205        node = self._create()
2206        for value in dict.values(self):
2207            dict.__setitem__(node, value.__class__, value.clone())
2208        return self._decoratenode(node)
2209
2210    def __copy__(self):
2211        node = self._create()
2212        for value in dict.values(self):
2213            dict.__setitem__(node, value.__class__, value)
2214        return self._decoratenode(node)
2215
2216    def __deepcopy__(self, memo=None):
2217        node = self._create()
2218        if memo is None:
2219            memo = {}
2220        memo[id(self)] = node
2221        for value in dict.values(self):
2222            dict.__setitem__(node, value.__class__, copy.deepcopy(value, memo))
2223        return self._decoratenode(node)
2224
2225    def convert(self, converter):
2226        node = self._create()
2227        for value in self.values():
2228            newvalue = value.convert(converter)
2229            assert isinstance(newvalue, Node), "the convert method returned the illegal object %r (type %r) when converting the attribute %s with the value %r" % (newvalue, type(newvalue), value.__class__.__name__, value)
2230            node[value.__class__] = newvalue
2231        return node
2232
2233    def compact(self):
2234        node = self._create()
2235        for value in self.values():
2236            newvalue = value.compact()
2237            assert isinstance(newvalue, Node), "the compact method returned the illegal object %r (type %r) when compacting the attribute %s with the value %r" % (newvalue, type(newvalue), value.__class__.__name__, value)
2238            node[value.__class__] = newvalue
2239        return node
2240
2241    def normalized(self):
2242        node = self._create()
2243        for value in self.values():
2244            newvalue = value.normalized()
2245            assert isinstance(newvalue, Node), "the normalized method returned the illegal object %r (type %r) when normalizing the attribute %s with the value %r" % (newvalue, type(newvalue), value.__class__.__name__, value)
2246            node[value.__class__] = newvalue
2247        return node
2248
2249    def _walk(self, filter, path):
2250        path.append(None)
2251        for child in self.values():
2252            path[-1] = child
2253            for result in child._walk(filter, path):
2254                yield result
2255        path.pop()
2256
2257    def present(self, presenter):
2258        return presenter.presentAttrs(self) # return a generator-iterator
2259
2260    def checkvalid(self):
2261        # collect required attributes
2262        attrs = set()
2263        for value in self.allowedattrs():
2264            if value.required:
2265                attrs.add(value)
2266        # Check each attribute and remove it from the list of required ones
2267        for value in self.values():
2268            value.checkvalid()
2269            try:
2270                attrs.remove(value.__class__)
2271            except KeyError:
2272                pass
2273        # are there any required attributes remaining that haven't been specified? => warn about it
2274        if attrs:
2275            warnings.warn(RequiredAttrMissingWarning(self, list(attrs)))
2276
2277    def publish(self, publisher):
2278        if publisher.validate:
2279            self.checkvalid()
2280        for value in self.values():
2281            yield publisher.encode(u" ")
2282            for part in value.publish(publisher):
2283                yield part
2284
2285    def __unicode__(self):
2286        return u""
2287
2288    @classmethod
2289    def isallowed(cls, name):
2290        if isinstance(name, basestring):
2291            return name in cls._bypyname
2292        if name.xmlns is not None:
2293            return True
2294        try:
2295            candidate = cls._bypyname[name.__name__]
2296        except KeyError:
2297            return False
2298        # make sure that both Python name and XML name match
2299        return candidate.xmlname == name.xmlname
2300
2301    @classmethod
2302    def isallowed_xml(cls, name, xmlns=None):
2303        if isinstance(name, basestring):
2304            return name in cls._byxmlname
2305        if name.xmlns is not None:
2306            return True
2307        try:
2308            candidate = cls._bypyname[name.__name__]
2309        except KeyError:
2310            return False
2311        # make sure that both Python name and XML name match
2312        return candidate.xmlname == name.xmlname
2313
2314    def __getattribute__(self, name):
2315        sup = super(Attrs, self)
2316        if name in sup.__getattribute__("_bypyname"): # avoid recursion
2317            return self.__getitem__(name)
2318        else:
2319            return sup.__getattribute__(name)
2320
2321    def __setattr__(self, name, value):
2322        sup = super(Attrs, self)
2323        if name in sup.__getattribute__("_bypyname"): # avoid recursion
2324            return self.__setitem__(name, value)
2325        else:
2326            return sup.__setattr__(name, value)
2327
2328    def __delattr__(self, name):
2329        sup = super(Attrs, self)
2330        if name in sup.__getattribute__("_bypyname"): # avoid recursion
2331            return self.__detitem__(name)
2332        else:
2333            return sup.__delattr__(name)
2334
2335    def __getitem__(self, name):
2336        if isinstance(name, list):
2337            node = self
2338            for subname in name:
2339                node = node[subname]
2340            return node
2341        return self.attr(name)
2342
2343    def __setitem__(self, name, value):
2344        if isinstance(name, list):
2345            if not name:
2346                raise ValueError("can't replace self")
2347            node = self
2348            for subname in name[:-1]:
2349                node = node[subname]
2350            node[name[-1]] = value
2351        return self.set(name, value)
2352
2353    def __delitem__(self, name):
2354        if isinstance(name, list):
2355            if not name:
2356                raise ValueError("can't delete self")
2357            node = self
2358            for subname in name[:-1]:
2359                node = node[subname]
2360            del node[name[-1]]
2361        dict.__delitem__(self, self.allowedattr(name))
2362
2363    def has(self, name):
2364        """
2365        <par>return whether <self/> has an attribute with a Python name <arg>name</arg>.
2366        <arg>name</arg> may also be an attribute class (either from <lit><self/>.Attrs</lit>
2367        are a global attribute).</par>
2368        """
2369        try:
2370            attr = dict.__getitem__(self, self.allowedattr(name))
2371        except KeyError:
2372            return False
2373        return len(attr)>0
2374
2375    def has_xml(self, name):
2376        """
2377        <par>return whether <self/> has an attribute with an XML name <arg>name</arg>.
2378        <arg>name</arg> may also be an attribute class (either from <lit><self/>.Attrs</lit>
2379        or for a global attribute).</par>
2380        """
2381        try:
2382            attr = dict.__getitem__(self, self.allowedattr_xml(name))
2383        except KeyError:
2384            return False
2385        return len(attr)>0
2386
2387    def __contains__(self, name):
2388        return self.has(name)
2389
2390    def get(self, name, default=None):
2391        """
2392        <par>works like the dictionary method <method>get</method>,
2393        it returns the attribute with the Python name <arg>name</arg>,
2394        or <arg>default</arg> if <self/> has no such attribute.
2395        <arg>name</arg> may also be an attribute class (either from
2396        <lit><self/>.Attrs</lit> or for a global attribute).</par>
2397        """
2398        attr = self.attr(name)
2399        if not attr:
2400            attr = self.allowedattr(name)(default) # pack the attribute into an attribute object
2401        return attr
2402
2403    def get_xml(self, name, default=None):
2404        """
2405        <par>works like the dictionary method <method>get</method>,
2406        it returns the attribute with the XML name <arg>name</arg>,
2407        or <arg>default</arg> if <self/> has no such attribute.
2408        <arg>name</arg> may also be an attribute class (either from
2409        <lit><self/>.Attrs</lit> or for a global attribute).</par>
2410        """
2411        attr = self.attr_xml(name)
2412        if not attr:
2413            attr = self.allowedattr_xml(name)(default) # pack the attribute into an attribute object
2414        return attr
2415
2416    def set(self, name, value):
2417        """
2418        <par>Set the attribute with the Python <arg>name</arg> to the value <arg>value</arg>.
2419        <arg>name</arg> may be a string or an attribute class.</par>
2420        <par>The newly set attribute will be returned.</par>
2421        """
2422        attr = self.allowedattr(name)
2423        value = attr(value)
2424        dict.__setitem__(self, attr, value) # put the attribute in our dict
2425        return value
2426
2427    def set_xml(self, name, value):
2428        """
2429        <par>Set the attribute with the XML <arg>name</arg> to the value <arg>value</arg>.
2430        <par>The newly set attribute will be returned.</par>
2431        """
2432        attr = self.allowedattr_xml(name)
2433        value = attr(value)
2434        dict.__setitem__(self, attr, value) # put the attribute in our dict
2435        return value
2436
2437    def setdefault(self, name, default):
2438        """
2439        <par>works like the dictionary method <method>setdefault</method>,
2440        it returns the attribute with the Python name <arg>name</arg>.
2441        If <self/> has no such attribute, it will be set to <arg>default</arg>
2442        and <arg>default</arg> will be returned as the new attribute value.</par>
2443        """
2444        value = self.attr(name)
2445        if not value:
2446            attr = self.allowedattr(name)
2447            value = attr(default) # pack the attribute into an attribute object
2448            dict.__setitem__(self, attr, value)
2449        return value
2450
2451    def setdefault_xml(self, name, default):
2452        """
2453        <par>works like the dictionary method <method>setdefault</method>,
2454        it returns the attribute with the XML name <arg>name</arg>.
2455        If <self/> has no such attribute, it will be set to <arg>default</arg>
2456        and <arg>default</arg> will be returned as the new attribute value.</par>
2457        """
2458        value = self.attr_xml(name)
2459        if not value:
2460            attr = self.allowedattr(name)
2461            value = attr(default) # pack the attribute into an attribute object
2462            dict.__setitem__(self, attr, value)
2463        return value
2464
2465    def update(self, *args, **kwargs):
2466        """
2467        Copies attributes over from all mappings in <arg>args</arg> and from <arg>kwargs</arg>.
2468        """
2469        for mapping in args + (kwargs,):
2470            if mapping is not None:
2471                if isinstance(mapping, Attrs):
2472                    # This makes sure that global attributes are copied properly
2473                    for value in mapping._iterallvalues():
2474                        self[value.__class__] = value
2475                else:
2476                    for (attrname, attrvalue) in mapping.iteritems():
2477                        self[attrname] = attrvalue
2478
2479    @classmethod
2480    def allowedattrs(cls):
2481        """
2482        <par>Return an iterator over all allowed attribute classes.
2483        """
2484        return cls._bypyname.itervalues()
2485
2486    @classmethod
2487    def allowedattr(cls, name):
2488        if isinstance(name, basestring):
2489            try:
2490                return cls._bypyname[name]
2491            except KeyError:
2492                raise IllegalAttrError(name, cls, False)
2493        # name is an attribute class
2494        if name.xmlns is not None: # if the attribute class is for a global attribute we accept it
2495            return name
2496        try:
2497            candidate = cls._bypyname[name.__name__]
2498        except KeyError:
2499            raise IllegalAttrError(name, cls, False)
2500        else:
2501            # make sure that both Python name and XML name match
2502            if candidate.xmlname == name.xmlname:
2503                return candidate
2504            raise IllegalAttrError(name, cls, False)
2505
2506    @classmethod
2507    def allowedattr_xml(cls, name):
2508        if isinstance(name, basestring):
2509            try:
2510                return cls._byxmlname[name]
2511            except KeyError:
2512                raise IllegalAttrError(name, cls, True)
2513        # name is an attribute class
2514        if name.xmlns is not None: # if the attribute class is for a global attribute we accept it
2515            return name
2516        try:
2517            candidate = cls._bypyname[name.__name__]
2518        except KeyError:
2519            raise IllegalAttrError(name, cls, False)
2520        else:
2521            # make sure that both Python name and XML name match
2522            if candidate.xmlname == name.xmlname:
2523                return candidate
2524            raise IllegalAttrError(name, cls, True)
2525
2526    def __len__(self):
2527        return misc.count(self.values())
2528
2529    def keys(self):
2530        for value in dict.itervalues(self):
2531            if value:
2532                yield value.__class__
2533
2534    iterkeys = __iter__ = keys
2535
2536    def values(self):
2537        for value in dict.itervalues(self):
2538            if value:
2539                yield value
2540
2541    itervalues = values
2542
2543    def items(self):
2544        for value in dict.itervalues(self):
2545            if value:
2546                yield (value.__class__, value)
2547
2548    iteritems = items
2549
2550    def _iterallvalues(self):
2551        """
2552        Iterate all values, even the unset ones
2553        """
2554        return dict.itervalues(self)
2555
2556    def attr(self, name):
2557        attr = self.allowedattr(name)
2558        try:
2559            value = dict.__getitem__(self, attr)
2560        except KeyError: # if the attribute is not there generate a new empty one
2561            value = attr()
2562            dict.__setitem__(self, attr, value)
2563        return value
2564
2565    def attr_xml(self, name):
2566        attr = self.allowedattr_xml(name)
2567        try:
2568            value = dict.__getitem__(self, attr)
2569        except KeyError: # if the attribute is not there generate a new empty one
2570            value = attr()
2571            dict.__setitem__(self, attr, value)
2572        return value
2573
2574    def filtered(self, function):
2575        """
2576        returns a filtered version of the <self/>.
2577        """
2578        node = self._create()
2579        for (name, value) in self.items():
2580            if function(value):
2581                node[name] = value
2582        return node
2583
2584    def _fixnames(self, names):
2585        newnames = []
2586        for name in names:
2587            if isinstance(name, basestring):
2588                try:
2589                    name = self.allowedattr(name)
2590                except IllegalAttrError:
2591                    continue
2592            newnames.append(name)
2593        return tuple(newnames)
2594
2595    def _fixnames_xml(self, names):
2596        newnames = []
2597        for name in names:
2598            if isinstance(name, basestring):
2599                try:
2600                    name = self.allowedattr_xml(name)
2601                except IllegalAttrError:
2602                    continue
2603            newnames.append(name)
2604        return tuple(newnames)
2605
2606    def withnames(self, *names):
2607        """
2608        <par>Return a copy of <self/> where only the attributes with Python names
2609        in <arg>names</arg> are kept, all others are removed.</par>
2610        """
2611        def isok(node):
2612            return isinstance(node, names)
2613
2614        names = self._fixnames(names)
2615        return self.filtered(isok)
2616
2617    def withnames_xml(self, *names):
2618        """
2619        <par>Return a copy of <self/> where only the attributes with XML names
2620        in <arg>names</arg> are kept, all others are removed.</par>
2621        """
2622        def isok(node):
2623            return isinstance(node, names)
2624
2625        names = self._fixnames_xml(names)
2626        return self.filtered(isok)
2627
2628    def withoutnames(self, *names):
2629        """
2630        <par>Return a copy of <self/> where all the attributes with Python names
2631        in <arg>names</arg> are removed.</par>
2632        """
2633        def isok(node):
2634            return not isinstance(node, names)
2635
2636        names = self._fixnames(names)
2637        return self.filtered(isok)
2638
2639    def withoutnames_xml(self, *names):
2640        """
2641        <par>Return a copy of <self/> where all the attributes with XML names
2642        in <arg>names</arg> are removed.</par>
2643        """
2644        def isok(node):
2645            return not isinstance(node, names)
2646
2647        names = self._fixnames_xml(names)
2648        return self.filtered(isok)
2649
2650    def __repr__(self):
2651        l = len(self)
2652        if l==0:
2653            info = "(no attrs)"
2654        elif l==1:
2655            info = "(1 attr)"
2656        else:
2657            info = "(%d attrs)" % l
2658        if self.startloc is not None:
2659            loc = " (from %s)" % self.startloc
2660        else:
2661            loc = ""
2662        return "<%s.%s attrs %s%s at 0x%x>" % (self.__class__.__module__, self.__fullname__, info, loc, id(self))
2663
2664
2665def _patchclassnames(dict, name):
2666    # If an Attrs class has been provided patch up its class names
2667    try:
2668        attrs = dict["Attrs"]
2669    except KeyError:
2670        pass
2671    else:
2672        attrs.__fullname__ = "%s.Attrs" % name
2673        for (key, value) in attrs.__dict__.iteritems():
2674            if isinstance(value, type) and issubclass(value, Attr):
2675                value.__fullname__ = "%s.%s" % (name, value.__fullname__)
2676
2677    # If a Context has been provided patch up its class names
2678    try:
2679        context = dict["Context"]
2680    except KeyError:
2681        pass
2682    else:
2683        context.__fullname__ = "%s.%s" % (name, context.__fullname__)
2684
2685
2686class _Element_Meta(Node.__metaclass__):
2687    def __new__(cls, name, bases, dict):
2688        if "model" in dict and isinstance(dict["model"], bool):
2689            from ll.xist import sims
2690            if dict["model"]:
2691                dict["model"] = sims.Any()
2692            else:
2693                dict["model"] = sims.Empty()
2694        _patchclassnames(dict, name)
2695        self = super(_Element_Meta, cls).__new__(cls, name, bases, dict)
2696        if dict.get("register") is not None:
2697            getpoolstack()[-1].register(self)
2698        return self
2699
2700    def __repr__(self):
2701        return "<element class %s:%s at 0x%x>" % (self.__module__, self.__fullname__, id(self))
2702
2703
2704class Element(Node):
2705    """
2706    <par>This class represents &xml;/&xist; elements. All elements implemented
2707    by the user must be derived from this class.</par>
2708
2709    <par>Elements support the following class variables:</par>
2710    <dlist>
2711    <term><lit>model</lit></term><item>This is an object that is used for
2712    validating the content of the element. See the module
2713    <pyref module="ll.xist.sims"><module>ll.xist.sims</module></pyref>
2714    for more info. If <lit>model</lit> is <lit>None</lit> validation will
2715    be skipped, otherwise it will be performed when parsing or publishing.</item>
2716
2717    <term><lit>Attrs</lit></term><item>This is a class derived from
2718    <pyref class="Element.Attrs"><class>Element.Attrs</class></pyref>
2719    and should define all attributes as classes nested inside this
2720    <class>Attrs</class> class.</item>
2721
2722    <term><lit>xmlns</lit></term><item>This is the name of the namespace this
2723    element belong to.</item>
2724
2725    <term><lit>register</lit></term><item>If <lit>register</lit> is false the
2726    element won't be registered with the parser.</item>
2727
2728    <term><lit>xmlname</lit></term><item>If the class name has to be different
2729    from the &xml; name (e.g. because the &xml; name is not a valid Python identifier)
2730    <lit>xmlname</lit> can be used to specify the real &xml; name.</item>
2731    </dlist>
2732    """
2733    __metaclass__ = _Element_Meta
2734
2735    model = None
2736    register = None
2737
2738    Attrs = Attrs
2739
2740    def __enter__(self):
2741        stack = getstack()
2742        if stack:
2743            stack[-1].append(self)
2744        stack.append(self)
2745        return self
2746
2747    def __exit__(self, type, value, traceback):
2748        getstack().pop()
2749
2750    def __init__(self, *content, **attrs):
2751        """
2752        <par>Create a new <class>Element</class> instance.</par>
2753
2754        <par>positional arguments are treated as content nodes.
2755        Keyword arguments and dictionaries are treated as attributes.</par>
2756        """
2757        self.attrs = self.Attrs()
2758        newcontent = []
2759        for child in content:
2760            if isinstance(child, dict):
2761                self.attrs.update(child)
2762            else:
2763                newcontent.append(child)
2764        self.content = Frag(*newcontent)
2765        self.attrs.update(attrs)
2766
2767    def __getstate__(self):
2768        attrs = {}
2769        for (key, value) in self.attrs.iteritems():
2770            if key.xmlns is None:
2771                key = key.__name__
2772            else:
2773                key = (key.__module__, key.__fullname__)
2774            attrs[key] = Frag(value)
2775        return (self.content, attrs)
2776
2777    def __setstate__(self, (content, attrs)):
2778        self.content = content
2779        self.attrs = self.Attrs()
2780        for (key, value) in attrs.iteritems():
2781            if not isinstance(key, basestring):
2782                obj = __import__(key[0])
2783                for name in key[0].split(".")[1:]:
2784                    obj = getattr(obj, name)
2785                for name in key[1].split("."):
2786                    obj = getattr(obj, name)
2787                key = obj
2788            self.attrs[key] = value
2789
2790    def __call__(self, *content, **attrs):
2791        for child in content:
2792            if isinstance(child, dict):
2793                self.attrs.update(child)
2794            else:
2795                self.content.append(child)
2796        for (attrname, attrvalue) in attrs.iteritems():
2797            self.attrs[attrname] = attrvalue
2798        return self
2799
2800    def __eq__(self, other):
2801        return self.__class__ is other.__class__ and self.content==other.content and self.attrs==other.attrs
2802
2803    @classmethod
2804    def _str(cls, fullname=True, xml=True, decorate=True):
2805        s = cls._strbase(fullname=fullname, xml=xml)
2806        if decorate:
2807            if cls.model is not None and cls.model.empty:
2808                s = "<%s/>" % s
2809            else:
2810                s = "<%s>" % s
2811        return s
2812
2813    def checkvalid(self):
2814        if self.model is not None:
2815            self.model.checkvalid(self)
2816        self.attrs.checkvalid()
2817
2818    def append(self, *items):
2819        """
2820        <par>Append every item in <arg>items</arg> to the element content.</par>
2821        """
2822        self.content.append(*items)
2823
2824    def extend(self, items):
2825        """
2826        <par>Append all items in <arg>items</arg> to element content.</par>
2827        """
2828        self.content.extend(items)
2829
2830    def insert(self, index, *items):
2831        """
2832        <par>Insert every item in <arg>items</arg> at the position <arg>index</arg>.</par>
2833        """
2834        self.content.insert(index, *items)
2835
2836    def convert(self, converter):
2837        node = self.__class__() # "virtual" constructor
2838        node.content = self.content.convert(converter)
2839        node.attrs = self.attrs.convert(converter)
2840        return self._decoratenode(node)
2841
2842    def clone(self):
2843        node = self.__class__() # "virtual" constructor
2844        node.content = self.content.clone() # this is faster than passing it in the constructor (no tonode call)
2845        node.attrs = self.attrs.clone()
2846        return self._decoratenode(node)
2847
2848    def __copy__(self):
2849        node = self.__class__()
2850        node.content = copy.copy(self.content)
2851        node.attrs = copy.copy(self.attrs)
2852        return self._decoratenode(node)
2853
2854    def __deepcopy__(self, memo=None):
2855        node = self.__class__()
2856        if memo is None:
2857            memo = {}
2858        memo[id(self)] = node
2859        node.content = copy.deepcopy(self.content, memo)
2860        node.attrs = copy.deepcopy(self.attrs, memo)
2861        return self._decoratenode(node)
2862
2863    def __unicode__(self):
2864        return unicode(self.content)
2865
2866    def _addimagesizeattributes(self, url, widthattr=None, heightattr=None):
2867        """
2868        <par>Automatically set image width and height attributes.</par>
2869
2870        <par>The size of the image with the &url; <arg>url</arg> will be determined and
2871        the width of the image will be put into the attribute with the name <arg>widthattr</arg>
2872        if <arg>widthattr</arg> is not <lit>None</lit> and the attribute is not set. The
2873        same will happen for the height, which will be put into the <arg>heighattr</arg>.</par>
2874        """
2875
2876        try:
2877            size = url.imagesize()
2878        except IOError, exc:
2879            warnings.warn(FileNotFoundWarning("can't read image", url, exc))
2880        else:
2881            for attr in (heightattr, widthattr):
2882                if attr is not None: # do something to the width/height
2883                    if not self.attrs.has(attr):
2884                        self[attr] = size[attr==heightattr]
2885
2886    def present(self, presenter):
2887        return presenter.presentElement(self) # return a generator-iterator
2888
2889    def _publishname(self, publisher):
2890        if self.xmlns is not None:
2891            prefix = publisher._ns2prefix.get(self.xmlns)
2892            if prefix is not None:
2893                return u"%s:%s" % (prefix, self.xmlname)
2894        return self.xmlname
2895
2896    def _publishfull(self, publisher):
2897        """
2898        Does the full publication of the element. If you need full elements
2899        inside attributes (e.g. for &jsp; tag libraries), you can overwrite
2900        <method>publish</method> and simply call this method.
2901        """
2902        name = self._publishname(publisher)
2903        yield publisher.encode(u"<")
2904        yield publisher.encode(name)
2905        # we're the first element to be published, so we have to create the xmlns attributes
2906        if publisher._publishxmlns:
2907            for (xmlns, prefix) in publisher._ns2prefix.iteritems():
2908                if xmlns not in publisher.hidexmlns:
2909                    yield publisher.encode(u" xmlns")
2910                    if prefix is not None:
2911                        yield publisher.encode(u":")
2912                        yield publisher.encode(prefix)
2913                    yield publisher.encode(u'="')
2914                    yield publisher.encode(xmlns)
2915                    yield publisher.encode('"')
2916            # reset the note, so the next element won't create the attributes again
2917            publisher._publishxmlns = False
2918        for part in self.attrs.publish(publisher):
2919            yield part
2920        if len(self):
2921            yield publisher.encode(u">")
2922            for part in self.content.publish(publisher):
2923                yield part
2924            yield publisher.encode(u"</")
2925            yield publisher.encode(name)
2926            yield publisher.encode(u">")
2927        else:
2928            if publisher.xhtml in (0, 1):
2929                if self.model is not None and self.model.empty:
2930                    if publisher.xhtml==1:
2931                        yield publisher.encode(u" /")
2932                    yield publisher.encode(u">")
2933                else:
2934                    yield publisher.encode(u"></")
2935                    yield publisher.encode(name)
2936                    yield publisher.encode(u">")
2937            elif publisher.xhtml == 2:
2938                yield publisher.encode(u"/>")
2939
2940    def publish(self, publisher):
2941        if publisher.validate:
2942            self.checkvalid()
2943        if publisher.inattr:
2944            # publish the content only when we are inside an attribute. This works much like using the plain string value,
2945            # but even works with processing instructions, or what the abbreviation entities return
2946            return self.content.publish(publisher) # return a generator-iterator
2947        else:
2948            return self._publishfull(publisher) # return a generator-iterator
2949
2950    def __getitem__(self, index):
2951        """
2952        <par>If <arg>index</arg> is a string, return the attribute with this (Python) name.</par>
2953        <par>If <arg>index</arg> is a tuple consisting of a namespace and a string,
2954        return the global attribute with this (Python) name.</par>
2955        <par>If <arg>index</arg> is a number return the appropriate content node.</par>
2956        <par><arg>index</arg> may also be a list, in with case <method>__getitem__</method>
2957        will be applied recusively.</par>
2958        """
2959        if isinstance(index, list):
2960            node = self
2961            for subindex in index:
2962                node = node[subindex]
2963            return node
2964        elif isinstance(index, type):
2965            if issubclass(index, Node):
2966                if issubclass(index, Attr):
2967                    return self.attrs[index]
2968                return self.content[index]
2969        elif isinstance(index, (int, long)):
2970            return self.content[index]
2971        elif isinstance(index, slice):
2972            return self.__class__(self.content[index], self.attrs)
2973        else:
2974            return self.attrs[index]
2975
2976    def __setitem__(self, index, value):
2977        """
2978        <par>Set an attribute or content node to the value <arg>value</arg>.</par>
2979        <par>For possible types for <arg>index</arg> see <pyref method="__getitem__"><method>__getitem__</method></pyref>.</par>
2980        """
2981        if isinstance(index, list):
2982            if not index:
2983                raise ValueError("can't replace self")
2984            node = self
2985            for subindex in index[:-1]:
2986                node = node[subindex]
2987            node[index[-1]] = value
2988        elif isinstance(index, (int, long, slice)):
2989            self.content[index] = value
2990        else:
2991            self.attrs[index] = value
2992
2993    def __delitem__(self, index):
2994        """
2995        <par>Remove an attribute or content node.</par>
2996        <par>For possible types for <arg>index</arg> see <pyref method="__getitem__"><method>__getitem__</method></pyref>.</par>
2997        """
2998        if isinstance(index, list):
2999            if not index:
3000                raise ValueError("can't delete self")
3001            node = self
3002            for subindex in index[:-1]:
3003                node = node[subindex]
3004            del node[index[-1]]
3005        elif isinstance(index, (int, long, slice)):
3006            del self.content[index]
3007        else:
3008            del self.attrs[index]
3009
3010    def __getslice__(self, index1, index2):
3011        """
3012        Returns a copy of the element that contains a slice of the content.
3013        """
3014        return self.__class__(self.content[index1:index2], self.attrs)
3015
3016    def __setslice__(self, index1, index2, sequence):
3017        """
3018        Replaces a slice of the content of the element.
3019        """
3020        self.content[index1:index2] = sequence
3021
3022    def __delslice__(self, index1, index2):
3023        """
3024        Removes a slice of the content of the element.
3025        """
3026        del self.content[index1:index2]
3027
3028    def __iadd__(self, other):
3029        self.extend(other)
3030        return self
3031
3032    def __len__(self):
3033        """
3034        return the number of children.
3035        """
3036        return len(self.content)
3037
3038    def __iter__(self):
3039        return iter(self.content)
3040
3041    def compact(self):
3042        node = self.__class__()
3043        node.content = self.content.compact()
3044        node.attrs = self.attrs.compact()
3045        return self._decoratenode(node)
3046
3047    def _walk(self, filter, path):
3048        if callable(filter):
3049            found = filter(path)
3050        else:
3051            found = filter
3052
3053        for option in found:
3054            if option is entercontent:
3055                for result in self.content._walk(filter, path):
3056                    yield result
3057            elif option is enterattrs:
3058                for result in self.attrs._walk(filter, path):
3059                    yield result
3060            elif option:
3061                yield path
3062
3063    def withsep(self, separator, clone=False):
3064        """
3065        <par>returns a version of <self/> with a separator node between the child
3066        nodes of <self/>. For more info see
3067        <pyref class="Frag" method="withsep"><method>Frag.withsep</method></pyref>.</par>
3068        """
3069        node = self.__class__()
3070        node.attrs = self.attrs.clone()
3071        node.content = self.content.withsep(separator, clone)
3072        return node
3073
3074    def sorted(self, cmp=None, key=None, reverse=False):
3075        """
3076        returns a sorted version of <self/>. <arg>compare</arg> is a comparison
3077        function. If <arg>compare</arg> is omitted, the character content will
3078        be compared.
3079        """
3080        node = self.__class__()
3081        node.attrs = self.attrs.clone()
3082        node.content = self.content.sorted(cmp, key, reverse)
3083        return node
3084
3085    def reversed(self):
3086        """
3087        returns a reversed version of <self/>.
3088        """
3089        node = self.__class__()
3090        node.attrs = self.attrs.clone()
3091        node.content = self.content.reversed()
3092        return node
3093
3094    def filtered(self, function):
3095        """
3096        returns a filtered version of the <self/>.
3097        """
3098        node = self.__class__()
3099        node.attrs = self.attrs.clone()
3100        node.content = self.content.filtered(function)
3101        return node
3102
3103    def shuffled(self):
3104        """
3105        returns a shuffled version of the <self/>.
3106        """
3107        node = self.__class__()
3108        node.attrs = self.attrs.clone()
3109        node.content = self.content.shuffled()
3110        return node
3111
3112    def mapped(self, function, converter=None, **converterargs):
3113        if converter is None:
3114            converter = converters.Converter(**converterargs)
3115        node = function(self, converter)
3116        assert isinstance(node, Node), "the mapped method returned the illegal object %r (type %r) when mapping %r" % (node, type(node), self)
3117        if node is self:
3118            node = self.__class__(self.content.mapped(function, converter))
3119            node.attrs = self.attrs.clone()
3120        return node
3121
3122    def normalized(self):
3123        node = self.__class__()
3124        node.attrs = self.attrs.normalized()
3125        node.content = self.content.normalized()
3126        return node
3127
3128    def pretty(self, level=0, indent="\t"):
3129        node = self.__class__(self.attrs)
3130        if len(self):
3131            # search for text content
3132            for child in self:
3133                if isinstance(child, Text):
3134                    # leave content alone
3135                    node.append(self.content.clone())
3136                    break
3137            else:
3138                for child in self:
3139                    node.append("\n", child.pretty(level+1, indent))
3140                node.append("\n", indent*level)
3141        if level>0:
3142            node = Frag(indent*level, node)
3143        return node
3144
3145    def __repr__(self):
3146        lc = len(self.content)
3147        if lc==0:
3148            infoc = "no children"
3149        elif lc==1:
3150            infoc = "1 child"
3151        else:
3152            infoc = "%d children" % lc
3153        la = len(self.attrs)
3154        if la==0:
3155            infoa = "no attrs"
3156        elif la==1:
3157            infoa = "1 attr"
3158        else:
3159            infoa = "%d attrs" % la
3160        if self.startloc is not None:
3161            loc = " (from %s)" % self.startloc
3162        else:
3163            loc = ""
3164        return "<%s.%s element object (%s/%s)%s at 0x%x>" % (self.__class__.__module__, self.__fullname__, infoc, infoa, loc, id(self))
3165
3166
3167class _Entity_Meta(Node.__metaclass__):
3168    def __new__(cls, name, bases, dict):
3169        self = super(_Entity_Meta, cls).__new__(cls, name, bases, dict)
3170        if dict.get("register") is not None:
3171            getpoolstack()[-1].register(self)
3172        return self
3173
3174    def __repr__(self):
3175        return "<entity class %s:%s at 0x%x>" % (self.__module__, self.__fullname__, id(self))
3176
3177
3178class Entity(Node):
3179    """
3180    <par>Class for entities. Derive your own entities from it and overwrite
3181    <pyref class="Node" method="convert"><method>convert</method></pyref>.</par>
3182    """
3183    __metaclass__ = _Entity_Meta
3184
3185    register = None
3186
3187    @classmethod
3188    def _str(cls, fullname=True, xml=True, decorate=True):
3189        s = cls._strbase(fullname=fullname, xml=xml)
3190        if decorate:
3191            s = "&%s;" % s
3192        return s
3193
3194    def __eq__(self, other):
3195        return self.__class__ is other.__class__
3196
3197    def compact(self):
3198        return self
3199
3200    def present(self, presenter):
3201        return presenter.presentEntity(self) # return a generator-iterator
3202
3203    def publish(self, publisher):
3204        yield publisher.encode(u"&")
3205        yield publisher.encode(self.xmlname)
3206        yield publisher.encode(u";")
3207
3208    def __repr__(self):
3209        if self.startloc is not None:
3210            loc = " (from %s)" % self.startloc
3211        else:
3212            loc = ""
3213        return "<%s.%s entity object%s at 0x%x>" % (self.__class__.__module__, self.__fullname__, loc, id(self))
3214
3215
3216class _CharRef_Meta(Entity.__metaclass__): # don't subclass Text.__metaclass__, as this is redundant
3217    def __repr__(self):
3218        return "<charref class %s:%s at 0x%x>" % (self.__module__, self.__fullname__, id(self))
3219
3220
3221class CharRef(Text, Entity):
3222    """
3223    <par>A simple character reference, the codepoint is in the class attribute
3224    <lit>codepoint</lit>.</par>
3225    """
3226    __metaclass__ = _CharRef_Meta
3227    register = None
3228
3229    def __init__(self):
3230        Text.__init__(self, unichr(self.codepoint))
3231        Entity.__init__(self)
3232
3233    def __getnewargs__(self):
3234        return ()
3235
3236    def present(self, presenter):
3237        return presenter.presentEntity(self) # return a generator-iterator
3238
3239    # The rest is the same as for Text, but does not return CharRefs, but Texts
3240    def __getitem__(self, index):
3241        return Text(self.content.__getitem__(index))
3242
3243    def __add__(self, other):
3244        return Text(self.content + other)
3245
3246    def __radd__(self, other):
3247        return Text(unicode(other) + self.content)
3248
3249    def __mul__(self, n):
3250        return Text(n * self.content)
3251
3252    def __rmul__(self, n):
3253        return Text(n * self.content)
3254
3255    def __getslice__(self, index1, index2):
3256        return Text(self.content.__getslice__(index1, index2))
3257
3258    def capitalize(self):
3259        return Text(self.content.capitalize())
3260
3261    def center(self, width):
3262        return Text(self.content.center(width))
3263
3264    def ljust(self, width, fill=u" "):
3265        return Text(self.content.ljust(width, fill))
3266
3267    def lower(self):
3268        return Text(self.content.lower())
3269
3270    def lstrip(self, chars=None):
3271        return Text(self.content.lstrip(chars))
3272
3273    def replace(self, old, new, maxsplit=-1):
3274        return Text(self.content.replace(old, new, maxsplit))
3275
3276    def rjust(self, width, fill=u" "):
3277        return Text(self.content.rjust(width, fill))
3278
3279    def rstrip(self, chars=None):
3280        return Text(self.content.rstrip(chars))
3281
3282    def strip(self, chars=None):
3283        return Text(self.content.strip(chars))
3284
3285    def swapcase(self):
3286        return Text(self.content.swapcase())
3287
3288    def title(self):
3289        return Text(self.content.title())
3290
3291    def translate(self, table):
3292        return Text(self.content.translate(table))
3293
3294    def upper(self):
3295        return Text(self.content.upper())
3296
3297
3298import publishers, cssparsers, converters, utils, helpers
3299
3300
3301###
3302### XML class pool
3303###
3304
3305class Pool(object):
3306    """
3307    <par>Class pool for <pyref class="Element">element</pyref>,
3308    <pyref class="ProcInst">procinst</pyref>, <pyref class="Entity">entity</pyref>,
3309    <pyref class="CharRef">charref</pyref> and <pyref class="Attr">attribute</pyref> classes.</par>
3310
3311    <par>This is used by the parser to map names to classes.</par>
3312    """
3313    def __init__(self, *objects):
3314        """
3315        <par>Create a new pool. All objects in <arg>objects</arg> will be passed
3316        to the <method>register</method> method.</par>
3317        """
3318        self._elementsbyxmlname = weakref.WeakValueDictionary()
3319        self._elementsbypyname = weakref.WeakValueDictionary()
3320        self._procinstsbyxmlname = weakref.WeakValueDictionary()
3321        self._procinstsbypyname = weakref.WeakValueDictionary()
3322        self._entitiesbyxmlname = weakref.WeakValueDictionary()
3323        self._entitiesbypyname = weakref.WeakValueDictionary()
3324        self._charrefsbyxmlname = weakref.WeakValueDictionary()
3325        self._charrefsbypyname = weakref.WeakValueDictionary()
3326        self._charrefsbycodepoint = weakref.WeakValueDictionary()
3327        self._attrsbyxmlname = weakref.WeakValueDictionary()
3328        self._attrsbypyname = weakref.WeakValueDictionary()
3329        self.bases = []
3330        for object in objects:
3331            self.register(object)
3332
3333    def register(self, object):
3334        """
3335        <par>Register <arg>object</arg> in the pool. <arg>object</arg> can be:</par>
3336        <ulist>
3337        <item>A <pyref class="Element"><class>Element</class></pyref>,
3338        <pyref class="ProcInst"><class>ProcInst</class></pyref>,
3339        <pyref class="Entity"><class>Entity</class></pyref>,
3340        <pyref class="CharRef"><class>CharRef</class></pyref> class;</item>
3341        <item>An <pyref class="Attr"><class>Attr</class></pyref> class
3342        for a global attribute;</item>
3343        <item>An <pyref class="Attrs"><class>Attrs</class></pyref> class
3344        containing global attributes;</item>
3345        <item>A <class>dict</class> (all <class>Node</class> classes in the
3346        values will be registered, this makes it possible to e.g. register all
3347        local variables by passing <lit>vars()</lit>);</item>
3348        <item>A module (all <class>Node</class> classes in the
3349        module will be registered);</item>
3350        <item>A <class>Pool</class> object (this pool object will be added to the
3351        base pools. If a class isn't found in <self/> the search continues in these
3352        base pool;</item>
3353        <item><lit>True</lit>, which add the current default pool to the base pools.</item>
3354        </ulist>
3355        """
3356        if isinstance(object, type):
3357            if issubclass(object, Element):
3358                if object.register:
3359                    self._elementsbyxmlname[(object.xmlname, object.xmlns)] = object
3360                    self._elementsbypyname[(object.__name__, object.xmlns)] = object
3361            elif issubclass(object, ProcInst):
3362                if object.register:
3363                    self._procinstsbyxmlname[object.xmlname] = object
3364                    self._procinstsbypyname[object.__name__] = object
3365            elif issubclass(object, Entity):
3366                if object.register:
3367                    self._entitiesbyxmlname[object.xmlname] = object
3368                    self._entitiesbypyname[object.__name__] = object
3369                    if issubclass(object, CharRef):
3370                        self._charrefsbyxmlname[object.xmlname] = object
3371                        self._charrefsbypyname[object.__name__] = object
3372                        self._charrefsbycodepoint[object.codepoint] = object
3373            elif issubclass(object, Attr):
3374                if object.register:
3375                    self._attrsbyxmlname[(object.xmlname, object.xmlns)] = object
3376                    self._attrsbypyname[(object.__name__, object.xmlns)] = object
3377            elif issubclass(object, Attrs):
3378                for attr in object.allowedattrs():
3379                    self.register(attr)
3380        elif isinstance(object, types.ModuleType):
3381            for value in object.__dict__.itervalues():
3382                if isinstance(value, type): # This avoids recursive module registration
3383                    self.register(value)
3384        elif isinstance(object, dict):
3385            for value in object.itervalues():
3386                if isinstance(value, type): # This avoids recursive module registration
3387                    self.register(value)
3388        elif object is True:
3389            self.bases.append(getpoolstack()[-1])
3390        elif isinstance(object, Pool):
3391            self.bases.append(object)
3392
3393    def __enter__(self):
3394        getpoolstack().append(self)
3395        return self
3396
3397    def __exit__(self, type, value, traceback):
3398        getpoolstack().pop()
3399
3400    def elements(self):
3401        """
3402        Return an iterator for all registered element classes.
3403        """
3404        return self._elementsbypyname.itervalues()
3405
3406    def elementclass(self, name, xmlns):
3407        """
3408        Return the element class for the element with the Python name
3409        <arg>name</arg> and the namespace <arg>xmlns</arg>. If the class can't
3410        be found the search continues in the base pools. If the element can't be
3411        found a <class>IllegalElementError</class> will be raised.
3412        """
3413        if isinstance(xmlns, (list, tuple)):
3414            for xmlns in xmlns:
3415                xmlns = nsname(xmlns)
3416                try:
3417                    return self._elementsbypyname[(name, xmlns)]
3418                except KeyError:
3419                    pass
3420        else:
3421            xmlns = nsname(xmlns)
3422            try:
3423                return self._elementsbypyname[(name, xmlns)]
3424            except KeyError:
3425                pass
3426        for base in self.bases:
3427            try:
3428                return base.elementclass(name, xmlns)
3429            except IllegalElementError:
3430                pass
3431        raise IllegalElementError(name, xmlns, False)
3432
3433    def elementclass_xml(self, name, xmlns):
3434        """
3435        Return the element class for the element type with the &xml; name
3436        <arg>name</arg> and the namespace <arg>xmlns</arg>. If the class can't
3437        be found the search continues in the base pools. If the element can't be
3438        found a <class>IllegalElementError</class> will be raised.
3439        """
3440        if isinstance(xmlns, (list, tuple)):
3441            for xmlns in xmlns:
3442                xmlns = nsname(xmlns)
3443                try:
3444                    return self._elementsbyxmlname[(name, xmlns)]
3445                except KeyError:
3446                    pass
3447        else:
3448            xmlns = nsname(xmlns)
3449            try:
3450                return self._elementsbyxmlname[(name, xmlns)]
3451            except KeyError:
3452                pass
3453        for base in self.bases:
3454            try:
3455                return base.elementclass_xml(name, xmlns)
3456            except IllegalElementError:
3457                pass
3458        raise IllegalElementError(name, xmlns, True)
3459
3460    def element(self, name, xmlns):
3461        """
3462        Return an element object for the element type with the Python name
3463        <arg>name</arg> and the namespace <arg>xmlns</arg>. If the class can't
3464        be found the search continues in the base pools.
3465        """
3466        return self.elementclass(name, xmlns)()
3467
3468    def element_xml(self, name, xmlns):
3469        """
3470        Return an element object for the element type with the &xml; name
3471        <arg>name</arg> and the namespace <arg>xmlns</arg>. If the class can't
3472        be found the search continues in the base pools.
3473        """
3474        return self.elementclass_xml(name, xmlns)()
3475
3476    def haselement(self, name, xmlns):
3477        """
3478        Is there a registered element class in <self/> for the element type
3479        with the Python name <arg>name</arg> and the namespace <arg>xmlns</arg>?
3480        """
3481        return (name, nsname(xmlns)) in self._elementsbypyname
3482
3483    def haselement_xml(self, name, xmlns):
3484        """
3485        Is there a registered element class in <self/> for the element type
3486        with the &xml; name <arg>name</arg> and the namespace <arg>xmlns</arg>?
3487        """
3488        return (name, nsname(xmlns)) in self._elementsbyxmlname
3489
3490    def procinsts(self):
3491        """
3492        Return an iterator for all registered processing instruction classes.
3493        """
3494        return self._procinstsbypyname.itervalues()
3495
3496    def procinstclass(self, name):
3497        """
3498        Return the processing instruction class for the PI with the Python name
3499        <arg>name</arg>. If the class can't be found the search continues in the
3500        base pools. If the element can't be found a <class>IllegalProcInstError</class>
3501        will be raised.
3502        """
3503        try:
3504            return self._procinstsbypyname[name]
3505        except KeyError:
3506            for base in self.bases:
3507                try:
3508                    return self.base.procinstclass(name)
3509                except IllegalProcInstError:
3510                    pass
3511            raise IllegalProcInstError(name, False)
3512
3513    def procinstclass_xml(self, name):
3514        """
3515        Return the processing instruction class for the PI with the &xml; name
3516        <arg>name</arg>. If the class can't be found the search continues in the
3517        base pools. If the element can't be found a <class>IllegalProcInstError</class>
3518        will be raised.
3519        """
3520        try:
3521            return self._procinstsbyxmlname[name]
3522        except KeyError:
3523            for base in self.bases:
3524                try:
3525                    return self.base.procinstclass_xml(name)
3526                except IllegalProcInstError:
3527                    pass
3528            raise IllegalProcInstError(name, True)
3529
3530    def procinst(self, name, content):
3531        """
3532        Return a processing instruction object for the PI type with the Python
3533        target name <arg>name</arg>. If the class can't be found the search
3534        continues in the base pools.
3535        """
3536        return self.procinstclass(name)(content)
3537
3538    def procinst_xml(self, name, content):
3539        """
3540        Return a processing instruction object for the PI type with the &xml;
3541        target name <arg>name</arg>. If the class can't be found the search
3542        continues in the base pools.
3543        """
3544        return self.procinstclass_xml(name)(content)
3545
3546    def hasprocinst(self, name):
3547        """
3548        Is there a registered processing instruction class in <self/> for the
3549        PI with the Python name <arg>name</arg>?
3550        """
3551        return name in self._procinstsbypyname
3552
3553    def hasprocinst_xml(self, name):
3554        """
3555        Is there a registered processing instruction class in <self/> for the
3556        PI with the &xml; name <arg>name</arg>?
3557        """
3558        return name in self._procinstsbyxmlname
3559
3560    def entities(self):
3561        """
3562        Return an iterator for all registered entity classes.
3563        """
3564        return self._entitiesbypyname.itervalues()
3565
3566    def entityclass(self, name):
3567        """
3568        Return the entity for the entity with the Python name <arg>name</arg>.
3569        If the class can't be found the search continues in the base pools. If the
3570        element can't be found a <class>IllegalEntityError</class> will be raised.
3571        """
3572        try:
3573            return self._entitiesbypyname[name]
3574        except KeyError:
3575            for base in self.bases:
3576                try:
3577                    return self.base.entityclass(name)
3578                except IllegalEntityError:
3579                    pass
3580            raise IllegalEntityError(name, False)
3581
3582    def entityclass_xml(self, name):
3583        """
3584        Return the entity for the entity with the &xml; name <arg>name</arg>.
3585        If the class can't be found the search continues in the base pools. If the
3586        element can't be found a <class>IllegalEntityError</class> will be raised.
3587        """
3588        try:
3589            return self._entitiesbyxmlname[name]
3590        except KeyError:
3591            for base in self.bases:
3592                try:
3593                    return self.base.entityclass_xml(name)
3594                except IllegalEntityError:
3595                    pass
3596            raise IllegalEntityError(name, True)
3597
3598    def entity(self, name):
3599        """
3600        Return an entity object for the entity with the Python name
3601        <arg>name</arg>. If the class can't be found the search continues in
3602        the base pools.
3603        """
3604        return self.entityclass(name)()
3605
3606    def entity_xml(self, name):
3607        """
3608        Return an entity object for the entity with the &xml; name
3609        <arg>name</arg>. If the class can't be found the search continues in
3610        the base pools.
3611        """
3612        return self.entityclass_xml(name)()
3613
3614    def hasentity(self, name):
3615        """
3616        Is there a registered entity class in <self/> for the entity with the
3617        Python name <arg>name</arg>?
3618        """
3619        return name in self._entitiesbypyname
3620
3621    def hasentity_xml(self, name):
3622        """
3623        Is there a registered entity class in <self/> for the entity with the
3624        &xml; name <arg>name</arg>?
3625        """
3626        return name in self._entitiesbyxmlname
3627
3628    def charrefs(self):
3629        """
3630        Return an iterator for all character entity classes.
3631        """
3632        return self._charrefsbypyname.itervalues()
3633
3634    def charrefclass(self, name):
3635        """
3636        Return the character entity with the Python name <arg>name</arg>.
3637        <arg>name</arg> can also be an <class>int</class> specifying the codepoint.
3638        If the class can't be found the search continues in the base pools. If the
3639        element can't be found a <class>IllegalEntityError</class> will be raised.
3640        """
3641        try:
3642            if isinstance(name, (int, long)):
3643                return self._charrefsbycodepoint[name]
3644            return self._charrefsbypyname[name]
3645        except KeyError:
3646            for base in self.bases:
3647                try:
3648                    return self.base.charrefclass(name)
3649                except IllegalEntityError:
3650                    pass
3651            raise IllegalEntityError(name, False)
3652
3653    def charrefclass_xml(self, name):
3654        """
3655        Return the character entity with the &xml; name <arg>name</arg>.
3656        <arg>name</arg> can also be an <class>int</class> specifying the codepoint.
3657        If the class can't be found the search continues in the base pools. If the
3658        element can't be found a <class>IllegalEntityError</class> will be raised.
3659        """
3660        try:
3661            if isinstance(name, (int, long)):
3662                return self._charrefsbycodepoint[name]
3663            return self._charrefsbyxmlname[name]
3664        except KeyError:
3665            for base in self.bases:
3666                try:
3667                    return self.base.charrefclass_xml(name)
3668                except IllegalEntityError:
3669                    pass
3670            raise IllegalEntityError(name, True)
3671
3672    def charref(self, name):
3673        """
3674        Return a character entity object for the chacter with the Python name
3675        or codepoint <arg>name</arg>. If the class can't be found the search
3676        continues in the base pools.
3677        """
3678        return self.charrefclass(name)()
3679
3680    def charref_xml(self, name):
3681        """
3682        Return a character entity object for the chacter with the &xml; name
3683        or codepoint <arg>name</arg>. If the class can't be found the search
3684        continues in the base pools.
3685        """
3686        return self.charrefclass_xml(name)()
3687
3688    def hascharref(self, name):
3689        """
3690        Is there a registered character entity class in <self/> with the Python
3691        name or codepoint <arg>name</arg>?
3692        """
3693        if isinstance(name, (int, long)):
3694            return name in self._charrefsbycodepoint
3695        else:
3696            return name in self._charrefsbypyname
3697
3698    def hascharref_xml(self, name):
3699        """
3700        Is there a registered character entity class in <self/> with the &xml;
3701        name or codepoint <arg>name</arg>?
3702        """
3703        if isinstance(name, (int, long)):
3704            return name in self._charrefsbycodepoint
3705        else:
3706            return name in self._charrefsbypyname
3707
3708    def attrclass(self, name, xmlns):
3709        """
3710        Return the aatribute class for the global attribute with the Python name
3711        <arg>name</arg> and the namespace <arg>xmlns</arg>. If the class can't be
3712        found the search continues in the base pools. If the attribute can't be
3713        found a <class>IllegalAttrError</class> will be raised.
3714        """
3715        if not isinstance(xmlns, (list, tuple)):
3716            xmlns = (xmlns,)
3717        for xmlns in xmlns:
3718            xmlns = nsname(xmlns)
3719            try:
3720                return self._attrsbypyname[(name, xmlns)]
3721            except KeyError:
3722                pass
3723        for base in self.bases:
3724            try:
3725                return base.attrclass(name, xmlns)
3726            except IllegalAttrError:
3727                pass
3728        raise IllegalAttrError(name, xmlns, False)
3729
3730    def attrclass_xml(self, name, xmlns):
3731        """
3732        Return the aatribute class for the global attribute with the &xml; name
3733        <arg>name</arg> and the namespace <arg>xmlns</arg>. If the class can't be
3734        found the search continues in the base pools. If the attribute can't be
3735        found a <class>IllegalAttrError</class> will be raised.
3736        """
3737        if not isinstance(xmlns, (list, tuple)):
3738            xmlns = (xmlns,)
3739        for xmlns in xmlns:
3740            xmlns = nsname(xmlns)
3741            try:
3742                return self._attrsbyxmlname[(name, xmlns)]
3743            except KeyError:
3744                pass
3745        for base in self.bases:
3746            try:
3747                return base.attrclass_xml(name, xmlns)
3748            except IllegalAttrError:
3749                pass
3750        raise IllegalAttrError(name, xmlns, True)
3751
3752    def text(self, content):
3753        """
3754        Create a text node with the content <arg>content</arg>.
3755        """
3756        return Text(content)
3757
3758    def comment(self, content):
3759        """
3760        Create a comment node with the content <arg>content</arg>.
3761        """
3762        return Comment(content)
3763
3764    def clone(self):
3765        """
3766        Return a copy of <self/>.
3767        """
3768        copy = Pool()
3769        copy._elementsbyxmlname = self._elementsbyxmlname.copy()
3770        copy._elementsbypyname = self._elementsbypyname.copy()
3771        copy._procinstsbyxmlname = self._procinstsbyxmlname.copy()
3772        copy._procinstsbypyname = self._procinstsbypyname.copy()
3773        copy._entitiesbyxmlname = self._entitiesbyxmlname.copy()
3774        copy._entitiesbypyname = self._entitiesbypyname.copy()
3775        copy._charrefsbyxmlname = self._charrefsbyxmlname.copy()
3776        copy._charrefsbypyname = self._charrefsbypyname.copy()
3777        copy._charrefsbycodepoint = self._charrefsbycodepoint.copy()
3778        copy._attrsbyxmlname = self._attrsbyxmlname.copy()
3779        copy._attrsbypyname = self._attrsbypyname.copy()
3780        copy.bases = self.bases[:]
3781        return copy
3782
3783# Default class pool
3784defaultpool = Pool()
3785
3786
3787def getpoolstack():
3788    try:
3789        stack = getattr(local, "ll.xist.xsc.pools")
3790    except AttributeError:
3791        stack = [defaultpool]
3792        setattr(local, "ll.xist.xsc.pools", stack)
3793    return stack
3794
3795
3796###
3797### Functions for namespace handling
3798###
3799
3800def docprefixes():
3801    """
3802    Return a prefix mapping suitable for parsing &xist; docstrings.
3803    """
3804    from ll.xist.ns import html, chars, abbr, doc, specials
3805    return {None: (doc, specials, html, chars, abbr)}
3806
3807
3808def nsname(xmlns):
3809    """
3810    If <arg>xmlns</arg> is a module, return <lit><arg>xmlns</arg>.xmlns</lit>.
3811    Else return <arg>xmlns</arg> unchanged.
3812    """
3813    if xmlns is not None and not isinstance(xmlns, basestring):
3814        xmlns = xmlns.xmlns
3815    return xmlns
3816
3817
3818def nsclark(xmlns):
3819    """
3820    Return a namespace name in Clark notation. <arg>xmlns</arg> can be
3821    <lit>None</lit>, a string or a module.
3822    """
3823    if xmlns is None:
3824        return "{}"
3825    elif not isinstance(xmlns, basestring):
3826        xmlns = xmlns.xmlns
3827    return "{%s}" % xmlns
3828
3829
3830# C0 Controls and Basic Latin
3831class quot(CharRef): "quotation mark = APL quote, U+0022 ISOnum"; codepoint = 34
3832class amp(CharRef): "ampersand, U+0026 ISOnum"; codepoint = 38
3833class lt(CharRef): "less-than sign, U+003C ISOnum"; codepoint = 60
3834class gt(CharRef): "greater-than sign, U+003E ISOnum"; codepoint = 62
3835class apos(CharRef): "apostrophe mark, U+0027 ISOnum"; codepoint = 39
3836
3837
3838###
3839###
3840###
3841
3842class Location(object):
3843    """
3844    <par>Represents a location in an &xml; entity.</par>
3845    """
3846    __slots__ = ("sysid", "pubid", "line", "col")
3847
3848    def __init__(self, locator=None, sysid=None, pubid=None, line=None, col=None):
3849        """
3850        <par>Create a new <class>Location</class> instance by reading off the
3851        current location from the <arg>locator</arg>, which is then stored
3852        internally. In addition to that the system ID, public ID, line number and
3853        column number can be overwritten by explicit arguments.</par>
3854        """
3855        self.sysid = None
3856        self.pubid = None
3857        self.line = None
3858        self.col = None
3859
3860        if locator is not None:
3861            self.sysid = locator.getSystemId()
3862            self.pubid = locator.getPublicId()
3863            self.line = locator.getLineNumber()
3864            self.col = locator.getColumnNumber()
3865
3866        if sysid is not None:
3867            self.sysid = sysid
3868
3869        if pubid is not None:
3870            self.pubid = pubid
3871
3872        if line is not None:
3873            self.line = line
3874
3875        if col is not None:
3876            self.col = col
3877
3878    def getColumnNumber(self):
3879        "<par>Return the column number of this location.</par>"
3880        return self.col
3881
3882    def getLineNumber(self):
3883        "<par>Return the line number of this location.</par>"
3884        return self.line
3885
3886    def getPublicId(self):
3887        "<par>Return the public identifier for this location.</par>"
3888        return self.pubid
3889
3890    def getSystemId(self):
3891        "<par>Return the system identifier for this location.</par>"
3892        return self.sysid
3893
3894    def offset(self, offset):
3895        """
3896        <par>Return a location where the line number is incremented by offset
3897        (and the column number is reset to 1).</par>
3898        """
3899        if offset==0:
3900            return self
3901        elif self.line is None:
3902            return Location(sysid=self.sysid, pubid=self.pubid, line=None, col=1)
3903        return Location(sysid=self.sysid, pubid=self.pubid, line=self.line+offset, col=1)
3904
3905    def __str__(self):
3906        # get and format the system ID
3907        sysid = self.sysid
3908        if sysid is None:
3909            sysid = "???"
3910        else:
3911            sysid = str(sysid)
3912
3913        # get and format the line number
3914        line = self.line
3915        if line is None or line < 0:
3916            line = "?"
3917        else:
3918            line = str(line)
3919
3920        # get and format the column number
3921        col = self.col
3922        if col is None or col < 0:
3923            col = "?"
3924        else:
3925            col = str(col)
3926
3927        # now we have the parts => format them
3928        return "%s:%s:%s" % (sysid, line, col)
3929
3930    def __repr__(self):
3931        return "<%s object sysid=%r, pubid=%r, line=%r, col=%r at %08x>" % (self.__class__.__name__, self.sysid, self.pubid, self.line, self.col, id(self))
3932
3933    def __eq__(self, other):
3934        return self.__class__ is other.__class__ and self.pubid==other.pubid and self.sysid==other.sysid and self.line==other.line and self.col==other.col
3935
3936    def __ne__(self, other):
3937        return not self==other
3938
3939    def __xattrs__(self, mode="default"):
3940        return ("sysid", "pubid", "line", "col")
3941
3942    def __xrepr__(self, mode="default"):
3943        yield (astyle.style_url, self.sysid)
3944        yield (astyle.style_default, ":")
3945        for part in ipipe.xrepr(self.line, mode):
3946            yield part
3947        yield (astyle.style_default, ":")
3948        for part in ipipe.xrepr(self.col, mode):
3949            yield part
Note: See TracBrowser for help on using the browser.