Project

General

Profile

1
# XPath-based XML tree manipulation
2

    
3
import copy
4

    
5
import xml_dom
6
import xpath
7

    
8
def get(doc, path, create=False, last_only=None, parent=None):
9
    # Warning: The last_only optimization may put data that should be together
10
    # into separate nodes
11
    if parent == None: parent = doc.documentElement
12
    if last_only == None: last_only = create
13
    elem_idx = 0
14
    for elem in path:
15
        # Find possible matches
16
        children = []
17
        if elem.is_attr:
18
            child = parent.getAttributeNode(elem.name)
19
            if child != None: children = [child]
20
        elif elem.name == '.': children = [parent]
21
        else: children = xml_dom.by_tag_name(parent, elem.name, last_only)
22
        
23
        # Check each match
24
        node = None
25
        for child in children:
26
            is_match = elem.value == None or xml_dom.value(child) == elem.value
27
            for attr in elem.attrs:
28
                if not is_match: break
29
                is_match = get(doc, attr, False, last_only, child) != None
30
            if is_match: node = child; break
31
        
32
        # Create node
33
        if node == None:
34
            if not create: return None
35
            if elem.is_attr:
36
                parent.setAttribute(elem.name, '')
37
                node = parent.getAttributeNode(elem.name)
38
            else: node = parent.appendChild(doc.createElement(elem.name))
39
            if elem.value != None: xml_dom.set_value(doc, node, elem.value)
40
            for attr in elem.attrs: get(doc, attr, create, last_only, node)
41
        
42
        # Follow pointer
43
        if elem.is_ptr:
44
            path = copy.deepcopy(path[elem_idx+1:]) # rest of path
45
            id_elem = xpath.backward_id(path[xpath.instance_level])
46
            if id_elem != None:
47
                # backward (child-to-parent) pointer with target ID attr
48
                xpath.set_value(id_elem, xml_dom.get_id(node))
49
            else: # forward (parent-to-child) pointer
50
                id_ = xml_dom.value(node)
51
                obj_path = xpath.obj(path) # target object
52
                if id_ == None or get(doc, obj_path, False, True) == None:
53
                    # no target or target attrs don't match
54
                    if not create: return None
55
                    
56
                    # Use last target object's ID + 1
57
                    obj_path[-1].attrs = [] # just get by tag name
58
                    last = get(doc, obj_path, False, True)
59
                    if last != None: id_ = str(int(xml_dom.get_id(last)) + 1)
60
                    else: id_ = '0'
61
                    
62
                    # Will append if target attrs didn't match. Place ! in XPath
63
                    # after element to fork at to avoid this.
64
                    xml_dom.set_value(doc, node, id_)
65
                else: last_only = False
66
                xpath.set_id(path, id_)
67
            return get(doc, path, create, last_only)
68
        
69
        parent = node
70
        elem_idx += 1
71
    return parent
(7-7/8)