Home | Trees | Indices | Help |
|
---|
|
1 # -*- coding: utf-8 -*- 2 # Copyright 2009-2013, Peter A. Bigot 3 # 4 # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 # not use this file except in compliance with the License. You may obtain a 6 # copy of the License at: 7 # 8 # http://www.apache.org/licenses/LICENSE-2.0 9 # 10 # Unless required by applicable law or agreed to in writing, software 11 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 # License for the specific language governing permissions and limitations 14 # under the License. 15 16 """This module contains support for generating bindings from an XML stream 17 using a SAX parser.""" 18 19 import xml.dom 20 import pyxb.namespace 21 import pyxb.utils.saxutils 22 import pyxb.utils.saxdom 23 import pyxb.utils.utility 24 from pyxb.binding import basis 25 from pyxb.namespace.builtin import XMLSchema_instance as XSI 26 import logging 27 28 _log = logging.getLogger(__name__) 2931 """State required to generate bindings for a specific element. 32 33 If the document being parsed includes references to unrecognized elements, 34 a DOM instance of the element and its content is created and treated as a 35 wildcard element. 36 """ 37 38 # An expanded name corresponding to xsi:nil 39 __XSINilTuple = XSI.nil.uriTuple() 40 41 # The binding instance being created for this element. When the 42 # element type has simple content, the binding instance cannot be 43 # created until the end of the element has been reached and the 44 # content of the element has been processed accumulated for use in 45 # the instance constructor. When the element type has complex 46 # content, the binding instance must be created at the start of 47 # the element, so contained elements can be properly stored. 48 __bindingInstance = None 49 50 # The schema binding for the element being constructed. 51 __elementBinding = None 5223454 """Record the binding to be used for this element. 55 56 Generally ignored, except at the top level this is the only way to 57 associate a binding instance created from an xsi:type description with 58 a specific element.""" 59 self.__elementBinding = element_binding60 61 # The nearest enclosing complex type definition63 """The nearest enclosing complex type definition, as used for 64 resolving local element/attribute names. 65 66 @return: An instance of L{basis.complexTypeDefinition}, or C{None} if 67 the element is top-level 68 """ 69 return self.__enclosingCTD70 __enclosingCTD = None 71 72 # The factory that is called to create a binding instance for this 73 # element; None if the binding instance was created at the start 74 # of the element. 75 __delayedConstructor = None 76 77 # An xml.sax.xmlreader.Attributes instance providing the 78 # attributes for the element. 79 __attributes = None 80 81 # An xml.dom.Node corresponding to the (sub-)document 82 __domDocument = None 83 84 __domDepth = None 8587 super(_SAXElementState, self).__init__(**kw) 88 self.__bindingInstance = None 89 parent_state = self.parentState() 90 if isinstance(parent_state, _SAXElementState): 91 self.__enclosingCTD = parent_state.enclosingCTD() 92 self.__domDocument = parent_state.__domDocument 93 if self.__domDocument is not None: 94 self.__domDepth = parent_state.__domDepth + 19597 """Set the enclosing complex type definition for this element. 98 99 @param enclosing_ctd: The scope for a local element. 100 @type enclosing_ctd: L{basis.complexTypeDefinition} 101 @return: C{self} 102 """ 103 self.__enclosingCTD = enclosing_ctd104 105 # Create the binding instance for this element.107 kw = { '_from_xml' : True, 108 '_location' : self.location() } 109 110 # Note whether the node is marked nil 111 if self.__XSINilTuple in attrs: 112 kw['_nil'] = pyxb.binding.datatypes.boolean(attrs.getValue(self.__XSINilTuple)) 113 114 if content is None: 115 content = [] 116 self.__bindingInstance = new_object_factory(*content, **kw) 117 if isinstance(self.__bindingInstance, pyxb.utils.utility.Locatable_mixin): 118 self.__bindingInstance._setLocation(self.location()) 119 120 # Record the namespace context so users of the binding can 121 # interpret QNames within the attributes and content. 122 self.__bindingInstance._setNamespaceContext(self.__namespaceContext) 123 124 # Set instance attributes 125 # NB: attrs implements the SAX AttributesNS interface, meaning 126 # that names are pairs of (namespaceURI, localName), just like we 127 # want them to be. 128 for attr_name in self.__attributes.getNames(): 129 attr_en = pyxb.namespace.ExpandedName(attr_name) 130 # Ignore xmlns and xsi attributes; we've already handled those 131 if attr_en.namespace() in ( pyxb.namespace.XMLNamespaces, XSI ): 132 continue 133 # The binding instance may be a simple type that does not support 134 # attributes; the following raises an exception in that case. 135 self.__bindingInstance._setAttribute(attr_en, attrs.getValue(attr_name)) 136 137 return self.__bindingInstance138140 return self.__domDocument is not None141143 """Actions upon first encountering an element for which we cannot create a binding. 144 145 Invoking this transitions the parser into DOM mode, creating a new DOM 146 document that will represent this element including its content.""" 147 assert not self.__domDocument 148 self.__domDocument = pyxb.utils.saxdom.Document(namespace_context=self.namespaceContext()) 149 self.__domDepth = 0 150 return self.startDOMElement(attrs)151153 """Actions upon entering an element that is part of a DOM subtree.""" 154 self.__domDepth += 1 155 self.__attributes = pyxb.utils.saxdom.NamedNodeMap() 156 ns_ctx = self.namespaceContext() 157 for name in attrs.getNames(): 158 attr_en = pyxb.namespace.ExpandedName(name) 159 self.__attributes._addItem(pyxb.utils.saxdom.Attr(expanded_name=attr_en, namespace_context=ns_ctx, value=attrs.getValue(name), location=self.location()))160162 """Actions upon leaving an element that is part of a DOM subtree.""" 163 ns_ctx = self.namespaceContext() 164 element = pyxb.utils.saxdom.Element(namespace_context=ns_ctx, expanded_name=self.expandedName(), attributes=self.__attributes, location=self.location()) 165 for info in self.content(): 166 if isinstance(info.item, xml.dom.Node): 167 element.appendChild(info.item) 168 else: 169 element.appendChild(pyxb.utils.saxdom.Text(info.item, namespace_context=ns_ctx)) 170 self.__domDepth -= 1 171 if 0 == self.__domDepth: 172 self.__domDocument.appendChild(element) 173 #pyxb.utils.saxdom._DumpDOM(self.__domDocument) 174 self.__domDepth = None 175 self.__domDocument = None 176 parent_state = self.parentState() 177 parent_state.addElementContent(self.location(), element, None) 178 return element179181 """Actions upon entering an element that will produce a binding instance. 182 183 The element use is recorded. If the type is a subclass of 184 L{basis.simpleTypeDefinition}, a delayed constructor is recorded so 185 the binding instance can be created upon completion of the element; 186 otherwise, a binding instance is created and stored. The attributes 187 are used to initialize the binding instance (now, or upon element 188 end). 189 190 @param type_class: The Python type of the binding instance 191 @type type_class: subclass of L{basis._TypeBinding_mixin} 192 @param new_object_factory: A callable object that creates an instance of the C{type_class} 193 @param element_decl: The element use with which the binding instance is associated. Will be C{None} for top-level elements 194 @type element_decl: L{basis.element} 195 @param attrs: The XML attributes associated with the element 196 @type attrs: C{xml.sax.xmlreader.Attributes} 197 @return: The generated binding instance, or C{None} if creation is delayed 198 """ 199 self.__delayedConstructor = None 200 self.__elementDecl = element_decl 201 self.__attributes = attrs 202 if type_class._IsSimpleTypeContent(): 203 self.__delayedConstructor = new_object_factory 204 else: 205 self.__constructElement(new_object_factory, attrs) 206 return self.__bindingInstance207209 """Perform any end-of-element processing. 210 211 For simple type instances, this creates the binding instance. 212 @return: The generated binding instance 213 """ 214 if self.__delayedConstructor is not None: 215 args = [] 216 for info in self.content(): 217 if info.maybe_element or (info.element_decl is not None): 218 raise pyxb.NonElementValidationError(info.item, info.location) 219 args.append(info.item) 220 self.__constructElement(self.__delayedConstructor, self.__attributes, args) 221 else: 222 for info in self.content(): 223 self.__bindingInstance.append(info.item, 224 _element_decl=info.element_decl, 225 _maybe_element=info.maybe_element, 226 _location=info.location) 227 parent_state = self.parentState() 228 if parent_state is not None: 229 parent_state.addElementContent(self.location(), self.__bindingInstance, self.__elementDecl) 230 # As CreateFromDOM does, validate the resulting element 231 if self.__bindingInstance._element() is None: 232 self.__bindingInstance._setElement(self.__elementBinding) 233 return self.__bindingInstance._postDOMValidate()236 """A SAX handler class which generates a binding instance for a document 237 through a streaming parser. 238 239 An example of using this to parse the document held in the (unicode) text 240 value C{xmlt} is:: 241 242 import pyxb.binding.saxer 243 import io 244 245 saxer = pyxb.binding.saxer.make_parser() 246 handler = saxer.getContentHandler() 247 saxer.parse(io.StringIO(xmlt)) 248 instance = handler.rootObject() 249 250 """ 251 252 # Whether invocation of handler methods should be traced 253 __trace = False 254 255 # An expanded name corresponding to xsi:type 256 __XSITypeTuple = XSI.type.uriTuple() 257 258 __domHandler = None 259 __domDepth = None 260391262 """Return the binding object corresponding to the top-most 263 element in the document 264 265 @return: An instance of L{basis._TypeBinding_mixin} (most usually a 266 L{basis.complexTypeDefinition}. 267 268 @raise pyxb.[UnrecognizedDOMRootNodeError: No binding could be found to 269 match the top-level element in the document.""" 270 if not isinstance(self.__rootObject, basis._TypeBinding_mixin): 271 # Happens if the top-level element got processed as a DOM instance. 272 assert isinstance(self.__rootObject, xml.dom.Node) 273 raise pyxb.UnrecognizedDOMRootNodeError(self.__rootObject) 274 return self.__rootObject._postDOMValidate()275 __rootObject = None 276278 """Reset the state of the handler in preparation for processing a new 279 document. 280 281 @return: C{self} 282 """ 283 super(PyXBSAXHandler, self).reset() 284 self.__rootObject = None 285 return self286288 """Create a parser instance for converting XML to bindings. 289 290 @keyword element_state_constructor: Overridden with the value 291 L{_SAXElementState} before invoking the L{superclass 292 constructor<pyxb.utils.saxutils.BaseSAXHandler.__init__>}. 293 """ 294 295 kw.setdefault('element_state_constructor', _SAXElementState) 296 super(PyXBSAXHandler, self).__init__(**kw) 297 self.reset()298300 (this_state, parent_state, ns_ctx, name_en) = super(PyXBSAXHandler, self).startElementNS(name, qname, attrs) 301 302 # Delegate processing if in DOM mode 303 if this_state.inDOMMode(): 304 return this_state.startDOMElement(attrs) 305 306 # Resolve the element within the appropriate context. Note 307 # that global elements have no use, only the binding. 308 if parent_state.enclosingCTD() is not None: 309 (element_binding, element_decl) = parent_state.enclosingCTD()._ElementBindingDeclForName(name_en) 310 else: 311 element_decl = None 312 element_binding = name_en.elementBinding() 313 this_state.setElementBinding(element_binding) 314 315 # Non-root elements should have an element use, from which we can 316 # extract the binding if we couldn't find one elsewhere. (Keep any 317 # current binding, since it may be a member of a substitution group.) 318 if (element_decl is not None) and (element_binding is None): 319 assert self.__rootObject is not None 320 element_binding = element_decl.elementBinding() 321 assert element_binding is not None 322 323 # Start knowing nothing 324 type_class = None 325 if element_binding is not None: 326 element_binding = element_binding.elementForName(name) 327 type_class = element_binding.typeDefinition() 328 329 # Process an xsi:type attribute, if present 330 if self.__XSITypeTuple in attrs: 331 (did_replace, type_class) = XSI._InterpretTypeAttribute(attrs.getValue(self.__XSITypeTuple), ns_ctx, self.fallbackNamespace(), type_class) 332 if did_replace: 333 element_binding = None 334 335 if type_class is None: 336 # Bother. We don't know what this thing is. But that's not an 337 # error, if the schema accepts wildcards. For consistency with 338 # the DOM-based interface, we need to build a DOM node. 339 return this_state.enterDOMMode(attrs) 340 341 if element_binding is not None: 342 # Invoke binding __call__ method not Factory, so can check for 343 # abstract elements. 344 new_object_factory = element_binding 345 else: 346 new_object_factory = type_class.Factory 347 348 # Update the enclosing complex type definition for this 349 # element state. 350 assert type_class is not None 351 if issubclass(type_class, pyxb.binding.basis.complexTypeDefinition): 352 this_state.setEnclosingCTD(type_class) 353 else: 354 this_state.setEnclosingCTD(parent_state.enclosingCTD()) 355 356 # Process the element start. This may or may not return a 357 # binding object. 358 binding_object = this_state.startBindingElement(type_class, new_object_factory, element_decl, attrs) 359 360 # If the top-level element has complex content, this sets the 361 # root object. If it has simple content, see endElementNS. 362 if self.__rootObject is None: 363 self.__rootObject = binding_object364366 this_state = super(PyXBSAXHandler, self).endElementNS(name, qname) 367 if this_state.inDOMMode(): 368 # Delegate processing if in DOM mode. Note that completing this 369 # element may take us out of DOM mode. In any case, the returned 370 # binding object is a DOM element instance. 371 binding_object = this_state.endDOMElement() 372 else: 373 # Process the element end. This will return a binding object, 374 # either the one created at the start or the one created at 375 # the end. 376 binding_object = this_state.endBindingElement() 377 assert binding_object is not None 378 379 # If we don't have a root object, save it. No, there is not a 380 # problem doing this on the close of the element. If the 381 # top-level element has complex content, the object was 382 # created on start, and the root object has been assigned. If 383 # it has simple content, then there are no internal elements 384 # that could slip in and set this before we get to it here. 385 # 386 # Unless we're still in DOM mode, in which case this isn't really the 387 # root object. Then the real root object will be the one that caused 388 # us to enter DOM mode. 389 if (self.__rootObject is None) and not this_state.inDOMMode(): 390 self.__rootObject = binding_object393 """Extend L{pyxb.utils.saxutils.make_parser} to change the default 394 C{content_handler_constructor} to be L{PyXBSAXHandler}. 395 """ 396 kw.setdefault('content_handler_constructor', PyXBSAXHandler) 397 return pyxb.utils.saxutils.make_parser(*args, **kw)398 399 ## Local Variables: 400 ## fill-column:78 401 ## End: 402
Home | Trees | Indices | Help |
|
---|
Generated by Epydoc 3.0.1 on Wed Sep 18 10:35:40 2013 | http://epydoc.sourceforge.net |