root/livinglogic.python.xist/src/ll/xist/xsc.py @ 3169:50a8c3be9bcd

Revision 3169:50a8c3be9bcd, 114.1 KB (checked in by Walter Doerwald <walter@…>, 12 years ago)

Fix handling of multiple bases and use dict instead of WeakValueDictionary? in Pool. Add clear() method.

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