root/livinglogic.python.xist/src/ll/xist/xsc.py @ 2646:b46272118d37

Revision 2646:b46272118d37, 110.2 KB (checked in by Walter Doerwald <walter@…>, 13 years ago)

Add methods haselement(), hasprocinst(), hasentity() and hascharref() (and
XML versions of them) to xsc.Pool.

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