Merge fixes from branch 'xorn'
[geda-gaf.git] / xorn / src / gaf / xmlread.py
blobb4d63554b95d93b8da26ddb2547ff7a9f338b7df
1 # Copyright (C) 2013-2020 Roland Lutz
3 # This program is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 2 of the License, or
6 # (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 ## \namespace gaf.xmlread
18 ## Reading gEDA schematic/symbol files in XML format.
20 import cStringIO, xml.parsers.expat
21 from gettext import gettext as _
22 import xorn.base64
23 import xorn.fixednum
24 import xorn.hybridnum
25 import xorn.proxy
26 import xorn.storage
27 import gaf.ref
28 from gaf.xmlformat import *
30 NSSEP = '!'
32 class VoidHandler:
33 def start_element(self, name, attributes):
34 return self
36 def end_element(self, name):
37 pass
39 def character_data(self, data):
40 pass
42 class NullHandler:
43 def __init__(self, log):
44 self.log = log
46 def start_element(self, name, attributes):
47 self.log.error(_("unexpected element \"%s\"") % name)
48 return VoidHandler()
50 def end_element(self, name):
51 pass
53 def character_data(self, data):
54 s = data.strip()
55 if s:
56 self.log.error(_("unexpected character data \"%s\"") % s)
58 class OverbarHandler(NullHandler):
59 def __init__(self, log, text):
60 self.log = log
61 self.text = text
63 def start_element(self, name, attributes):
64 if name != 'br':
65 return NullHandler.start_element(self, name, attributes)
67 self.text.append('\n')
68 return NullHandler(self.log)
70 def character_data(self, data):
71 self.text.append(data.replace('\\', '\\\\'))
73 def end_element(self, name):
74 self.text.append('\\_')
76 class TextHandler(NullHandler):
77 def __init__(self, log, rev, attached_to, data, attribute_name):
78 self.log = log
79 self.rev = rev
80 self.attached_to = attached_to
81 self.data = data
82 self.text = []
83 if attribute_name is not None:
84 self.text.append('%s=' % attribute_name)
86 def start_element(self, name, attributes):
87 if name == 'br':
88 self.text.append('\n')
89 return NullHandler(self.log)
91 if name == 'overbar':
92 self.text.append('\\_')
93 return OverbarHandler(self.log, self.text)
95 return NullHandler.start_element(self, name, attributes)
97 def character_data(self, data):
98 self.text.append(data.replace('\\', '\\\\'))
100 def end_element(self, name):
101 self.data.text = ''.join(self.text).encode('utf-8')
102 ob = self.rev.add_object(self.data)
103 if self.attached_to is not None:
104 self.rev.relocate_object(ob, self.attached_to, None)
106 class PathHandler(NullHandler):
107 def __init__(self, log, rev, data):
108 self.log = log
109 self.rev = rev
110 self.data = data
111 self.fragments = []
113 def start_element(self, name, attributes):
114 if name != 'br':
115 return NullHandler.start_element(self, name, attributes)
117 self.fragments.append('\n')
118 return NullHandler(self.log)
120 def character_data(self, data):
121 try:
122 self.fragments.append(data.encode())
123 except UnicodeEncodeError:
124 self.log.error(_("non-ASCII character in path data"))
126 def end_element(self, name):
127 self.data.pathdata = ''.join(self.fragments)
128 self.rev.add_object(self.data)
130 def parse_angle(x):
131 angle = int(x)
132 if angle != 0 and angle != 90 and angle != 180 and angle != 270:
133 raise ValueError
134 return angle
136 class ContentHandler(NullHandler):
137 def __init__(self, c, rev, attached_to):
138 self.log = c.log
139 self.c = c
140 self.rev = rev
141 self.attached_to = attached_to
143 def start_element(self, name, attributes):
144 if name == 'text' or name == 'attribute':
145 is_attribute = name == 'attribute'
146 data = xorn.storage.Text(
147 x = self.c.parse_attribute(
148 attributes, 'x', None,
149 self.c.parse, 'X coordinate'),
150 y = self.c.parse_attribute(
151 attributes, 'y', None,
152 self.c.parse, 'Y coordinate'),
153 color = self.c.parse_attribute(
154 attributes, 'color',
155 5 if is_attribute else 9,
156 ENUM_COLOR.index, 'color'),
157 text_size = self.c.parse_attribute(
158 attributes, 'size', None,
159 int, 'text size'),
160 visibility = self.c.parse_attribute(
161 attributes, 'visible', None if is_attribute else 1,
162 ENUM_BOOLEAN.index, 'text visibility'),
163 show_name_value = self.c.parse_attribute(
164 attributes, 'show', None if is_attribute else 0,
165 ENUM_SHOW_NAME_VALUE.index, 'show name/value value'),
166 angle = self.c.parse_attribute(
167 attributes, 'angle', 0,
168 parse_angle, 'angle'),
169 alignment = self.c.parse_attribute(
170 attributes, 'alignment', 0,
171 ENUM_ALIGNMENT.index, 'alignment'))
172 if is_attribute:
173 try:
174 name = attributes.pop('name')
175 except KeyError:
176 self.c.log.error(_("attribute name not specified"))
177 name = None
178 else:
179 name = None
180 return TextHandler(
181 self.c.log, self.rev, self.attached_to, data, name)
183 if self.attached_to:
184 self.c.log.error(_("non-text element can't be attached"))
185 return VoidHandler()
187 if name == 'arc':
188 self.rev.add_object(
189 xorn.storage.Arc(
190 x = self.c.parse_attribute(
191 attributes, 'x', None,
192 self.c.parse, 'X coordinate'),
193 y = self.c.parse_attribute(
194 attributes, 'y', None,
195 self.c.parse, 'Y coordinate'),
196 radius = self.c.parse_attribute(
197 attributes, 'radius', None,
198 self.c.parse, 'radius'),
199 startangle = self.c.parse_attribute(
200 attributes, 'startangle', None,
201 int, 'start angle'),
202 sweepangle = self.c.parse_attribute(
203 attributes, 'sweepangle', None,
204 int, 'sweep angle'),
205 color = self.c.parse_attribute(
206 attributes, 'color', 3,
207 ENUM_COLOR.index, 'color'),
208 line = self.c.parse_line(attributes)))
209 return NullHandler(self.c.log)
211 if name == 'box':
212 self.rev.add_object(
213 xorn.storage.Box(
214 x = self.c.parse_attribute(
215 attributes, 'x', None,
216 self.c.parse, 'X coordinate'),
217 y = self.c.parse_attribute(
218 attributes, 'y', None,
219 self.c.parse, 'Y coordinate'),
220 width = self.c.parse_attribute(
221 attributes, 'width', None,
222 self.c.parse, 'width'),
223 height = self.c.parse_attribute(
224 attributes, 'height', None,
225 self.c.parse, 'height'),
226 color = self.c.parse_attribute(
227 attributes, 'color', 3,
228 ENUM_COLOR.index, 'color'),
229 line = self.c.parse_line(attributes),
230 fill = self.c.parse_fill(attributes)))
231 return NullHandler(self.c.log)
233 if name == 'circle':
234 self.rev.add_object(
235 xorn.storage.Circle(
236 x = self.c.parse_attribute(
237 attributes, 'x', None,
238 self.c.parse, 'X coordinate'),
239 y = self.c.parse_attribute(
240 attributes, 'y', None,
241 self.c.parse, 'Y coordinate'),
242 radius = self.c.parse_attribute(
243 attributes, 'radius', None,
244 self.c.parse, 'radius'),
245 color = self.c.parse_attribute(
246 attributes, 'color', 3,
247 ENUM_COLOR.index, 'color'),
248 line = self.c.parse_line(attributes),
249 fill = self.c.parse_fill(attributes)))
250 return NullHandler(self.c.log)
252 if name == 'component':
253 ob = self.rev.add_object(
254 xorn.storage.Component(
255 x = self.c.parse_attribute(
256 attributes, 'x', None,
257 self.c.parse, 'X coordinate'),
258 y = self.c.parse_attribute(
259 attributes, 'y', None,
260 self.c.parse, 'Y coordinate'),
261 selectable = self.c.parse_attribute(
262 attributes, 'selectable', True,
263 ENUM_BOOLEAN.index, 'selectability'),
264 angle = self.c.parse_attribute(
265 attributes, 'angle', 0,
266 parse_angle, 'angle'),
267 mirror = self.c.parse_attribute(
268 attributes, 'mirror', False,
269 ENUM_BOOLEAN.index, 'mirror flag')))
270 try:
271 symbol_id = attributes.pop('symbol')
272 except KeyError:
273 self.c.log.error(_("symbol not specified"))
274 else:
275 if not symbol_id:
276 self.c.log.error(_("symbol id can't be empty"))
277 else:
278 self.c.symbol_refs.append(
279 (self.rev, ob, symbol_id, self.c.log.lineno))
281 return ContentHandler(self.c, self.rev, ob)
283 if name == 'line':
284 x0 = self.c.parse_attribute(attributes, 'x0', None,
285 self.c.parse, 'first X coordinate')
286 y0 = self.c.parse_attribute(attributes, 'y0', None,
287 self.c.parse, 'first Y coordinate')
288 x1 = self.c.parse_attribute(attributes, 'x1', None,
289 self.c.parse, 'second X coordinate')
290 y1 = self.c.parse_attribute(attributes, 'y1', None,
291 self.c.parse, 'second Y coordinate')
292 self.rev.add_object(
293 xorn.storage.Line(
294 x = x0, y = y0, width = x1 - x0, height = y1 - y0,
295 color = self.c.parse_attribute(
296 attributes, 'color', 3,
297 ENUM_COLOR.index, 'color'),
298 line = self.c.parse_line(attributes)))
299 return NullHandler(self.c.log)
301 if name == 'net' or name == 'pin':
302 is_pin = name == 'pin'
303 is_bus = self.c.parse_attribute(attributes, 'type', False,
304 ENUM_NETTYPE.index, 'net/pin type')
305 if is_pin:
306 default_color = 1
307 is_inverted = self.c.parse_attribute(
308 attributes, 'inverted', False,
309 ENUM_BOOLEAN.index, 'invertedness')
310 else:
311 if is_bus:
312 default_color = 10
313 else:
314 default_color = 4
315 is_inverted = False
317 x0 = self.c.parse_attribute(attributes, 'x0', None,
318 self.c.parse, 'first X coordinate')
319 y0 = self.c.parse_attribute(attributes, 'y0', None,
320 self.c.parse, 'first Y coordinate')
321 x1 = self.c.parse_attribute(attributes, 'x1', None,
322 self.c.parse, 'second X coordinate')
323 y1 = self.c.parse_attribute(attributes, 'y1', None,
324 self.c.parse, 'second Y coordinate')
325 ob = self.rev.add_object(
326 xorn.storage.Net(
327 x = x0, y = y0, width = x1 - x0, height = y1 - y0,
328 color = self.c.parse_attribute(
329 attributes, 'color', default_color,
330 ENUM_COLOR.index, 'color'),
331 is_bus = is_bus,
332 is_pin = is_pin,
333 is_inverted = is_inverted))
334 return ContentHandler(self.c, self.rev, ob)
336 if name == 'path':
337 return PathHandler(self.c.log, self.rev, xorn.storage.Path(
338 color = self.c.parse_attribute(attributes, 'color', 3,
339 ENUM_COLOR.index, 'color'),
340 line = self.c.parse_line(attributes),
341 fill = self.c.parse_fill(attributes)))
343 if name == 'picture':
344 ob = self.rev.add_object(
345 xorn.storage.Picture(
346 x = self.c.parse_attribute(
347 attributes, 'x', None,
348 self.c.parse, 'X coordinate'),
349 y = self.c.parse_attribute(
350 attributes, 'y', None,
351 self.c.parse, 'Y coordinate'),
352 width = self.c.parse_attribute(
353 attributes, 'width', None,
354 self.c.parse, 'width'),
355 height = self.c.parse_attribute(
356 attributes, 'height', None,
357 self.c.parse, 'height'),
358 angle = self.c.parse_attribute(
359 attributes, 'angle', 0,
360 parse_angle, 'angle'),
361 mirror = self.c.parse_attribute(
362 attributes, 'mirrored', False,
363 ENUM_BOOLEAN.index, 'mirror flag'),
364 pixmap = None))
365 try:
366 pixmap_id = attributes.pop('pixmap')
367 except KeyError:
368 self.c.log.error(_("pixmap not specified"))
369 else:
370 if not pixmap_id:
371 self.c.log.error(_("pixmap id can't be empty"))
372 else:
373 self.c.pixmap_refs.append(
374 (self.rev, ob, pixmap_id, self.c.log.lineno))
376 return NullHandler(self.c.log)
378 self.c.log.error(_("unexpected element \"%s\"") % name)
379 return VoidHandler()
381 class PixmapHandler(NullHandler):
382 def __init__(self, log, pixmap, just_verify):
383 self.log = log
384 self.pixmap = pixmap
385 self.just_verify = just_verify
386 self.f = cStringIO.StringIO()
388 def character_data(self, data):
389 self.f.write(data)
391 def end_element(self, name):
392 self.f.seek(0)
393 try:
394 data = xorn.base64.decode(self.f)
395 except xorn.base64.DecodingError:
396 self.log.error(_("base64 decoding error"))
397 return
398 if not self.just_verify:
399 self.pixmap.data = data
400 elif data != self.pixmap.data:
401 self.log.warn(_("contents of pixmap file \"%s\" don't match "
402 "embedded data") % self.pixmap.filename)
404 class LoadContext:
405 def __init__(self, log, load_symbol, load_pixmap):
406 self.log = log
407 self.ids = set()
408 self.symbols = {}
409 self.pixmaps = {}
410 self.symbol_refs = []
411 self.pixmap_refs = []
412 self.load_symbol = load_symbol
413 self.load_pixmap = load_pixmap
414 self.use_hybridnum = False
416 def parse(self, x):
417 if self.use_hybridnum:
418 return xorn.hybridnum.parse(x, 3)
419 else:
420 return float(xorn.fixednum.parse(x, 3))
422 def parse_attribute(self, d, key, default, processor, msg_fragment):
423 try:
424 x = d.pop(key)
425 except KeyError:
426 if default is not None:
427 return default
428 self.log.error(_("%s not specified") % msg_fragment)
429 else:
430 try:
431 return processor(x)
432 except (KeyError, ValueError):
433 self.log.error(_("invalid %s \"%s\"") % (msg_fragment, x))
435 # guess a well-formed return value from processor function
436 return 0. if processor == self.parse else 0
438 def parse_line(self, attributes):
439 line = xorn.storage.LineAttr()
440 line.width = self.parse_attribute(
441 attributes, 'linewidth', 0, self.parse, 'line width')
442 line.cap_style = self.parse_attribute(
443 attributes, 'capstyle', 0, ENUM_CAPSTYLE.index, 'cap style')
444 line.dash_style = self.parse_attribute(
445 attributes, 'dashstyle', 0, ENUM_DASHSTYLE.index, 'dash style')
446 if line.dash_style != 0 and line.dash_style != 1:
447 line.dash_length = self.parse_attribute(
448 attributes, 'dashlength', None, self.parse, 'dash length')
449 else:
450 line.dash_length = 0.
451 if line.dash_style != 0:
452 line.dash_space = self.parse_attribute(
453 attributes, 'dashspace', None, self.parse, 'dash space')
454 else:
455 line.dash_space = 0.
456 return line
458 def parse_fill(self, attributes):
459 fill = xorn.storage.FillAttr()
460 fill.type = self.parse_attribute(
461 attributes, 'filltype', 0, ENUM_FILLTYPE.index, 'fill type')
462 if fill.type == 2 or fill.type == 3:
463 fill.width = self.parse_attribute(
464 attributes, 'fillwidth', None, self.parse, 'fill width')
465 fill.angle0 = self.parse_attribute(
466 attributes, 'angle0', None, int, 'first fill angle')
467 fill.pitch0 = self.parse_attribute(
468 attributes, 'pitch0', None, self.parse, 'first fill pitch')
469 else:
470 fill.width = 0.
471 fill.angle0 = 0
472 fill.pitch0 = 0.
473 if fill.type == 2:
474 fill.angle1 = self.parse_attribute(
475 attributes, 'angle1', None, int, 'second fill angle')
476 fill.pitch1 = self.parse_attribute(
477 attributes, 'pitch1', None, self.parse, 'second fill pitch')
478 else:
479 fill.angle1 = 0
480 fill.pitch1 = 0.
481 return fill
483 class RootElementHandler(NullHandler):
484 def __init__(self, c):
485 self.log = c.log
486 self.c = c
487 self.rev = xorn.storage.Revision()
488 self.had_content = False
490 def start_element(self, name, attributes):
491 if name == 'content':
492 if self.had_content:
493 self.c.log.error(_("duplicate content tag"))
494 return VoidHandler()
495 self.had_content = True
496 return ContentHandler(self.c, self.rev, None)
498 if name == 'symbol':
499 try:
500 mode = attributes.pop('mode')
501 except KeyError:
502 self.c.log.error(_("symbol mode not specified"))
503 return VoidHandler()
504 if mode == 'omitted':
505 read_symbol = False
506 is_embedded = False
507 elif mode == 'referenced':
508 read_symbol = True
509 is_embedded = False
510 elif mode == 'embedded':
511 read_symbol = True
512 is_embedded = True
513 else:
514 self.c.log.error(_("invalid symbol mode \"%s\"") % mode)
515 return VoidHandler()
517 try:
518 name = attributes.pop('name')
519 except KeyError:
520 if not is_embedded:
521 self.c.log.error(_("symbol name not specified"))
522 return VoidHandler()
523 name = None
524 if is_embedded:
525 symbol = gaf.ref.Symbol(name, None, True)
526 else:
527 symbol = self.c.load_symbol(name, read_symbol)
528 if symbol is None:
529 symbol = gaf.ref.Symbol(name, None, False)
530 is_embedded = True
531 assert not symbol.embedded
532 try:
533 symbol_id = attributes.pop('id')
534 except KeyError:
535 self.c.log.error(_("symbol id not specified"))
536 return VoidHandler()
537 if not symbol_id:
538 self.c.log.error(_("symbol id can't be empty"))
539 return VoidHandler()
540 if symbol_id in self.c.ids:
541 self.c.log.error(_("duplicate id \"%s\"") % symbol_id)
542 return VoidHandler()
543 self.c.ids.add(symbol_id)
544 self.c.symbols[symbol_id] = symbol
545 if not read_symbol:
546 return NullHandler(self.c.log)
548 reh = RootElementHandler(self.c)
549 if is_embedded:
550 symbol.prim_objs = reh.rev
551 return reh
553 if name == 'pixmap':
554 try:
555 mode = attributes.pop('mode')
556 except KeyError:
557 self.c.log.error(_("pixmap mode not specified"))
558 return VoidHandler()
559 if mode == 'omitted':
560 read_pixmap = False
561 is_embedded = False
562 elif mode == 'referenced':
563 read_pixmap = True
564 is_embedded = False
565 elif mode == 'embedded':
566 read_pixmap = True
567 is_embedded = True
568 else:
569 self.c.log.error(_("invalid pixmap mode \"%s\"") % mode)
570 return VoidHandler()
572 try:
573 name = attributes.pop('name')
574 except KeyError:
575 if not is_embedded:
576 self.c.log.error(_("pixmap name not specified"))
577 return VoidHandler()
578 name = None
579 if is_embedded:
580 pixmap = gaf.ref.Pixmap(name, None, True)
581 else:
582 pixmap = self.c.load_pixmap(name, read_pixmap)
583 if pixmap is None:
584 pixmap = gaf.ref.Pixmap(name, None, False)
585 is_embedded = True
586 assert not pixmap.embedded
587 try:
588 pixmap_id = attributes.pop('id')
589 except KeyError:
590 self.c.log.error(_("pixmap id not specified"))
591 return VoidHandler()
592 if not pixmap_id:
593 self.c.log.error(_("pixmap id can't be empty"))
594 return VoidHandler()
595 if pixmap_id in self.c.ids:
596 self.c.log.error(_("duplicate id \"%s\"") % pixmap_id)
597 return VoidHandler()
598 self.c.ids.add(pixmap_id)
599 self.c.pixmaps[pixmap_id] = pixmap
600 if read_pixmap:
601 return PixmapHandler(self.c.log, pixmap, not is_embedded)
602 else:
603 return NullHandler(self.c.log)
605 self.c.log.error(_("unexpected element \"%s\"") % name)
606 return VoidHandler()
608 def end_element(self, name):
609 if not self.had_content:
610 self.c.log.error(_("content missing"))
612 def read_file(f, name, log, load_symbol, load_pixmap):
613 context = LoadContext(log, load_symbol, load_pixmap)
614 reh = RootElementHandler(context)
616 def start_root_element(name, attributes):
617 if name != 'symbol' and name != 'schematic':
618 log.error(_("invalid root element \"%s\"") % name)
619 return VoidHandler()
621 for feature in attributes.pop('file-format-features', '').split(' '):
622 if not feature:
623 continue
624 if feature == 'experimental':
625 pass
626 elif feature == 'hybridnum':
627 if context.use_hybridnum:
628 log.error(_("duplicate file format feature"))
629 context.use_hybridnum = True
630 else:
631 log.error(_("unsupported file format feature \"%s\"")
632 % feature)
634 return reh
636 read_xml_file(f, log, NAMESPACE, start_root_element)
638 for rev, ob, symbol_id, lineno in context.symbol_refs:
639 if symbol_id not in context.symbols:
640 log.lineno = lineno
641 log.error(_("undefined symbol \"%s\"") % symbol_id)
642 continue
643 data = rev.get_object_data(ob)
644 data.symbol = context.symbols[symbol_id]
645 rev.set_object_data(ob, data)
647 for rev, ob, pixmap_id, lineno in context.pixmap_refs:
648 if pixmap_id not in context.pixmaps:
649 log.lineno = lineno
650 log.error(_("undefined pixmap \"%s\"") % pixmap_id)
651 continue
652 data = rev.get_object_data(ob)
653 data.pixmap = context.pixmaps[pixmap_id]
654 rev.set_object_data(ob, data)
656 return xorn.proxy.RevisionProxy(reh.rev)
658 def read_xml_file(f, log, namespace, start_root_element):
659 stack = []
661 def strip_namespace(name, ignore_errors):
662 try:
663 pos = name.index(NSSEP)
664 except ValueError:
665 if not ignore_errors:
666 log.error(_("element name \"%s\" without namespace") % name)
667 return None
669 if name[:pos] != namespace and not ignore_errors:
670 log.error(_("invalid namespace \"%s\"") % name[:pos])
671 return None
673 return name[pos + 1:]
675 def StartElementHandler(name, attributes):
676 log.lineno = p.CurrentLineNumber - 1
677 name = strip_namespace(name, False)
678 if name is None:
679 new_handler = VoidHandler()
680 elif stack:
681 new_handler = stack[-1].start_element(name, attributes)
682 else:
683 new_handler = start_root_element(name, attributes)
684 stack.append(new_handler)
686 if attributes and not isinstance(new_handler, VoidHandler):
687 log.error(_("unexpected attribute(s) %s") % _(", ").join(
688 _("\"%s\"") % attr for attr in sorted(attributes)))
690 def EndElementHandler(name):
691 log.lineno = p.CurrentLineNumber - 1
692 name = strip_namespace(name, True)
693 stack.pop().end_element(name)
695 def CharacterDataHandler(data):
696 log.lineno = p.CurrentLineNumber - 1
697 stack[-1].character_data(data)
699 def StartDoctypeDeclHandler(doctype_name, system_id, public_id,
700 has_internal_subset):
701 log.lineno = p.CurrentLineNumber - 1
702 log.error(_("unexpected XML document type declaration"))
704 def ElementDeclHandler(name, model):
705 log.lineno = p.CurrentLineNumber - 1
706 log.error(_("unexpected XML element type declaration"))
708 def AttlistDeclHandler(elname, attname, type, default, required):
709 log.lineno = p.CurrentLineNumber - 1
710 log.error(_("unexpected XML element type attribute declaration"))
712 def ProcessingInstructionHandler(target, data):
713 log.lineno = p.CurrentLineNumber - 1
714 log.error(_("unexpected XML processing instruction"))
716 def UnparsedEntityDeclHandler(entity_name, base, system_id, public_id,
717 notationName):
718 log.lineno = p.CurrentLineNumber - 1
719 log.error(_("unexpected XML unparsed entity declaration"))
721 def EntityDeclHandler(entity_name, is_parameter_entity, value, base,
722 system_id, public_id, notation_name):
723 log.lineno = p.CurrentLineNumber - 1
724 log.error(_("unexpected XML entity declaration"))
726 def NotationDeclHandler(notation_name, base, system_id, public_id):
727 log.lineno = p.CurrentLineNumber - 1
728 log.error(_("unexpected XML notation declaration"))
730 def StartCdataSectionHandler():
731 log.lineno = p.CurrentLineNumber - 1
732 log.error(_("unexpected XML CDATA section"))
734 def DefaultHandler(data):
735 log.lineno = p.CurrentLineNumber - 1
736 log.error(_("unexpected characters in XML document"))
738 def NotStandaloneHandler():
739 log.lineno = p.CurrentLineNumber - 1
740 log.error(_("XML document hasn't been declared as standalone"))
742 def ExternalEntityRefHandler(context, base, systemId, publicId):
743 log.lineno = p.CurrentLineNumber - 1
744 log.error(_("unexpected reference to external XML entity"))
746 p = xml.parsers.expat.ParserCreate(namespace_separator = '!')
748 p.XmlDeclHandler = None
749 p.StartDoctypeDeclHandler = StartDoctypeDeclHandler
750 p.EndDoctypeDeclHandler = None
751 p.ElementDeclHandler = ElementDeclHandler
752 p.AttlistDeclHandler = AttlistDeclHandler
753 p.StartElementHandler = StartElementHandler
754 p.EndElementHandler = EndElementHandler
755 p.ProcessingInstructionHandler = ProcessingInstructionHandler
756 p.CharacterDataHandler = CharacterDataHandler
757 p.UnparsedEntityDeclHandler = UnparsedEntityDeclHandler
758 p.EntityDeclHandler = EntityDeclHandler
759 p.NotationDeclHandler = NotationDeclHandler
760 p.StartNamespaceDeclHandler = None
761 p.EndNamespaceDeclHandler = None
762 p.CommentHandler = None
763 p.StartCdataSectionHandler = StartCdataSectionHandler
764 p.EndCdataSectionHandler = None
765 p.DefaultHandler = DefaultHandler
766 p.DefaultHandlerExpand = None
767 p.NotStandaloneHandler = NotStandaloneHandler
768 p.ExternalEntityRefHandler = ExternalEntityRefHandler
770 try:
771 p.ParseFile(f)
772 except xml.parsers.expat.ExpatError as e:
773 log.lineno = e.lineno - 1
774 log.error(_("%s") % e)