1 """A quick DOM implementation.
2
3 Python's xml.dom is very slow. The xml.sax module is also slow (as it imports urllib2).
4 This is our light-weight version.
5 """
6
7
8
9
10 from xml.parsers import expat
11
12 import zeroinstall
13 from zeroinstall.injector import versions
14
15 _parsed_version = versions.parse_version(zeroinstall.version)
16
18 """An XML element.
19 @ivar uri: the element's namespace
20 @type uri: str
21 @ivar name: the element's localName
22 @type name: str
23 @ivar attrs: the element's attributes (key is in the form [namespace " "] localName)
24 @type attrs: {str: str}
25 @ivar childNodes: children
26 @type childNodes: [L{Element}]
27 @ivar content: the text content
28 @type content: str"""
29 __slots__ = ['uri', 'name', 'attrs', 'childNodes', 'content']
31 """@type uri: str
32 @type name: str
33 @type attrs: {str: str}"""
34 self.uri = uri
35 self.name = name
36 self.attrs = attrs.copy()
37 self.content = None
38 self.childNodes = []
39
41 """@rtype: str"""
42 attrs = [n + '=' + self.attrs[n] for n in self.attrs]
43 start = '<{%s}%s %s' % (self.uri, self.name, ' '.join(attrs))
44 if self.childNodes:
45 return start + '>' + '\n'.join(map(str, self.childNodes)) + ('</%s>' % (self.name))
46 elif self.content:
47 return start + '>' + self.content + ('</%s>' % (self.name))
48 else:
49 return start + '/>'
50
52 """@type name: str
53 @rtype: str"""
54 return self.attrs.get(name, None)
55
56 - def toDOM(self, doc, prefixes):
57 """Create a DOM Element for this qdom.Element.
58 @param doc: document to use to create the element
59 @type prefixes: L{Prefixes}
60 @return: the new element"""
61 elem = prefixes.createElementNS(doc, self.uri, self.name)
62
63 for fullname, value in self.attrs.items():
64 if ' ' in fullname:
65 ns, localName = fullname.split(' ', 1)
66 else:
67 ns, localName = None, fullname
68 prefixes.setAttributeNS(elem, ns, localName, value)
69 for child in self.childNodes:
70 elem.appendChild(child.toDOM(doc, prefixes))
71 if self.content:
72 elem.appendChild(doc.createTextNode(self.content))
73 return elem
74
76 """SAXHandler that builds a tree of L{Element}s"""
77 - def __init__(self, filter_for_version = False):
78 """@param filter_for_version: skip elements if their if-0install-version attribute doesn't match L{zeroinstall.version} (since 1.13).
79 @type filter_for_version: bool
80 @rtype: bool"""
81 self.stack = []
82 if filter_for_version:
83 self.filter_range = lambda expr: versions.parse_version_expression(expr)(_parsed_version)
84 else:
85 self.filter_range = lambda x: True
86
88 """@type fullname: str
89 @type attrs: {str: str}"""
90 split = fullname.split(' ', 1)
91 if len(split) == 2:
92 self.stack.append(Element(split[0], split[1], attrs))
93 else:
94 self.stack.append(Element(None, fullname, attrs))
95 self.contents = ''
96
98 """@type data: str"""
99 self.contents += data
100
102 """@type name: str"""
103 contents = self.contents.strip()
104 self.stack[-1].content = contents
105 self.contents = ''
106 new = self.stack.pop()
107 if self.stack:
108 target_versions = new.attrs.get('if-0install-version')
109 if target_versions and not self.filter_range(target_versions):
110 return
111
112 self.stack[-1].childNodes.append(new)
113 else:
114 self.doc = new
115
116 -def parse(source, filter_for_version = False):
117 """Parse an XML stream into a tree of L{Element}s.
118 @param source: data to parse
119 @type source: file
120 @param filter_for_version: skip elements if their if-0install-version attribute doesn't match L{zeroinstall.version} (since 1.13).
121 @type filter_for_version: bool
122 @return: the root
123 @rtype: L{Element}"""
124 handler = QSAXhandler(filter_for_version)
125 parser = expat.ParserCreate(namespace_separator = ' ')
126
127 parser.StartElementHandler = handler.startElementNS
128 parser.EndElementHandler = handler.endElementNS
129 parser.CharacterDataHandler = handler.characters
130
131 parser.ParseFile(source)
132 return handler.doc
133
135 """Keep track of namespace prefixes. Used when serialising a document.
136 @since: 0.54
137 """
139 """@type default_ns: str"""
140 self.prefixes = {}
141 self.default_ns = default_ns
142
144 """@type ns: str
145 @rtype: str"""
146 prefix = self.prefixes.get(ns, None)
147 if prefix:
148 return prefix
149 prefix = 'ns%d' % len(self.prefixes)
150 self.prefixes[ns] = prefix
151 return prefix
152
154 """@type uri: str
155 @type localName: str
156 @type value: str"""
157 if uri is None:
158 elem.setAttributeNS(None, localName, value)
159 else:
160 elem.setAttributeNS(uri, self.get(uri) + ':' + localName, value)
161
169