root/livinglogic.python.xist/src/ll/xist/xsc.py @ 2711:113988ef6cf0

Revision 2711:113988ef6cf0, 114.0 KB (checked in by Walter Doerwald <walter@…>, 13 years ago)

Raise error in tonode() instead of issueing a warning.

Remove second implementation for Attrs.iter() (which was wrong).

Raise an error for Attrs passed to tonode().

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