root/livinglogic.python.xist/src/ll/xist/parsers.py @ 2590:7b6bba35f3a0

Revision 2590:7b6bba35f3a0, 52.9 KB (checked in by Walter Doerwald <walter@…>, 13 years ago)

htmlspecials.pixel() now no longer uses colored pixels, instead color is
done via CSS. The URL for the one remaining transparent pixel can now be
specified via src (either as an XML attribute or via the converter context).

Rename attrs methods with() and without() to withnames() and withoutnames()
(for Python 2.5 compatibility).

Use elinks instead of w3m for asText() and move/rename this method to a
function ll.xist.ns.html.astext().

Try to make XIST independent from PyXML (however PyXML is still required
for parsing via expat and for dtd2xsc.py (because this requires xmlproc)).

Remove the long dperecated method withSep().

Use Py_ssize_t in the C source where appropriate.

Line 
1#! /usr/bin/env python
2# -*- coding: iso-8859-1 -*-
3
4## Copyright 1999-2006 by LivingLogic AG, Bayreuth/Germany.
5## Copyright 1999-2006 by Walter Dörwald
6##
7## All Rights Reserved
8##
9## See xist/__init__.py for the license
10
11"""
12<par>This file contains everything you need to parse &xist; objects from files, strings, &url;s etc.</par>
13
14<par>It contains different &sax;2 parser driver classes (mostly for sgmlop, everything else
15is from <app moreinfo="http://pyxml.sf.net/">PyXML</app>). It includes a
16<pyref class="HTMLParser"><class>HTMLParser</class></pyref> that uses sgmlop
17to parse &html; and emit &sax;2 events.</par>
18"""
19
20import sys, os, os.path, warnings, cStringIO
21
22from xml import sax
23from xml.sax import expatreader
24from xml.sax import handler
25
26from ll import url
27from ll.xist import xsc, utils, cssparsers, sgmlop
28from ll.xist.ns import html
29
30# from PyXML/dom/html/__init__.py
31HTML_OPT_END = ["body", "colgroup", "dd", "dt", "head", "html", "li", "option", "p", "tbody", "td", "tfoot", "th", "thead", "tr"]
32
33HTML_FORBIDDEN_END = ["area", "base", "basefont", "br", "col", "frame", "hr", "img", "input", "isindex", "link", "meta", "param"]
34
35HTML_DTD = {
36    "col": [],
37    "u": ["#PCDATA", "a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br", "button", "cite", "code", "del", "dfn", "em", "font", "i", "iframe", "img", "input", "ins", "kbd", "label", "map", "noscript", "object", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "textarea", "tt", "u", "var"],
38    "p": ["#PCDATA", "a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br", "button", "cite", "code", "del", "dfn", "em", "font", "i", "iframe", "img", "input", "ins", "kbd", "label", "map", "noscript", "object", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "textarea", "tt", "u", "var"],
39    "caption": ["#PCDATA", "a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br", "button", "cite", "code", "del", "dfn", "em", "font", "i", "iframe", "img", "input", "ins", "kbd", "label", "map", "noscript", "object", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "textarea", "tt", "u", "var"],
40    "q": ["#PCDATA", "a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br", "button", "cite", "code", "del", "dfn", "em", "font", "i", "iframe", "img", "input", "ins", "kbd", "label", "map", "noscript", "object", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "textarea", "tt", "u", "var"],
41    "i": ["#PCDATA", "a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br", "button", "cite", "code", "del", "dfn", "em", "font", "i", "iframe", "img", "input", "ins", "kbd", "label", "map", "noscript", "object", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "textarea", "tt", "u", "var"],
42    "textarea": ["#PCDATA"],
43    "center": ["#PCDATA", "a", "abbr", "acronym", "address", "applet", "b", "basefont", "bdo", "big", "blockquote", "br", "button", "center", "cite", "code", "del", "dfn", "dir", "div", "dl", "em", "fieldset", "font", "form", "h1", "h2", "h3", "h4", "h5", "h6", "hr", "i", "iframe", "img", "input", "ins", "isindex", "kbd", "label", "map", "menu", "noframes", "noscript", "object", "ol", "p", "pre", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "table", "textarea", "tt", "u", "ul", "var"],
44    "script": ["#PCDATA"],
45    "ol": ["li"],
46    "a": ["#PCDATA", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br", "button", "cite", "code", "del", "dfn", "em", "font", "i", "iframe", "img", "input", "ins", "kbd", "label", "map", "noscript", "object", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "textarea", "tt", "u", "var"],
47    "legend": ["#PCDATA", "a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br", "button", "cite", "code", "del", "dfn", "em", "font", "i", "iframe", "img", "input", "ins", "kbd", "label", "map", "noscript", "object", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "textarea", "tt", "u", "var"],
48    "strong": ["#PCDATA", "a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br", "button", "cite", "code", "del", "dfn", "em", "font", "i", "iframe", "img", "input", "ins", "kbd", "label", "map", "noscript", "object", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "textarea", "tt", "u", "var"],
49    "address": ["#PCDATA", "a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br", "button", "cite", "code", "del", "dfn", "em", "font", "i", "iframe", "img", "input", "ins", "kbd", "label", "map", "noscript", "object", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "textarea", "tt", "u", "var"],
50    "br": [],
51    "base": [],
52    "object": ["#PCDATA", "a", "abbr", "acronym", "address", "applet", "b", "basefont", "bdo", "big", "blockquote", "br", "button", "center", "cite", "code", "del", "dfn", "dir", "div", "dl", "em", "fieldset", "font", "form", "h1", "h2", "h3", "h4", "h5", "h6", "hr", "i", "iframe", "img", "input", "ins", "isindex", "kbd", "label", "map", "menu", "noframes", "noscript", "object", "ol", "p", "param", "pre", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "table", "textarea", "tt", "u", "ul", "var"],
53    "basefont": [],
54    "map": ["address", "area", "blockquote", "center", "del", "dir", "div", "dl", "fieldset", "form", "h1", "h2", "h3", "h4", "h5", "h6", "hr", "ins", "isindex", "menu", "noframes", "noscript", "ol", "p", "pre", "script", "table", "ul"],
55    "body": ["#PCDATA", "a", "abbr", "acronym", "address", "applet", "b", "basefont", "bdo", "big", "blockquote", "br", "button", "center", "cite", "code", "del", "dfn", "dir", "div", "dl", "em", "fieldset", "font", "form", "h1", "h2", "h3", "h4", "h5", "h6", "hr", "i", "iframe", "img", "input", "ins", "isindex", "kbd", "label", "map", "menu", "noframes", "noscript", "object", "ol", "p", "pre", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "table", "textarea", "tt", "u", "ul", "var"],
56    "samp": ["#PCDATA", "a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br", "button", "cite", "code", "del", "dfn", "em", "font", "i", "iframe", "img", "input", "ins", "kbd", "label", "map", "noscript", "object", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "textarea", "tt", "u", "var"],
57    "dl": ["dd", "dt"],
58    "acronym": ["#PCDATA", "a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br", "button", "cite", "code", "del", "dfn", "em", "font", "i", "iframe", "img", "input", "ins", "kbd", "label", "map", "noscript", "object", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "textarea", "tt", "u", "var"],
59    "html": ["body", "frameset", "head"],
60    "em": ["#PCDATA", "a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br", "button", "cite", "code", "del", "dfn", "em", "font", "i", "iframe", "img", "input", "ins", "kbd", "label", "map", "noscript", "object", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "textarea", "tt", "u", "var"],
61    "label": ["#PCDATA", "a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br", "button", "cite", "code", "del", "dfn", "em", "font", "i", "iframe", "img", "input", "ins", "kbd", "label", "map", "noscript", "object", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "textarea", "tt", "u", "var"],
62    "tbody": ["tr"],
63    "bdo": ["#PCDATA", "a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br", "button", "cite", "code", "del", "dfn", "em", "font", "i", "iframe", "img", "input", "ins", "kbd", "label", "map", "noscript", "object", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "textarea", "tt", "u", "var"],
64    "sub": ["#PCDATA", "a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br", "button", "cite", "code", "del", "dfn", "em", "font", "i", "iframe", "img", "input", "ins", "kbd", "label", "map", "noscript", "object", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "textarea", "tt", "u", "var"],
65    "meta": [],
66    "ins": ["#PCDATA", "a", "abbr", "acronym", "address", "applet", "b", "basefont", "bdo", "big", "blockquote", "br", "button", "center", "cite", "code", "del", "dfn", "dir", "div", "dl", "em", "fieldset", "font", "form", "h1", "h2", "h3", "h4", "h5", "h6", "hr", "i", "iframe", "img", "input", "ins", "isindex", "kbd", "label", "map", "menu", "noframes", "noscript", "object", "ol", "p", "pre", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "table", "textarea", "tt", "u", "ul", "var"],
67    "frame": [],
68    "s": ["#PCDATA", "a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br", "button", "cite", "code", "del", "dfn", "em", "font", "i", "iframe", "img", "input", "ins", "kbd", "label", "map", "noscript", "object", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "textarea", "tt", "u", "var"],
69    "title": ["#PCDATA"],
70    "frameset": ["frame", "frameset", "noframes"],
71    "pre": ["#PCDATA", "a", "abbr", "acronym", "b", "bdo", "br", "button", "cite", "code", "dfn", "em", "i", "input", "kbd", "label", "map", "q", "s", "samp", "select", "span", "strong", "sub", "sup", "textarea", "tt", "u", "var"],
72    "dir": ["li"],
73    "div": ["#PCDATA", "a", "abbr", "acronym", "address", "applet", "b", "basefont", "bdo", "big", "blockquote", "br", "button", "center", "cite", "code", "del", "dfn", "dir", "div", "dl", "em", "fieldset", "font", "form", "h1", "h2", "h3", "h4", "h5", "h6", "hr", "i", "iframe", "img", "input", "ins", "isindex", "kbd", "label", "map", "menu", "noframes", "noscript", "object", "ol", "p", "pre", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "table", "textarea", "tt", "u", "ul", "var"],
74    "small": ["#PCDATA", "a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br", "button", "cite", "code", "del", "dfn", "em", "font", "i", "iframe", "img", "input", "ins", "kbd", "label", "map", "noscript", "object", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "textarea", "tt", "u", "var"],
75    "iframe": ["#PCDATA", "a", "abbr", "acronym", "address", "applet", "b", "basefont", "bdo", "big", "blockquote", "br", "button", "center", "cite", "code", "del", "dfn", "dir", "div", "dl", "em", "fieldset", "font", "form", "h1", "h2", "h3", "h4", "h5", "h6", "hr", "i", "iframe", "img", "input", "ins", "isindex", "kbd", "label", "map", "menu", "noframes", "noscript", "object", "ol", "p", "pre", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "table", "textarea", "tt", "u", "ul", "var"],
76    "del": ["#PCDATA", "a", "abbr", "acronym", "address", "applet", "b", "basefont", "bdo", "big", "blockquote", "br", "button", "center", "cite", "code", "del", "dfn", "dir", "div", "dl", "em", "fieldset", "font", "form", "h1", "h2", "h3", "h4", "h5", "h6", "hr", "i", "iframe", "img", "input", "ins", "isindex", "kbd", "label", "map", "menu", "noframes", "noscript", "object", "ol", "p", "pre", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "table", "textarea", "tt", "u", "ul", "var"],
77    "applet": ["#PCDATA", "a", "abbr", "acronym", "address", "applet", "b", "basefont", "bdo", "big", "blockquote", "br", "button", "center", "cite", "code", "del", "dfn", "dir", "div", "dl", "em", "fieldset", "font", "form", "h1", "h2", "h3", "h4", "h5", "h6", "hr", "i", "iframe", "img", "input", "ins", "isindex", "kbd", "label", "map", "menu", "noframes", "noscript", "object", "ol", "p", "param", "pre", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "table", "textarea", "tt", "u", "ul", "var"],
78    "ul": ["li"],
79    "isindex": [],
80    "button": ["#PCDATA", "abbr", "acronym", "address", "applet", "b", "basefont", "bdo", "big", "blockquote", "br", "center", "cite", "code", "del", "dfn", "dir", "div", "dl", "em", "font", "h1", "h2", "h3", "h4", "h5", "h6", "hr", "i", "img", "ins", "kbd", "map", "menu", "noframes", "noscript", "object", "ol", "p", "pre", "q", "s", "samp", "script", "small", "span", "strike", "strong", "sub", "sup", "table", "tt", "u", "ul", "var"],
81    "colgroup": ["col"],
82    "b": ["#PCDATA", "a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br", "button", "cite", "code", "del", "dfn", "em", "font", "i", "iframe", "img", "input", "ins", "kbd", "label", "map", "noscript", "object", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "textarea", "tt", "u", "var"],
83    "table": ["caption", "col", "colgroup", "tbody", "tfoot", "thead", "tr"],
84    "dt": ["#PCDATA", "a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br", "button", "cite", "code", "del", "dfn", "em", "font", "i", "iframe", "img", "input", "ins", "kbd", "label", "map", "noscript", "object", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "textarea", "tt", "u", "var"],
85    "optgroup": ["option"],
86    "abbr": ["#PCDATA", "a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br", "button", "cite", "code", "del", "dfn", "em", "font", "i", "iframe", "img", "input", "ins", "kbd", "label", "map", "noscript", "object", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "textarea", "tt", "u", "var"],
87    "link": [],
88    "h4": ["#PCDATA", "a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br", "button", "cite", "code", "del", "dfn", "em", "font", "i", "iframe", "img", "input", "ins", "kbd", "label", "map", "noscript", "object", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "textarea", "tt", "u", "var"],
89    "dd": ["#PCDATA", "a", "abbr", "acronym", "address", "applet", "b", "basefont", "bdo", "big", "blockquote", "br", "button", "center", "cite", "code", "del", "dfn", "dir", "div", "dl", "em", "fieldset", "font", "form", "h1", "h2", "h3", "h4", "h5", "h6", "hr", "i", "iframe", "img", "input", "ins", "isindex", "kbd", "label", "map", "menu", "noframes", "noscript", "object", "ol", "p", "pre", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "table", "textarea", "tt", "u", "ul", "var"],
90    "big": ["#PCDATA", "a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br", "button", "cite", "code", "del", "dfn", "em", "font", "i", "iframe", "img", "input", "ins", "kbd", "label", "map", "noscript", "object", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "textarea", "tt", "u", "var"],
91    "hr": [],
92    "form": ["#PCDATA", "a", "abbr", "acronym", "address", "applet", "b", "basefont", "bdo", "big", "blockquote", "br", "button", "center", "cite", "code", "del", "dfn", "dir", "div", "dl", "em", "fieldset", "font", "h1", "h2", "h3", "h4", "h5", "h6", "hr", "i", "iframe", "img", "input", "ins", "isindex", "kbd", "label", "map", "menu", "noframes", "noscript", "object", "ol", "p", "pre", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "table", "textarea", "tt", "u", "ul", "var"],
93    "option": ["#PCDATA"],
94    "fieldset": ["#PCDATA", "a", "abbr", "acronym", "address", "applet", "b", "basefont", "bdo", "big", "blockquote", "br", "button", "center", "cite", "code", "del", "dfn", "dir", "div", "dl", "em", "fieldset", "font", "form", "h1", "h2", "h3", "h4", "h5", "h6", "hr", "i", "iframe", "img", "input", "ins", "isindex", "kbd", "label", "legend", "map", "menu", "noframes", "noscript", "object", "ol", "p", "pre", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "table", "textarea", "tt", "u", "ul", "var"],
95    "blockquote": ["#PCDATA", "a", "abbr", "acronym", "address", "applet", "b", "basefont", "bdo", "big", "blockquote", "br", "button", "center", "cite", "code", "del", "dfn", "dir", "div", "dl", "em", "fieldset", "font", "form", "h1", "h2", "h3", "h4", "h5", "h6", "hr", "i", "iframe", "img", "input", "ins", "isindex", "kbd", "label", "map", "menu", "noframes", "noscript", "object", "ol", "p", "pre", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "table", "textarea", "tt", "u", "ul", "var"],
96    "head": ["base", "isindex", "link", "meta", "object", "script", "style", "title"],
97    "thead": ["tr"],
98    "cite": ["#PCDATA", "a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br", "button", "cite", "code", "del", "dfn", "em", "font", "i", "iframe", "img", "input", "ins", "kbd", "label", "map", "noscript", "object", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "textarea", "tt", "u", "var"],
99    "td": ["#PCDATA", "a", "abbr", "acronym", "address", "applet", "b", "basefont", "bdo", "big", "blockquote", "br", "button", "center", "cite", "code", "del", "dfn", "dir", "div", "dl", "em", "fieldset", "font", "form", "h1", "h2", "h3", "h4", "h5", "h6", "hr", "i", "iframe", "img", "input", "ins", "isindex", "kbd", "label", "map", "menu", "noframes", "noscript", "object", "ol", "p", "pre", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "table", "textarea", "tt", "u", "ul", "var"],
100    "input": [],
101    "var": ["#PCDATA", "a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br", "button", "cite", "code", "del", "dfn", "em", "font", "i", "iframe", "img", "input", "ins", "kbd", "label", "map", "noscript", "object", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "textarea", "tt", "u", "var"],
102    "th": ["#PCDATA", "a", "abbr", "acronym", "address", "applet", "b", "basefont", "bdo", "big", "blockquote", "br", "button", "center", "cite", "code", "del", "dfn", "dir", "div", "dl", "em", "fieldset", "font", "form", "h1", "h2", "h3", "h4", "h5", "h6", "hr", "i", "iframe", "img", "input", "ins", "isindex", "kbd", "label", "map", "menu", "noframes", "noscript", "object", "ol", "p", "pre", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "table", "textarea", "tt", "u", "ul", "var"],
103    "tfoot": ["tr"],
104    "dfn": ["#PCDATA", "a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br", "button", "cite", "code", "del", "dfn", "em", "font", "i", "iframe", "img", "input", "ins", "kbd", "label", "map", "noscript", "object", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "textarea", "tt", "u", "var"],
105    "li": ["#PCDATA", "a", "abbr", "acronym", "address", "applet", "b", "basefont", "bdo", "big", "blockquote", "br", "button", "center", "cite", "code", "del", "dfn", "dir", "div", "dl", "em", "fieldset", "font", "form", "h1", "h2", "h3", "h4", "h5", "h6", "hr", "i", "iframe", "img", "input", "ins", "isindex", "kbd", "label", "map", "menu", "noframes", "noscript", "object", "ol", "p", "pre", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "table", "textarea", "tt", "u", "ul", "var"],
106    "param": [],
107    "tr": ["td", "th"],
108    "tt": ["#PCDATA", "a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br", "button", "cite", "code", "del", "dfn", "em", "font", "i", "iframe", "img", "input", "ins", "kbd", "label", "map", "noscript", "object", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "textarea", "tt", "u", "var"],
109    "menu": ["li"],
110    "area": [],
111    "img": [],
112    "span": ["#PCDATA", "a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br", "button", "cite", "code", "del", "dfn", "em", "font", "i", "iframe", "img", "input", "ins", "kbd", "label", "map", "noscript", "object", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "textarea", "tt", "u", "var"],
113    "style": [],
114    "noscript": ["#PCDATA", "a", "abbr", "acronym", "address", "applet", "b", "basefont", "bdo", "big", "blockquote", "br", "button", "center", "cite", "code", "del", "dfn", "dir", "div", "dl", "em", "fieldset", "font", "form", "h1", "h2", "h3", "h4", "h5", "h6", "hr", "i", "iframe", "img", "input", "ins", "isindex", "kbd", "label", "map", "menu", "noframes", "noscript", "object", "ol", "p", "pre", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "table", "textarea", "tt", "u", "ul", "var"],
115    "noframes": ["#PCDATA", "a", "abbr", "acronym", "address", "applet", "b", "basefont", "bdo", "big", "blockquote", "br", "button", "center", "cite", "code", "del", "dfn", "dir", "div", "dl", "em", "fieldset", "font", "form", "h1", "h2", "h3", "h4", "h5", "h6", "hr", "i", "iframe", "img", "input", "ins", "isindex", "kbd", "label", "map", "menu", "noframes", "noscript", "object", "ol", "p", "pre", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "table", "textarea", "tt", "u", "ul", "var"],
116    "select": ["optgroup", "option"],
117    "font": ["#PCDATA", "a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br", "button", "cite", "code", "del", "dfn", "em", "font", "i", "iframe", "img", "input", "ins", "kbd", "label", "map", "noscript", "object", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "textarea", "tt", "u", "var"],
118    "strike": ["#PCDATA", "a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br", "button", "cite", "code", "del", "dfn", "em", "font", "i", "iframe", "img", "input", "ins", "kbd", "label", "map", "noscript", "object", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "textarea", "tt", "u", "var"],
119    "sup": ["#PCDATA", "a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br", "button", "cite", "code", "del", "dfn", "em", "font", "i", "iframe", "img", "input", "ins", "kbd", "label", "map", "noscript", "object", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "textarea", "tt", "u", "var"],
120    "h5": ["#PCDATA", "a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br", "button", "cite", "code", "del", "dfn", "em", "font", "i", "iframe", "img", "input", "ins", "kbd", "label", "map", "noscript", "object", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "textarea", "tt", "u", "var"],
121    "kbd": ["#PCDATA", "a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br", "button", "cite", "code", "del", "dfn", "em", "font", "i", "iframe", "img", "input", "ins", "kbd", "label", "map", "noscript", "object", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "textarea", "tt", "u", "var"],
122    "h6": ["#PCDATA", "a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br", "button", "cite", "code", "del", "dfn", "em", "font", "i", "iframe", "img", "input", "ins", "kbd", "label", "map", "noscript", "object", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "textarea", "tt", "u", "var"],
123    "h1": ["#PCDATA", "a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br", "button", "cite", "code", "del", "dfn", "em", "font", "i", "iframe", "img", "input", "ins", "kbd", "label", "map", "noscript", "object", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "textarea", "tt", "u", "var"],
124    "h3": ["#PCDATA", "a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br", "button", "cite", "code", "del", "dfn", "em", "font", "i", "iframe", "img", "input", "ins", "kbd", "label", "map", "noscript", "object", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "textarea", "tt", "u", "var"],
125    "h2": ["#PCDATA", "a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br", "button", "cite", "code", "del", "dfn", "em", "font", "i", "iframe", "img", "input", "ins", "kbd", "label", "map", "noscript", "object", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "textarea", "tt", "u", "var"],
126    "code": ["#PCDATA", "a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br", "button", "cite", "code", "del", "dfn", "em", "font", "i", "iframe", "img", "input", "ins", "kbd", "label", "map", "noscript", "object", "q", "s", "samp", "script", "select", "small", "span", "strike", "strong", "sub", "sup", "textarea", "tt", "u", "var"]
127}
128
129
130class SGMLOPParser(sax.xmlreader.XMLReader, sax.xmlreader.Locator):
131    """
132    This is a rudimentary, buggy, halfworking, untested SAX2 drivers for sgmlop that
133    only works in the context of &xist;. And I didn't even know, what I was doing.
134    """
135    _whichparser = sgmlop.XMLParser
136
137    def __init__(self, bufsize=8000):
138        sax.xmlreader.XMLReader.__init__(self)
139        self.encoding = None
140        self._parser = None
141        self._bufsize = bufsize
142
143    def _makestring(self, data):
144        return unicode(data, self.encoding).replace(u"\r\n", u"\n").replace(u"\r", u"\n")
145
146    def reset(self):
147        self.close()
148        self.source = None
149        self.lineNumber = -1
150
151    def feed(self, data):
152        if self._parser is None:
153            self._parser = self._whichparser()
154            self._parser.register(self)
155            self._cont_handler.startDocument()
156        self._parser.feed(data)
157
158    def close(self):
159        if self._parser is not None:
160            self._cont_handler.endDocument()
161            self._parser.close()
162            self._parser.register(None)
163            self._parser = None
164
165    def parse(self, source):
166        self.source = source
167        stream = source.getByteStream()
168        encoding = source.getEncoding()
169        if encoding is None:
170            encoding = "utf-8"
171        self.encoding = encoding
172        self._cont_handler.setDocumentLocator(self)
173        self._cont_handler.startDocument()
174        self.lineNumber = 1
175        # we don't keep a column number, because otherwise parsing would be much to slow
176        self.headerJustRead = False # will be used for skipping whitespace after the XML header
177
178        parsed = False
179        try:
180            while True:
181                data = stream.read(self._bufsize)
182                if not data:
183                    if not parsed:
184                        self.feed("")
185                    break
186                while True:
187                    pos = data.find("\n")
188                    if pos==-1:
189                        break
190                    self.feed(data[:pos+1])
191                    self.parsed = True
192                    data = data[pos+1:]
193                    self.lineNumber += 1
194                self.feed(data)
195                self.parsed = True
196        except (SystemExit, KeyboardInterrupt):
197            self.close()
198            self.source = None
199            self.encoding = None
200            raise
201        except Exception, exc:
202            try:
203                self.close()
204            except SystemExit:
205                raise
206            except KeyboardInterrupt:
207                raise
208            except Exception, exc2:
209                self.source = None
210                self.encoding = None
211            errhandler = self.getErrorHandler()
212            if errhandler is not None:
213                errhandler.fatalError(exc)
214            else:
215                raise
216        self.close()
217        self.source = None
218        self.encoding = None
219
220    # Locator methods will be called by the application
221    def getColumnNumber(self):
222        return -1
223
224    def getLineNumber(self):
225        if self._parser is None:
226            return -1
227        return self.lineNumber
228
229    def getPublicId(self):
230        if self.source is None:
231            return None
232        return self.source.getPublicId()
233
234    def getSystemId(self):
235        if self.source is None:
236            return None
237        return self.source.getSystemId()
238
239    def setFeature(self, name, state):
240        if name == handler.feature_namespaces:
241            if state:
242                raise sax.SAXNotSupportedException("no namespace processing available")
243        elif name == handler.feature_external_ges:
244            if state:
245                raise sax.SAXNotSupportedException("processing of external general entities not available")
246        else:
247            sax.xmlreader.IncrementalParser.setFeature(self, name, state)
248
249    def getFeature(self, name):
250        if name == handler.feature_namespaces:
251            return 0
252        else:
253            return sax.xmlreader.IncrementalParser.setFeature(self, name)
254
255    def handle_comment(self, data):
256        self.getContentHandler().comment(self._makestring(data))
257        self.headerJustRead = False
258
259    # don't define handle_charref or handle_cdata, so we will get those through handle_data
260    # but unfortunately we have to define handle_charref here, because of a bug in
261    # sgmlop: unicode characters i.e. "&#8364;" don't work.
262
263    def handle_charref(self, data):
264        data = self._makestring(data)
265        if data.startswith(u"x"):
266            data = unichr(int(data[1:], 16))
267        else:
268            data = unichr(int(data))
269        if not self.headerJustRead or not data.isspace():
270            self.getContentHandler().characters(data)
271            self.headerJustRead = False
272
273    def handle_data(self, data):
274        data = self._makestring(data)
275        if not self.headerJustRead or not data.isspace():
276            self.getContentHandler().characters(data)
277            self.headerJustRead = False
278
279    def handle_proc(self, target, data):
280        target = self._makestring(target)
281        data = self._makestring(data)
282        if target != u'xml': # Don't report <?xml?> as a processing instruction
283            self.getContentHandler().processingInstruction(target, data)
284            self.headerJustRead = False
285        else: # extract the encoding
286            encoding = utils.findattr(data, u"encoding")
287            if encoding is not None:
288                self.encoding = encoding
289            self.headerJustRead = True
290
291    def handle_entityref(self, name):
292        try:
293            c = {"lt": u"<", "gt": u">", "amp": u"&", "quot": u'"', "apos": u"'"}[name]
294        except KeyError:
295            self.getContentHandler().skippedEntity(self._makestring(name))
296        else:
297            self.getContentHandler().characters(c)
298        self.headerJustRead = False
299
300    def finish_starttag(self, name, attrs):
301        newattrs = sax.xmlreader.AttributesImpl({})
302        for (attrname, attrvalue) in attrs.items():
303            if attrvalue is None:
304                attrvalue = attrname
305            else:
306                attrvalue = self._string2fragment(self._makestring(attrvalue))
307            newattrs._attrs[self._makestring(attrname)] = attrvalue
308        self.getContentHandler().startElement(self._makestring(name), newattrs)
309        self.headerJustRead = False
310
311    def finish_endtag(self, name):
312        self.getContentHandler().endElement(self._makestring(name))
313        self.headerJustRead = False
314
315    def _string2fragment(self, text):
316        """
317        parses a string that might contain entities into a fragment
318        with text nodes, entities and character references.
319        """
320        if text is None:
321            return xsc.Null
322        ct = getattr(self.getContentHandler(), "createText", xsc.Text)
323        node = xsc.Frag()
324        while True:
325            texts = text.split(u"&", 1)
326            text = texts[0]
327            if text:
328                node.append(ct(text))
329            if len(texts)==1:
330                break
331            texts = texts[1].split(u";", 1)
332            name = texts[0]
333            if len(texts)==1:
334                raise xsc.MalformedCharRefWarning(name)
335            if name.startswith(u"#"):
336                try:
337                    if name.startswith(u"#x"):
338                        node.append(ct(unichr(int(name[2:], 16))))
339                    else:
340                        node.append(ct(unichr(int(name[1:]))))
341                except ValueError:
342                    raise xsc.MalformedCharRefWarning(name)
343            else:
344                try:
345                    c = {"lt": u"<", "gt": u">", "amp": u"&", "quot": u'"', "apos": u"'"}[name]
346                except KeyError:
347                    node.append(self.getContentHandler().createEntity(name))
348                else:
349                    node.append(ct(c))
350            text = texts[1]
351        if not node:
352            node.append(ct(u""))
353        return node
354
355
356class BadEntityParser(SGMLOPParser):
357    """
358    <par>A &sax;2 parser that recognizes the character entities
359    defined in &html; and tries to pass on unknown or malformed
360    entities to the handler literally.</par>
361    """
362
363    def handle_entityref(self, name):
364        try:
365            c = {"lt": u"<", "gt": u">", "amp": u"&", "quot": u'"', "apos": u"'"}[name]
366        except KeyError:
367            name = self._makestring(name)
368            try:
369                self.getContentHandler().skippedEntity(name)
370            except xsc.IllegalEntityError:
371                try:
372                    entity = html.entity(name, xml=True)
373                except xsc.IllegalEntityError:
374                    self.getContentHandler().characters(u"&%s;" % name)
375                else:
376                    if issubclass(entity, xsc.CharRef):
377                        self.getContentHandler().characters(unichr(entity.codepoint))
378                    else:
379                        self.getContentHandler().characters(u"&%s;" % name)
380        else:
381            self.getContentHandler().characters(c)
382        self.headerJustRead = False
383
384    def _string2fragment(self, text):
385        """
386        This version tries to pass illegal content literally.
387        """
388        if text is None:
389            return xsc.Null
390        node = xsc.Frag()
391        ct = self.getContentHandler().createText
392        while True:
393            texts = text.split(u"&", 1)
394            text = texts[0]
395            if text:
396                node.append(ct(text))
397            if len(texts)==1:
398                break
399            texts = texts[1].split(u";", 1)
400            name = texts[0]
401            if len(texts)==1: # no ; found, so it's no entity => append it literally
402                name = u"&" + name
403                warnings.warn(xsc.MalformedCharRefWarning(name))
404                node.append(ct(name))
405                break
406            else:
407                if name.startswith(u"#"): # character reference
408                    try:
409                        if name.startswith(u"#x"): # hexadecimal character reference
410                            node.append(ct(unichr(int(name[2:], 16))))
411                        else: # decimal character reference
412                            node.append(ct(unichr(int(name[1:]))))
413                    except (ValueError, OverflowError): # illegal format => append it literally
414                        name = u"&%s;" % name
415                        warnings.warn(xsc.MalformedCharRefWarning(name))
416                        node.append(ct(name))
417                else: # entity reference
418                    try:
419                        entity = {"lt": u"<", "gt": u">", "amp": u"&", "quot": u'"', "apos": u"'"}[name]
420                    except KeyError:
421                        try:
422                            entity = self.getContentHandler().createEntity(name)
423                        except xsc.IllegalEntityError:
424                            try:
425                                entity = html.entity(name, xml=True)
426                                if issubclass(entity, xsc.CharRef):
427                                    entity = ct(unichr(entity.codepoint))
428                                else:
429                                    entity = entity()
430                            except xsc.IllegalEntityError:
431                                name = u"&%s;" % name
432                                warnings.warn(xsc.MalformedCharRefWarning(name))
433                                entity = ct(name)
434                    else:
435                        entity = ct(entity)
436                    node.append(entity)
437            text = texts[1]
438        return node
439
440    def handle_charref(self, data):
441        data = self._makestring(data)
442        try:
443            if data.startswith("x"):
444                data = unichr(int(data[1:], 16))
445            else:
446                data = unichr(int(data))
447        except (ValueError, OverflowError):
448            data = u"&#%s;" % data
449        if not self.headerJustRead or not data.isspace():
450            self.getContentHandler().characters(data)
451            self.headerJustRead = False
452
453
454class HTMLParser(BadEntityParser):
455    """
456    <par>A &sax;2 parser that can parse &html;.</par>
457    """
458
459    _whichparser = sgmlop.SGMLParser
460
461    def __init__(self, bufsize=2**16-20):
462        BadEntityParser.__init__(self, bufsize)
463        self._stack = []
464
465    def reset(self):
466        self._stack = []
467        BadEntityParser.reset(self)
468
469    def close(self):
470        while self._stack: # close all open elements
471            self.finish_endtag(self._stack[-1])
472        BadEntityParser.close(self)
473
474    def finish_starttag(self, name, attrs):
475        name = name.lower()
476
477        # guess omitted close tags
478        while self._stack and self._stack[-1] in HTML_OPT_END and name not in HTML_DTD.get(self._stack[-1], []):
479            BadEntityParser.finish_endtag(self, self._stack[-1])
480            del self._stack[-1]
481
482        # Check whether this element is allowed in the current context
483        if self._stack and name not in HTML_DTD.get(self._stack[-1], []):
484            warnings.warn(xsc.IllegalDTDChildWarning(name, self._stack[-1]))
485
486        # Skip unknown attributes (but warn about them)
487        newattrs = {}
488        element = html.element(name, xml=True)
489        for (attrname, attrvalue) in attrs:
490            if attrname=="xmlns" or ":" in attrname or element.Attrs.isallowed(attrname.lower(), xml=True):
491                newattrs[attrname.lower()] = attrvalue
492            else:
493                warnings.warn(xsc.IllegalAttrError(element.Attrs, attrname.lower(), xml=True))
494        BadEntityParser.finish_starttag(self, name, newattrs)
495
496        if name in HTML_FORBIDDEN_END:
497            # close tags immediately for which we won't get an end
498            BadEntityParser.finish_endtag(self, name)
499            return 0
500        else:
501            self._stack.append(name)
502        return 1
503
504    def finish_endtag(self, name):
505        name = name.lower()
506        if name in HTML_FORBIDDEN_END:
507            # do nothing: we've already closed it
508            return
509        if name in self._stack:
510            # close any open elements that were not closed explicitely
511            while self._stack and self._stack[-1] != name:
512                BadEntityParser.finish_endtag(self, self._stack[-1])
513                del self._stack[-1]
514            BadEntityParser.finish_endtag(self, name)
515            del self._stack[-1]
516        else:
517            warnings.warn(xsc.IllegalCloseTagWarning(name))
518
519
520class ExpatParser(expatreader.ExpatParser):
521    def external_entity_ref(self, context, base, sysid, pubid):
522        print locals()
523        return expatreader.ExpatParser.external_entity_ref(self, context, base, sysid, pubid)
524
525    def reset(self):
526        expatreader.ExpatParser.reset(self)
527        self._parser.UseForeignDTD(True)
528
529
530class Parser(object):
531    """
532    <par>It is the job of a <class>Parser</class> to create the object tree from the
533    &sax; events generated by the underlying &sax; parser.</par>
534    """
535
536    def __init__(self, saxparser=SGMLOPParser, nspool=None, prefixes=None, tidy=False, loc=True, validate=True, encoding=None):
537        """
538        <par>Create a new <class>Parser</class> instance.</par>
539
540        <par>Arguments have the following meaning:</par>
541        <dlist>
542        <term><arg>saxparser</arg></term><item><par>a callable that returns an instance of a &sax;2 compatible parser.
543        &xist; itself provides several &sax;2 parsers
544        (all based on Fredrik Lundh's <app>sgmlop</app> from <app moreinfo="http://pyxml.sf.net/">PyXML</app>):</par>
545        <ulist>
546        <item><pyref module="ll.xist.parsers" class="SGMLOPParser"><class>SGMLOPParser</class></pyref>
547        (which is the default if the <arg>saxparser</arg> argument is not given);</item>
548        <item><pyref module="ll.xist.parsers" class="BadEntityParser"><class>BadEntityParser</class></pyref>
549        (which is based on <class>SGMLOPParser</class> and tries to pass on unknown entity references as literal content);</item>
550        <item><pyref module="ll.xist.parsers" class="HTMLParser"><class>HTMLParser</class></pyref> (which is
551        based on BadEntityParser and tries to make sense of &html; sources).</item>
552        </ulist>
553        </item>
554
555        <term><arg>tidy</arg></term><item>If <arg>tidy</arg> is true, <link href="http://xmlsoft.org/">libxml2</link>'s
556        &html; parser will be used for parsing broken &html;.</item>
557        <term><arg>nspool</arg></term><item>an instance of <pyref module="ll.xist.xsc" class="NSPool"><class>ll.xist.xsc.NSPool</class></pyref>;
558        From this namespace pool namespaces will be taken when the parser
559        encounters <lit>xmlns</lit> attributes.</item>
560
561        <term><arg>prefixes</arg></term><item>an instance of <pyref module="ll.xist.xsc" class="Prefixes"><class>ll.xist.xsc.Prefixes</class></pyref>;
562        Specifies which namespace modules should be available during parsing
563        and to which prefixes they are mapped (this mapping can change during
564        parsing by <lit>xmlns</lit> attributes are encountered).</item>
565
566        <term><arg>loc</arg></term><item>Should location information be attached to the generated nodes?</item>
567
568        <term><arg>validate</arg></term><item>Should the parsed &xml; nodes be validated after parsing?</item>
569
570        <term><arg>encoding</arg></term><item>The default encoding to use, when the
571        source doesn't provide an encoding. The default <lit>None</lit> results in
572        <lit>"utf-8"</lit> for parsing &xml; and <lit>"iso-8859-1"</lit> when parsing
573        broken &html; (when <lit><arg>tidy</arg></lit> is true).</item>
574        </dlist>
575        """
576        self.saxparser = saxparser
577
578        if nspool is None:
579            nspool = xsc.defaultnspool
580        self.nspool = nspool
581
582        if prefixes is None:
583            prefixes = xsc.defaultPrefixes.clone()
584        self.prefixes = prefixes # the currently active prefix mapping (will be replaced once xmlns attributes are encountered)
585
586        self._locator = None
587        self.tidy = tidy
588        self.loc = loc
589        self.validate = validate
590        self.encoding = encoding
591
592    def _last(self):
593        """
594        Return the newest node from the stack that is a real node.
595        (There might be fake nodes on the stack, because we are inside
596        of illegal elements).
597        """
598        for (node, prefixes) in reversed(self._nesting):
599            if node is not None:
600                return node
601
602    def _parseHTML(self, stream, base, sysid, encoding):
603        """
604        Internal helper method for parsing &html; via <module>libxml2</module>.
605        """
606        import libxml2 # This requires libxml2 (see http://www.xmlsoft.org/)
607
608        def decode(s):
609            try:
610                return s.decode("utf-8")
611            except UnicodeDecodeError:
612                return s.decode("iso-8859-1")
613
614        def toxsc(node):
615            if node.type == "document_html":
616                newnode = xsc.Frag()
617                child = node.children
618                while child is not None:
619                    newnode.append(toxsc(child))
620                    child = child.next
621            elif node.type == "element":
622                name = decode(node.name).lower()
623                try:
624                    newnode = ns.element(name, xml=True)()
625                    if self.loc:
626                        newnode.startloc = xsc.Location(sysid=sysid, line=node.lineNo())
627                except xsc.IllegalElementError:
628                    newnode = xsc.Frag()
629                else:
630                    attr = node.properties
631                    while attr is not None:
632                        name = decode(attr.name).lower()
633                        if attr.content is None:
634                            content = u""
635                        else:
636                            content = decode(attr.content)
637                        try:
638                            attrnode = newnode.attrs.set(name, content, xml=True)
639                        except xsc.IllegalAttrError:
640                            pass
641                        else:
642                            attrnode = attrnode.parsed(self)
643                        attr = attr.next
644                    newnode.attrs = newnode.attrs.parsed(self)
645                    newnode = newnode.parsed(self, start=True)
646                child = node.children
647                while child is not None:
648                    newnode.append(toxsc(child))
649                    child = child.next
650                if isinstance(node, xsc.Element): # if we did recognize the element, otherwise we're in a Frag
651                    newnode = newnode.parsed(self, start=False)
652            elif node.type in ("text", "cdata"):
653                newnode = xsc.Text(decode(node.content))
654                if self.loc:
655                    newnode.startloc = xsc.Location(sysid=sysid, line=node.lineNo())
656            elif node.type == "comment":
657                newnode = xsc.Comment(decode(node.content))
658                if self.loc:
659                    newnode.startloc = xsc.Location(sysid=sysid, line=node.lineNo())
660            else:
661                newnode = xsc.Null
662            return newnode
663
664        data = stream.read()
665        ns = self.nspool.get(html.xmlname, html)
666        try:
667            olddefault = libxml2.lineNumbersDefault(1)
668            doc = libxml2.htmlReadMemory(data, len(data), sysid, encoding, 0x160)
669            try:
670                node = toxsc(doc)
671            finally:
672                doc.freeDoc()
673        finally:
674            libxml2.lineNumbersDefault(olddefault)
675        return node
676
677    def _parse(self, stream, base, sysid, encoding):
678        self.base = url.URL(base)
679
680        parser = self.saxparser()
681        # register us for callbacks
682        parser.setErrorHandler(self)
683        parser.setContentHandler(self)
684        parser.setDTDHandler(self)
685        parser.setEntityResolver(self)
686
687        # Configure the parser
688        try:
689            parser.setFeature(handler.feature_namespaces, False) # We do our own namespace processing
690        except sax.SAXNotSupportedException:
691            pass
692        try:
693            parser.setFeature(handler.feature_external_ges, False) # Don't process external entities, but pass them to skippedEntity
694        except sax.SAXNotSupportedException:
695            pass
696
697        self.skippingwhitespace = False
698
699        if self.tidy:
700            if encoding is None:
701                encoding = "iso-8859-1"
702            return self._parseHTML(stream, base, sysid, encoding)
703
704        if encoding is None:
705            encoding = "utf-8"
706
707        source = sax.xmlreader.InputSource(sysid)
708        source.setByteStream(stream)
709        source.setEncoding(encoding)
710
711        # XIST nodes do not have a parent link, therefore we have to store the
712        # active path through the tree in a stack (which we call _nesting)
713        # together with the namespace prefixes defined by each element.
714        #
715        # After we've finished parsing, the Frag that we put at the bottom of the
716        # stack will be our document root.
717        #
718        # The parser provides the ability to skip illegal elements, attributes,
719        # processing instructions or entity references, but for illegal elements,
720        # it must still record the new namespaces defined by the illegal element.
721        # In this case None is stored in the stack instead of the element node.
722
723        self._nesting = [ (xsc.Frag(), self.prefixes) ]
724        try:
725            parser.parse(source)
726            root = self._nesting[0][0]
727        finally:
728            self._nesting = None
729        return root
730
731    def parse(self, stream, base=None, sysid=None):
732        """
733        Parse &xml; from the stream <arg>stream</arg> into an &xist; tree.
734        <arg>base</arg> is the base &url; for the parsing process, <arg>sysid</arg>
735        is the &xml; system identifier (defaulting to <arg>base</arg> if it is <lit>None</lit>).
736        """
737        if sysid is None:
738            sysid = base
739        return self._parse(stream, base, sysid, self.encoding)
740
741    def parseString(self, text, base=None, sysid=None):
742        """
743        Parse the string <arg>text</arg> (<class>str</class> or <class>unicode</class>)
744        into an &xist; tree. <arg>base</arg> is the base &url; for the parsing process, <arg>sysid</arg>
745        is the &xml; system identifier (defaulting to <arg>base</arg> if it is <lit>None</lit>).
746        """
747        if isinstance(text, unicode):
748            encoding = "utf-8"
749            text = text.encode(encoding)
750        else:
751            encoding = self.encoding
752        stream = cStringIO.StringIO(text)
753        if base is None:
754            base = url.URL("STRING")
755        if sysid is None:
756            sysid = str(base)
757        return self._parse(stream, base, sysid, encoding)
758
759    def parseURL(self, name, base=None, sysid=None, headers=None, data=None):
760        """
761        Parse &xml; input from the &url; <arg>name</arg> which might be a string
762        or an <pyref module="ll.url" class="URL"><class>URL</class></pyref> object
763        into an &xist; tree. <arg>base</arg> is the base &url; for the parsing process
764        (defaulting to the final &url; of the response (i.e. including redirects)),
765        <arg>sysid</arg> is the &xml; system identifier (defaulting to <arg>base</arg>
766        if it is <lit>None</lit>). <arg>headers</arg> is a dictionary with additional
767        headers used in the &http; request. If <arg>data</arg> is not <lit>None</lit>
768        it must be a dictionary. In this case <arg>data</arg> will be used as the data
769        for a <lit>POST</lit> request.
770        """
771        name = url.URL(name)
772        stream = name.openread(headers=headers, data=data)
773        if base is None:
774            base = stream.finalurl
775        if sysid is None:
776            sysid = str(base)
777        encoding = self.encoding
778        if encoding is None:
779            encoding = stream.encoding
780        return self._parse(stream, base, sysid, encoding)
781
782    def parseFile(self, filename, base=None, sysid=None):
783        """
784        Parse &xml; input from the file named <arg>filename</arg>. <arg>base</arg> is
785        the base &url; for the parsing process (defaulting to <arg>filename</arg>),
786        <arg>sysid</arg> is the &xml; system identifier (defaulting to <arg>base</arg>).
787        """
788        filename = os.path.expanduser(filename)
789        stream = open(filename, "rb")
790        if base is None:
791            base = url.File(filename)
792        if sysid is None:
793            sysid = str(base)
794        return self._parse(stream, base, sysid, self.encoding)
795
796    def setDocumentLocator(self, locator):
797        self._locator = locator
798
799    def startDocument(self):
800        pass
801
802    def endDocument(self):
803        pass
804
805    def startElement(self, name, attrs):
806        oldprefixes = self.prefixes
807
808        newprefixes = {}
809        for (attrname, attrvalue) in attrs.items():
810            if attrname==u"xmlns" or attrname.startswith(u"xmlns:"):
811                ns = self.nspool[unicode(attrvalue)]
812                newprefixes[attrname[6:] or None] = [ns]
813
814        if newprefixes:
815            prefixes = oldprefixes.clone()
816            prefixes.update(newprefixes)
817            self.prefixes = newprefixes = prefixes
818        else:
819            newprefixes = oldprefixes
820
821        node = self.createElement(name)
822        if node is not None:
823            for (attrname, attrvalue) in attrs.items():
824                if attrname != u"xmlns" and not attrname.startswith(u"xmlns:"):
825                    attrname = newprefixes.attrnamefromqname(node, attrname)
826                    if attrname is not None: # None means an illegal attribute
827                        node[attrname] = attrvalue
828                        node[attrname] = node[attrname].parsed(self)
829            node.attrs = node.attrs.parsed(self)
830            node = node.parsed(self, start=True)
831            self.__appendNode(node)
832        # push new innermost element onto the stack, together with the list of prefix mappings to which we have to return when we leave this element
833        # If the element is bad (i.e. createElement returned None), we push None as the node
834        self._nesting.append((node, oldprefixes))
835        self.skippingwhitespace = False
836
837    def endElement(self, name):
838        currentelement = self._last()
839        if currentelement is not None: # we're not in an bad element
840            currentelement.parsed(self, start=False) # ignore return value
841            if self.validate:
842                currentelement.checkvalid()
843            if self.loc:
844                currentelement.endloc = self.getLocation()
845
846        # We have to check with the currently active prefixes
847        element = self.createElement(name) # Unfortunately this creates the element a second time.
848        if element is not None and element.__class__ is not currentelement.__class__:
849            raise xsc.ElementNestingError(currentelement.__class__, element.__class__)
850
851        self.prefixes = self._nesting.pop()[1] # pop the innermost element off the stack and restore the old prefixes mapping (from outside this element)
852
853        self.skippingwhitespace = False
854
855    def characters(self, content):
856        if self.skippingwhitespace:
857            # the following could be content = content.lstrip(), but this would remove nbsps
858            while content and content[0].isspace() and content[0] != u"\xa0":
859                content = content[1:]
860        if content:
861            node = self.createText(content)
862            node = node.parsed(self)
863            last = self._last()
864            if len(last) and isinstance(last[-1], xsc.Text):
865                node = last[-1] + unicode(node) # join consecutive Text nodes
866                node.startloc = last[-1].startloc # make sure the replacement node has the original location
867                last[-1] = node # replace it
868            else:
869                self.__appendNode(node)
870            self.skippingwhitespace = False
871
872    def comment(self, content):
873        node = self.createComment(content)
874        node = node.parsed(self)
875        self.__appendNode(node)
876        self.skippingwhitespace = False
877
878    def processingInstruction(self, target, data):
879        if target=="x":
880            self.skippingwhitespace = True
881        else:
882            node = self.createProcInst(target, data)
883            if node is not None:
884                node = node.parsed(self)
885                self.__appendNode(node)
886            self.skippingwhitespace = False
887
888    def skippedEntity(self, name):
889        node = self.createEntity(name)
890        if node is not None:
891            if isinstance(node, xsc.CharRef):
892                self.characters(unichr(node.codepoint))
893            else:
894                node = node.parsed(self)
895                self.__appendNode(node)
896        self.skippingwhitespace = False
897
898    def __decorateException(self, exception):
899        if not isinstance(exception, sax.SAXParseException):
900            msg = exception.__class__.__name__
901            msg2 = str(exception)
902            if msg2:
903                msg += ": " + msg2
904            exception = sax.SAXParseException(msg, exception, self._locator)
905        return exception
906
907    def error(self, exception):
908        "Handle a recoverable error."
909        # This doesn't work properly with expat, as expat fiddles with the traceback
910        raise self.__decorateException(exception)
911
912    def fatalError(self, exception):
913        "Handle a non-recoverable error."
914        # This doesn't work properly with expat, as expat fiddles with the traceback
915        raise self.__decorateException(exception)
916
917    def warning(self, exception):
918        "Handle a warning."
919        print self.__decorateException(exception)
920
921    def getLocation(self):
922        return xsc.Location(self._locator)
923
924    def __appendNode(self, node):
925        if self.loc:
926            node.startloc = self.getLocation()
927        self._last().append(node) # add the new node to the content of the innermost element (or fragment)
928
929    def createText(self, content):
930        return xsc.Text(content)
931
932    def createComment(self, content):
933        return xsc.Comment(content)
934
935    def createElement(self, name):
936        element = self.prefixes.element(name)
937        if element is not None: # None means that the element should be ignored
938            element = element()
939        return element
940
941    def createProcInst(self, target, data):
942        procinst = self.prefixes.procinst(target)
943        if procinst is not None: # None means that the procinst should be ignored
944            procinst = procinst(data)
945        return procinst
946
947    def createEntity(self, name):
948        entity = self.prefixes.entity(name)
949        if entity is not None: # None means that the entity should be ignored
950            entity = entity()
951        return entity
952
953
954def parse(stream, base=None, sysid=None, **parserargs):
955    """
956    Parse &xml; from the stream <arg>stream</arg> into an &xist; tree.
957    For the arguments <arg>base</arg> and <arg>sysid</arg> see the method
958    <pyref class="Parser" method="parse"><method>parse</method></pyref>
959    in the <class>Parser</class> class. You can pass any other argument that the
960    <pyref class="Parser" method="__init__"><class>Parser</class> constructor</pyref>
961    takes as keyword arguments via <arg>parserargs</arg>.
962    """
963    parser = Parser(**parserargs)
964    return parser.parse(stream, base, sysid)
965
966
967def parseString(text, base=None, sysid=None, **parserargs):
968    """
969    Parse the string <arg>text</arg> (<class>str</class> or <class>unicode</class>) into an
970    &xist; tree. For the arguments <arg>base</arg> and <arg>sysid</arg> see the method
971    <pyref class="Parser" method="parseString"><method>parseString</method></pyref>
972    in the <class>Parser</class> class. You can pass any other argument that the
973    <pyref class="Parser" method="__init__"><class>Parser</class> constructor</pyref>
974    takes as keyword arguments via <arg>parserargs</arg>.
975    """
976    parser = Parser(**parserargs)
977    return parser.parseString(text, base, sysid)
978
979
980def parseURL(url, base=None, sysid=None, headers=None, data=None, **parserargs):
981    """
982    Parse &xml; input from the &url; <arg>name</arg> which might be a string
983    or an <pyref module="ll.url" class="URL"><class>URL</class></pyref> object
984    into an &xist; tree. For the arguments <arg>base</arg>, <arg>sysid</arg>,
985    <arg>headers</arg> and <arg>data</arg> see the method
986    <pyref class="Parser" method="parseURL"><method>parseURL</method></pyref>
987    in the <class>Parser</class> class. You can pass any other argument that the
988    <pyref class="Parser" method="__init__"><class>Parser</class> constructor</pyref>
989    takes as keyword arguments via <arg>parserargs</arg>.
990    """
991    parser = Parser(**parserargs)
992    return parser.parseURL(url, base, sysid, headers, data)
993
994
995def parseFile(filename, base=None, sysid=None, **parserargs):
996    """
997    Parse &xml; input from the file named <arg>filename</arg>. For the arguments
998    <arg>base</arg> and <arg>sysid</arg> see the method
999    <pyref class="Parser" method="parseFile"><method>parseFile</method></pyref>
1000    in the <class>Parser</class> class. You can pass any other argument that the
1001    <pyref class="Parser" method="__init__"><class>Parser</class> constructor</pyref>
1002    takes as keyword arguments via <arg>parserargs</arg>.
1003    """
1004    parser = Parser(**parserargs)
1005    return parser.parseFile(filename, base, sysid)
Note: See TracBrowser for help on using the browser.