1 """Implementation of the DOM Level 3 'LS-Load' feature."""
6 from xml
.dom
.minicompat
import *
8 from xml
.dom
.NodeFilter
import NodeFilter
11 __all__
= ["DOMBuilder", "DOMEntityResolver", "DOMInputSource"]
15 """Features object that has variables set for each DOMBuilder feature.
17 The DOMBuilder class uses an instance of this class to pass settings to
18 the ExpatBuilder class.
21 # Note that the DOMBuilder class in LoadSave constrains which of these
22 # values can be set using the DOM Level 3 LoadSave feature.
25 namespace_declarations
= True
27 external_parameter_entities
= True
28 external_general_entities
= True
29 external_dtd_subset
= True
30 validate_if_schema
= False
32 datatype_normalization
= False
33 create_entity_ref_nodes
= True
35 whitespace_in_element_content
= True
38 charset_overrides_xml_encoding
= True
40 supported_mediatypes_only
= False
52 ACTION_APPEND_AS_CHILDREN
= 2
53 ACTION_INSERT_AFTER
= 3
54 ACTION_INSERT_BEFORE
= 4
56 _legal_actions
= (ACTION_REPLACE
, ACTION_APPEND_AS_CHILDREN
,
57 ACTION_INSERT_AFTER
, ACTION_INSERT_BEFORE
)
60 self
._options
= Options()
62 def _get_entityResolver(self
):
63 return self
.entityResolver
64 def _set_entityResolver(self
, entityResolver
):
65 self
.entityResolver
= entityResolver
67 def _get_errorHandler(self
):
68 return self
.errorHandler
69 def _set_errorHandler(self
, errorHandler
):
70 self
.errorHandler
= errorHandler
72 def _get_filter(self
):
74 def _set_filter(self
, filter):
77 def setFeature(self
, name
, state
):
78 if self
.supportsFeature(name
):
79 state
= state
and 1 or 0
81 settings
= self
._settings
[(_name_xform(name
), state
)]
83 raise xml
.dom
.NotSupportedErr(
84 "unsupported feature: " + `name`
)
86 for name
, value
in settings
:
87 setattr(self
._options
, name
, value
)
89 raise xml
.dom
.NotFoundErr("unknown feature: " + repr(name
))
91 def supportsFeature(self
, name
):
92 return hasattr(self
._options
, _name_xform(name
))
94 def canSetFeature(self
, name
, state
):
95 key
= (_name_xform(name
), state
and 1 or 0)
96 return self
._settings
.has_key(key
)
98 # This dictionary maps from (feature,value) to a list of
99 # (option,value) pairs that should be set on the Options object.
100 # If a (feature,value) setting is not in this dictionary, it is
101 # not supported by the DOMBuilder.
104 ("namespace_declarations", 0): [
105 ("namespace_declarations", 0)],
106 ("namespace_declarations", 1): [
107 ("namespace_declarations", 1)],
110 ("external_general_entities", 0): [
111 ("external_general_entities", 0)],
112 ("external_general_entities", 1): [
113 ("external_general_entities", 1)],
114 ("external_parameter_entities", 0): [
115 ("external_parameter_entities", 0)],
116 ("external_parameter_entities", 1): [
117 ("external_parameter_entities", 1)],
118 ("validate_if_schema", 0): [
119 ("validate_if_schema", 0)],
120 ("create_entity_ref_nodes", 0): [
121 ("create_entity_ref_nodes", 0)],
122 ("create_entity_ref_nodes", 1): [
123 ("create_entity_ref_nodes", 1)],
125 ("create_entity_ref_nodes", 0),
129 ("whitespace_in_element_content", 0): [
130 ("whitespace_in_element_content", 0)],
131 ("whitespace_in_element_content", 1): [
132 ("whitespace_in_element_content", 1)],
133 ("cdata_sections", 0): [
134 ("cdata_sections", 0)],
135 ("cdata_sections", 1): [
136 ("cdata_sections", 1)],
141 ("charset_overrides_xml_encoding", 0): [
142 ("charset_overrides_xml_encoding", 0)],
143 ("charset_overrides_xml_encoding", 1): [
144 ("charset_overrides_xml_encoding", 1)],
147 ("namespace_declarations", 0),
148 ("validate_if_schema", 0),
149 ("create_entity_ref_nodes", 0),
151 ("cdata_sections", 0),
152 ("datatype_normalization", 1),
153 ("whitespace_in_element_content", 1),
155 ("charset_overrides_xml_encoding", 1)],
156 ("supported_mediatypes_only", 0): [
157 ("supported_mediatypes_only", 0)],
164 def getFeature(self
, name
):
165 xname
= _name_xform(name
)
167 return getattr(self
._options
, xname
)
168 except AttributeError:
169 if name
== "infoset":
170 options
= self
._options
171 return (options
.datatype_normalization
172 and options
.whitespace_in_element_content
174 and options
.charset_overrides_xml_encoding
175 and not (options
.namespace_declarations
176 or options
.validate_if_schema
177 or options
.create_entity_ref_nodes
179 or options
.cdata_sections
))
180 raise xml
.dom
.NotFoundErr("feature %s not known" % repr(name
))
182 def parseURI(self
, uri
):
183 if self
.entityResolver
:
184 input = self
.entityResolver
.resolveEntity(None, uri
)
186 input = DOMEntityResolver().resolveEntity(None, uri
)
187 return self
.parse(input)
189 def parse(self
, input):
190 options
= copy
.copy(self
._options
)
191 options
.filter = self
.filter
192 options
.errorHandler
= self
.errorHandler
193 fp
= input.byteStream
194 if fp
is None and options
.systemId
:
196 fp
= urllib2
.urlopen(input.systemId
)
197 return self
._parse
_bytestream
(fp
, options
)
199 def parseWithContext(self
, input, cnode
, action
):
200 if action
not in self
._legal
_actions
:
201 raise ValueError("not a legal action")
202 raise NotImplementedError("Haven't written this yet...")
204 def _parse_bytestream(self
, stream
, options
):
205 import xml
.dom
.expatbuilder
206 builder
= xml
.dom
.expatbuilder
.makeBuilder(options
)
207 return builder
.parseFile(stream
)
210 def _name_xform(name
):
211 return name
.lower().replace('-', '_')
214 class DOMEntityResolver(NewStyle
):
215 __slots__
= '_opener',
217 def resolveEntity(self
, publicId
, systemId
):
218 assert systemId
is not None
219 source
= DOMInputSource()
220 source
.publicId
= publicId
221 source
.systemId
= systemId
222 source
.byteStream
= self
._get
_opener
().open(systemId
)
224 # determine the encoding if the transport provided it
225 source
.encoding
= self
._guess
_media
_encoding
(source
)
227 # determine the base URI is we can
228 import posixpath
, urlparse
229 parts
= urlparse
.urlparse(systemId
)
230 scheme
, netloc
, path
, params
, query
, fragment
= parts
231 # XXX should we check the scheme here as well?
232 if path
and not path
.endswith("/"):
233 path
= posixpath
.dirname(path
) + "/"
234 parts
= scheme
, netloc
, path
, params
, query
, fragment
235 source
.baseURI
= urlparse
.urlunparse(parts
)
239 def _get_opener(self
):
242 except AttributeError:
243 self
._opener
= self
._create
_opener
()
246 def _create_opener(self
):
248 return urllib2
.build_opener()
250 def _guess_media_encoding(self
, source
):
251 info
= source
.byteStream
.info()
252 if info
.has_key("Content-Type"):
253 for param
in info
.getplist():
254 if param
.startswith("charset="):
255 return param
.split("=", 1)[1].lower()
258 class DOMInputSource(NewStyle
):
259 __slots__
= ('byteStream', 'characterStream', 'stringData',
260 'encoding', 'publicId', 'systemId', 'baseURI')
263 self
.byteStream
= None
264 self
.characterStream
= None
265 self
.stringData
= None
271 def _get_byteStream(self
):
272 return self
.byteStream
273 def _set_byteStream(self
, byteStream
):
274 self
.byteStream
= byteStream
276 def _get_characterStream(self
):
277 return self
.characterStream
278 def _set_characterStream(self
, characterStream
):
279 self
.characterStream
= characterStream
281 def _get_stringData(self
):
282 return self
.stringData
283 def _set_stringData(self
, data
):
284 self
.stringData
= data
286 def _get_encoding(self
):
288 def _set_encoding(self
, encoding
):
289 self
.encoding
= encoding
291 def _get_publicId(self
):
293 def _set_publicId(self
, publicId
):
294 self
.publicId
= publicId
296 def _get_systemId(self
):
298 def _set_systemId(self
, systemId
):
299 self
.systemId
= systemId
301 def _get_baseURI(self
):
303 def _set_baseURI(self
, uri
):
307 class DOMBuilderFilter
:
308 """Element filter which can be used to tailor construction of
312 # There's really no need for this class; concrete implementations
313 # should just implement the endElement() and startElement()
314 # methods as appropriate. Using this makes it easy to only
315 # implement one of them.
322 whatToShow
= NodeFilter
.SHOW_ALL
324 def _get_whatToShow(self
):
325 return self
.whatToShow
327 def acceptNode(self
, element
):
328 return self
.FILTER_ACCEPT
330 def startContainer(self
, element
):
331 return self
.FILTER_ACCEPT
337 """Mixin to create documents that conform to the load/save spec."""
341 def _get_async(self
):
343 def _set_async(self
, async):
345 raise xml
.dom
.NotSupportedErr(
346 "asynchronous document loading is not supported")
349 # What does it mean to "clear" a document? Does the
350 # documentElement disappear?
351 raise NotImplementedError(
352 "haven't figured out what this means yet")
355 raise NotImplementedError("haven't written this yet")
357 def loadXML(self
, source
):
358 raise NotImplementedError("haven't written this yet")
360 def saveXML(self
, snode
):
363 elif snode
.ownerDocument
is not self
:
364 raise xml
.dom
.WrongDocumentErr()
368 class DOMImplementationLS
:
370 MODE_ASYNCHRONOUS
= 2
372 def createDOMBuilder(self
, mode
, schemaType
):
373 if schemaType
is not None:
374 raise xml
.dom
.NotSupportedErr(
375 "schemaType not yet supported")
376 if mode
== self
.MODE_SYNCHRONOUS
:
378 if mode
== self
.MODE_ASYNCHRONOUS
:
379 raise xml
.dom
.NotSupportedErr(
380 "asynchronous builders are not supported")
381 raise ValueError("unknown value for mode")
383 def createDOMWriter(self
):
384 raise NotImplementedError(
385 "the writer interface hasn't been written yet!")
387 def createDOMInputSource(self
):
388 return DOMInputSource()