Salome HOME
Apply modifications according to changes in KERNEL API.
[modules/yacs.git] / src / salomeloader / ElementPath.py
1 #
2 # ElementTree
3 # $Id$
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
51 import re
52
53 xpath_tokenizer = re.compile(
54     "(::|\.\.|\(\)|[/.*:\[\]\(\)@=])|((?:\{[^}]+\})?[^/:\[\]\(\)@=\s]+)|\s+"
55     ).findall
56
57 class xpath_descendant_or_self:
58     pass
59
60 ##
61 # Wrapper for a compiled XPath.
62
63 class 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
169 def _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
182 def find(element, path):
183     return _compile(path).find(element)
184
185 ##
186 # Find text for first matching object.
187
188 def findtext(element, path, default=None):
189     return _compile(path).findtext(element, default)
190
191 ##
192 # Find all matching objects.
193
194 def findall(element, path):
195     return _compile(path).findall(element)
196