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, xliff
26 from translate.misc.typecheck import accepts, Self, IsOneOf
27 from translate.misc.typecheck.typeclasses import Number
30 @accepts(Self(), IsOneOf(base.TranslationUnit, type(None)))
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 if xliff.ID_SEPARATOR in xpath:
64 xpath = xpath.split(xliff.ID_SEPARATOR)[-1]
65 components = xpath.split(u'/')
66 components = [_split_xpath_component(component) for component in components]
67 return list(reversed(components))
68
69 @accepts(IsOneOf(etree._Element, XPathTree), [(unicode, Number)], base.TranslationUnit)
71 """Walk down the tree rooted a node, and follow nodes which correspond to the
72 components of xpath_components. When reaching the end of xpath_components,
73 set the reference of the node to unit.
74
75 With reference to the tree diagram in build_unit_tree::
76
77 add_unit_to_tree(node, [('p', 2), ('text', 3), ('body', 2), ('document-content', 1)], unit)
78
79 would begin by popping ('document-content', 1) from the path and following the node marked
80 ('document-content', 1) in the tree. Likewise, will descend down the nodes marked ('body', 2)
81 and ('text', 3).
82
83 Since the node marked ('text', 3) has no child node marked ('p', 2), this node is created. Then
84 the add_unit_to_tree descends down this node. When this happens, there are no xpath components
85 left to pop. Thus, node.unit = unit is executed.
86 """
87 if len(xpath_components) > 0:
88 component = xpath_components.pop()
89
90
91 if component not in node.children:
92 node.children[component] = XPathTree()
93 _add_unit_to_tree(node.children[component], xpath_components, unit)
94 else:
95 node.unit = unit
96
97 @accepts(base.TranslationStore)
99 """Enumerate a translation store and build a tree with XPath components as nodes
100 and where a node contains a unit if a path from the root of the tree to the node
101 containing the unit, is equal to the XPath of the unit.
102
103 The tree looks something like this::
104 root
105 `- ('document-content', 1)
106 `- ('body', 2)
107 |- ('text', 1)
108 | `- ('p', 1)
109 | `- <reference to a unit>
110 |- ('text', 2)
111 | `- ('p', 1)
112 | `- <reference to a unit>
113 `- ('text', 3)
114 `- ('p', 1)
115 `- <reference to a unit>
116 """
117 tree = XPathTree()
118 for unit in store.units:
119 location = _split_xpath(unit.getlocations()[0])
120 _add_unit_to_tree(tree, location, unit)
121 return tree
122