root/livinglogic.python.xist/src/ll/xist/xsc.py @ 2590:7b6bba35f3a0

Revision 2590:7b6bba35f3a0, 134.1 KB (checked in by Walter Doerwald <walter@…>, 13 years ago)

htmlspecials.pixel() now no longer uses colored pixels, instead color is
done via CSS. The URL for the one remaining transparent pixel can now be
specified via src (either as an XML attribute or via the converter context).

Rename attrs methods with() and without() to withnames() and withoutnames()
(for Python 2.5 compatibility).

Use elinks instead of w3m for asText() and move/rename this method to a
function ll.xist.ns.html.astext().

Try to make XIST independent from PyXML (however PyXML is still required
for parsing via expat and for dtd2xsc.py (because this requires xmlproc)).

Remove the long dperecated method withSep().

Use Py_ssize_t in the C source where appropriate.

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