root/livinglogic.python.xist/src/ll/xist/xsc.py @ 2639:2a1b56fb84f1

Revision 2639:2a1b56fb84f1, 107.8 KB (checked in by Walter Doerwald <walter@…>, 13 years ago)

Remove obsolte needsxmlns methods.

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