1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 from lxml import etree
24
25 from translate.storage import base
26 from translate.misc.typecheck import accepts, Self, IsCallable, IsOneOf, Any
27 from translate.misc.typecheck.typeclasses import Number
30 @accepts(Self(), base.TranslationUnit)
32 self.unit = unit
33 self.children = {}
34
36 return isinstance(other, XPathTree) and \
37 self.unit == other.unit and \
38 self.children == other.children
39
42 """Split an xpath component into a tag-index tuple.
43
44 >>> split_xpath_component('{urn:oasis:names:tc:opendocument:xmlns:office:1.0}document-content[0]')
45 ('{urn:oasis:names:tc:opendocument:xmlns:office:1.0}document-content', 0).
46 """
47 lbrac = xpath_component.rfind(u'[')
48 rbrac = xpath_component.rfind(u']')
49 tag = xpath_component[:lbrac]
50 index = int(xpath_component[lbrac+1:rbrac])
51 return tag, index
52
55 """Split an 'xpath' string separated by / into a reversed list of its components. Thus:
56
57 >>> split_xpath('document-content[1]/body[2]/text[3]/p[4]')
58 [('p', 4), ('text', 3), ('body', 2), ('document-content', 1)]
59
60 The list is reversed so that it can be used as a stack, where the top of the stack is
61 the first component.
62 """
63 components = xpath.split(u'/')
64 components = [_split_xpath_component(component) for component in components]
65 return list(reversed(components))
66
67 @accepts(etree._Element, [(unicode, Number)], base.TranslationUnit)
69 """Walk down the tree rooted a node, and follow nodes which correspond to the
70 components of xpath_components. When reaching the end of xpath_components,
71 set the reference of the node to unit.
72
73 With reference to the tree diagram in build_unit_tree::
74
75 add_unit_to_tree(node, [('p', 2), ('text', 3), ('body', 2), ('document-content', 1)], unit)
76
77 would begin by popping ('document-content', 1) from the path and following the node marked
78 ('document-content', 1) in the tree. Likewise, will descend down the nodes marked ('body', 2)
79 and ('text', 3).
80
81 Since the node marked ('text', 3) has no child node marked ('p', 2), this node is created. Then
82 the add_unit_to_tree descends down this node. When this happens, there are no xpath components
83 left to pop. Thus, node.unit = unit is executed.
84 """
85 if len(xpath_components) > 0:
86 component = xpath_components.pop()
87
88
89 if component not in node.children:
90 node.children[component] = XPathTree()
91 _add_unit_to_tree(node.children[component], xpath_components, unit)
92 else:
93 node.unit = unit
94
95 @accepts(base.TranslationStore)
97 """Enumerate a translation store and build a tree with XPath components as nodes
98 and where a node contains a unit if a path from the root of the tree to the node
99 containing the unit, is equal to the XPath of the unit.
100
101 The tree looks something like this::
102 root
103 `- ('document-content', 1)
104 `- ('body', 2)
105 |- ('text', 1)
106 | `- ('p', 1)
107 | `- <reference to a unit>
108 |- ('text', 2)
109 | `- ('p', 1)
110 | `- <reference to a unit>
111 `- ('text', 3)
112 `- ('p', 1)
113 `- <reference to a unit>
114 """
115 tree = XPathTree()
116 for unit in store.units:
117 location = _split_xpath(unit.getlocations()[0])
118 _add_unit_to_tree(tree, location, unit)
119 return tree
120