Project

General

Profile

« Previous | Next » 

Revision 94

xpath.py: Added concept of keys vs attrs in XPath elem

View differences:

scripts/lib/xpath.py
6 6
import xml_dom
7 7

  
8 8
class XpathElem:
9
    def __init__(self, name, value=None, attrs=None, is_attr=False,
10
        is_ptr=False):
11
        if attrs == None: attrs = []
9
    def __init__(self, name, value=None, is_attr=False):
12 10
        self.name = name
13 11
        self.value = value
14
        self.attrs = attrs
15 12
        self.is_attr = is_attr
16
        self.is_ptr = is_ptr
13
        self.is_ptr = False
14
        self.keys = []
15
        self.attrs = []
17 16
        self.other_branches = [] # temp implementation for split paths
18 17
    
19 18
    def __repr__(self):
20 19
        str_ = ''
21 20
        if self.is_attr: str_ += '@'
22 21
        str_ += self.name
23
        if self.attrs != []: str_ += repr(self.attrs)
22
        if self.keys != []: str_ += repr(self.keys)
23
        if self.attrs != []: str_ += ':'+repr(self.attrs)
24
        if self.is_ptr: str_ += '->'
25
        if self.other_branches != []: str_ += '{'+repr(self.other_branches)+'}'
24 26
        if self.value != None: str_ += '='+repr(self.value)
25
        if self.is_ptr: str_ += '->'
26 27
        return str_
27 28
    
28 29
    def __eq__(self, other): return self.__dict__ == other.__dict__
......
32 33
def set_value(path, value): path[-1].value = value
33 34

  
34 35
def backward_id(elem):
35
    if len(elem.attrs) >= 1 and value(elem.attrs[0]) == None and\
36
    len(elem.attrs[0]) == 1: return elem.attrs[0]
36
    if len(elem.keys) >= 1 and value(elem.keys[0]) == None and\
37
    len(elem.keys[0]) == 1: return elem.keys[0]
37 38
    else: return None
38 39

  
39 40
def parse(str_):
......
44 45
        while True:
45 46
            # Split path
46 47
            if parser.str_('{'):
47
                paths = tree[-1].other_branches
48
                while True:
49
                    paths.append(_path())
50
                    if not parser.str_(','): break
48
                tree[-1].other_branches = _paths()
51 49
                parser.str_('}', required=True)
52
                tree += paths.pop(0) # use first subpath for now
50
                tree += tree[-1].other_branches.pop(0) # use first path for now
53 51
                break # nothing allowed after split path
54 52
            
55 53
            elem = XpathElem(is_attr=parser.str_('@'),
56 54
                name=parser.re(r'[\w.*]+', required=True))
57 55
            
58
            # Attrs
56
            # Keys used to match nodes
59 57
            if parser.str_('['):
60
                while True:
61
                    elem.attrs.append(_path())
62
                    if not parser.str_(','): break
58
                elem.keys = _paths()
63 59
                parser.str_(']', required=True)
64 60
            
61
            # Attrs created when no matching node exists
62
            if parser.str_(':'):
63
                parser.str_('[', required=True)
64
                elem.attrs = _paths()
65
                parser.str_(']', required=True)
66
            
65 67
            elem.is_ptr = parser.str_('->')
66 68
            tree.append(elem)
67 69
            
......
70 72
                parser.str_('/', required=True) # next / is inside ()
71 73
                path = _path()
72 74
                parser.str_(')', required=True)
73
                elem.attrs.append(path)
75
                elem.keys.append(path)
74 76
                tree += path
75 77
            
76 78
            if not parser.str_('/'): break
......
96 98
        
97 99
        return tree
98 100
    
101
    def _paths():
102
        paths = []
103
        while True:
104
            paths.append(_path())
105
            if not parser.str_(','): break
106
        return paths
107
    
99 108
    parser.str_('/') # optional leading /
100 109
    path = _path()
101 110
    parser.end()
......
111 120
def set_id(path, id_, has_types=True):
112 121
    if has_types: id_level = instance_level
113 122
    else: id_level = 0
114
    path[id_level].attrs.append([XpathElem('id', id_, is_attr=True)])
123
    path[id_level].keys.append([XpathElem('id', id_, True)])
115 124

  
116 125
def is_id(path): return path[0].is_attr and path[0].name == 'id'
117 126

  
118
def is_instance(elem): return elem.attrs != [] and is_id(elem.attrs[0]) 
127
def is_instance(elem): return elem.keys != [] and is_id(elem.keys[0]) 
119 128

  
120 129
def get(doc, xpath, create=False, last_only=None, parent=None):
121 130
    # Warning: The last_only optimization may put data that should be together
......
131 140
        elif elem.name == '.': children = [parent]
132 141
        else:
133 142
            children = xml_dom.by_tag_name(parent, elem.name,
134
            last_only and (elem.attrs == [] or is_instance(elem)))
143
            last_only and (elem.keys == [] or is_instance(elem)))
135 144
        
136 145
        # Check each match
137 146
        node = None
138 147
        for child in children:
139 148
            is_match = elem.value == None or xml_dom.value(child) == elem.value
140
            for attr in elem.attrs:
149
            for attr in elem.keys:
141 150
                if not is_match: break
142 151
                is_match = get(doc, attr, False, last_only, child) != None
143 152
            if is_match: node = child; break
......
150 159
                node = parent.getAttributeNode(elem.name)
151 160
            else: node = parent.appendChild(doc.createElement(elem.name))
152 161
            if elem.value != None: xml_dom.set_value(doc, node, elem.value)
153
            for attr in elem.attrs: get(doc, attr, create, last_only, node)
162
            for attr in elem.keys + elem.attrs:
163
                get(doc, attr, create, last_only, node)
154 164
        
155 165
        for branch in elem.other_branches:
156 166
            branch = copy.deepcopy(branch)
......
168 178
                id_ = xml_dom.value(node)
169 179
                obj_xpath = obj(xpath) # target object
170 180
                if id_ == None or get(doc, obj_xpath, False, True) == None:
171
                    # no target or target attrs don't match
181
                    # no target or target keys don't match
172 182
                    if not create: return None
173 183
                    
174 184
                    # Use last target object's ID + 1
175
                    obj_xpath[-1].attrs = [] # just get by tag name
185
                    obj_xpath[-1].keys = [] # just get by tag name
176 186
                    last = get(doc, obj_xpath, False, True)
177 187
                    if last != None: id_ = str(int(xml_dom.get_id(last)) + 1)
178 188
                    else: id_ = '0'
179 189
                    
180
                    # Will append if target attrs didn't match. Place ! in XPath
190
                    # Will append if target keys didn't match. Place ! in XPath
181 191
                    # after element to fork at to avoid this.
182 192
                    xml_dom.set_value(doc, node, id_)
183 193
                else: last_only = False

Also available in: Unified diff