source: TI07-MOLES/trunk/StubB/XSLT/browse/portal/cgi/browse/ElementPath.py @ 1164

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI07-MOLES/trunk/StubB/XSLT/browse/portal/cgi/browse/ElementPath.py@1164
Revision 1164, 5.8 KB checked in by lawrence, 14 years ago (diff)

Putting browse code into a module to make it easier
to coexist with other cgi scripts etc ...

RevLine 
[801]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##
46# Implementation module for XPath support.  There's usually no reason
47# to import this module directly; the <b>ElementTree</b> does this for
48# you, if needed.
49##
50
51import re
52
53xpath_tokenizer = re.compile(
54    "(::|\.\.|\(\)|[/.*:\[\]\(\)@=])|((?:\{[^}]+\})?[^/:\[\]\(\)@=\s]+)|\s+"
55    ).findall
56
57class xpath_descendant_or_self:
58    pass
59
60##
61# Wrapper for a compiled XPath.
62
63class Path:
64
65    ##
66    # Create an Path instance from an XPath expression.
67
68    def __init__(self, path):
69        tokens = xpath_tokenizer(path)
70        # the current version supports 'path/path'-style expressions only
71        self.path = []
72        self.tag = None
73        if tokens and tokens[0][0] == "/":
74            raise SyntaxError("cannot use absolute path on element")
75        while tokens:
76            op, tag = tokens.pop(0)
77            if tag or op == "*":
78                self.path.append(tag or op)
79            elif op == ".":
80                pass
81            elif op == "/":
82                self.path.append(xpath_descendant_or_self())
83                continue
84            else:
85                raise SyntaxError("unsupported path syntax (%s)" % op)
86            if tokens:
87                op, tag = tokens.pop(0)
88                if op != "/":
89                    raise SyntaxError(
90                        "expected path separator (%s)" % (op or tag)
91                        )
92        if self.path and isinstance(self.path[-1], xpath_descendant_or_self):
93            raise SyntaxError("path cannot end with //")
94        if len(self.path) == 1 and isinstance(self.path[0], type("")):
95            self.tag = self.path[0]
96
97    ##
98    # Find first matching object.
99
100    def find(self, element):
101        tag = self.tag
102        if tag is None:
103            nodeset = self.findall(element)
104            if not nodeset:
105                return None
106            return nodeset[0]
107        for elem in element:
108            if elem.tag == tag:
109                return elem
110        return None
111
112    ##
113    # Find text for first matching object.
114
115    def findtext(self, element, default=None):
116        tag = self.tag
117        if tag is None:
118            nodeset = self.findall(element)
119            if not nodeset:
120                return default
121            return nodeset[0].text or ""
122        for elem in element:
123            if elem.tag == tag:
124                return elem.text or ""
125        return default
126
127    ##
128    # Find all matching objects.
129
130    def findall(self, element):
131        nodeset = [element]
132        index = 0
133        while 1:
134            try:
135                path = self.path[index]
136                index = index + 1
137            except IndexError:
138                return nodeset
139            set = []
140            if isinstance(path, xpath_descendant_or_self):
141                try:
142                    tag = self.path[index]
143                    if not isinstance(tag, type("")):
144                        tag = None
145                    else:
146                        index = index + 1
147                except IndexError:
148                    tag = None # invalid path
149                for node in nodeset:
150                    new = list(node.getiterator(tag))
151                    if new and new[0] is node:
152                        set.extend(new[1:])
153                    else:
154                        set.extend(new)
155            else:
156                for node in nodeset:
157                    for node in node:
158                        if path == "*" or node.tag == path:
159                            set.append(node)
160            if not set:
161                return []
162            nodeset = set
163
164_cache = {}
165
166##
167# (Internal) Compile path.
168
169def _compile(path):
170    p = _cache.get(path)
171    if p is not None:
172        return p
173    p = Path(path)
174    if len(_cache) >= 100:
175        _cache.clear()
176    _cache[path] = p
177    return p
178
179##
180# Find first matching object.
181
182def find(element, path):
183    return _compile(path).find(element)
184
185##
186# Find text for first matching object.
187
188def findtext(element, path, default=None):
189    return _compile(path).findtext(element, default)
190
191##
192# Find all matching objects.
193
194def findall(element, path):
195    return _compile(path).findall(element)
196
Note: See TracBrowser for help on using the repository browser.