Project

General

Profile

1 21 aaronmk
# XPath-based XML tree manipulation
2
3
from xml.dom import Node
4
5
from Parser import Parser
6
import xml_util
7
8
class XpathElem:
9
    def __init__(self, name, value=None, attrs=None, is_attr=False,
10
        is_ptr=False):
11
        if attrs == None: attrs = []
12
        self.name = name
13
        self.value = value
14
        self.attrs = attrs
15
        self.is_attr = is_attr
16
        self.is_ptr = is_ptr
17
18
    def __repr__(self):
19
        str_ = ''
20
        if self.is_attr: str_ += '@'
21
        str_ += self.name+repr(self.attrs)+'='+repr(self.value)
22
        if self.is_ptr: str_ += '->'
23
        return str_
24
25
    def __eq__(self, other): return self.__dict__ == other.__dict__
26
27
class XpathParser(Parser):
28
    def _main(self):
29
        self._match_str('/', required=True)
30
        return self._path()
31
32
    def _path(self):
33
        tree = []
34
        while True:
35
            elem = XpathElem(is_attr=self._match_str('@'), name=self._fields())
36
            if self._match_str('['):
37
                elem.attrs = self._attrs()
38
                self._match_str(']', required=True)
39
            elem.is_ptr = self._match_str('->')
40
            tree.append(elem)
41
            if not self._match_str('/'): break
42
        return tree
43
44
    def _fields(self):
45
        if self._match_str('{'):
46
            tree = []
47
            while True:
48
                tree.append(self._field())
49
                if not self._match_str(','): break
50
            self._match_str('}', required=True)
51
            tree = tuple(tree)
52
            tree = tree[0] # just use first field for now
53
        else: tree = self._field()
54
        return tree
55
56
    def _attrs(self):
57
        tree = []
58
        while True:
59
            path = self._path()
60
            self._match_str('=', required=True)
61
            path[-1].value = self._value()
62
            tree.append(path)
63
            if not self._match_str(','): break
64
        return tree
65
66
    def _field(self):
67
        return self._name()
68
69
    def _name(self): return self._match_re(r'[\w.]+', required=True)
70
71
    def _value(self): return self._match_re(r'[\w.|]+', required=True)
72
73
def set_id(path, id_, has_type_containers=True):
74
    if has_type_containers: id_level = 1
75
    else: id_level = 0
76
    path[id_level].attrs.append([XpathElem('id', id_, is_attr=True)])
77
78
def get(doc, path, create=False, parent=None):
79
    if parent == None: parent = doc.documentElement
80
    for elem in path:
81
        node = None
82
        if elem.is_attr: node = parent.getAttributeNode(elem.name)
83
        elif elem.name == '.': node = parent
84
        else: node = xml_util.by_tag_name(parent, elem.name)
85
        if node != None and elem.value != None\
86
        and xml_util.value(node) != elem.value: node = None
87
        for attr in elem.attrs:
88
            if get(doc, attr, parent=node) == None: node = None; break
89
        if node == None:
90
            if not create: return None
91
            if elem.is_attr:
92
                parent.setAttribute(elem.name, '')
93
                node = parent.getAttributeNode(elem.name)
94
            else: node = parent.appendChild(doc.createElement(elem.name))
95
            if elem.value != None:
96
                if node.nodeType == Node.ELEMENT_NODE:
97
                    node.appendChild(doc.createTextNode(elem.value))
98
                else: node.nodeValue = elem.value
99
            for attr in elem.attrs: get(doc, attr, create, node)
100
        if elem.is_ptr:
101
            pass
102
        parent = node
103
    return parent