Project

General

Profile

1 55 aaronmk
# 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 62 aaronmk
        else:
22
            children = xml_dom.by_tag_name(parent, elem.name,
23
            last_only and (elem.attrs == [] or xpath.is_instance(elem)))
24 55 aaronmk
25
        # Check each match
26
        node = None
27
        for child in children:
28
            is_match = elem.value == None or xml_dom.value(child) == elem.value
29
            for attr in elem.attrs:
30
                if not is_match: break
31
                is_match = get(doc, attr, False, last_only, child) != None
32
            if is_match: node = child; break
33
34
        # Create node
35
        if node == None:
36
            if not create: return None
37
            if elem.is_attr:
38
                parent.setAttribute(elem.name, '')
39
                node = parent.getAttributeNode(elem.name)
40
            else: node = parent.appendChild(doc.createElement(elem.name))
41
            if elem.value != None: xml_dom.set_value(doc, node, elem.value)
42
            for attr in elem.attrs: get(doc, attr, create, last_only, node)
43
44
        # Follow pointer
45
        if elem.is_ptr:
46
            path = copy.deepcopy(path[elem_idx+1:]) # rest of path
47
            id_elem = xpath.backward_id(path[xpath.instance_level])
48
            if id_elem != None:
49
                # backward (child-to-parent) pointer with target ID attr
50
                xpath.set_value(id_elem, xml_dom.get_id(node))
51
            else: # forward (parent-to-child) pointer
52
                id_ = xml_dom.value(node)
53
                obj_path = xpath.obj(path) # target object
54
                if id_ == None or get(doc, obj_path, False, True) == None:
55
                    # no target or target attrs don't match
56
                    if not create: return None
57
58
                    # Use last target object's ID + 1
59
                    obj_path[-1].attrs = [] # just get by tag name
60
                    last = get(doc, obj_path, False, True)
61
                    if last != None: id_ = str(int(xml_dom.get_id(last)) + 1)
62
                    else: id_ = '0'
63
64
                    # Will append if target attrs didn't match. Place ! in XPath
65
                    # after element to fork at to avoid this.
66
                    xml_dom.set_value(doc, node, id_)
67
                else: last_only = False
68
                xpath.set_id(path, id_)
69
            return get(doc, path, create, last_only)
70
71
        parent = node
72
        elem_idx += 1
73
    return parent