Changeset 428

Show
Ignore:
Timestamp:
01/27/03 16:42:50 (6 years ago)
Author:
sholloway
Message:

*** empty log message ***

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/RBFoundation/RBFoundation/XMLNode.py

    r418 r428  
    2424#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    2525 
     26from __future__ import generators 
     27import sys 
    2628from xml.sax.saxutils import quoteattr as xmlquoteattr 
    2729from xml.sax.saxutils import escape as xmlescape 
     30from types import SliceType as _SliceType 
     31from types import NoneType as _NoneType 
    2832 
    2933#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
     
    3135#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    3236 
    33 try: basestring 
    34 except NameError: basestring = (str, unicode) 
     37if sys.version_info < (2,2,3): 
     38    basestring = (str, unicode) 
     39 
     40#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
     41 
     42class matchall(object): 
     43    def __eq__(self, other): 
     44        return True 
     45    def __ne__(self, other): 
     46        return not self.__eq__(other) 
     47_any = matchall() 
     48 
     49#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
     50 
     51class matchobj(object): 
     52    def __init__(self, testcall): 
     53        self.testcall = testcall 
     54    def __eq__(self, other): 
     55        return self.testcall(other) and True or False 
     56    def __ne__(self, other): 
     57        return not self.__eq__(other) 
    3558 
    3659#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    3760 
    3861class xmlnode(object): 
     62    """Simple class to build valid XML. 
     63    Also has some basic access, query, and iteration methods. 
     64     
     65    >>> node = xmlnode('mynode', 'mynamespace') 
     66    >>> node.attrs['aname'] = 'avalue' 
     67    >>> node.aname 
     68    'avalue' 
     69    >>> node += "\n" 
     70    >>> node += "some cdata text \n" 
     71    >>> node += "some more cdata text \n" 
     72    >>> node += ('another_node', ) 
     73    >>> node += "\n" 
     74    >>> node += ('third', 'namespace-for-third') 
     75    >>> node += "\n" 
     76    >>> node += ('forth', 'namespace-for-forth', 'four') 
     77    >>> node += "\n" 
     78    >>> print node 
     79    <mynode xmlns="mynamespace" aname="avalue"> 
     80    some cdata text 
     81    some more cdata text 
     82    <another_node/> 
     83    <third xmlns="namespace-for-third"/> 
     84    <four:forth xmlns:four="namespace-for-forth"/> 
     85    </mynode> 
     86    """ 
     87 
    3988    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    4089    #~ Constants / Variables / Etc.  
     
    56105 
    57106    def __getattribute__(self, name): 
     107        """Returns self.attrs[name] when possible.  Use explicit self.attrs[name] for more uniform access.""" 
    58108        try: 
    59109            return object.__getattribute__(self, name) 
     
    64114 
    65115    def __setattr__(self, name, value): 
     116        """Sets self.attrs[name] = value when possible.  Use explicit self.attrs[name] for more uniform access.""" 
    66117        try: 
    67118            # This works because of the __slots__ definition 
    68119            object.__setattr__(self, name, value) 
    69120        except AttributeError: 
    70             self.addattr((name,value)) 
    71  
    72     def __getitem__(self, *args, **kw): 
    73         return self.elems.__getitem__(*args, **kw) 
    74     def __setitem__(self, *args, **kw): 
    75         return self.elems.__setitem__(*args, **kw) 
    76     def __delitem__(self, *args, **kw): 
    77         return self.elems.__delitem__(*args, **kw) 
     121            self.addattr((name,str(value))) 
     122 
     123    def __getitem__(self, key, *args, **kw): 
     124        """Returns a list of matching child elements of xmlnode.  See listelems.""" 
     125        if isinstance(key, (int, long, _SliceType)): 
     126            return self.elems.__getitem__(key, *args, **kw) 
     127        else: 
     128            return self.listelems(key, *args, **kw) 
     129    def __setitem__(self, key, *args, **kw): 
     130        """Sets child element at self.elem[key] = value.""" 
     131        if isinstance(key, (int, long, _SliceType)): 
     132            return self.elems.__setitem__(*args, **kw) 
     133        else: 
     134            raise TypeError, "Cannot set node elemenets with non integer key item operations" 
     135    def __delitem__(self, key, *args, **kw): 
     136        """Removes all matching child elements of xmlnode.  See delelems.""" 
     137        if isinstance(key, (int, long, _SliceType)): 
     138            return self.elems.__delitem__(*args, **kw) 
     139        else: 
     140            self.delelems(key, *args, **kw) 
     141 
     142    def __contains__(self, key, *args, **kw): 
     143        """Returns True if key is a child element of xmlnode.  See haselem.""" 
     144        if isinstance(key, (int, long, _SliceType)): 
     145            return self.elems.__contains__(key, *args, **kw) 
     146        else: 
     147            return self.haselem(key, *args, **kw) 
    78148 
    79149    def __iadd__(self, other): 
    80         self.addelement(other) 
     150        """Adds an element to xmlnode.  See addelem.""" 
     151        self.addelem(other) 
    81152        return self 
    82153 
     154    def __iter__(self): 
     155        """Returns an iterator of child elements""" 
     156        return iter(self.elems) 
     157 
     158    def __str__(self): 
     159        """Returns xmlnode as a string in XML form""" 
     160        return str(self.toXML()) 
     161 
    83162    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    84163    #~ Public Methods  
     
    86165 
    87166    def addattr(self, *args, **kw): 
     167        """Adds attributes to xmlnode.""" 
    88168        if args: self.attrs.update(dict(args)) 
    89169        if kw: self.attrs.update(kw) 
    90170 
    91     def addtext(self, text): 
     171    def adddata(self, text): 
     172        """Adds a child cdata to xmlnode.""" 
    92173        self.elems.append(text) 
    93174        return text 
     175    def addtext(self, *args, **kw):  
     176        """See adddata.""" 
     177        return self.adddata(*args, **kw) 
     178    def write(self, data): 
     179        """For use in conjunction with file-like uses. 
     180 
     181        >>> node = xmlnode('mynode', 'mynamespace') 
     182        >>> print >> node 
     183        >>> print >> node, 'some cdata' 
     184        >>> node.toxml() 
     185        '<mynode xmlns="mynamespace" softspace="0">\\nsome cdata\\n</mynode>' 
     186        """ 
     187        self.adddata(data) 
    94188 
    95189    def addnode(self, *args, **kw): 
     190        """Adds a child node to xmlnode.""" 
    96191        nodebuilder = self.nodebuilder or self.__class__ 
    97192        result = nodebuilder(*args, **kw) 
     
    99194        return result 
    100195 
    101     def addelement(self, elem): 
     196    def addelem(self, elem): 
     197        """Adds a child element to xmlnode. 
     198        Elem is considered cdata if it is of string type,  
     199        or a node, otherwise.""" 
    102200        if isinstance(elem, basestring): 
    103             return self.addtext(elem) 
     201            return self.adddata(elem) 
    104202        else: return self.addnode(*elem) 
    105203 
    106     def setnamespace(self, prefix, namespace): 
     204    def setxmlns(self, prefix, namespace): 
    107205        if namespace is None: 
    108206            try: del self.namespaces[prefix or ''] 
     
    110208        else: 
    111209            self.namespaces[prefix or ''] = namespace 
    112     setns = setnamespace # short alias 
     210 
     211    #~ iteration over elements ~~~~~~~~~~~~~~~~~~~~~~~~~~ 
     212 
     213    def enumdata(self, match=None): 
     214        """Returns a generator to iterate through the matching data indices in xmlnode""" 
     215        if not match: match = _any 
     216        elif callable(match): match = matchobj(match) 
     217        idx = 0 
     218        for each in self.elems: 
     219            if isinstance(each, basestring): 
     220                if each == match: 
     221                    yield idx 
     222            idx += 1 
     223 
     224    def iterdata(self, match=None): 
     225        """Returns a generator to iterate through the matching data in xmlnode""" 
     226        if not match: match = _any 
     227        elif callable(match): match = matchobj(match) 
     228        for each in self.elems: 
     229            if isinstance(each, basestring): 
     230                if each == match: 
     231                    yield each 
     232 
     233    def listdata(self, *args, **kw): 
     234        """Returns a list of matching data in xmlnode""" 
     235        return [x for x in self.iterdata(*args, **kw)] 
     236 
     237    def deldata(self, *args, **kw): 
     238        dellist = [x for x in self.enumdata(*args, **kw)] 
     239        for idx in dellist: 
     240            del self.elems[idx] 
     241 
     242    def hasdata(self, *args, **kw): 
     243        """Returns True if data is in xmlnode""" 
     244        try: 
     245            self.iterdata(*args, **kw).next() 
     246            return True 
     247        except StopIteration: 
     248            return False 
     249 
     250    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
     251 
     252    def enumnodes(self, node=_any, namespace=_any, prefix=_any): 
     253        """Returns a generator to iterate through the matching child node indicies of xmlnode""" 
     254        idx = 0 
     255        for each in self.elems: 
     256            if not isinstance(each, basestring): 
     257                if node == each.node and namespace == each.namespace and prefix == each.prefix: 
     258                    yield idx 
     259            idx += 1 
     260 
     261    def iternodes(self, node=_any, namespace=_any, prefix=_any): 
     262        """Returns a generator to iterate through the matching child nodes of xmlnode""" 
     263        for each in self.elems: 
     264            if not isinstance(each, basestring): 
     265                if node == each.node and namespace == each.namespace and prefix == each.prefix: 
     266                    yield each 
     267 
     268    def listnodes(self, *args, **kw): 
     269        """Returns a list of matching child nodes of xmlnode""" 
     270        return [x for x in self.iternodes(*args, **kw)] 
     271 
     272    def delnodes(self, *args, **kw): 
     273        """Removes all matching child nodes of xmlnode""" 
     274        dellist = [x for x in self.enumnodes(*args, **kw)] 
     275        for idx in dellist: 
     276            del self.elems[idx] 
     277 
     278    def hasnode(self, *args, **kw): 
     279        """Returns True if elem is a child node of the xmlnode""" 
     280        try: 
     281            self.iternodes(*args, **kw).next() 
     282            return True 
     283        except StopIteration: 
     284            return False 
     285 
     286    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
     287 
     288    def enumelems(self, key=None, **kw): 
     289        """Returns a generator to iterate through the matching child element indicies of xmlnode""" 
     290        if isinstance(key, tuple): 
     291            return self.enumnodes(*key, **kw) 
     292        elif isinstance(key, _NoneType): 
     293            return xrange(0, len(self.elems)) 
     294        else: 
     295            return self.enumdata(key, **kw) 
     296 
     297    def iterelems(self, key=None, **kw): 
     298        """Returns a generator to iterate through the matching child elements of xmlnode""" 
     299        if isinstance(key, tuple): 
     300            return self.iternodes(*key, **kw) 
     301        elif isinstance(key, _NoneType): 
     302            return iter(self.elems) 
     303        else: 
     304            return self.iterdata(key, **kw) 
     305 
     306    def listelems(self, *args, **kw): 
     307        """Returns a list of matching child elements of xmlnode""" 
     308        return [x for x in self.iterelems(*args, **kw)] 
     309 
     310    def delelems(self, key, **kw): 
     311        """Removes all matching child elements of xmlnode""" 
     312        if isinstance(key, tuple): 
     313            self.delnodes(*key, **kw) 
     314        elif isinstance(key, _NoneType): 
     315            del self.elems[:] 
     316        else: 
     317            self.deldata(key, **kw) 
     318 
     319    def haselem(self, elem, *args, **kw): 
     320        """Returns True if elem is a child element of the xmlnode""" 
     321        try: 
     322            if isinstance(elem, tuple): 
     323                return self.hasnode(*elem, **kw) 
     324            elif isinstance(elem, _NoneType): 
     325                return False 
     326            else: 
     327                return self.hasdata(*elem, **kw) 
     328        except StopIteration: 
     329            return False 
    113330 
    114331    #~ namespace property ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    115332 
    116333    def _getnamespace(self): 
     334        """Returns the namespace of the xmlnode""" 
    117335        return self.namespaces.get(self.prefix or '', None) 
    118336    def _setnamespace(self, namespace): 
     337        """Sets the namespace of the xmlnode""" 
    119338        if isinstance(namespace, tuple): 
    120339            if len(namespace) == 1: 
     
    125344            self.namespaces[self.prefix or ''] = namespace 
    126345    def _delnamespace(self): 
     346        """Removes the namespace of the xmlnode""" 
    127347        try: del self.namespaces[self.prefix or ''] 
    128348        except KeyError: pass 
     
    131351    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    132352 
    133     def toXML(self, pretty=False, level=0, indent='    '): 
     353    def toxml(self, pretty=False, level=0, indent='    ', newline='\n'): 
     354        """Converts the xmlnode to valid XML.""" 
    134355        result = ['<'] 
    135356        if self.prefix: 
     
    148369        if self.elems: 
    149370            result.append('>') 
    150             result.extend(self._elemsToXML(pretty, level+1, indent)) 
    151             if pretty: result.append('\n' + indent * level) 
     371            result.extend(self._elemstoxml(pretty, level+1, indent, newline)) 
     372            if pretty: result.append(newline + indent * level) 
    152373            result.append('</%s>' % nodename) 
    153374        else: 
    154375            result.append('/>') 
    155376        return ''.join(result)   
    156  
    157     def _elemsToXML(self, pretty, level, indent): 
    158         if pretty: prettyindent = '\n' + indent * level 
     377    def toXML(self, *args, **kw):  
     378        """See toxml.""" 
     379        return self.toxml(*args, **kw) 
     380    def _toXML(self, *args, **kw): 
     381        """See toxml.""" 
     382        return self.toxml(*args, **kw) 
     383 
     384    def _elemstoxml(self, pretty, level, indent, newline): 
     385        """Converts child elements of xmlnode to valid XML.""" 
     386        if pretty: prettyindent = newline + indent * level 
    159387        result = [] 
    160388        for elem in self.elems: 
     
    162390            if isinstance(elem, basestring): 
    163391                result.append(xmlescape(elem)) 
    164             else: result.append(elem.toXML(pretty, level, indent)) 
     392            else: result.append(elem.toXML(pretty, level, indent, newline)) 
    165393        return result 
    166394 
     
    171399else: 
    172400    class NodeXML(xmlnode, XMLBuilder.XMLBuilderObjectBase): 
     401        """An adaptor to bridge between XMLBuilderObjectBase class and xmlnode""" 
    173402        def __init__(self, owner, parent, node, attributes, namespacemap): 
    174403            xmlnode.__init__(self, node[1], node[0], namespacemap.get(node[0], '')) 
     404            self.addattr(attributes) 
     405            for xmlns, prefix in namespacemap.iteritems(): 
     406                self.namespaces[prefix] = xmlns 
    175407        def _addElement(self, node, obj):  
    176408            self.elems.append(obj) 
     
    199431    x += 'Some cdata' 
    200432    x += 'subelem', 
     433    x += 'More data' 
     434    x += ('element',) 
    201435    x += 'anotherelem', 'SubNamespace' 
    202436    anotherelem = x[-1]