root/livinglogic.python.xist/src/ll/xist/xsc.py @ 2710:26fab3a32d4a

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

Don't allow Attrs dicts in append() (and +).

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