source: trunk/suds/sax/element.py @ 462

Revision 462, 32.4 KB checked in by jortel, 5 years ago (diff)

fix ticket 208, further enhance processing for document/literal bare

Line 
1# This program is free software; you can redistribute it and/or modify
2# it under the terms of the (LGPL) GNU Lesser General Public License as
3# published by the Free Software Foundation; either version 3 of the
4# License, or (at your option) any later version.
5#
6# This program is distributed in the hope that it will be useful,
7# but WITHOUT ANY WARRANTY; without even the implied warranty of
8# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
9# GNU Library Lesser General Public License for more details at
10# ( http://www.gnu.org/licenses/lgpl.html ).
11#
12# You should have received a copy of the GNU Lesser General Public License
13# along with this program; if not, write to the Free Software
14# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15# written by: Jeff Ortel ( jortel@redhat.com )
16
17"""
18Provides XML I{element} classes.
19"""
20
21from logging import getLogger
22from suds import *
23from suds.sax import *
24from suds.sax.attribute import Attribute
25import sys 
26if sys.version_info < (2, 4, 0): 
27    from sets import Set as set 
28    del sys
29
30log = getLogger(__name__)
31
32class Element:
33    """
34    An XML element object.
35    @ivar parent: The node containing this attribute
36    @type parent: L{Element}
37    @ivar prefix: The I{optional} namespace prefix.
38    @type prefix: basestring
39    @ivar name: The I{unqualified} name of the attribute
40    @type name: basestring
41    @ivar expns: An explicit namespace (xmlns="...").
42    @type expns: (I{prefix}, I{name})
43    @ivar nsprefixes: A mapping of prefixes to namespaces.
44    @type nsprefixes: dict
45    @ivar attributes: A list of XML attributes.
46    @type attributes: [I{Attribute},]
47    @ivar text: The element's I{text} content.
48    @type text: basestring
49    @ivar children: A list of child elements.
50    @type children: [I{Element},]
51    @cvar matcher: A collection of I{lambda} for string matching.
52    @cvar specialprefixes: A dictionary of builtin-special prefixes.
53    """
54
55    matcher = \
56    {
57        'eq': lambda a,b: a == b,
58        'startswith' : lambda a,b: a.startswith(b),
59        'endswith' : lambda a,b: a.endswith(b),
60        'contains' : lambda a,b: b in a
61    }
62   
63    specialprefixes = { Namespace.xmlns[0] : Namespace.xmlns[1]  }
64   
65    @classmethod
66    def buildPath(self, parent, path):
67        """
68        Build the specifed pat as a/b/c where missing intermediate nodes are built
69        automatically.
70        @param parent: A parent element on which the path is built.
71        @type parent: I{Element}
72        @param path: A simple path separated by (/).
73        @type path: basestring
74        @return: The leaf node of I{path}.
75        @rtype: L{Element}
76        """
77        for tag in path.split('/'):
78            child = parent.getChild(tag)
79            if child is None:
80                child = Element(tag, parent)
81            parent = child
82        return child
83
84    def __init__(self, name, parent=None, ns=None):
85        """
86        @param name: The element's (tag) name.  May cotain a prefix.
87        @type name: basestring
88        @param parent: An optional parent element.
89        @type parent: I{Element}
90        @param ns: An optional namespace
91        @type ns: (I{prefix}, I{name})
92        """
93       
94        self.rename(name)
95        self.expns = None
96        self.nsprefixes = {}
97        self.attributes = []
98        self.text = None
99        if parent is not None:
100            if isinstance(parent, Element):
101                self.parent = parent
102            else:
103                raise Exception('parent (%s) not-valid', parent.__class__.__name__)
104        else:
105            self.parent = None
106        self.children = []
107        self.applyns(ns)
108       
109    def rename(self, name):
110        """
111        Rename the element.
112        @param name: A new name for the element.
113        @type name: basestring
114        """
115        if name is None:
116            raise Exception('name (%s) not-valid' % name)
117        else:
118            self.prefix, self.name = splitPrefix(name)
119           
120    def setPrefix(self, p, u=None):
121        """
122        Set the element namespace prefix.
123        @param p: A new prefix for the element.
124        @type p: basestring
125        @param u: A namespace URI to be mapped to the prefix.
126        @type u: basestring
127        @return: self
128        @rtype: L{Element}
129        """
130        self.prefix = p
131        if p is not None and u is not None:
132            self.addPrefix(p, u)
133        return self
134
135    def qname(self):
136        """
137        Get the B{fully} qualified name of this element
138        @return: The fully qualified name.
139        @rtype: basestring
140        """
141        if self.prefix is None:
142            return self.name
143        else:
144            return '%s:%s' % (self.prefix, self.name)
145       
146    def getRoot(self):
147        """
148        Get the root (top) node of the tree.
149        @return: The I{top} node of this tree.
150        @rtype: I{Element}
151        """
152        if self.parent is None:
153            return self
154        else:
155            return self.parent.getRoot()
156       
157    def clone(self, parent=None):
158        """
159        Deep clone of this element and children.
160        @param parent: An optional parent for the copied fragment.
161        @type parent: I{Element}
162        @return: A deep copy parented by I{parent}
163        @rtype: I{Element}
164        """
165        root = Element(self.qname(), parent, self.namespace())
166        for a in self.attributes:
167            root.append(a.clone(self))
168        for c in self.children:
169            root.append(c.clone(self))
170        for item in self.nsprefixes.items():
171            root.addPrefix(item[0], item[1])
172        return root
173   
174    def detach(self):
175        """
176        Detach from parent.
177        @return: This element removed from its parent's
178            child list and I{parent}=I{None}
179        @rtype: L{Element}
180        """
181        if self.parent is not None:
182            if self in self.parent.children:
183                self.parent.children.remove(self)
184            self.parent = None
185        return self
186       
187    def set(self, name, value):
188        """
189        Set an attribute's value.
190        @param name: The name of the attribute.
191        @type name: basestring
192        @param value: The attribute value.
193        @type value: basestring
194        @see: __setitem__()
195        """
196        attr = self.attrib(name)
197        if attr is None:
198            attr = Attribute(name, value)
199            self.append(attr)
200        else:
201            attr.setValue(value)
202           
203    def get(self, name, ns=None, default=None):
204        """
205        Get the value of an attribute by name.
206        @param name: The name of the attribute.
207        @type name: basestring
208        @param ns: The optional attribute's namespace.
209        @type ns: (I{prefix}, I{name})
210        @param default: An optional value to be returned when either
211            the attribute does not exist of has not value.
212        @type default: basestring
213        @return: The attribute's value or I{default}
214        @rtype: basestring
215        @see: __getitem__()
216        """
217        attr = self.attrib(name, ns)
218        if attr is None or attr.value is None:
219            return default
220        else:
221            return attr.getValue()   
222
223    def setText(self, value):
224        """
225        Set the element's text content.
226        @param value: The element's text value.
227        @type value: basestring
228        @return: self
229        @rtype: I{Element}
230        """
231        self.text = sax.encoder.encode(value)
232        return self
233       
234    def getText(self, default=None, trim=False):
235        """
236        Get the element's text content with optional default
237        @param default: A value to be returned when no text content exists.
238        @type default: basestring
239        @param trim: Return a trimmed value ( see str.strip() ).
240        @type trim: boolean
241        @return: The text content, or I{default}
242        @rtype: basestring
243        """
244        result = sax.encoder.decode(self.text)
245        if result is None:
246            result = default
247        if trim and result is not None:
248            result = result.strip()
249        return result
250   
251    def hasText(self):
252        """
253        Get whether the element has I{text} and that it is not an empty
254        (zero length) string.
255        @return: True when has I{text}.
256        @rtype: boolean
257        """
258        return ( self.text is not None and len(self.text) )
259   
260    def attrib(self, name, ns=None):
261        """
262        Get an attribute by name and (optional) namespace
263        @param name: The name of a contained attribute (may contain prefix).
264        @type name: basestring
265        @param ns: An optional namespace
266        @type ns: (I{prefix}, I{name})
267        @return: The requested attribute object.
268        @rtype: L{Attribute}
269        """
270        result = None
271        if len(self.attributes) == 0:
272            return result
273        if ns is None:
274            p, n = splitPrefix(name)
275            p = [p]
276        else:
277            prefixes = self.findPrefixes(ns[1])
278            p, n = (prefixes, name)
279        for a in self.attributes:
280            if a.prefix in p and a.name == n:
281                result = a
282                break
283        return result
284       
285    def namespace(self):
286        """
287        Get the element's namespace.
288        @return: The element's namespace by resolving the prefix, the explicit
289            namespace or the inherited namespace.
290        @rtype: (I{prefix}, I{name})
291        """
292        if self.prefix is None:
293            return self.defaultNamespace()
294        else:
295            return self.resolvePrefix(self.prefix)
296       
297    def defaultNamespace(self):
298        """
299        Get the default (unqualified namespace). 
300        This is the expns of the first node (looking up the tree)
301        that has it set.
302        @return: The namespace of a node when not qualified.
303        @rtype: (I{prefix}, I{name})
304        """
305        p = self
306        while p is not None:
307            if p.expns is not None:
308                return (None, p.expns)
309            else:
310                p = p.parent
311        return Namespace.default
312           
313    def append(self, objects):
314        """
315        Append the specified child based on whether it is an
316        element or an attrbuite.
317        @param objects: A (single|collection) of attribute(s) or element(s)
318            to be added as children.
319        @type objects: (L{Element}|L{Attribute})
320        @return: self
321        @rtype: L{Element}
322        """
323        if not isinstance(objects, (list, tuple)):
324            objects = (objects,)
325        for child in objects:
326            if isinstance(child, Element):
327                self.children.append(child)
328                child.parent = self
329                continue
330            if isinstance(child, Attribute):
331                self.attributes.append(child)
332                child.parent = self
333                continue
334            raise Exception('append %s not-valid' % child.__class__.__name__)
335        return self
336   
337    def insert(self, objects, index=0):
338        """
339        Insert an L{Element} content at the specified index.
340        @param objects: A (single|collection) of attribute(s) or element(s)
341            to be added as children.
342        @type objects: (L{Element}|L{Attribute})
343        @param index: The position in the list of children to insert.
344        @type index: int
345        @return: self
346        @rtype: L{Element}
347        """
348        objects = (objects,)
349        for child in objects:
350            if isinstance(child, Element):
351                self.children.insert(index, child)
352                child.parent = self
353            else:
354                raise Exception('append %s not-valid' % child.__class__.__name__)
355        return self
356   
357    def remove(self, child):
358        """
359        Remove the specified child element or attribute.
360        @param child: A child to remove.
361        @type child: L{Element}|L{Attribute}
362        @return: The detached I{child} when I{child} is an element, else None.
363        @rtype: L{Element}|None
364        """
365        if isinstance(child, Element):
366            return child.detach()
367        if isinstance(child, Attribute):
368            self.attributes.remove(child)
369        return None
370           
371    def replaceChild(self, child, content):
372        """
373        Replace I{child} with the specified I{content}.
374        @param child: A child element.
375        @type child: L{Element}
376        @param content: An element or collection of elements.
377        @type content: L{Element} or [L{Element},]
378        """
379        if child not in self.children:
380            raise Exception('child not-found')
381        index = self.children.index(child)
382        self.remove(child)
383        if not isinstance(content, (list, tuple)):
384            content = (content,)
385        for node in content:
386            self.children.insert(index, node.detach())
387            node.parent = self
388            index += 1
389
390    def getChild(self, name, ns=None, default=None):
391        """
392        Get a child by (optional) name and/or (optional) namespace.
393        @param name: The name of a child element (may contain prefix).
394        @type name: basestring
395        @param ns: An optional namespace used to match the child.
396        @type ns: (I{prefix}, I{name})
397        @param default: Returned when child not-found.
398        @type default: L{Element}
399        @return: The requested child, or I{default} when not-found.
400        @rtype: L{Element}
401        """
402        if ns is None:
403            prefix, name = splitPrefix(name)
404            if prefix is None:
405                ns = None
406            else:
407                ns = self.resolvePrefix(prefix)
408        for c in self.children:
409            if c.match(name, ns):
410                return c
411        return default
412   
413    def childAtPath(self, path):
414        """
415        Get a child at I{path} where I{path} is a (/) separated
416        list of element names that are expected to be children.
417        @param path: A (/) separated list of element names.
418        @type path: basestring
419        @return: The leaf node at the end of I{path}
420        @rtype: L{Element}
421        """
422        result = None
423        node = self
424        for name in [p for p in path.split('/') if len(p) > 0]:
425            ns = None
426            prefix, name = splitPrefix(name)
427            if prefix is not None:
428                ns = node.resolvePrefix(prefix)
429            result = node.getChild(name, ns)
430            if result is None:
431                break;
432            else:
433                node = result
434        return result
435
436    def childrenAtPath(self, path):
437        """
438        Get a list of children at I{path} where I{path} is a (/) separated
439        list of element names that are expected to be children.
440        @param path: A (/) separated list of element names.
441        @type path: basestring
442        @return: The collection leaf nodes at the end of I{path}
443        @rtype: [L{Element},...]
444        """
445        parts = [p for p in path.split('/') if len(p) > 0]
446        if len(parts) == 1:
447            result = self.getChildren(path)
448        else:
449            result = self.__childrenAtPath(parts)
450        return result
451       
452    def getChildren(self, name=None, ns=None):
453        """
454        Get a list of children by (optional) name and/or (optional) namespace.
455        @param name: The name of a child element (may contain prefix).
456        @type name: basestring
457        @param ns: An optional namespace used to match the child.
458        @type ns: (I{prefix}, I{name})
459        @return: The list of matching children.
460        @rtype: [L{Element},...]
461        """
462        if ns is None:
463            if name is None:
464                return self.children
465            prefix, name = splitPrefix(name)
466            if prefix is None:
467                ns = None
468            else:
469                ns = self.resolvePrefix(prefix)
470        return [c for c in self.children if c.match(name, ns)]
471   
472    def detachChildren(self):
473        """
474        Detach and return this element's children.
475        @return: The element's children (detached).
476        @rtype: [L{Element},...]
477        """
478        detached = self.children
479        self.children = []
480        for child in detached:
481            child.parent = None
482        return detached
483       
484    def resolvePrefix(self, prefix, default=Namespace.default):
485        """
486        Resolve the specified prefix to a namespace.  The I{nsprefixes} is
487        searched.  If not found, it walks up the tree until either resolved or
488        the top of the tree is reached.  Searching up the tree provides for
489        inherited mappings.
490        @param prefix: A namespace prefix to resolve.
491        @type prefix: basestring
492        @param default: An optional value to be returned when the prefix
493            cannot be resolved.
494        @type default: (I{prefix},I{URI})
495        @return: The namespace that is mapped to I{prefix} in this context.
496        @rtype: (I{prefix},I{URI})
497        """
498        n = self
499        while n is not None:
500            if prefix in n.nsprefixes:
501                return (prefix, n.nsprefixes[prefix])
502            if prefix in self.specialprefixes:
503                return (prefix, self.specialprefixes[prefix])
504            n = n.parent
505        return default
506   
507    def addPrefix(self, p, u):
508        """
509        Add or update a prefix mapping.
510        @param p: A prefix.
511        @type p: basestring
512        @param u: A namespace URI.
513        @type u: basestring
514        @return: self
515        @rtype: L{Element}
516        """
517        self.nsprefixes[p] = u
518        return self
519 
520    def updatePrefix(self, p, u):
521        """
522        Update (redefine) a prefix mapping for the branch.
523        @param p: A prefix.
524        @type p: basestring
525        @param u: A namespace URI.
526        @type u: basestring
527        @return: self
528        @rtype: L{Element}
529        @note: This method traverses down the entire branch!
530        """
531        if p in self.nsprefixes:
532            self.nsprefixes[p] = u
533        for c in self.children:
534            c.updatePrefix(p, u)
535        return self
536           
537    def clearPrefix(self, prefix):
538        """
539        Clear the specified prefix from the prefix mappings.
540        @param prefix: A prefix to clear.
541        @type prefix: basestring
542        @return: self
543        @rtype: L{Element}
544        """
545        if prefix in self.nsprefixes:
546            del self.nsprefixes[prefix]
547        return self
548   
549    def findPrefix(self, uri, default=None):
550        """
551        Find the first prefix that has been mapped to a namespace URI.
552        The local mapping is searched, then it walks up the tree until
553        it reaches the top or finds a match.
554        @param uri: A namespace URI.
555        @type uri: basestring
556        @param default: A default prefix when not found.
557        @type default: basestring
558        @return: A mapped prefix.
559        @rtype: basestring
560        """
561        for item in self.nsprefixes.items():
562            if item[1] == uri:
563                prefix = item[0]
564                return prefix
565        for item in self.specialprefixes.items():
566            if item[1] == uri:
567                prefix = item[0]
568                return prefix     
569        if self.parent is not None:
570            return self.parent.findPrefix(uri, default)
571        else:
572            return default
573
574    def findPrefixes(self, uri, match='eq'):
575        """
576        Find all prefixes that has been mapped to a namespace URI.
577        The local mapping is searched, then it walks up the tree until
578        it reaches the top collecting all matches.
579        @param uri: A namespace URI.
580        @type uri: basestring
581        @param match: A matching function L{Element.matcher}.
582        @type match: basestring
583        @return: A list of mapped prefixes.
584        @rtype: [basestring,...]
585        """
586        result = []
587        for item in self.nsprefixes.items():
588            if self.matcher[match](item[1], uri):
589                prefix = item[0]
590                result.append(prefix)
591        for item in self.specialprefixes.items():
592            if self.matcher[match](item[1], uri):
593                prefix = item[0]
594                result.append(prefix)
595        if self.parent is not None:
596            result += self.parent.findPrefixes(uri, match)
597        return result
598   
599    def promotePrefixes(self):
600        """
601        Push prefix declarations up the tree as far as possible.  Prefix
602        mapping are pushed to its parent unless the parent has the
603        prefix mapped to another URI or the parent has the prefix.
604        This is propagated up the tree until the top is reached.
605        @return: self
606        @rtype: L{Element}
607        """
608        for c in self.children:
609            c.promotePrefixes()
610        if self.parent is None:
611            return
612        for p,u in self.nsprefixes.items():
613            if p in self.parent.nsprefixes:
614                pu = self.parent.nsprefixes[p]
615                if pu == u:
616                    del self.nsprefixes[p]
617                continue
618            if p != self.parent.prefix:
619                self.parent.nsprefixes[p] = u
620                del self.nsprefixes[p]
621        return self
622               
623    def normalizePrefixes(self):
624        """
625        Normalize the namespace prefixes.
626        This generates unique prefixes for all namespaces.  Then retrofits all
627        prefixes and prefix mappings.  Further, it will retrofix attribute values
628        that have values containing (:).
629        @return: self
630        @rtype: L{Element}
631        """
632        PrefixNormalizer.apply(self)
633        return self
634
635    def isempty(self, content=True):
636        """
637        Get whether the element has no children.
638        @param content: Test content (children & text) only.
639        @type content: boolean
640        @return: True when element has not children.
641        @rtype: boolean
642        """
643        noattrs = not len(self.attributes)
644        nochildren = not len(self.children)
645        notext = ( self.text is None )
646        nocontent = ( nochildren and notext )
647        if content:
648            return nocontent
649        else:
650            return ( nocontent and noattrs )
651           
652           
653    def isnil(self):
654        """
655        Get whether the element is I{nil} as defined by having
656        an attribute in the I{xsi:nil="true"}
657        @return: True if I{nil}, else False
658        @rtype: boolean
659        """
660        nilattr = self.attrib('nil', ns=Namespace.xsins)
661        if nilattr is None:
662            return False
663        else:
664            return ( nilattr.getValue().lower() == 'true' )
665       
666    def setnil(self, flag=True):
667        """
668        Set this node to I{nil} as defined by having an
669        attribute I{xsi:nil}=I{flag}.
670        @param flag: A flag inidcating how I{xsi:nil} will be set.
671        @type flag: boolean
672        @return: self
673        @rtype: L{Element}
674        """
675        p, u = Namespace.xsins
676        name  = ':'.join((p, 'nil'))
677        self.set(name, str(flag).lower())
678        self.addPrefix(p, u)
679        if flag:
680            self.text = None
681        return self
682           
683    def applyns(self, ns):
684        """
685        Apply the namespace to this node.  If the prefix is I{None} then
686        this element's explicit namespace I{expns} is set to the
687        URI defined by I{ns}.  Otherwise, the I{ns} is simply mapped.
688        @param ns: A namespace.
689        @type ns: (I{prefix},I{URI})
690        """
691        if ns is None:
692            return
693        if not isinstance(ns, (tuple,list)):
694            raise Exception('namespace must be tuple')
695        if ns[0] is None:
696            self.expns = ns[1]
697        else:
698            self.prefix = ns[0]
699            self.nsprefixes[ns[0]] = ns[1]
700           
701    def str(self, indent=0):
702        """
703        Get a string representation of this XML fragment.
704        @param indent: The indent to be used in formatting the output.
705        @type indent: int
706        @return: A I{pretty} string.
707        @rtype: basestring
708        """
709        tab = '%*s'%(indent*3,'')
710        result = []
711        result.append('%s<%s' % (tab, self.qname()))
712        result.append(self.nsdeclarations())
713        for a in [unicode(a) for a in self.attributes]:
714            result.append(' %s' % a)
715        if self.isempty():
716            result.append('/>')
717            return ''.join(result)
718        result.append('>')
719        if self.text is not None:
720            result.append(self.text)
721        for c in self.children:
722            result.append('\n')
723            result.append(c.str(indent+1))
724        if len(self.children):
725            result.append('\n%s' % tab)
726        result.append('</%s>' % self.qname())
727        result = ''.join(result)
728        return result
729
730    def nsdeclarations(self):
731        """
732        Get a string representation for all namespace declarations
733        as xmlns="" and xmlns:p="".
734        @return: A separated list of declarations.
735        @rtype: basestring
736        """
737        s = []
738        myns = (None, self.expns)
739        if self.parent is None:
740            pns = Namespace.default
741        else:
742            pns = (None, self.parent.expns)
743        if myns[1] != pns[1]:
744            if self.expns is not None:
745                d = ' xmlns="%s"' % self.expns
746                s.append(d)
747        for item in self.nsprefixes.items():
748            (p,u) = item
749            if self.parent is not None:
750                ns = self.parent.resolvePrefix(p)
751                if ns[1] == u: continue
752            d = ' xmlns:%s="%s"' % (p, u)
753            s.append(d)
754        return ''.join(s)
755   
756    def match(self, name=None, ns=None):
757        """
758        Match by (optional) name and/or (optional) namespace.
759        @param name: The optional element tag name.
760        @type name: str
761        @param ns: An optional namespace.
762        @type ns: (I{prefix}, I{name})
763        @return: True if matched.
764        @rtype: boolean
765        """
766        if name is None:
767            byname = True
768        else:
769            byname = ( self.name == name )
770        if ns is None:
771            byns = True
772        else:
773            byns = ( self.namespace()[1] == ns[1] )
774        return ( byname and byns )
775   
776    def branch(self):
777        """
778        Get a flattened representation of the branch.
779        @return: A flat list of nodes.
780        @rtype: [L{Element},..]
781        """
782        branch = []
783        for c in self.children:
784            branch.append(c)
785            branch += c.branch()
786        return branch
787   
788    def prune(self):
789        """
790        Prune the branch of empty nodes.
791        """
792        pruned = []
793        for c in self.children:
794            c.prune()
795            if c.isempty(False):
796                pruned.append(c)
797        for p in pruned:
798            self.children.remove(p)
799               
800           
801    def __childrenAtPath(self, parts):
802        result = []
803        node = self
804        last = len(parts)-1
805        ancestors = parts[:last]
806        leaf = parts[last]
807        for name in ancestors:
808            ns = None
809            prefix, name = splitPrefix(name)
810            if prefix is not None:
811                ns = node.resolvePrefix(prefix)
812            child = node.getChild(name, ns)
813            if child is None:
814                break
815            else:
816                node = child
817        if child is not None:
818            ns = None
819            prefix, leaf = splitPrefix(leaf)
820            if prefix is not None:
821                ns = node.resolvePrefix(prefix)
822            result = child.getChildren(leaf)
823        return result
824   
825    def __len__(self):
826        return len(self.children)
827               
828    def __getitem__(self, index):
829        if isinstance(index, basestring):
830            return self.get(index)
831        else:
832            if index < len(self.children):
833                return self.children[index]
834            else:
835                return None
836       
837    def __setitem__(self, index, value):
838        if isinstance(index, basestring):
839            self.set(index, value)
840        else:
841            if index < len(self.children) and \
842                isinstance(value, Element):
843                self.children.insert(index, value)
844
845    def __eq__(self, rhs):
846        return  rhs is not None and \
847            isinstance(rhs, Element) and \
848            self.name == rhs.name and \
849            self.namespace()[1] == rhs.namespace()[1]
850       
851    def __repr__(self):
852        return \
853            'Element (prefix=%s, name=%s)' % (self.prefix, self.name)
854   
855    def __str__(self):
856        return unicode(self).encode('utf-8')
857   
858    def __unicode__(self):
859        return self.str()
860
861
862
863class PrefixNormalizer:
864    """
865    The prefix normalizer provides namespace prefix normalization.
866    @ivar node: A node to normalize.
867    @type node: L{Element}
868    @ivar branch: The nodes flattened branch.
869    @type branch: [L{Element},..]
870    @ivar namespaces: A unique list of namespaces (URI).
871    @type namespaces: [str,]
872    @ivar prefixes: A reverse dict of prefixes.
873    @type prefixes: {u, p}
874    """
875   
876    @classmethod
877    def apply(cls, node):
878        """
879        Normalize the specified node.
880        @param node: A node to normalize.
881        @type node: L{Element}
882        @return: The normalized node.
883        @rtype: L{Element}
884        """
885        pn = PrefixNormalizer(node)
886        return pn.refit()
887   
888    def __init__(self, node):
889        """
890        @param node: A node to normalize.
891        @type node: L{Element}
892        """
893        self.node = node
894        self.branch = node.branch()
895        self.namespaces = self.getNamespaces()
896        self.prefixes = self.genPrefixes()
897       
898    def getNamespaces(self):
899        """
900        Get the I{unique} set of namespaces referenced in the branch.
901        @return: A set of namespaces.
902        @rtype: set
903        """
904        s = set()
905        for n in self.branch:
906            if self.permit(n.expns):
907                s.add(n.expns)
908            s = s.union(self.pset(n))
909        return s
910   
911    def pset(self, n):
912        """
913        Convert the nodes nsprefixes into a set.
914        @param n: A node.
915        @type n: L{Element}
916        @return: A set of namespaces.
917        @rtype: set
918        """
919        s = set()
920        for ns in n.nsprefixes.items():
921            if self.permit(ns):
922                s.add(ns[1])
923        return s
924           
925    def genPrefixes(self):
926        """
927        Generate a I{reverse} mapping of unique prefixes for all namespaces.
928        @return: A referse dict of prefixes.
929        @rtype: {u, p}
930        """
931        prefixes = {}
932        n = 0
933        for u in self.namespaces:
934            p = 'ns%d' % n
935            prefixes[u] = p
936            n += 1
937        return prefixes
938   
939    def refit(self):
940        """
941        Refit (normalize) the prefixes in the node.
942        """
943        self.refitNodes()
944        self.refitMappings()
945   
946    def refitNodes(self):
947        """
948        Refit (normalize) all of the nodes in the branch.
949        """
950        for n in self.branch:
951            if n.prefix is not None:
952                ns = n.namespace()
953                if self.permit(ns):
954                    n.prefix = self.prefixes[ns[1]]
955            self.refitAttrs(n)
956               
957    def refitAttrs(self, n):
958        """
959        Refit (normalize) all of the attributes in the node.
960        @param n: A node.
961        @type n: L{Element}
962        """
963        for a in n.attributes:
964            self.refitAddr(a)
965   
966    def refitAddr(self, a):
967        """
968        Refit (normalize) the attribute.
969        @param a: An attribute.
970        @type a: L{Attribute}
971        """
972        if a.prefix is not None:
973            ns = a.namespace()
974            if self.permit(ns):
975                a.prefix = self.prefixes[ns[1]]
976        self.refitValue(a)
977   
978    def refitValue(self, a):
979        """
980        Refit (normalize) the attribute's value.
981        @param a: An attribute.
982        @type a: L{Attribute}
983        """
984        p,name = splitPrefix(a.getValue())
985        if p is None: return
986        ns = a.resolvePrefix(p)
987        if self.permit(ns):
988            u = ns[1]
989            p = self.prefixes[u]
990            a.setValue(':'.join((p, name)))
991           
992    def refitMappings(self):
993        """
994        Refit (normalize) all of the nsprefix mappings.
995        """
996        for n in self.branch:
997            n.nsprefixes = {}
998        n = self.node
999        for u, p in self.prefixes.items():
1000            n.addPrefix(p, u)
1001           
1002    def permit(self, ns):
1003        """
1004        Get whether the I{ns} is to be normalized.
1005        @param ns: A namespace.
1006        @type ns: (p,u)
1007        @return: True if to be included.
1008        @rtype: boolean
1009        """
1010        return not self.skip(ns)
1011           
1012    def skip(self, ns):
1013        """
1014        Get whether the I{ns} is to B{not} be normalized.
1015        @param ns: A namespace.
1016        @type ns: (p,u)
1017        @return: True if to be skipped.
1018        @rtype: boolean
1019        """
1020        return ns is None or \
1021            ( ns == Namespace.default ) or \
1022            ( ns == Namespace.xsdns ) or \
1023            ( ns == Namespace.xsins) or \
1024            ( ns == Namespace.xmlns )
Note: See TracBrowser for help on using the repository browser.