_make_boundary(): Fix for SF bug #745478, broken boundary calculation
[python/dscho.git] / Lib / xml / dom / xmlbuilder.py
blobfd43c37ed3f87bf2ed49cbc13bde7a602c3034df
1 """Implementation of the DOM Level 3 'LS-Load' feature."""
3 import copy
4 import xml.dom
6 from xml.dom.minicompat import *
8 from xml.dom.NodeFilter import NodeFilter
11 __all__ = ["DOMBuilder", "DOMEntityResolver", "DOMInputSource"]
14 class Options:
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.
19 """
21 # Note that the DOMBuilder class in LoadSave constrains which of these
22 # values can be set using the DOM Level 3 LoadSave feature.
24 namespaces = 1
25 namespace_declarations = True
26 validation = False
27 external_parameter_entities = True
28 external_general_entities = True
29 external_dtd_subset = True
30 validate_if_schema = False
31 validate = False
32 datatype_normalization = False
33 create_entity_ref_nodes = True
34 entities = True
35 whitespace_in_element_content = True
36 cdata_sections = True
37 comments = True
38 charset_overrides_xml_encoding = True
39 infoset = False
40 supported_mediatypes_only = False
42 errorHandler = None
43 filter = None
46 class DOMBuilder:
47 entityResolver = None
48 errorHandler = None
49 filter = None
51 ACTION_REPLACE = 1
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)
59 def __init__(self):
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):
73 return self.filter
74 def _set_filter(self, filter):
75 self.filter = filter
77 def setFeature(self, name, state):
78 if self.supportsFeature(name):
79 state = state and 1 or 0
80 try:
81 settings = self._settings[(_name_xform(name), state)]
82 except KeyError:
83 raise xml.dom.NotSupportedErr(
84 "unsupported feature: " + `name`)
85 else:
86 for name, value in settings:
87 setattr(self._options, name, value)
88 else:
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.
103 _settings = {
104 ("namespace_declarations", 0): [
105 ("namespace_declarations", 0)],
106 ("namespace_declarations", 1): [
107 ("namespace_declarations", 1)],
108 ("validation", 0): [
109 ("validation", 0)],
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)],
124 ("entities", 0): [
125 ("create_entity_ref_nodes", 0),
126 ("entities", 0)],
127 ("entities", 1): [
128 ("entities", 1)],
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)],
137 ("comments", 0): [
138 ("comments", 0)],
139 ("comments", 1): [
140 ("comments", 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)],
145 ("infoset", 0): [],
146 ("infoset", 1): [
147 ("namespace_declarations", 0),
148 ("validate_if_schema", 0),
149 ("create_entity_ref_nodes", 0),
150 ("entities", 0),
151 ("cdata_sections", 0),
152 ("datatype_normalization", 1),
153 ("whitespace_in_element_content", 1),
154 ("comments", 1),
155 ("charset_overrides_xml_encoding", 1)],
156 ("supported_mediatypes_only", 0): [
157 ("supported_mediatypes_only", 0)],
158 ("namespaces", 0): [
159 ("namespaces", 0)],
160 ("namespaces", 1): [
161 ("namespaces", 1)],
164 def getFeature(self, name):
165 xname = _name_xform(name)
166 try:
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
173 and options.comments
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
178 or options.entities
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)
185 else:
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:
195 import urllib2
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)
237 return source
239 def _get_opener(self):
240 try:
241 return self._opener
242 except AttributeError:
243 self._opener = self._create_opener()
244 return self._opener
246 def _create_opener(self):
247 import urllib2
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')
262 def __init__(self):
263 self.byteStream = None
264 self.characterStream = None
265 self.stringData = None
266 self.encoding = None
267 self.publicId = None
268 self.systemId = None
269 self.baseURI = 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):
287 return self.encoding
288 def _set_encoding(self, encoding):
289 self.encoding = encoding
291 def _get_publicId(self):
292 return self.publicId
293 def _set_publicId(self, publicId):
294 self.publicId = publicId
296 def _get_systemId(self):
297 return self.systemId
298 def _set_systemId(self, systemId):
299 self.systemId = systemId
301 def _get_baseURI(self):
302 return self.baseURI
303 def _set_baseURI(self, uri):
304 self.baseURI = uri
307 class DOMBuilderFilter:
308 """Element filter which can be used to tailor construction of
309 a DOM instance.
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.
317 FILTER_ACCEPT = 1
318 FILTER_REJECT = 2
319 FILTER_SKIP = 3
320 FILTER_INTERRUPT = 4
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
333 del NodeFilter
336 class DocumentLS:
337 """Mixin to create documents that conform to the load/save spec."""
339 async = False
341 def _get_async(self):
342 return False
343 def _set_async(self, async):
344 if async:
345 raise xml.dom.NotSupportedErr(
346 "asynchronous document loading is not supported")
348 def abort(self):
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")
354 def load(self, uri):
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):
361 if snode is None:
362 snode = self
363 elif snode.ownerDocument is not self:
364 raise xml.dom.WrongDocumentErr()
365 return snode.toxml()
368 class DOMImplementationLS:
369 MODE_SYNCHRONOUS = 1
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:
377 return DOMBuilder()
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()