root/livinglogic.python.xist/src/ll/xist/parsers.py @ 2632:b4d90755e4ae

Revision 2632:b4d90755e4ae, 53.3 KB (checked in by Walter Doerwald <walter@…>, 13 years ago)

Use the pool specified in the constructor instead of the currently active one from
the stack.

Line 
1#! /usr/bin/env python
2# -*- coding: iso-8859-1 -*-
3
4## Copyright 1999-2007 by LivingLogic AG, Bayreuth/Germany.
5## Copyright 1999-2007 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.lineNumber = 1
173        self._cont_handler.setDocumentLocator(self)
174        self._cont_handler.startDocument()
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            try:
181                while True:
182                    data = stream.read(self._bufsize)
183                    if not data:
184                        if not parsed:
185                            self.feed("")
186                        break
187                    while True:
188                        pos = data.find("\n")
189                        if pos==-1:
190                            break
191                        self.feed(data[:pos+1])
192                        self.parsed = True
193                        data = data[pos+1:]
194                        self.lineNumber += 1
195                    self.feed(data)
196                    self.parsed = True
197                self._cont_handler.endDocument()
198                self._cont_handler.setDocumentLocator(None)
199            except (SystemExit, KeyboardInterrupt):
200                raise
201            except Exception, exc:
202                errhandler = self.getErrorHandler()
203                if errhandler is not None:
204                    errhandler.fatalError(exc)
205                else:
206                    raise
207        finally:
208            self.close()
209            self.source = None
210            self.encoding = None
211
212    # Locator methods will be called by the application
213    def getColumnNumber(self):
214        return -1
215
216    def getLineNumber(self):
217        if self._parser is None:
218            return -1
219        return self.lineNumber
220
221    def getPublicId(self):
222        if self.source is None:
223            return None
224        return self.source.getPublicId()
225
226    def getSystemId(self):
227        if self.source is None:
228            return None
229        return self.source.getSystemId()
230
231    def setFeature(self, name, state):
232        if name == handler.feature_namespaces:
233            if state:
234                raise sax.SAXNotSupportedException("no namespace processing available")
235        elif name == handler.feature_external_ges:
236            if state:
237                raise sax.SAXNotSupportedException("processing of external general entities not available")
238        else:
239            sax.xmlreader.IncrementalParser.setFeature(self, name, state)
240
241    def getFeature(self, name):
242        if name == handler.feature_namespaces:
243            return 0
244        else:
245            return sax.xmlreader.IncrementalParser.setFeature(self, name)
246
247    def handle_comment(self, data):
248        self.getContentHandler().comment(self._makestring(data))
249        self.headerJustRead = False
250
251    # don't define handle_charref or handle_cdata, so we will get those through handle_data
252    # but unfortunately we have to define handle_charref here, because of a bug in
253    # sgmlop: unicode characters i.e. "&#8364;" don't work.
254
255    def handle_charref(self, data):
256        data = self._makestring(data)
257        if data.startswith(u"x"):
258            data = unichr(int(data[1:], 16))
259        else:
260            data = unichr(int(data))
261        if not self.headerJustRead or not data.isspace():
262            self.getContentHandler().characters(data)
263            self.headerJustRead = False
264
265    def handle_data(self, data):
266        data = self._makestring(data)
267        if not self.headerJustRead or not data.isspace():
268            self.getContentHandler().characters(data)
269            self.headerJustRead = False
270
271    def handle_proc(self, target, data):
272        target = self._makestring(target)
273        data = self._makestring(data)
274        if target != u'xml': # Don't report <?xml?> as a processing instruction
275            self.getContentHandler().processingInstruction(target, data)
276            self.headerJustRead = False
277        else: # extract the encoding
278            encoding = utils.findattr(data, u"encoding")
279            if encoding is not None:
280                self.encoding = encoding
281            self.headerJustRead = True
282
283    def handle_entityref(self, name):
284        try:
285            c = {"lt": u"<", "gt": u">", "amp": u"&", "quot": u'"', "apos": u"'"}[name]
286        except KeyError:
287            self.getContentHandler().skippedEntity(self._makestring(name))
288        else:
289            self.getContentHandler().characters(c)
290        self.headerJustRead = False
291
292    def finish_starttag(self, name, attrs):
293        newattrs = sax.xmlreader.AttributesImpl({})
294        for (attrname, attrvalue) in attrs.items():
295            if attrvalue is None:
296                attrvalue = attrname
297            else:
298                attrvalue = self._string2fragment(self._makestring(attrvalue))
299            newattrs._attrs[self._makestring(attrname)] = attrvalue
300        self.getContentHandler().startElement(self._makestring(name), newattrs)
301        self.headerJustRead = False
302
303    def finish_endtag(self, name):
304        self.getContentHandler().endElement(self._makestring(name))
305        self.headerJustRead = False
306
307    def _string2fragment(self, text):
308        """
309        parses a string that might contain entities into a fragment
310        with text nodes, entities and character references.
311        """
312        if text is None:
313            return xsc.Null
314        ct = getattr(self.getContentHandler(), "createText", xsc.Text)
315        node = xsc.Frag()
316        while True:
317            texts = text.split(u"&", 1)
318            text = texts[0]
319            if text:
320                node.append(ct(text))
321            if len(texts)==1:
322                break
323            texts = texts[1].split(u";", 1)
324            name = texts[0]
325            if len(texts)==1:
326                raise xsc.MalformedCharRefWarning(name)
327            if name.startswith(u"#"):
328                try:
329                    if name.startswith(u"#x"):
330                        node.append(ct(unichr(int(name[2:], 16))))
331                    else:
332                        node.append(ct(unichr(int(name[1:]))))
333                except ValueError:
334                    raise xsc.MalformedCharRefWarning(name)
335            else:
336                try:
337                    c = {"lt": u"<", "gt": u">", "amp": u"&", "quot": u'"', "apos": u"'"}[name]
338                except KeyError:
339                    node.append(self.getContentHandler().createEntity(name))
340                else:
341                    node.append(ct(c))
342            text = texts[1]
343        if not node:
344            node.append(ct(u""))
345        return node
346
347
348class BadEntityParser(SGMLOPParser):
349    """
350    <par>A &sax;2 parser that recognizes the character entities
351    defined in &html; and tries to pass on unknown or malformed
352    entities to the handler literally.</par>
353    """
354
355    def handle_entityref(self, name):
356        try:
357            c = {"lt": u"<", "gt": u">", "amp": u"&", "quot": u'"', "apos": u"'"}[name]
358        except KeyError:
359            name = self._makestring(name)
360            try:
361                self.getContentHandler().skippedEntity(name)
362            except xsc.IllegalEntityError:
363                try:
364                    entity = xsc.Entity.create(name, xml=True)
365                except xsc.IllegalEntityError:
366                    self.getContentHandler().characters(u"&%s;" % name)
367                else:
368                    if issubclass(entity, xsc.CharRef):
369                        self.getContentHandler().characters(unichr(entity.codepoint))
370                    else:
371                        self.getContentHandler().characters(u"&%s;" % name)
372        else:
373            self.getContentHandler().characters(c)
374        self.headerJustRead = False
375
376    def _string2fragment(self, text):
377        """
378        This version tries to pass illegal content literally.
379        """
380        if text is None:
381            return xsc.Null
382        node = xsc.Frag()
383        ct = self.getContentHandler().createText
384        while True:
385            texts = text.split(u"&", 1)
386            text = texts[0]
387            if text:
388                node.append(ct(text))
389            if len(texts)==1:
390                break
391            texts = texts[1].split(u";", 1)
392            name = texts[0]
393            if len(texts)==1: # no ; found, so it's no entity => append it literally
394                name = u"&" + name
395                warnings.warn(xsc.MalformedCharRefWarning(name))
396                node.append(ct(name))
397                break
398            else:
399                if name.startswith(u"#"): # character reference
400                    try:
401                        if name.startswith(u"#x"): # hexadecimal character reference
402                            node.append(ct(unichr(int(name[2:], 16))))
403                        else: # decimal character reference
404                            node.append(ct(unichr(int(name[1:]))))
405                    except (ValueError, OverflowError): # illegal format => append it literally
406                        name = u"&%s;" % name
407                        warnings.warn(xsc.MalformedCharRefWarning(name))
408                        node.append(ct(name))
409                else: # entity reference
410                    try:
411                        entity = {"lt": u"<", "gt": u">", "amp": u"&", "quot": u'"', "apos": u"'"}[name]
412                    except KeyError:
413                        try:
414                            entity = self.getContentHandler().createEntity(name)
415                        except xsc.IllegalEntityError:
416                            try:
417                                entity = xsc.Entity.create(name, xml=True)
418                                if issubclass(entity, xsc.CharRef):
419                                    entity = ct(unichr(entity.codepoint))
420                                else:
421                                    entity = entity()
422                            except xsc.IllegalEntityError:
423                                name = u"&%s;" % name
424                                warnings.warn(xsc.MalformedCharRefWarning(name))
425                                entity = ct(name)
426                    else:
427                        entity = ct(entity)
428                    node.append(entity)
429            text = texts[1]
430        return node
431
432    def handle_charref(self, data):
433        data = self._makestring(data)
434        try:
435            if data.startswith("x"):
436                data = unichr(int(data[1:], 16))
437            else:
438                data = unichr(int(data))
439        except (ValueError, OverflowError):
440            data = u"&#%s;" % data
441        if not self.headerJustRead or not data.isspace():
442            self.getContentHandler().characters(data)
443            self.headerJustRead = False
444
445
446class HTMLParser(BadEntityParser):
447    """
448    <par>A &sax;2 parser that can parse &html;.</par>
449    """
450
451    _whichparser = sgmlop.SGMLParser
452
453    def __init__(self, bufsize=2**16-20):
454        BadEntityParser.__init__(self, bufsize)
455        self._stack = []
456        self.pool = xsc.Pool(html)
457
458    def reset(self):
459        self._stack = []
460        BadEntityParser.reset(self)
461
462    def close(self):
463        while self._stack: # close all open elements
464            self.finish_endtag(self._stack[-1])
465        BadEntityParser.close(self)
466
467    def finish_starttag(self, name, attrs):
468        name = name.lower()
469
470        # guess omitted close tags
471        while self._stack and self._stack[-1] in HTML_OPT_END and name not in HTML_DTD.get(self._stack[-1], []):
472            BadEntityParser.finish_endtag(self, self._stack[-1])
473            del self._stack[-1]
474
475        # Check whether this element is allowed in the current context
476        if self._stack and name not in HTML_DTD.get(self._stack[-1], []):
477            warnings.warn(xsc.IllegalDTDChildWarning(name, self._stack[-1]))
478
479        # Skip unknown attributes (but warn about them)
480        newattrs = {}
481        element = self.pool.create_element_xml(name, html)
482        for (attrname, attrvalue) in attrs:
483            if attrname=="xmlns" or ":" in attrname or element.Attrs.isallowed(attrname.lower(), xml=True):
484                newattrs[attrname.lower()] = attrvalue
485            else:
486                warnings.warn(xsc.IllegalAttrError(attrname.lower(), None, xml=True))
487        BadEntityParser.finish_starttag(self, name, newattrs)
488
489        if name in HTML_FORBIDDEN_END:
490            # close tags immediately for which we won't get an end
491            BadEntityParser.finish_endtag(self, name)
492            return 0
493        else:
494            self._stack.append(name)
495        return 1
496
497    def finish_endtag(self, name):
498        name = name.lower()
499        if name in HTML_FORBIDDEN_END:
500            # do nothing: we've already closed it
501            return
502        if name in self._stack:
503            # close any open elements that were not closed explicitely
504            while self._stack and self._stack[-1] != name:
505                BadEntityParser.finish_endtag(self, self._stack[-1])
506                del self._stack[-1]
507            BadEntityParser.finish_endtag(self, name)
508            del self._stack[-1]
509        else:
510            warnings.warn(xsc.IllegalCloseTagWarning(name))
511
512
513class ExpatParser(expatreader.ExpatParser):
514    def external_entity_ref(self, context, base, sysid, pubid):
515        return expatreader.ExpatParser.external_entity_ref(self, context, base, sysid, pubid)
516
517    def reset(self):
518        expatreader.ExpatParser.reset(self)
519        self._parser.UseForeignDTD(True)
520
521
522class LaxAttrs(xsc.Attrs):
523    @classmethod
524    def _allowedattrkey(cls, name, xmlns=None, xml=False):
525        if xmlns is not None:
526            xmlns = xsc.nsname(xmlns)
527            try:
528                return (Attr.get(name, xmlns, xml).__name__, xmlns) # ask namespace about global attribute
529            except xsc.IllegalAttrError:
530                return (name, xmlns)
531        return name
532
533    def set(self, name, xmlns=None, value=None, xml=False):
534        """
535        <par>Set the attribute named <arg>name</arg> to the value <arg>value</arg>.
536        <arg>xml</arg> specifies whether <arg>name</arg> should be treated as an
537        &xml; name (<lit><arg>xml</arg>==True</lit>) or a Python name
538        (<lit><arg>xml</arg>==False</lit>).</par>
539        <par>The newly set attribute will be returned.</par>
540        """
541        attr = self.allowedattr(name, xmlns, xml)(value)()
542        attr.xmlname = name
543        dict.__setitem__(self, self._allowedattrkey(name, xmlns, xml), attr) # put the attribute in our dict
544        return attr
545
546    @classmethod
547    def allowedattr(cls, name, xmlns, xml=False):
548        if xmlns is not None:
549            xmlns = xsc.nsname(xmlns)
550            try:
551                return Attr.get(name, xmlns, xml) # return global attribute
552            except xsc.IllegalAttrError:
553                return xsc.TextAttr
554        else:
555            return xsc.TextAttr
556
557
558class LaxElement(xsc.Element):
559    register = None
560    Attrs = LaxAttrs
561
562
563class Parser(object):
564    """
565    <par>It is the job of a <class>Parser</class> to create the object tree from the
566    &sax; events generated by the underlying &sax; parser.</par>
567    """
568
569    def __init__(self, saxparser=SGMLOPParser, prefixes=None, tidy=False, loc=True, validate=True, encoding=None, pool=None):
570        """
571        <par>Create a new <class>Parser</class> instance.</par>
572
573        <par>Arguments have the following meaning:</par>
574        <dlist>
575        <term><arg>saxparser</arg></term><item><par>a callable that returns an instance of a &sax;2 compatible parser.
576        &xist; itself provides several &sax;2 parsers
577        (all based on Fredrik Lundh's <app>sgmlop</app> from <app moreinfo="http://pyxml.sf.net/">PyXML</app>):</par>
578        <ulist>
579        <item><pyref module="ll.xist.parsers" class="SGMLOPParser"><class>SGMLOPParser</class></pyref>
580        (which is the default if the <arg>saxparser</arg> argument is not given);</item>
581        <item><pyref module="ll.xist.parsers" class="BadEntityParser"><class>BadEntityParser</class></pyref>
582        (which is based on <class>SGMLOPParser</class> and tries to pass on unknown entity references as literal content);</item>
583        <item><pyref module="ll.xist.parsers" class="HTMLParser"><class>HTMLParser</class></pyref> (which is
584        based on BadEntityParser and tries to make sense of &html; sources).</item>
585        </ulist>
586        </item>
587
588        <term><arg>tidy</arg></term><item>If <arg>tidy</arg> is true, <link href="http://xmlsoft.org/">libxml2</link>'s
589        &html; parser will be used for parsing broken &html;.</item>
590        <term><arg>nspool</arg></term><item>an instance of <pyref module="ll.xist.xsc" class="NSPool"><class>ll.xist.xsc.NSPool</class></pyref>;
591        From this namespace pool namespaces will be taken when the parser
592        encounters <lit>xmlns</lit> attributes.</item>
593
594        <term><arg>loc</arg></term><item>Should location information be attached to the generated nodes?</item>
595
596        <term><arg>validate</arg></term><item>Should the parsed &xml; nodes be validated after parsing?</item>
597
598        <term><arg>encoding</arg></term><item>The default encoding to use, when the
599        source doesn't provide an encoding. The default <lit>None</lit> results in
600        <lit>"utf-8"</lit> for parsing &xml; and <lit>"iso-8859-1"</lit> when parsing
601        broken &html; (when <lit><arg>tidy</arg></lit> is true).</item>
602
603        <term><arg>pool</arg></term><item>A <pyref module="ll.xist.xsc" class="Pool"><class>ll.xist.xsc.Pool</class></pyref>
604        object which will be used for instantiating all nodes.</item>
605        </dlist>
606        """
607        self.saxparser = saxparser
608
609        self.pool = (pool if pool is not None else xsc.getpoolstack()[-1])
610
611        # the currently active prefix mapping (will be replaced once xmlns attributes are encountered)
612        if prefixes is None:
613            # make all currently known namespaces available without prefix
614            # (if there are elements with colliding namespace, which one will be used is random (based on dict iteration order)
615            self.prefixes = {None: list(set(xsc.nsname(c.xmlns) for c in self.pool.element_values()))}
616        else:
617            self.prefixes = {}
618            for (prefix, xmlns) in prefixes.iteritems():
619                if isinstance(xmlns, (list, tuple)):
620                    self.prefixes[prefix] = map(xsc.nsname, xmlns)
621                else:
622                    self.prefixes[prefix] = xsc.nsname(xmlns)
623
624        self._locator = None
625        self.tidy = tidy
626        self.loc = loc
627        self.validate = validate
628        self.encoding = encoding
629
630    def _parseHTML(self, stream, base, sysid, encoding):
631        """
632        Internal helper method for parsing &html; via <module>libxml2</module>.
633        """
634        import libxml2 # This requires libxml2 (see http://www.xmlsoft.org/)
635
636        def decode(s):
637            try:
638                return s.decode("utf-8")
639            except UnicodeDecodeError:
640                return s.decode("iso-8859-1")
641
642        def toxsc(node):
643            if node.type == "document_html":
644                newnode = xsc.Frag()
645                child = node.children
646                while child is not None:
647                    newnode.append(toxsc(child))
648                    child = child.next
649            elif node.type == "element":
650                name = decode(node.name).lower()
651                try:
652                    newnode = self.pool.create_element_xml(name, html)
653                    if self.loc:
654                        newnode.startloc = xsc.Location(sysid=sysid, line=node.lineNo())
655                except xsc.IllegalElementError:
656                    newnode = xsc.Frag()
657                else:
658                    attr = node.properties
659                    while attr is not None:
660                        name = decode(attr.name).lower()
661                        if attr.content is None:
662                            content = u""
663                        else:
664                            content = decode(attr.content)
665                        try:
666                            attrnode = newnode.attrs.set(name, value=content, xml=True)
667                        except xsc.IllegalAttrError:
668                            pass
669                        else:
670                            attrnode = attrnode.parsed(self)
671                            newnode.attrs.set(name, value=attrnode, xml=True)
672                        attr = attr.next
673                    newnode.attrs = newnode.attrs.parsed(self)
674                    newnode = newnode.parsed(self, start=True)
675                child = node.children
676                while child is not None:
677                    newnode.append(toxsc(child))
678                    child = child.next
679                if isinstance(node, xsc.Element): # if we did recognize the element, otherwise we're in a Frag
680                    newnode = newnode.parsed(self, start=False)
681            elif node.type in ("text", "cdata"):
682                newnode = self.pool.create_text(decode(node.content))
683                if self.loc:
684                    newnode.startloc = xsc.Location(sysid=sysid, line=node.lineNo())
685            elif node.type == "comment":
686                newnode = self.pool.create_comment(decode(node.content))
687                if self.loc:
688                    newnode.startloc = xsc.Location(sysid=sysid, line=node.lineNo())
689            else:
690                newnode = xsc.Null
691            return newnode
692
693        self.base = base
694
695        data = stream.read()
696        try:
697            olddefault = libxml2.lineNumbersDefault(1)
698            doc = libxml2.htmlReadMemory(data, len(data), sysid, encoding, 0x160)
699            try:
700                node = toxsc(doc)
701            finally:
702                doc.freeDoc()
703        finally:
704            libxml2.lineNumbersDefault(olddefault)
705        return node
706
707    def _parse(self, stream, base, sysid, encoding):
708        self.base = url.URL(base)
709
710        parser = self.saxparser()
711        # register us for callbacks
712        parser.setErrorHandler(self)
713        parser.setContentHandler(self)
714        parser.setDTDHandler(self)
715        parser.setEntityResolver(self)
716
717        # Configure the parser
718        try:
719            parser.setFeature(handler.feature_namespaces, False) # We do our own namespace processing
720        except sax.SAXNotSupportedException:
721            pass
722        try:
723            parser.setFeature(handler.feature_external_ges, False) # Don't process external entities, but pass them to skippedEntity
724        except sax.SAXNotSupportedException:
725            pass
726
727        self.skippingwhitespace = False
728
729        if self.tidy:
730            if encoding is None:
731                encoding = "iso-8859-1"
732            return self._parseHTML(stream, base, sysid, encoding)
733
734        if encoding is None:
735            encoding = "utf-8"
736
737        source = sax.xmlreader.InputSource(sysid)
738        source.setByteStream(stream)
739        source.setEncoding(encoding)
740
741        # XIST nodes do not have a parent link, therefore we have to store the
742        # active path through the tree in a stack (which we call _nesting)
743        # together with the namespace prefixes defined by each element.
744        #
745        # After we've finished parsing, the Frag that we put at the bottom of the
746        # stack will be our document root.
747        #
748        # The parser provides the ability to skip illegal elements, attributes,
749        # processing instructions or entity references, but for illegal elements,
750        # it must still record the new namespaces defined by the illegal element.
751        # In this case None is stored in the stack instead of the element node.
752
753        self._nesting = [ (xsc.Frag(), self.prefixes) ]
754        try:
755            parser.parse(source)
756            root = self._nesting[0][0]
757        finally:
758            self._nesting = None
759        return root
760
761    def parse(self, stream, base=None, sysid=None):
762        """
763        Parse &xml; from the stream <arg>stream</arg> into an &xist; tree.
764        <arg>base</arg> is the base &url; for the parsing process, <arg>sysid</arg>
765        is the &xml; system identifier (defaulting to <arg>base</arg> if it is <lit>None</lit>).
766        """
767        if sysid is None:
768            sysid = base
769        return self._parse(stream, base, sysid, self.encoding)
770
771    def parseString(self, text, base=None, sysid=None):
772        """
773        Parse the string <arg>text</arg> (<class>str</class> or <class>unicode</class>)
774        into an &xist; tree. <arg>base</arg> is the base &url; for the parsing process, <arg>sysid</arg>
775        is the &xml; system identifier (defaulting to <arg>base</arg> if it is <lit>None</lit>).
776        """
777        if isinstance(text, unicode):
778            encoding = "utf-8"
779            text = text.encode(encoding)
780        else:
781            encoding = self.encoding
782        stream = cStringIO.StringIO(text)
783        if base is None:
784            base = url.URL("STRING")
785        if sysid is None:
786            sysid = str(base)
787        return self._parse(stream, base, sysid, encoding)
788
789    def parseURL(self, name, base=None, sysid=None, *args, **kwargs):
790        """
791        Parse &xml; input from the &url; <arg>name</arg> which might be a string
792        or an <pyref module="ll.url" class="URL"><class>URL</class></pyref> object
793        into an &xist; tree. <arg>base</arg> is the base &url; for the parsing process
794        (defaulting to the final &url; of the response (i.e. including redirects)),
795        <arg>sysid</arg> is the &xml; system identifier (defaulting to <arg>base</arg>
796        if it is <lit>None</lit>). <arg>*args</arg> and <arg>**kwargs</arg> will
797        be passed on to the <method>open</method> call.
798        """
799        name = url.URL(name)
800        stream = name.open("rb", *args, **kwargs)
801        if base is None:
802            base = stream.finalurl()
803        if sysid is None:
804            sysid = str(base)
805        encoding = self.encoding
806        if encoding is None:
807            encoding = stream.encoding()
808        result = self._parse(stream, base, sysid, encoding)
809        stream.close()
810        return result
811
812    def parseFile(self, filename, base=None, sysid=None):
813        """
814        Parse &xml; input from the file named <arg>filename</arg>. <arg>base</arg> is
815        the base &url; for the parsing process (defaulting to <arg>filename</arg>),
816        <arg>sysid</arg> is the &xml; system identifier (defaulting to <arg>base</arg>).
817        """
818        filename = os.path.expanduser(filename)
819        stream = open(filename, "rb")
820        if base is None:
821            base = url.File(filename)
822        if sysid is None:
823            sysid = str(base)
824        result = self._parse(stream, base, sysid, self.encoding)
825        stream.close()
826        return result
827
828    def setDocumentLocator(self, locator):
829        self._locator = locator
830
831    def startDocument(self):
832        pass
833
834    def endDocument(self):
835        pass
836
837    def startElement(self, name, attrs):
838        oldprefixes = self.prefixes
839
840        attritems = attrs.items()
841        newprefixes = {}
842        for (attrname, xmlns) in attritems:
843            if attrname==u"xmlns" or attrname.startswith(u"xmlns:"):
844                prefix = attrname[6:] or None
845                newprefixes[prefix] = unicode(xmlns)
846
847        if newprefixes:
848            prefixes = oldprefixes.copy()
849            prefixes.update(newprefixes)
850            self.prefixes = newprefixes = prefixes
851        else:
852            newprefixes = oldprefixes
853        if u":" in name:
854            (prefix, name) = name.split(u":", 1)
855        else:
856            prefix = None
857
858        try:
859            xmlns = newprefixes[prefix]
860        except KeyError:
861            raise xsc.IllegalPrefixError(prefix)
862        else:
863            node = self.pool.create_element_xml(name, xmlns)
864
865        for (attrname, attrvalue) in attritems:
866            if attrname != u"xmlns" and not attrname.startswith(u"xmlns:"):
867                if u":" in attrname:
868                    (attrprefix, attrname) = attrname.split(u":", 1)
869                    if attrprefix == "xml":
870                        xmlns = xsc.xml_xmlns
871                    else:
872                        try:
873                            xmlns = newprefixes[attrprefix]
874                        except KeyError:
875                            raise xsc.IllegalPrefixError(attrprefix)
876                else:
877                    xmlns = None
878                attrname = node.Attrs._allowedattrkey(attrname, xmlns, True)
879                node[attrname] = attrvalue
880                node[attrname] = node[attrname].parsed(self)
881        node.attrs = node.attrs.parsed(self)
882        node = node.parsed(self, start=True)
883        self.__appendNode(node)
884        # 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
885        self._nesting.append((node, oldprefixes))
886        self.skippingwhitespace = False
887
888    def endElement(self, name):
889        currentelement = self._nesting[-1][0]
890        currentelement.parsed(self, start=False) # ignore return value
891        if self.validate:
892            currentelement.checkvalid()
893        if self.loc:
894            currentelement.endloc = self.getLocation()
895
896        self.prefixes = self._nesting.pop()[1] # pop the innermost element off the stack and restore the old prefixes mapping (from outside this element)
897
898        self.skippingwhitespace = False
899
900    def characters(self, content):
901        if self.skippingwhitespace:
902            # the following could be content = content.lstrip(), but this would remove nbsps
903            while content and content[0].isspace() and content[0] != u"\xa0":
904                content = content[1:]
905        if content:
906            node = self.pool.create_text(content)
907            node = node.parsed(self)
908            last = self._nesting[-1][0]
909            if len(last) and isinstance(last[-1], xsc.Text):
910                node = last[-1] + unicode(node) # join consecutive Text nodes
911                node.startloc = last[-1].startloc # make sure the replacement node has the original location
912                last[-1] = node # replace it
913            else:
914                self.__appendNode(node)
915            self.skippingwhitespace = False
916
917    def comment(self, content):
918        node = self.pool.create_comment(content)
919        node = node.parsed(self)
920        self.__appendNode(node)
921        self.skippingwhitespace = False
922
923    def processingInstruction(self, target, data):
924        if target=="x":
925            self.skippingwhitespace = True
926        else:
927            node = self.pool.create_procinst_xml(target, data)
928            node = node.parsed(self)
929            self.__appendNode(node)
930            self.skippingwhitespace = False
931
932    def skippedEntity(self, name):
933        node = self.pool.create_entity_xml(name)
934        if isinstance(node, xsc.CharRef):
935            self.characters(unichr(node.codepoint))
936        else:
937            node = node.parsed(self)
938            self.__appendNode(node)
939        self.skippingwhitespace = False
940
941    def __decorateException(self, exception):
942        if not isinstance(exception, sax.SAXParseException):
943            msg = exception.__class__.__name__
944            msg2 = str(exception)
945            if msg2:
946                msg += ": " + msg2
947            exception = sax.SAXParseException(msg, exception, self._locator)
948        return exception
949
950    def error(self, exception):
951        "Handle a recoverable error."
952        # This doesn't work properly with expat, as expat fiddles with the traceback
953        raise self.__decorateException(exception)
954
955    def fatalError(self, exception):
956        "Handle a non-recoverable error."
957        # This doesn't work properly with expat, as expat fiddles with the traceback
958        raise self.__decorateException(exception)
959
960    def warning(self, exception):
961        "Handle a warning."
962        print self.__decorateException(exception)
963
964    def getLocation(self):
965        return xsc.Location(self._locator)
966
967    def __appendNode(self, node):
968        if self.loc:
969            node.startloc = self.getLocation()
970        self._nesting[-1][0].append(node) # add the new node to the content of the innermost element (or fragment)
971
972
973def parse(stream, base=None, sysid=None, **parserargs):
974    """
975    Parse &xml; from the stream <arg>stream</arg> into an &xist; tree.
976    For the arguments <arg>base</arg> and <arg>sysid</arg> see the method
977    <pyref class="Parser" method="parse"><method>parse</method></pyref>
978    in the <class>Parser</class> class. You can pass any other argument that the
979    <pyref class="Parser" method="__init__"><class>Parser</class> constructor</pyref>
980    takes as keyword arguments via <arg>parserargs</arg>.
981    """
982    parser = Parser(**parserargs)
983    return parser.parse(stream, base, sysid)
984
985
986def parseString(text, base=None, sysid=None, **parserargs):
987    """
988    Parse the string <arg>text</arg> (<class>str</class> or <class>unicode</class>) into an
989    &xist; tree. For the arguments <arg>base</arg> and <arg>sysid</arg> see the method
990    <pyref class="Parser" method="parseString"><method>parseString</method></pyref>
991    in the <class>Parser</class> class. You can pass any other argument that the
992    <pyref class="Parser" method="__init__"><class>Parser</class> constructor</pyref>
993    takes as keyword arguments via <arg>parserargs</arg>.
994    """
995    parser = Parser(**parserargs)
996    return parser.parseString(text, base, sysid)
997
998
999def parseURL(url, base=None, sysid=None, headers=None, data=None, **parserargs):
1000    """
1001    Parse &xml; input from the &url; <arg>name</arg> which might be a string
1002    or an <pyref module="ll.url" class="URL"><class>URL</class></pyref> object
1003    into an &xist; tree. For the arguments <arg>base</arg>, <arg>sysid</arg>,
1004    <arg>headers</arg> and <arg>data</arg> see the method
1005    <pyref class="Parser" method="parseURL"><method>parseURL</method></pyref>
1006    in the <class>Parser</class> class. You can pass any other argument that the
1007    <pyref class="Parser" method="__init__"><class>Parser</class> constructor</pyref>
1008    takes as keyword arguments via <arg>parserargs</arg>.
1009    """
1010    parser = Parser(**parserargs)
1011    return parser.parseURL(url, base, sysid, headers=headers, data=data)
1012
1013
1014def parseFile(filename, base=None, sysid=None, **parserargs):
1015    """
1016    Parse &xml; input from the file named <arg>filename</arg>. For the arguments
1017    <arg>base</arg> and <arg>sysid</arg> see the method
1018    <pyref class="Parser" method="parseFile"><method>parseFile</method></pyref>
1019    in the <class>Parser</class> class. You can pass any other argument that the
1020    <pyref class="Parser" method="__init__"><class>Parser</class> constructor</pyref>
1021    takes as keyword arguments via <arg>parserargs</arg>.
1022    """
1023    parser = Parser(**parserargs)
1024    return parser.parseFile(filename, base, sysid)
Note: See TracBrowser for help on using the browser.