~openerp-groupes/openobject-server/6.0-fix-setup-windows

« back to all changes in this revision

Viewing changes to bin/reportlab/lib/xmllib.py

  • Committer: pinky
  • Date: 2006-12-07 13:41:40 UTC
  • Revision ID: pinky-3f10ee12cea3c4c75cef44ab04ad33ef47432907
New trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# A parser for XML, using the derived class as static DTD.
 
2
# Author: Sjoerd Mullender.
 
3
 
 
4
# sgmlop support added by fredrik@pythonware.com (May 19, 1998)
 
5
 
 
6
import re
 
7
import string
 
8
 
 
9
try:
 
10
    from _xmlplus.parsers import sgmlop
 
11
    #import sgmlop   # this works for both builtin on the path or relative
 
12
except ImportError:
 
13
    sgmlop = None
 
14
 
 
15
# standard entity defs
 
16
 
 
17
ENTITYDEFS = {
 
18
    'lt': '<',
 
19
    'gt': '>',
 
20
    'amp': '&',
 
21
    'quot': '"',
 
22
    'apos': '\''
 
23
    }
 
24
 
 
25
# XML parser base class -- find tags and call handler functions.
 
26
# Usage: p = XMLParser(); p.feed(data); ...; p.close().
 
27
# The dtd is defined by deriving a class which defines methods with
 
28
# special names to handle tags: start_foo and end_foo to handle <foo>
 
29
# and </foo>, respectively.  The data between tags is passed to the
 
30
# parser by calling self.handle_data() with some data as argument (the
 
31
# data may be split up in arbutrary chunks).  Entity references are
 
32
# passed by calling self.handle_entityref() with the entity reference
 
33
# as argument.
 
34
 
 
35
# --------------------------------------------------------------------
 
36
# original re-based XML parser
 
37
 
 
38
_S = '[ \t\r\n]+'
 
39
_opS = '[ \t\r\n]*'
 
40
_Name = '[a-zA-Z_:][-a-zA-Z0-9._:]*'
 
41
interesting = re.compile('[&<]')
 
42
incomplete = re.compile('&(' + _Name + '|#[0-9]*|#x[0-9a-fA-F]*)?|'
 
43
                           '<([a-zA-Z_:][^<>]*|'
 
44
                              '/([a-zA-Z_:][^<>]*)?|'
 
45
                              '![^<>]*|'
 
46
                              '\?[^<>]*)?')
 
47
 
 
48
ref = re.compile('&(' + _Name + '|#[0-9]+|#x[0-9a-fA-F]+);?')
 
49
entityref = re.compile('&(?P<name>' + _Name + ')[^-a-zA-Z0-9._:]')
 
50
charref = re.compile('&#(?P<char>[0-9]+[^0-9]|x[0-9a-fA-F]+[^0-9a-fA-F])')
 
51
space = re.compile(_S)
 
52
newline = re.compile('\n')
 
53
 
 
54
starttagopen = re.compile('<' + _Name)
 
55
endtagopen = re.compile('</')
 
56
starttagend = re.compile(_opS + '(?P<slash>/?)>')
 
57
endbracket = re.compile('>')
 
58
tagfind = re.compile(_Name)
 
59
cdataopen = re.compile('<!\[CDATA\[')
 
60
cdataclose = re.compile('\]\]>')
 
61
special = re.compile('<!(?P<special>[^<>]*)>')
 
62
procopen = re.compile('<\?(?P<proc>' + _Name + ')' + _S)
 
63
procclose = re.compile('\?>')
 
64
commentopen = re.compile('<!--')
 
65
commentclose = re.compile('-->')
 
66
doubledash = re.compile('--')
 
67
attrfind = re.compile(
 
68
    _opS + '(?P<name>' + _Name + ')'
 
69
    '(' + _opS + '=' + _opS +
 
70
    '(?P<value>\'[^\']*\'|"[^"]*"|[-a-zA-Z0-9.:+*%?!()_#=~]+))')
 
71
 
 
72
class SlowXMLParser:
 
73
 
 
74
    # Interface -- initialize and reset this instance
 
75
    def __init__(self, verbose=0):
 
76
        self.verbose = verbose
 
77
        self.reset()
 
78
 
 
79
    # Interface -- reset this instance.  Loses all unprocessed data
 
80
    def reset(self):
 
81
        self.rawdata = ''
 
82
        self.stack = []
 
83
        self.lasttag = '???'
 
84
        self.nomoretags = 0
 
85
        self.literal = 0
 
86
        self.lineno = 1
 
87
 
 
88
    # For derived classes only -- enter literal mode (CDATA) till EOF
 
89
    def setnomoretags(self):
 
90
        self.nomoretags = self.literal = 1
 
91
 
 
92
    # For derived classes only -- enter literal mode (CDATA)
 
93
    def setliteral(self, *args):
 
94
        self.literal = 1
 
95
 
 
96
    # Interface -- feed some data to the parser.  Call this as
 
97
    # often as you want, with as little or as much text as you
 
98
    # want (may include '\n').  (This just saves the text, all the
 
99
    # processing is done by goahead().)
 
100
    def feed(self, data):
 
101
        self.rawdata = self.rawdata + data
 
102
        self.goahead(0)
 
103
 
 
104
    # Interface -- handle the remaining data
 
105
    def close(self):
 
106
        self.goahead(1)
 
107
 
 
108
    # Interface -- translate references
 
109
    def translate_references(self, data):
 
110
        newdata = []
 
111
        i = 0
 
112
        while 1:
 
113
            res = ref.search(data, i)
 
114
            if res is None:
 
115
                newdata.append(data[i:])
 
116
                return string.join(newdata, '')
 
117
            if data[res.end(0) - 1] != ';':
 
118
                self.syntax_error(self.lineno,
 
119
                                  '; missing after entity/char reference')
 
120
            newdata.append(data[i:res.start(0)])
 
121
            str = res.group(1)
 
122
            if str[0] == '#':
 
123
                if str[1] == 'x':
 
124
                    newdata.append(chr(string.atoi(str[2:], 16)))
 
125
                else:
 
126
                    newdata.append(chr(string.atoi(str[1:])))
 
127
            else:
 
128
                try:
 
129
                    newdata.append(self.entitydefs[str])
 
130
                except KeyError:
 
131
                    # can't do it, so keep the entity ref in
 
132
                    newdata.append('&' + str + ';')
 
133
            i = res.end(0)
 
134
 
 
135
    # Internal -- handle data as far as reasonable.  May leave state
 
136
    # and data to be processed by a subsequent call.  If 'end' is
 
137
    # true, force handling all data as if followed by EOF marker.
 
138
    def goahead(self, end):
 
139
        rawdata = self.rawdata
 
140
        i = 0
 
141
        n = len(rawdata)
 
142
        while i < n:
 
143
            if self.nomoretags:
 
144
                data = rawdata[i:n]
 
145
                self.handle_data(data)
 
146
                self.lineno = self.lineno + string.count(data, '\n')
 
147
                i = n
 
148
                break
 
149
            res = interesting.search(rawdata, i)
 
150
            if res:
 
151
                    j = res.start(0)
 
152
            else:
 
153
                    j = n
 
154
            if i < j:
 
155
                data = rawdata[i:j]
 
156
                self.handle_data(data)
 
157
                self.lineno = self.lineno + string.count(data, '\n')
 
158
            i = j
 
159
            if i == n: break
 
160
            if rawdata[i] == '<':
 
161
                if starttagopen.match(rawdata, i):
 
162
                    if self.literal:
 
163
                        data = rawdata[i]
 
164
                        self.handle_data(data)
 
165
                        self.lineno = self.lineno + string.count(data, '\n')
 
166
                        i = i+1
 
167
                        continue
 
168
                    k = self.parse_starttag(i)
 
169
                    if k < 0: break
 
170
                    self.lineno = self.lineno + string.count(rawdata[i:k], '\n')
 
171
                    i = k
 
172
                    continue
 
173
                if endtagopen.match(rawdata, i):
 
174
                    k = self.parse_endtag(i)
 
175
                    if k < 0: break
 
176
                    self.lineno = self.lineno + string.count(rawdata[i:k], '\n')
 
177
                    i =  k
 
178
                    self.literal = 0
 
179
                    continue
 
180
                if commentopen.match(rawdata, i):
 
181
                    if self.literal:
 
182
                        data = rawdata[i]
 
183
                        self.handle_data(data)
 
184
                        self.lineno = self.lineno + string.count(data, '\n')
 
185
                        i = i+1
 
186
                        continue
 
187
                    k = self.parse_comment(i)
 
188
                    if k < 0: break
 
189
                    self.lineno = self.lineno + string.count(rawdata[i:k], '\n')
 
190
                    i = k
 
191
                    continue
 
192
                if cdataopen.match(rawdata, i):
 
193
                    k = self.parse_cdata(i)
 
194
                    if k < 0: break
 
195
                    self.lineno = self.lineno + string.count(rawdata[i:i], '\n')
 
196
                    i = k
 
197
                    continue
 
198
                res = procopen.match(rawdata, i)
 
199
                if res:
 
200
                    k = self.parse_proc(i, res)
 
201
                    if k < 0: break
 
202
                    self.lineno = self.lineno + string.count(rawdata[i:k], '\n')
 
203
                    i = k
 
204
                    continue
 
205
                res = special.match(rawdata, i)
 
206
                if res:
 
207
                    if self.literal:
 
208
                        data = rawdata[i]
 
209
                        self.handle_data(data)
 
210
                        self.lineno = self.lineno + string.count(data, '\n')
 
211
                        i = i+1
 
212
                        continue
 
213
                    self.handle_special(res.group('special'))
 
214
                    self.lineno = self.lineno + string.count(res.group(0), '\n')
 
215
                    i = res.end(0)
 
216
                    continue
 
217
            elif rawdata[i] == '&':
 
218
                res = charref.match(rawdata, i)
 
219
                if res is not None:
 
220
                    i = res.end(0)
 
221
                    if rawdata[i-1] != ';':
 
222
                        self.syntax_error(self.lineno, '; missing in charref')
 
223
                        i = i-1
 
224
                    self.handle_charref(res.group('char')[:-1])
 
225
                    self.lineno = self.lineno + string.count(res.group(0), '\n')
 
226
                    continue
 
227
                res = entityref.match(rawdata, i)
 
228
                if res is not None:
 
229
                    i = res.end(0)
 
230
                    if rawdata[i-1] != ';':
 
231
                        self.syntax_error(self.lineno, '; missing in entityref')
 
232
                        i = i-1
 
233
                    self.handle_entityref(res.group('name'))
 
234
                    self.lineno = self.lineno + string.count(res.group(0), '\n')
 
235
                    continue
 
236
            else:
 
237
                raise RuntimeError, 'neither < nor & ??'
 
238
            # We get here only if incomplete matches but
 
239
            # nothing else
 
240
            res = incomplete.match(rawdata, i)
 
241
            if not res:
 
242
                data = rawdata[i]
 
243
                self.handle_data(data)
 
244
                self.lineno = self.lineno + string.count(data, '\n')
 
245
                i = i+1
 
246
                continue
 
247
            j = res.end(0)
 
248
            if j == n:
 
249
                break # Really incomplete
 
250
            self.syntax_error(self.lineno, 'bogus < or &')
 
251
            data = res.group(0)
 
252
            self.handle_data(data)
 
253
            self.lineno = self.lineno + string.count(data, '\n')
 
254
            i = j
 
255
        # end while
 
256
        if end and i < n:
 
257
            data = rawdata[i:n]
 
258
            self.handle_data(data)
 
259
            self.lineno = self.lineno + string.count(data, '\n')
 
260
            i = n
 
261
        self.rawdata = rawdata[i:]
 
262
        # XXX if end: check for empty stack
 
263
 
 
264
    # Internal -- parse comment, return length or -1 if not terminated
 
265
    def parse_comment(self, i):
 
266
        rawdata = self.rawdata
 
267
        if rawdata[i:i+4] <> '<!--':
 
268
            raise RuntimeError, 'unexpected call to handle_comment'
 
269
        res = commentclose.search(rawdata, i+4)
 
270
        if not res:
 
271
            return -1
 
272
        # doubledash search will succeed because it's a subset of commentclose
 
273
        if doubledash.search(rawdata, i+4).start(0) < res.start(0):
 
274
            self.syntax_error(self.lineno, "`--' inside comment")
 
275
        self.handle_comment(rawdata[i+4: res.start(0)])
 
276
        return res.end(0)
 
277
 
 
278
    # Internal -- handle CDATA tag, return lenth or -1 if not terminated
 
279
    def parse_cdata(self, i):
 
280
        rawdata = self.rawdata
 
281
        if rawdata[i:i+9] <> '<![CDATA[':
 
282
            raise RuntimeError, 'unexpected call to handle_cdata'
 
283
        res = cdataclose.search(rawdata, i+9)
 
284
        if not res:
 
285
            return -1
 
286
        self.handle_cdata(rawdata[i+9:res.start(0)])
 
287
        return res.end(0)
 
288
 
 
289
    def parse_proc(self, i, res):
 
290
        rawdata = self.rawdata
 
291
        if not res:
 
292
            raise RuntimeError, 'unexpected call to parse_proc'
 
293
        name = res.group('proc')
 
294
        res = procclose.search(rawdata, res.end(0))
 
295
        if not res:
 
296
            return -1
 
297
        self.handle_proc(name, rawdata[res.pos:res.start(0)])
 
298
        return res.end(0)
 
299
 
 
300
    # Internal -- handle starttag, return length or -1 if not terminated
 
301
    def parse_starttag(self, i):
 
302
        rawdata = self.rawdata
 
303
        # i points to start of tag
 
304
        end = endbracket.search(rawdata, i+1)
 
305
        if not end:
 
306
            return -1
 
307
        j = end.start(0)
 
308
        # Now parse the data between i+1 and j into a tag and attrs
 
309
        attrdict = {}
 
310
        res = tagfind.match(rawdata, i+1)
 
311
        if not res:
 
312
            raise RuntimeError, 'unexpected call to parse_starttag'
 
313
        k = res.end(0)
 
314
        tag = res.group(0)
 
315
        if hasattr(self, tag + '_attributes'):
 
316
            attrlist = getattr(self, tag + '_attributes')
 
317
        else:
 
318
            attrlist = None
 
319
        self.lasttag = tag
 
320
        while k < j:
 
321
            res = attrfind.match(rawdata, k)
 
322
            if not res: break
 
323
            attrname, attrvalue = res.group('name', 'value')
 
324
            if attrvalue is None:
 
325
                self.syntax_error(self.lineno, 'no attribute value specified')
 
326
                attrvalue = attrname
 
327
            elif attrvalue[:1] == "'" == attrvalue[-1:] or \
 
328
                 attrvalue[:1] == '"' == attrvalue[-1:]:
 
329
                attrvalue = attrvalue[1:-1]
 
330
            else:
 
331
                self.syntax_error(self.lineno, 'attribute value not quoted')
 
332
            if attrlist is not None and attrname not in attrlist:
 
333
                self.syntax_error(self.lineno,
 
334
                                  'unknown attribute %s of element %s' %
 
335
                                  (attrname, tag))
 
336
            if attrdict.has_key(attrname):
 
337
                self.syntax_error(self.lineno, 'attribute specified twice')
 
338
            attrdict[attrname] = self.translate_references(attrvalue)
 
339
            k = res.end(0)
 
340
        res = starttagend.match(rawdata, k)
 
341
        if not res:
 
342
            self.syntax_error(self.lineno, 'garbage in start tag')
 
343
        self.finish_starttag(tag, attrdict)
 
344
        if res and res.group('slash') == '/':
 
345
            self.finish_endtag(tag)
 
346
        return end.end(0)
 
347
 
 
348
    # Internal -- parse endtag
 
349
    def parse_endtag(self, i):
 
350
        rawdata = self.rawdata
 
351
        end = endbracket.search(rawdata, i+1)
 
352
        if not end:
 
353
            return -1
 
354
        res = tagfind.match(rawdata, i+2)
 
355
        if not res:
 
356
            self.syntax_error(self.lineno, 'no name specified in end tag')
 
357
            tag = ''
 
358
            k = i+2
 
359
        else:
 
360
            tag = res.group(0)
 
361
            k = res.end(0)
 
362
        if k != end.start(0):
 
363
            # check that there is only white space at end of tag
 
364
            res = space.match(rawdata, k)
 
365
            if res is None or res.end(0) != end.start(0):
 
366
                self.syntax_error(self.lineno, 'garbage in end tag')
 
367
        self.finish_endtag(tag)
 
368
        return end.end(0)
 
369
 
 
370
    # Internal -- finish processing of start tag
 
371
    # Return -1 for unknown tag, 1 for balanced tag
 
372
    def finish_starttag(self, tag, attrs):
 
373
        self.stack.append(tag)
 
374
        try:
 
375
            method = getattr(self, 'start_' + tag)
 
376
        except AttributeError:
 
377
            self.unknown_starttag(tag, attrs)
 
378
            return -1
 
379
        else:
 
380
            self.handle_starttag(tag, method, attrs)
 
381
            return 1
 
382
 
 
383
    # Internal -- finish processing of end tag
 
384
    def finish_endtag(self, tag):
 
385
        if not tag:
 
386
            found = len(self.stack) - 1
 
387
            if found < 0:
 
388
                self.unknown_endtag(tag)
 
389
                return
 
390
        else:
 
391
            if tag not in self.stack:
 
392
                try:
 
393
                    method = getattr(self, 'end_' + tag)
 
394
                except AttributeError:
 
395
                    self.unknown_endtag(tag)
 
396
                return
 
397
            found = len(self.stack)
 
398
            for i in range(found):
 
399
                if self.stack[i] == tag: found = i
 
400
        while len(self.stack) > found:
 
401
            tag = self.stack[-1]
 
402
            try:
 
403
                method = getattr(self, 'end_' + tag)
 
404
            except AttributeError:
 
405
                method = None
 
406
            if method:
 
407
                self.handle_endtag(tag, method)
 
408
            else:
 
409
                self.unknown_endtag(tag)
 
410
            del self.stack[-1]
 
411
 
 
412
    # Overridable -- handle start tag
 
413
    def handle_starttag(self, tag, method, attrs):
 
414
        method(attrs)
 
415
 
 
416
    # Overridable -- handle end tag
 
417
    def handle_endtag(self, tag, method):
 
418
        method()
 
419
 
 
420
    # Example -- handle character reference, no need to override
 
421
    def handle_charref(self, name):
 
422
        try:
 
423
            if name[0] == 'x':
 
424
                n = string.atoi(name[1:], 16)
 
425
            else:
 
426
                n = string.atoi(name)
 
427
        except string.atoi_error:
 
428
            self.unknown_charref(name)
 
429
            return
 
430
        if not 0 <= n <= 255:
 
431
            self.unknown_charref(name)
 
432
            return
 
433
        self.handle_data(chr(n))
 
434
 
 
435
    # Definition of entities -- derived classes may override
 
436
    entitydefs = ENTITYDEFS
 
437
 
 
438
    # Example -- handle entity reference, no need to override
 
439
    def handle_entityref(self, name):
 
440
        table = self.entitydefs
 
441
        if table.has_key(name):
 
442
            self.handle_data(table[name])
 
443
        else:
 
444
            self.unknown_entityref(name)
 
445
            return
 
446
 
 
447
    # Example -- handle data, should be overridden
 
448
    def handle_data(self, data):
 
449
        pass
 
450
 
 
451
    # Example -- handle cdata, could be overridden
 
452
    def handle_cdata(self, data):
 
453
        pass
 
454
 
 
455
    # Example -- handle comment, could be overridden
 
456
    def handle_comment(self, data):
 
457
        pass
 
458
 
 
459
    # Example -- handle processing instructions, could be overridden
 
460
    def handle_proc(self, name, data):
 
461
        pass
 
462
 
 
463
    # Example -- handle special instructions, could be overridden
 
464
    def handle_special(self, data):
 
465
        pass
 
466
 
 
467
    # Example -- handle relatively harmless syntax errors, could be overridden
 
468
    def syntax_error(self, lineno, message):
 
469
        raise RuntimeError, 'Syntax error at line %d: %s' % (lineno, message)
 
470
 
 
471
    # To be overridden -- handlers for unknown objects
 
472
    def unknown_starttag(self, tag, attrs): pass
 
473
    def unknown_endtag(self, tag): pass
 
474
    def unknown_charref(self, ref): pass
 
475
    def unknown_entityref(self, ref): pass
 
476
 
 
477
 
 
478
# --------------------------------------------------------------------
 
479
# accelerated XML parser
 
480
 
 
481
class FastXMLParser:
 
482
 
 
483
    # Interface -- initialize and reset this instance
 
484
    def __init__(self, verbose=0):
 
485
        self.verbose = verbose
 
486
        self.reset()
 
487
 
 
488
    # Interface -- reset this instance.  Loses all unprocessed data
 
489
    def reset(self):
 
490
        self.rawdata = ''
 
491
        self.stack = []
 
492
        self.lasttag = '???'
 
493
        self.nomoretags = 0
 
494
        self.literal = 0
 
495
        self.lineno = 1
 
496
        self.parser = sgmlop.XMLParser()
 
497
        self.feed = self.parser.feed
 
498
        self.parser.register(self)
 
499
 
 
500
    # For derived classes only -- enter literal mode (CDATA) till EOF
 
501
    def setnomoretags(self):
 
502
        self.nomoretags = self.literal = 1
 
503
 
 
504
    # For derived classes only -- enter literal mode (CDATA)
 
505
    def setliteral(self, *args):
 
506
        self.literal = 1
 
507
 
 
508
    # Interface -- feed some data to the parser.  Call this as
 
509
    # often as you want, with as little or as much text as you
 
510
    # want (may include '\n').  (This just saves the text, all the
 
511
    # processing is done by goahead().)
 
512
    def feed(self, data): # overridden by reset
 
513
        self.parser.feed(data)
 
514
 
 
515
    # Interface -- handle the remaining data
 
516
    def close(self):
 
517
        try:
 
518
            self.parser.close()
 
519
        finally:
 
520
            self.parser = None
 
521
 
 
522
    # Interface -- translate references
 
523
    def translate_references(self, data):
 
524
        newdata = []
 
525
        i = 0
 
526
        while 1:
 
527
            res = ref.search(data, i)
 
528
            if res is None:
 
529
                newdata.append(data[i:])
 
530
                return string.join(newdata, '')
 
531
            if data[res.end(0) - 1] != ';':
 
532
                self.syntax_error(self.lineno,
 
533
                                  '; missing after entity/char reference')
 
534
            newdata.append(data[i:res.start(0)])
 
535
            str = res.group(1)
 
536
            if str[0] == '#':
 
537
                if str[1] == 'x':
 
538
                    newdata.append(chr(string.atoi(str[2:], 16)))
 
539
                else:
 
540
                    newdata.append(chr(string.atoi(str[1:])))
 
541
            else:
 
542
                try:
 
543
                    newdata.append(self.entitydefs[str])
 
544
                except KeyError:
 
545
                    # can't do it, so keep the entity ref in
 
546
                    newdata.append('&' + str + ';')
 
547
            i = res.end(0)
 
548
 
 
549
    # Internal -- finish processing of start tag
 
550
    # Return -1 for unknown tag, 1 for balanced tag
 
551
    def finish_starttag(self, tag, attrs):
 
552
        self.stack.append(tag)
 
553
        try:
 
554
            method = getattr(self, 'start_' + tag)
 
555
        except AttributeError:
 
556
            self.unknown_starttag(tag, attrs)
 
557
            return -1
 
558
        else:
 
559
            self.handle_starttag(tag, method, attrs)
 
560
            return 1
 
561
 
 
562
    # Internal -- finish processing of end tag
 
563
    def finish_endtag(self, tag):
 
564
        if not tag:
 
565
            found = len(self.stack) - 1
 
566
            if found < 0:
 
567
                self.unknown_endtag(tag)
 
568
                return
 
569
        else:
 
570
            if tag not in self.stack:
 
571
                try:
 
572
                    method = getattr(self, 'end_' + tag)
 
573
                except AttributeError:
 
574
                    self.unknown_endtag(tag)
 
575
                return
 
576
            found = len(self.stack)
 
577
            for i in range(found):
 
578
                if self.stack[i] == tag: found = i
 
579
        while len(self.stack) > found:
 
580
            tag = self.stack[-1]
 
581
            try:
 
582
                method = getattr(self, 'end_' + tag)
 
583
            except AttributeError:
 
584
                method = None
 
585
            if method:
 
586
                self.handle_endtag(tag, method)
 
587
            else:
 
588
                self.unknown_endtag(tag)
 
589
            del self.stack[-1]
 
590
 
 
591
    # Overridable -- handle start tag
 
592
    def handle_starttag(self, tag, method, attrs):
 
593
        method(attrs)
 
594
 
 
595
    # Overridable -- handle end tag
 
596
    def handle_endtag(self, tag, method):
 
597
        method()
 
598
 
 
599
    # Example -- handle character reference, no need to override
 
600
    def handle_charref(self, name):
 
601
        try:
 
602
            if name[0] == 'x':
 
603
                n = string.atoi(name[1:], 16)
 
604
            else:
 
605
                n = string.atoi(name)
 
606
        except string.atoi_error:
 
607
            self.unknown_charref(name)
 
608
            return
 
609
        if not 0 <= n <= 255:
 
610
            self.unknown_charref(name)
 
611
            return
 
612
        self.handle_data(chr(n))
 
613
 
 
614
    # Definition of entities -- derived classes may override
 
615
    entitydefs = ENTITYDEFS
 
616
 
 
617
    # Example -- handle entity reference, no need to override
 
618
    def handle_entityref(self, name):
 
619
        table = self.entitydefs
 
620
        if table.has_key(name):
 
621
            self.handle_data(table[name])
 
622
        else:
 
623
            self.unknown_entityref(name)
 
624
            return
 
625
 
 
626
    # Example -- handle data, should be overridden
 
627
    def handle_data(self, data):
 
628
        pass
 
629
 
 
630
    # Example -- handle cdata, could be overridden
 
631
    def handle_cdata(self, data):
 
632
        pass
 
633
 
 
634
    # Example -- handle comment, could be overridden
 
635
    def handle_comment(self, data):
 
636
        pass
 
637
 
 
638
    # Example -- handle processing instructions, could be overridden
 
639
    def handle_proc(self, name, data):
 
640
        pass
 
641
 
 
642
    # Example -- handle special instructions, could be overridden
 
643
    def handle_special(self, data):
 
644
        pass
 
645
 
 
646
    # Example -- handle relatively harmless syntax errors, could be overridden
 
647
    def syntax_error(self, lineno, message):
 
648
        raise RuntimeError, 'Syntax error at line %d: %s' % (lineno, message)
 
649
 
 
650
    # To be overridden -- handlers for unknown objects
 
651
    def unknown_starttag(self, tag, attrs): pass
 
652
    def unknown_endtag(self, tag): pass
 
653
    def unknown_charref(self, ref): pass
 
654
    def unknown_entityref(self, ref): pass
 
655
 
 
656
 
 
657
#sgmlop = None
 
658
 
 
659
# pick a suitable parser
 
660
if sgmlop:
 
661
    XMLParser = FastXMLParser
 
662
else:
 
663
    XMLParser = SlowXMLParser
 
664
 
 
665
# --------------------------------------------------------------------
 
666
# test stuff
 
667
 
 
668
class TestXMLParser(XMLParser):
 
669
 
 
670
    def __init__(self, verbose=0):
 
671
        self.testdata = ""
 
672
        XMLParser.__init__(self, verbose)
 
673
 
 
674
    def handle_data(self, data):
 
675
        self.testdata = self.testdata + data
 
676
        if len(`self.testdata`) >= 70:
 
677
            self.flush()
 
678
 
 
679
    def flush(self):
 
680
        data = self.testdata
 
681
        if data:
 
682
            self.testdata = ""
 
683
            print 'data:', `data`
 
684
 
 
685
    def handle_cdata(self, data):
 
686
        self.flush()
 
687
        print 'cdata:', `data`
 
688
 
 
689
    def handle_proc(self, name, data):
 
690
        self.flush()
 
691
        print 'processing:',name,`data`
 
692
 
 
693
    def handle_special(self, data):
 
694
        self.flush()
 
695
        print 'special:',`data`
 
696
 
 
697
    def handle_comment(self, data):
 
698
        self.flush()
 
699
        r = `data`
 
700
        if len(r) > 68:
 
701
            r = r[:32] + '...' + r[-32:]
 
702
        print 'comment:', r
 
703
 
 
704
    def syntax_error(self, lineno, message):
 
705
        print 'error at line %d:' % lineno, message
 
706
 
 
707
    def unknown_starttag(self, tag, attrs):
 
708
        self.flush()
 
709
        if not attrs:
 
710
            print 'start tag: <' + tag + '>'
 
711
        else:
 
712
            print 'start tag: <' + tag,
 
713
            for name, value in attrs.items():
 
714
                print name + '=' + '"' + value + '"',
 
715
            print '>'
 
716
 
 
717
    def unknown_endtag(self, tag):
 
718
        self.flush()
 
719
        print 'end tag: </' + tag + '>'
 
720
 
 
721
    def unknown_entityref(self, ref):
 
722
        self.flush()
 
723
        print '*** unknown entity ref: &' + ref + ';'
 
724
 
 
725
    def unknown_charref(self, ref):
 
726
        self.flush()
 
727
        print '*** unknown char ref: &#' + ref + ';'
 
728
 
 
729
    def close(self):
 
730
        XMLParser.close(self)
 
731
        self.flush()
 
732
 
 
733
def test(args = None):
 
734
    import sys
 
735
 
 
736
    if not args:
 
737
        args = sys.argv[1:]
 
738
 
 
739
    if args and args[0] == '-s':
 
740
        args = args[1:]
 
741
        klass = XMLParser
 
742
    else:
 
743
        klass = TestXMLParser
 
744
 
 
745
    if args:
 
746
        file = args[0]
 
747
    else:
 
748
        file = 'test.xml'
 
749
 
 
750
    if file == '-':
 
751
        f = sys.stdin
 
752
    else:
 
753
        try:
 
754
            f = open(file, 'r')
 
755
        except IOError, msg:
 
756
            print file, ":", msg
 
757
            sys.exit(1)
 
758
 
 
759
    data = f.read()
 
760
    if f is not sys.stdin:
 
761
        f.close()
 
762
 
 
763
    x = klass()
 
764
    for c in data:
 
765
        x.feed(c)
 
766
    x.close()
 
767
 
 
768
 
 
769
if __name__ == '__main__': #NO_REPORTLAB_TEST
 
770
    test()