root/livinglogic.python.xist/src/ll/xist/xsc.py @ 2559:8d4552168c39

Revision 2559:8d4552168c39, 135.0 KB (checked in by Walter Doerwald <walter@…>, 13 years ago)

Add setuptools to the list of requirements.

Update NEWS items.

parsed() can now return a different node (This is used by specials.url)

The PI specials.url has been added that does the same URL handling as
xsc.URLAttr.

Unused stuff (encode, Queue, Esc*Text) has been removed from presenters.
NormalPresenter? has been dropped.

Fixed a small bug in CodePresenter?.presentProcInst().

Exceptions and warning no longer have to output the node location
themselves as it is included in the normal repr() output.

Fixed a few str() methods for warnings that still used astyle.color().

The Node methods repr() and asrepr() have been removed.

Renamed venom to detox.

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