python-2.5.2/win32/Lib/xml/etree/ElementPath.py
changeset 0 ae805ac0140d
equal deleted inserted replaced
-1:000000000000 0:ae805ac0140d
       
     1 #
       
     2 # ElementTree
       
     3 # $Id: ElementPath.py 1858 2004-06-17 21:31:41Z Fredrik $
       
     4 #
       
     5 # limited xpath support for element trees
       
     6 #
       
     7 # history:
       
     8 # 2003-05-23 fl   created
       
     9 # 2003-05-28 fl   added support for // etc
       
    10 # 2003-08-27 fl   fixed parsing of periods in element names
       
    11 #
       
    12 # Copyright (c) 2003-2004 by Fredrik Lundh.  All rights reserved.
       
    13 #
       
    14 # fredrik@pythonware.com
       
    15 # http://www.pythonware.com
       
    16 #
       
    17 # --------------------------------------------------------------------
       
    18 # The ElementTree toolkit is
       
    19 #
       
    20 # Copyright (c) 1999-2004 by Fredrik Lundh
       
    21 #
       
    22 # By obtaining, using, and/or copying this software and/or its
       
    23 # associated documentation, you agree that you have read, understood,
       
    24 # and will comply with the following terms and conditions:
       
    25 #
       
    26 # Permission to use, copy, modify, and distribute this software and
       
    27 # its associated documentation for any purpose and without fee is
       
    28 # hereby granted, provided that the above copyright notice appears in
       
    29 # all copies, and that both that copyright notice and this permission
       
    30 # notice appear in supporting documentation, and that the name of
       
    31 # Secret Labs AB or the author not be used in advertising or publicity
       
    32 # pertaining to distribution of the software without specific, written
       
    33 # prior permission.
       
    34 #
       
    35 # SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
       
    36 # TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT-
       
    37 # ABILITY AND FITNESS.  IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR
       
    38 # BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
       
    39 # DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
       
    40 # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
       
    41 # ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
       
    42 # OF THIS SOFTWARE.
       
    43 # --------------------------------------------------------------------
       
    44 
       
    45 # Licensed to PSF under a Contributor Agreement.
       
    46 # See http://www.python.org/2.4/license for licensing details.
       
    47 
       
    48 ##
       
    49 # Implementation module for XPath support.  There's usually no reason
       
    50 # to import this module directly; the <b>ElementTree</b> does this for
       
    51 # you, if needed.
       
    52 ##
       
    53 
       
    54 import re
       
    55 
       
    56 xpath_tokenizer = re.compile(
       
    57     "(::|\.\.|\(\)|[/.*:\[\]\(\)@=])|((?:\{[^}]+\})?[^/:\[\]\(\)@=\s]+)|\s+"
       
    58     ).findall
       
    59 
       
    60 class xpath_descendant_or_self:
       
    61     pass
       
    62 
       
    63 ##
       
    64 # Wrapper for a compiled XPath.
       
    65 
       
    66 class Path:
       
    67 
       
    68     ##
       
    69     # Create an Path instance from an XPath expression.
       
    70 
       
    71     def __init__(self, path):
       
    72         tokens = xpath_tokenizer(path)
       
    73         # the current version supports 'path/path'-style expressions only
       
    74         self.path = []
       
    75         self.tag = None
       
    76         if tokens and tokens[0][0] == "/":
       
    77             raise SyntaxError("cannot use absolute path on element")
       
    78         while tokens:
       
    79             op, tag = tokens.pop(0)
       
    80             if tag or op == "*":
       
    81                 self.path.append(tag or op)
       
    82             elif op == ".":
       
    83                 pass
       
    84             elif op == "/":
       
    85                 self.path.append(xpath_descendant_or_self())
       
    86                 continue
       
    87             else:
       
    88                 raise SyntaxError("unsupported path syntax (%s)" % op)
       
    89             if tokens:
       
    90                 op, tag = tokens.pop(0)
       
    91                 if op != "/":
       
    92                     raise SyntaxError(
       
    93                         "expected path separator (%s)" % (op or tag)
       
    94                         )
       
    95         if self.path and isinstance(self.path[-1], xpath_descendant_or_self):
       
    96             raise SyntaxError("path cannot end with //")
       
    97         if len(self.path) == 1 and isinstance(self.path[0], type("")):
       
    98             self.tag = self.path[0]
       
    99 
       
   100     ##
       
   101     # Find first matching object.
       
   102 
       
   103     def find(self, element):
       
   104         tag = self.tag
       
   105         if tag is None:
       
   106             nodeset = self.findall(element)
       
   107             if not nodeset:
       
   108                 return None
       
   109             return nodeset[0]
       
   110         for elem in element:
       
   111             if elem.tag == tag:
       
   112                 return elem
       
   113         return None
       
   114 
       
   115     ##
       
   116     # Find text for first matching object.
       
   117 
       
   118     def findtext(self, element, default=None):
       
   119         tag = self.tag
       
   120         if tag is None:
       
   121             nodeset = self.findall(element)
       
   122             if not nodeset:
       
   123                 return default
       
   124             return nodeset[0].text or ""
       
   125         for elem in element:
       
   126             if elem.tag == tag:
       
   127                 return elem.text or ""
       
   128         return default
       
   129 
       
   130     ##
       
   131     # Find all matching objects.
       
   132 
       
   133     def findall(self, element):
       
   134         nodeset = [element]
       
   135         index = 0
       
   136         while 1:
       
   137             try:
       
   138                 path = self.path[index]
       
   139                 index = index + 1
       
   140             except IndexError:
       
   141                 return nodeset
       
   142             set = []
       
   143             if isinstance(path, xpath_descendant_or_self):
       
   144                 try:
       
   145                     tag = self.path[index]
       
   146                     if not isinstance(tag, type("")):
       
   147                         tag = None
       
   148                     else:
       
   149                         index = index + 1
       
   150                 except IndexError:
       
   151                     tag = None # invalid path
       
   152                 for node in nodeset:
       
   153                     new = list(node.getiterator(tag))
       
   154                     if new and new[0] is node:
       
   155                         set.extend(new[1:])
       
   156                     else:
       
   157                         set.extend(new)
       
   158             else:
       
   159                 for node in nodeset:
       
   160                     for node in node:
       
   161                         if path == "*" or node.tag == path:
       
   162                             set.append(node)
       
   163             if not set:
       
   164                 return []
       
   165             nodeset = set
       
   166 
       
   167 _cache = {}
       
   168 
       
   169 ##
       
   170 # (Internal) Compile path.
       
   171 
       
   172 def _compile(path):
       
   173     p = _cache.get(path)
       
   174     if p is not None:
       
   175         return p
       
   176     p = Path(path)
       
   177     if len(_cache) >= 100:
       
   178         _cache.clear()
       
   179     _cache[path] = p
       
   180     return p
       
   181 
       
   182 ##
       
   183 # Find first matching object.
       
   184 
       
   185 def find(element, path):
       
   186     return _compile(path).find(element)
       
   187 
       
   188 ##
       
   189 # Find text for first matching object.
       
   190 
       
   191 def findtext(element, path, default=None):
       
   192     return _compile(path).findtext(element, default)
       
   193 
       
   194 ##
       
   195 # Find all matching objects.
       
   196 
       
   197 def findall(element, path):
       
   198     return _compile(path).findall(element)