root/livinglogic.python.xist/src/ll/xist/xsc.py @ 2637:23ea3cc6d250

Revision 2637:23ea3cc6d250, 108.5 KB (checked in by Walter Doerwald <walter@…>, 13 years ago)

Fix fallback code to bases for attribute lookup in Pool.

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