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
31
32 @accepts(Self(), IsOneOf(base.TranslationUnit, type(None)))
34 self.unit = unit
35 self.children = {}
36
38 return isinstance(other, XPathTree) and \
39 self.unit == other.unit and \
40 self.children == other.children
41
45 """Split an xpath component into a tag-index tuple.
46
47 >>> split_xpath_component('{urn:oasis:names:tc:opendocument:xmlns:office:1.0}document-content[0]')
48 ('{urn:oasis:names:tc:opendocument:xmlns:office:1.0}document-content', 0).
49 """
50 lbrac = xpath_component.rfind(u'[')
51 rbrac = xpath_component.rfind(u']')
52 tag = xpath_component[:lbrac]
53 index = int(xpath_component[lbrac+1:rbrac])
54 return tag, index
55
59 """Split an 'xpath' string separated by / into a reversed list of its components. Thus:
60
61 >>> split_xpath('document-content[1]/body[2]/text[3]/p[4]')
62 [('p', 4), ('text', 3), ('body', 2), ('document-content', 1)]
63
64 The list is reversed so that it can be used as a stack, where the top of the stack is
65 the first component.
66 """
67 if xliff.ID_SEPARATOR in xpath:
68 xpath = xpath.split(xliff.ID_SEPARATOR)[-1]
69 components = xpath.split(u'/')
70 components = [_split_xpath_component(component) for component in components]
71 return list(reversed(components))
72
73
74 @accepts(IsOneOf(etree._Element, XPathTree), [(unicode, Number)], base.TranslationUnit)
76 """Walk down the tree rooted a node, and follow nodes which correspond to the
77 components of xpath_components. When reaching the end of xpath_components,
78 set the reference of the node to unit.
79
80 With reference to the tree diagram in build_unit_tree::
81
82 add_unit_to_tree(node, [('p', 2), ('text', 3), ('body', 2), ('document-content', 1)], unit)
83
84 would begin by popping ('document-content', 1) from the path and following the node marked
85 ('document-content', 1) in the tree. Likewise, will descend down the nodes marked ('body', 2)
86 and ('text', 3).
87
88 Since the node marked ('text', 3) has no child node marked ('p', 2), this node is created. Then
89 the add_unit_to_tree descends down this node. When this happens, there are no xpath components
90 left to pop. Thus, node.unit = unit is executed.
91 """
92 if len(xpath_components) > 0:
93 component = xpath_components.pop()
94
95
96 if component not in node.children:
97 node.children[component] = XPathTree()
98 _add_unit_to_tree(node.children[component], xpath_components, unit)
99 else:
100 node.unit = unit
101
102
103 @accepts(base.TranslationStore)
105 """Enumerate a translation store and build a tree with XPath components as nodes
106 and where a node contains a unit if a path from the root of the tree to the node
107 containing the unit, is equal to the XPath of the unit.
108
109 The tree looks something like this::
110 root
111 `- ('document-content', 1)
112 `- ('body', 2)
113 |- ('text', 1)
114 | `- ('p', 1)
115 | `- <reference to a unit>
116 |- ('text', 2)
117 | `- ('p', 1)
118 | `- <reference to a unit>
119 `- ('text', 3)
120 `- ('p', 1)
121 `- <reference to a unit>
122 """
123 tree = XPathTree()
124 for unit in store.units:
125 if not unit.isfuzzy():
126 location = _split_xpath(unit.getlocations()[0])
127 _add_unit_to_tree(tree, location, unit)
128 return tree
129