Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

330

331

332

333

334

335

336

337

338

339

340

341

342

343

344

345

346

347

348

349

350

351

import xml.sax 

import xml.sax.handler 

import types 

 

try: 

    _StringTypes = [types.StringType, types.UnicodeType] 

except AttributeError: 

    _StringTypes = [types.StringType] 

 

START_ELEMENT = "START_ELEMENT" 

END_ELEMENT = "END_ELEMENT" 

COMMENT = "COMMENT" 

START_DOCUMENT = "START_DOCUMENT" 

END_DOCUMENT = "END_DOCUMENT" 

PROCESSING_INSTRUCTION = "PROCESSING_INSTRUCTION" 

IGNORABLE_WHITESPACE = "IGNORABLE_WHITESPACE" 

CHARACTERS = "CHARACTERS" 

 

class PullDOM(xml.sax.ContentHandler): 

    _locator = None 

    document = None 

 

    def __init__(self, documentFactory=None): 

        from xml.dom import XML_NAMESPACE 

        self.documentFactory = documentFactory 

        self.firstEvent = [None, None] 

        self.lastEvent = self.firstEvent 

        self.elementStack = [] 

        self.push = self.elementStack.append 

        try: 

            self.pop = self.elementStack.pop 

        except AttributeError: 

            # use class' pop instead 

            pass 

        self._ns_contexts = [{XML_NAMESPACE:'xml'}] # contains uri -> prefix dicts 

        self._current_context = self._ns_contexts[-1] 

        self.pending_events = [] 

 

    def pop(self): 

        result = self.elementStack[-1] 

        del self.elementStack[-1] 

        return result 

 

    def setDocumentLocator(self, locator): 

        self._locator = locator 

 

    def startPrefixMapping(self, prefix, uri): 

        if not hasattr(self, '_xmlns_attrs'): 

            self._xmlns_attrs = [] 

        self._xmlns_attrs.append((prefix or 'xmlns', uri)) 

        self._ns_contexts.append(self._current_context.copy()) 

        self._current_context[uri] = prefix or None 

 

    def endPrefixMapping(self, prefix): 

        self._current_context = self._ns_contexts.pop() 

 

    def startElementNS(self, name, tagName , attrs): 

        # Retrieve xml namespace declaration attributes. 

        xmlns_uri = 'http://www.w3.org/2000/xmlns/' 

        xmlns_attrs = getattr(self, '_xmlns_attrs', None) 

        if xmlns_attrs is not None: 

            for aname, value in xmlns_attrs: 

                attrs._attrs[(xmlns_uri, aname)] = value 

            self._xmlns_attrs = [] 

        uri, localname = name 

        if uri: 

            # When using namespaces, the reader may or may not 

            # provide us with the original name. If not, create 

            # *a* valid tagName from the current context. 

            if tagName is None: 

                prefix = self._current_context[uri] 

                if prefix: 

                    tagName = prefix + ":" + localname 

                else: 

                    tagName = localname 

            if self.document: 

                node = self.document.createElementNS(uri, tagName) 

            else: 

                node = self.buildDocument(uri, tagName) 

        else: 

            # When the tagname is not prefixed, it just appears as 

            # localname 

            if self.document: 

                node = self.document.createElement(localname) 

            else: 

                node = self.buildDocument(None, localname) 

 

        for aname,value in attrs.items(): 

            a_uri, a_localname = aname 

            if a_uri == xmlns_uri: 

                if a_localname == 'xmlns': 

                    qname = a_localname 

                else: 

                    qname = 'xmlns:' + a_localname 

                attr = self.document.createAttributeNS(a_uri, qname) 

                node.setAttributeNodeNS(attr) 

            elif a_uri: 

                prefix = self._current_context[a_uri] 

                if prefix: 

                    qname = prefix + ":" + a_localname 

                else: 

                    qname = a_localname 

                attr = self.document.createAttributeNS(a_uri, qname) 

                node.setAttributeNodeNS(attr) 

            else: 

                attr = self.document.createAttribute(a_localname) 

                node.setAttributeNode(attr) 

            attr.value = value 

 

        self.lastEvent[1] = [(START_ELEMENT, node), None] 

        self.lastEvent = self.lastEvent[1] 

        self.push(node) 

 

    def endElementNS(self, name, tagName): 

        self.lastEvent[1] = [(END_ELEMENT, self.pop()), None] 

        self.lastEvent = self.lastEvent[1] 

 

    def startElement(self, name, attrs): 

        if self.document: 

            node = self.document.createElement(name) 

        else: 

            node = self.buildDocument(None, name) 

 

        for aname,value in attrs.items(): 

            attr = self.document.createAttribute(aname) 

            attr.value = value 

            node.setAttributeNode(attr) 

 

        self.lastEvent[1] = [(START_ELEMENT, node), None] 

        self.lastEvent = self.lastEvent[1] 

        self.push(node) 

 

    def endElement(self, name): 

        self.lastEvent[1] = [(END_ELEMENT, self.pop()), None] 

        self.lastEvent = self.lastEvent[1] 

 

    def comment(self, s): 

        if self.document: 

            node = self.document.createComment(s) 

            self.lastEvent[1] = [(COMMENT, node), None] 

            self.lastEvent = self.lastEvent[1] 

        else: 

            event = [(COMMENT, s), None] 

            self.pending_events.append(event) 

 

    def processingInstruction(self, target, data): 

        if self.document: 

            node = self.document.createProcessingInstruction(target, data) 

            self.lastEvent[1] = [(PROCESSING_INSTRUCTION, node), None] 

            self.lastEvent = self.lastEvent[1] 

        else: 

            event = [(PROCESSING_INSTRUCTION, target, data), None] 

            self.pending_events.append(event) 

 

    def ignorableWhitespace(self, chars): 

        node = self.document.createTextNode(chars) 

        self.lastEvent[1] = [(IGNORABLE_WHITESPACE, node), None] 

        self.lastEvent = self.lastEvent[1] 

 

    def characters(self, chars): 

        node = self.document.createTextNode(chars) 

        self.lastEvent[1] = [(CHARACTERS, node), None] 

        self.lastEvent = self.lastEvent[1] 

 

    def startDocument(self): 

        if self.documentFactory is None: 

            import xml.dom.minidom 

            self.documentFactory = xml.dom.minidom.Document.implementation 

 

    def buildDocument(self, uri, tagname): 

        # Can't do that in startDocument, since we need the tagname 

        # XXX: obtain DocumentType 

        node = self.documentFactory.createDocument(uri, tagname, None) 

        self.document = node 

        self.lastEvent[1] = [(START_DOCUMENT, node), None] 

        self.lastEvent = self.lastEvent[1] 

        self.push(node) 

        # Put everything we have seen so far into the document 

        for e in self.pending_events: 

            if e[0][0] == PROCESSING_INSTRUCTION: 

                _,target,data = e[0] 

                n = self.document.createProcessingInstruction(target, data) 

                e[0] = (PROCESSING_INSTRUCTION, n) 

            elif e[0][0] == COMMENT: 

                n = self.document.createComment(e[0][1]) 

                e[0] = (COMMENT, n) 

            else: 

                raise AssertionError("Unknown pending event ",e[0][0]) 

            self.lastEvent[1] = e 

            self.lastEvent = e 

        self.pending_events = None 

        return node.firstChild 

 

    def endDocument(self): 

        self.lastEvent[1] = [(END_DOCUMENT, self.document), None] 

        self.pop() 

 

    def clear(self): 

        "clear(): Explicitly release parsing structures" 

        self.document = None 

 

class ErrorHandler: 

    def warning(self, exception): 

        print exception 

    def error(self, exception): 

        raise exception 

    def fatalError(self, exception): 

        raise exception 

 

class DOMEventStream: 

    def __init__(self, stream, parser, bufsize): 

        self.stream = stream 

        self.parser = parser 

        self.bufsize = bufsize 

        if not hasattr(self.parser, 'feed'): 

            self.getEvent = self._slurp 

        self.reset() 

 

    def reset(self): 

        self.pulldom = PullDOM() 

        # This content handler relies on namespace support 

        self.parser.setFeature(xml.sax.handler.feature_namespaces, 1) 

        self.parser.setContentHandler(self.pulldom) 

 

    def __getitem__(self, pos): 

        rc = self.getEvent() 

        if rc: 

            return rc 

        raise IndexError 

 

    def next(self): 

        rc = self.getEvent() 

        if rc: 

            return rc 

        raise StopIteration 

 

    def __iter__(self): 

        return self 

 

    def expandNode(self, node): 

        event = self.getEvent() 

        parents = [node] 

        while event: 

            token, cur_node = event 

            if cur_node is node: 

                return 

            if token != END_ELEMENT: 

                parents[-1].appendChild(cur_node) 

            if token == START_ELEMENT: 

                parents.append(cur_node) 

            elif token == END_ELEMENT: 

                del parents[-1] 

            event = self.getEvent() 

 

    def getEvent(self): 

        # use IncrementalParser interface, so we get the desired 

        # pull effect 

        if not self.pulldom.firstEvent[1]: 

            self.pulldom.lastEvent = self.pulldom.firstEvent 

        while not self.pulldom.firstEvent[1]: 

            buf = self.stream.read(self.bufsize) 

            if not buf: 

                self.parser.close() 

                return None 

            self.parser.feed(buf) 

        rc = self.pulldom.firstEvent[1][0] 

        self.pulldom.firstEvent[1] = self.pulldom.firstEvent[1][1] 

        return rc 

 

    def _slurp(self): 

        """ Fallback replacement for getEvent() using the 

            standard SAX2 interface, which means we slurp the 

            SAX events into memory (no performance gain, but 

            we are compatible to all SAX parsers). 

        """ 

        self.parser.parse(self.stream) 

        self.getEvent = self._emit 

        return self._emit() 

 

    def _emit(self): 

        """ Fallback replacement for getEvent() that emits 

            the events that _slurp() read previously. 

        """ 

        rc = self.pulldom.firstEvent[1][0] 

        self.pulldom.firstEvent[1] = self.pulldom.firstEvent[1][1] 

        return rc 

 

    def clear(self): 

        """clear(): Explicitly release parsing objects""" 

        self.pulldom.clear() 

        del self.pulldom 

        self.parser = None 

        self.stream = None 

 

class SAX2DOM(PullDOM): 

 

    def startElementNS(self, name, tagName , attrs): 

        PullDOM.startElementNS(self, name, tagName, attrs) 

        curNode = self.elementStack[-1] 

        parentNode = self.elementStack[-2] 

        parentNode.appendChild(curNode) 

 

    def startElement(self, name, attrs): 

        PullDOM.startElement(self, name, attrs) 

        curNode = self.elementStack[-1] 

        parentNode = self.elementStack[-2] 

        parentNode.appendChild(curNode) 

 

    def processingInstruction(self, target, data): 

        PullDOM.processingInstruction(self, target, data) 

        node = self.lastEvent[0][1] 

        parentNode = self.elementStack[-1] 

        parentNode.appendChild(node) 

 

    def ignorableWhitespace(self, chars): 

        PullDOM.ignorableWhitespace(self, chars) 

        node = self.lastEvent[0][1] 

        parentNode = self.elementStack[-1] 

        parentNode.appendChild(node) 

 

    def characters(self, chars): 

        PullDOM.characters(self, chars) 

        node = self.lastEvent[0][1] 

        parentNode = self.elementStack[-1] 

        parentNode.appendChild(node) 

 

 

default_bufsize = (2 ** 14) - 20 

 

def parse(stream_or_string, parser=None, bufsize=None): 

    if bufsize is None: 

        bufsize = default_bufsize 

    if type(stream_or_string) in _StringTypes: 

        stream = open(stream_or_string) 

    else: 

        stream = stream_or_string 

    if not parser: 

        parser = xml.sax.make_parser() 

    return DOMEventStream(stream, parser, bufsize) 

 

def parseString(string, parser=None): 

    try: 

        from cStringIO import StringIO 

    except ImportError: 

        from StringIO import StringIO 

 

    bufsize = len(string) 

    buf = StringIO(string) 

    if not parser: 

        parser = xml.sax.make_parser() 

    return DOMEventStream(buf, parser, bufsize)