root/livinglogic.python.xist/src/ll/xist/parsers.py @ 2631:81a30d7983ae

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

Change the default for the prefixes argument: Now all namespaces known to
the current pool are mapped to the empty prefix.

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        # the currently active prefix mapping (will be replaced once xmlns attributes are encountered)
610        if prefixes is None:
611            # make all currently known namespaces available without prefix
612            # (if there are elements with colliding namespace, which one will be used is random (based on dict iteration order)
613            self.prefixes = {None: list(set(xsc.nsname(c.xmlns) for c in xsc.getpoolstack()[-1].element_values()))}
614        else:
615            self.prefixes = {}
616            for (prefix, xmlns) in prefixes.iteritems():
617                if isinstance(xmlns, (list, tuple)):
618                    self.prefixes[prefix] = map(xsc.nsname, xmlns)
619                else:
620                    self.prefixes[prefix] = xsc.nsname(xmlns)
621
622        self._locator = None
623        self.tidy = tidy
624        self.loc = loc
625        self.validate = validate
626        self.encoding = encoding
627        self.pool = (pool if pool is not None else xsc.getpoolstack()[-1])
628
629    def _parseHTML(self, stream, base, sysid, encoding):
630        """
631        Internal helper method for parsing &html; via <module>libxml2</module>.
632        """
633        import libxml2 # This requires libxml2 (see http://www.xmlsoft.org/)
634
635        def decode(s):
636            try:
637                return s.decode("utf-8")
638            except UnicodeDecodeError:
639                return s.decode("iso-8859-1")
640
641        def toxsc(node):
642            if node.type == "document_html":
643                newnode = xsc.Frag()
644                child = node.children
645                while child is not None:
646                    newnode.append(toxsc(child))
647                    child = child.next
648            elif node.type == "element":
649                name = decode(node.name).lower()
650                try:
651                    newnode = self.pool.create_element_xml(name, html)
652                    if self.loc:
653                        newnode.startloc = xsc.Location(sysid=sysid, line=node.lineNo())
654                except xsc.IllegalElementError:
655                    newnode = xsc.Frag()
656                else:
657                    attr = node.properties
658                    while attr is not None:
659                        name = decode(attr.name).lower()
660                        if attr.content is None:
661                            content = u""
662                        else:
663                            content = decode(attr.content)
664                        try:
665                            attrnode = newnode.attrs.set(name, value=content, xml=True)
666                        except xsc.IllegalAttrError:
667                            pass
668                        else:
669                            attrnode = attrnode.parsed(self)
670                            newnode.attrs.set(name, value=attrnode, xml=True)
671                        attr = attr.next
672                    newnode.attrs = newnode.attrs.parsed(self)
673                    newnode = newnode.parsed(self, start=True)
674                child = node.children
675                while child is not None:
676                    newnode.append(toxsc(child))
677                    child = child.next
678                if isinstance(node, xsc.Element): # if we did recognize the element, otherwise we're in a Frag
679                    newnode = newnode.parsed(self, start=False)
680            elif node.type in ("text", "cdata"):
681                newnode = self.pool.create_text(decode(node.content))
682                if self.loc:
683                    newnode.startloc = xsc.Location(sysid=sysid, line=node.lineNo())
684            elif node.type == "comment":
685                newnode = self.pool.create_comment(decode(node.content))
686                if self.loc:
687                    newnode.startloc = xsc.Location(sysid=sysid, line=node.lineNo())
688            else:
689                newnode = xsc.Null
690            return newnode
691
692        self.base = base
693
694        data = stream.read()
695        try:
696            olddefault = libxml2.lineNumbersDefault(1)
697            doc = libxml2.htmlReadMemory(data, len(data), sysid, encoding, 0x160)
698            try:
699                node = toxsc(doc)
700            finally:
701                doc.freeDoc()
702        finally:
703            libxml2.lineNumbersDefault(olddefault)
704        return node
705
706    def _parse(self, stream, base, sysid, encoding):
707        self.base = url.URL(base)
708
709        parser = self.saxparser()
710        # register us for callbacks
711        parser.setErrorHandler(self)
712        parser.setContentHandler(self)
713        parser.setDTDHandler(self)
714        parser.setEntityResolver(self)
715
716        # Configure the parser
717        try:
718            parser.setFeature(handler.feature_namespaces, False) # We do our own namespace processing
719        except sax.SAXNotSupportedException:
720            pass
721        try:
722            parser.setFeature(handler.feature_external_ges, False) # Don't process external entities, but pass them to skippedEntity
723        except sax.SAXNotSupportedException:
724            pass
725
726        self.skippingwhitespace = False
727
728        if self.tidy:
729            if encoding is None:
730                encoding = "iso-8859-1"
731            return self._parseHTML(stream, base, sysid, encoding)
732
733        if encoding is None:
734            encoding = "utf-8"
735
736        source = sax.xmlreader.InputSource(sysid)
737        source.setByteStream(stream)
738        source.setEncoding(encoding)
739
740        # XIST nodes do not have a parent link, therefore we have to store the
741        # active path through the tree in a stack (which we call _nesting)
742        # together with the namespace prefixes defined by each element.
743        #
744        # After we've finished parsing, the Frag that we put at the bottom of the
745        # stack will be our document root.
746        #
747        # The parser provides the ability to skip illegal elements, attributes,
748        # processing instructions or entity references, but for illegal elements,
749        # it must still record the new namespaces defined by the illegal element.
750        # In this case None is stored in the stack instead of the element node.
751
752        self._nesting = [ (xsc.Frag(), self.prefixes) ]
753        try:
754            parser.parse(source)
755            root = self._nesting[0][0]
756        finally:
757            self._nesting = None
758        return root
759
760    def parse(self, stream, base=None, sysid=None):
761        """
762        Parse &xml; from the stream <arg>stream</arg> into an &xist; tree.
763        <arg>base</arg> is the base &url; for the parsing process, <arg>sysid</arg>
764        is the &xml; system identifier (defaulting to <arg>base</arg> if it is <lit>None</lit>).
765        """
766        if sysid is None:
767            sysid = base
768        return self._parse(stream, base, sysid, self.encoding)
769
770    def parseString(self, text, base=None, sysid=None):
771        """
772        Parse the string <arg>text</arg> (<class>str</class> or <class>unicode</class>)
773        into an &xist; tree. <arg>base</arg> is the base &url; for the parsing process, <arg>sysid</arg>
774        is the &xml; system identifier (defaulting to <arg>base</arg> if it is <lit>None</lit>).
775        """
776        if isinstance(text, unicode):
777            encoding = "utf-8"
778            text = text.encode(encoding)
779        else:
780            encoding = self.encoding
781        stream = cStringIO.StringIO(text)
782        if base is None:
783            base = url.URL("STRING")
784        if sysid is None:
785            sysid = str(base)
786        return self._parse(stream, base, sysid, encoding)
787
788    def parseURL(self, name, base=None, sysid=None, *args, **kwargs):
789        """
790        Parse &xml; input from the &url; <arg>name</arg> which might be a string
791        or an <pyref module="ll.url" class="URL"><class>URL</class></pyref> object
792        into an &xist; tree. <arg>base</arg> is the base &url; for the parsing process
793        (defaulting to the final &url; of the response (i.e. including redirects)),
794        <arg>sysid</arg> is the &xml; system identifier (defaulting to <arg>base</arg>
795        if it is <lit>None</lit>). <arg>*args</arg> and <arg>**kwargs</arg> will
796        be passed on to the <method>open</method> call.
797        """
798        name = url.URL(name)
799        stream = name.open("rb", *args, **kwargs)
800        if base is None:
801            base = stream.finalurl()
802        if sysid is None:
803            sysid = str(base)
804        encoding = self.encoding
805        if encoding is None:
806            encoding = stream.encoding()
807        result = self._parse(stream, base, sysid, encoding)
808        stream.close()
809        return result
810
811    def parseFile(self, filename, base=None, sysid=None):
812        """
813        Parse &xml; input from the file named <arg>filename</arg>. <arg>base</arg> is
814        the base &url; for the parsing process (defaulting to <arg>filename</arg>),
815        <arg>sysid</arg> is the &xml; system identifier (defaulting to <arg>base</arg>).
816        """
817        filename = os.path.expanduser(filename)
818        stream = open(filename, "rb")
819        if base is None:
820            base = url.File(filename)
821        if sysid is None:
822            sysid = str(base)
823        result = self._parse(stream, base, sysid, self.encoding)
824        stream.close()
825        return result
826
827    def setDocumentLocator(self, locator):
828        self._locator = locator
829
830    def startDocument(self):
831        pass
832
833    def endDocument(self):
834        pass
835
836    def startElement(self, name, attrs):
837        oldprefixes = self.prefixes
838
839        attritems = attrs.items()
840        newprefixes = {}
841        for (attrname, xmlns) in attritems:
842            if attrname==u"xmlns" or attrname.startswith(u"xmlns:"):
843                prefix = attrname[6:] or None
844                newprefixes[prefix] = unicode(xmlns)
845
846        if newprefixes:
847            prefixes = oldprefixes.copy()
848            prefixes.update(newprefixes)
849            self.prefixes = newprefixes = prefixes
850        else:
851            newprefixes = oldprefixes
852        if u":" in name:
853            (prefix, name) = name.split(u":", 1)
854        else:
855            prefix = None
856
857        try:
858            xmlns = newprefixes[prefix]
859        except KeyError:
860            raise xsc.IllegalPrefixError(prefix)
861        else:
862            node = self.pool.create_element_xml(name, xmlns)
863
864        for (attrname, attrvalue) in attritems:
865            if attrname != u"xmlns" and not attrname.startswith(u"xmlns:"):
866                if u":" in attrname:
867                    (attrprefix, attrname) = attrname.split(u":", 1)
868                    if attrprefix == "xml":
869                        xmlns = xsc.xml_xmlns
870                    else:
871                        try:
872                            xmlns = newprefixes[attrprefix]
873                        except KeyError:
874                            raise xsc.IllegalPrefixError(attrprefix)
875                else:
876                    xmlns = None
877                attrname = node.Attrs._allowedattrkey(attrname, xmlns, True)
878                node[attrname] = attrvalue
879                node[attrname] = node[attrname].parsed(self)
880        node.attrs = node.attrs.parsed(self)
881        node = node.parsed(self, start=True)
882        self.__appendNode(node)
883        # 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
884        self._nesting.append((node, oldprefixes))
885        self.skippingwhitespace = False
886
887    def endElement(self, name):
888        currentelement = self._nesting[-1][0]
889        currentelement.parsed(self, start=False) # ignore return value
890        if self.validate:
891            currentelement.checkvalid()
892        if self.loc:
893            currentelement.endloc = self.getLocation()
894
895        self.prefixes = self._nesting.pop()[1] # pop the innermost element off the stack and restore the old prefixes mapping (from outside this element)
896
897        self.skippingwhitespace = False
898
899    def characters(self, content):
900        if self.skippingwhitespace:
901            # the following could be content = content.lstrip(), but this would remove nbsps
902            while content and content[0].isspace() and content[0] != u"\xa0":
903                content = content[1:]
904        if content:
905            node = self.pool.create_text(content)
906            node = node.parsed(self)
907            last = self._nesting[-1][0]
908            if len(last) and isinstance(last[-1], xsc.Text):
909                node = last[-1] + unicode(node) # join consecutive Text nodes
910                node.startloc = last[-1].startloc # make sure the replacement node has the original location
911                last[-1] = node # replace it
912            else:
913                self.__appendNode(node)
914            self.skippingwhitespace = False
915
916    def comment(self, content):
917        node = self.pool.create_comment(content)
918        node = node.parsed(self)
919        self.__appendNode(node)
920        self.skippingwhitespace = False
921
922    def processingInstruction(self, target, data):
923        if target=="x":
924            self.skippingwhitespace = True
925        else:
926            node = self.pool.create_procinst_xml(target, data)
927            node = node.parsed(self)
928            self.__appendNode(node)
929            self.skippingwhitespace = False
930
931    def skippedEntity(self, name):
932        node = self.pool.create_entity_xml(name)
933        if isinstance(node, xsc.CharRef):
934            self.characters(unichr(node.codepoint))
935        else:
936            node = node.parsed(self)
937            self.__appendNode(node)
938        self.skippingwhitespace = False
939
940    def __decorateException(self, exception):
941        if not isinstance(exception, sax.SAXParseException):
942            msg = exception.__class__.__name__
943            msg2 = str(exception)
944            if msg2:
945                msg += ": " + msg2
946            exception = sax.SAXParseException(msg, exception, self._locator)
947        return exception
948
949    def error(self, exception):
950        "Handle a recoverable error."
951        # This doesn't work properly with expat, as expat fiddles with the traceback
952        raise self.__decorateException(exception)
953
954    def fatalError(self, exception):
955        "Handle a non-recoverable error."
956        # This doesn't work properly with expat, as expat fiddles with the traceback
957        raise self.__decorateException(exception)
958
959    def warning(self, exception):
960        "Handle a warning."
961        print self.__decorateException(exception)
962
963    def getLocation(self):
964        return xsc.Location(self._locator)
965
966    def __appendNode(self, node):
967        if self.loc:
968            node.startloc = self.getLocation()
969        self._nesting[-1][0].append(node) # add the new node to the content of the innermost element (or fragment)
970
971
972def parse(stream, base=None, sysid=None, **parserargs):
973    """
974    Parse &xml; from the stream <arg>stream</arg> into an &xist; tree.
975    For the arguments <arg>base</arg> and <arg>sysid</arg> see the method
976    <pyref class="Parser" method="parse"><method>parse</method></pyref>
977    in the <class>Parser</class> class. You can pass any other argument that the
978    <pyref class="Parser" method="__init__"><class>Parser</class> constructor</pyref>
979    takes as keyword arguments via <arg>parserargs</arg>.
980    """
981    parser = Parser(**parserargs)
982    return parser.parse(stream, base, sysid)
983
984
985def parseString(text, base=None, sysid=None, **parserargs):
986    """
987    Parse the string <arg>text</arg> (<class>str</class> or <class>unicode</class>) into an
988    &xist; tree. For the arguments <arg>base</arg> and <arg>sysid</arg> see the method
989    <pyref class="Parser" method="parseString"><method>parseString</method></pyref>
990    in the <class>Parser</class> class. You can pass any other argument that the
991    <pyref class="Parser" method="__init__"><class>Parser</class> constructor</pyref>
992    takes as keyword arguments via <arg>parserargs</arg>.
993    """
994    parser = Parser(**parserargs)
995    return parser.parseString(text, base, sysid)
996
997
998def parseURL(url, base=None, sysid=None, headers=None, data=None, **parserargs):
999    """
1000    Parse &xml; input from the &url; <arg>name</arg> which might be a string
1001    or an <pyref module="ll.url" class="URL"><class>URL</class></pyref> object
1002    into an &xist; tree. For the arguments <arg>base</arg>, <arg>sysid</arg>,
1003    <arg>headers</arg> and <arg>data</arg> see the method
1004    <pyref class="Parser" method="parseURL"><method>parseURL</method></pyref>
1005    in the <class>Parser</class> class. You can pass any other argument that the
1006    <pyref class="Parser" method="__init__"><class>Parser</class> constructor</pyref>
1007    takes as keyword arguments via <arg>parserargs</arg>.
1008    """
1009    parser = Parser(**parserargs)
1010    return parser.parseURL(url, base, sysid, headers=headers, data=data)
1011
1012
1013def parseFile(filename, base=None, sysid=None, **parserargs):
1014    """
1015    Parse &xml; input from the file named <arg>filename</arg>. For the arguments
1016    <arg>base</arg> and <arg>sysid</arg> see the method
1017    <pyref class="Parser" method="parseFile"><method>parseFile</method></pyref>
1018    in the <class>Parser</class> class. You can pass any other argument that the
1019    <pyref class="Parser" method="__init__"><class>Parser</class> constructor</pyref>
1020    takes as keyword arguments via <arg>parserargs</arg>.
1021    """
1022    parser = Parser(**parserargs)
1023    return parser.parseFile(filename, base, sysid)
Note: See TracBrowser for help on using the browser.