Package pyxb :: Package binding :: Module content
[hide private]
[frames] | no frames]

Source Code for Module pyxb.binding.content

   1  # -*- coding: utf-8 -*- 
   2  # Copyright 2009-2012, 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  """Helper classes that maintain the content model of XMLSchema in the binding 
  17  classes. 
  18   
  19  L{AttributeUse} and L{ElementDeclaration} record information associated with a binding 
  20  class, for example the types of values, the original XML QName or NCName, and 
  21  the Python field in which the values are stored.  They also provide the 
  22  low-level interface to set and get the corresponding values in a binding 
  23  instance. 
  24   
  25  L{Wildcard} holds content-related information used in the content model. 
  26  """ 
  27   
  28  import pyxb 
  29  import pyxb.namespace 
  30  import pyxb.utils.fac 
  31  from pyxb.binding import basis 
  32   
  33  import xml.dom 
  34  import logging 
  35   
  36  _log = logging.getLogger(__name__) 
  37   
38 -class AttributeUse (pyxb.cscRoot):
39 """A helper class that encapsulates everything we need to know 40 about the way an attribute is used within a binding class. 41 42 Attributes are stored internally as pairs C{(provided, value)}, where 43 C{provided} is a boolean indicating whether a value for the attribute was 44 provided externally, and C{value} is an instance of the attribute 45 datatype. The C{provided} flag is used to determine whether an XML 46 attribute should be added to a created DOM node when generating the XML 47 corresponding to a binding instance. 48 """ 49 50 __name = None 51 """ExpandedName of the attribute""" 52 53 __id = None 54 """Identifier used for this attribute within the owning class""" 55 56 __key = None 57 """Private Python attribute used in instances to hold the attribute value""" 58 59 __dataType = None 60 """The L{pyxb.binding.basis.simpleTypeDefinition} for values of the attribute""" 61 62 __unicodeDefault = None 63 """The default attribute value as a unicode string, or C{None}""" 64 65 __defaultValue = None 66 """The default value as an instance of L{__dataType}, or C{None}""" 67 68 __fixed = False 69 """C{True} if the attribute value cannot be changed""" 70 71 __required = False 72 """C{True} if the attribute must appear in every instance of the type""" 73 74 __prohibited = False 75 """C{True} if the attribute must not appear in any instance of the type""" 76
77 - def __init__ (self, name, id, key, data_type, unicode_default=None, fixed=False, required=False, prohibited=False):
78 """Create an AttributeUse instance. 79 80 @param name: The name by which the attribute is referenced in the XML 81 @type name: L{pyxb.namespace.ExpandedName} 82 83 @param id: The Python identifier for the attribute within the 84 containing L{pyxb.basis.binding.complexTypeDefinition}. This is a 85 public identifier, derived from the local part of the attribute name 86 and modified to be unique, and is usually used as the name of the 87 attribute's inspector method. 88 @type id: C{str} 89 90 @param key: The string used to store the attribute 91 value in the dictionary of the containing 92 L{pyxb.basis.binding.complexTypeDefinition}. This is mangled so 93 that it is unique among and is treated as a Python private member. 94 @type key: C{str} 95 96 @param data_type: The class reference to the subclass of 97 L{pyxb.binding.basis.simpleTypeDefinition} of which the attribute 98 values must be instances. 99 @type data_type: C{type} 100 101 @keyword unicode_default: The default value of the attribute as 102 specified in the schema, or None if there is no default attribute 103 value. The default value (of the keyword) is C{None}. 104 @type unicode_default: C{unicode} 105 106 @keyword fixed: If C{True}, indicates that the attribute, if present, 107 must have the value that was given via C{unicode_default}. The 108 default value is C{False}. 109 @type fixed: C{bool} 110 111 @keyword required: If C{True}, indicates that the attribute must appear 112 in the DOM node used to create an instance of the corresponding 113 L{pyxb.binding.basis.complexTypeDefinition}. The default value is 114 C{False}. No more that one of L{required} and L{prohibited} should be 115 assigned C{True}. 116 @type required: C{bool} 117 118 @keyword prohibited: If C{True}, indicates that the attribute must 119 B{not} appear in the DOM node used to create an instance of the 120 corresponding L{pyxb.binding.basis.complexTypeDefinition}. The 121 default value is C{False}. No more that one of L{required} and 122 L{prohibited} should be assigned C{True}. 123 @type prohibited: C{bool} 124 125 @raise pyxb.SimpleTypeValueError: the L{unicode_default} cannot be used 126 to initialize an instance of L{data_type} 127 """ 128 129 self.__name = name 130 self.__id = id 131 self.__key = key 132 self.__dataType = data_type 133 self.__unicodeDefault = unicode_default 134 if self.__unicodeDefault is not None: 135 self.__defaultValue = self.__dataType.Factory(self.__unicodeDefault, _from_xml=True) 136 self.__fixed = fixed 137 self.__required = required 138 self.__prohibited = prohibited 139 super(AttributeUse, self).__init__()
140
141 - def name (self):
142 """The expanded name of the element. 143 144 @rtype: L{pyxb.namespace.ExpandedName} 145 """ 146 return self.__name
147
148 - def defaultValue (self):
149 """The default value of the attribute.""" 150 return self.__defaultValue
151
152 - def fixed (self):
153 """C{True} iff the value of the attribute cannot be changed.""" 154 return self.__fixed
155
156 - def required (self):
157 """C{True} iff the attribute must be assigned a value.""" 158 return self.__required
159
160 - def prohibited (self):
161 """C{True} iff the attribute must not be assigned a value.""" 162 return self.__prohibited
163
164 - def provided (self, ctd_instance):
165 """C{True} iff the given instance has been explicitly given a value 166 for the attribute. 167 168 This is used for things like only generating an XML attribute 169 assignment when a value was originally given (even if that value 170 happens to be the default). 171 """ 172 return self.__getProvided(ctd_instance)
173
174 - def id (self):
175 """Tag used within Python code for the attribute. 176 177 This is not used directly in the default code generation template.""" 178 return self.__id
179
180 - def key (self):
181 """String used as key within object dictionary when storing attribute value.""" 182 return self.__key
183
184 - def dataType (self):
185 """The subclass of L{pyxb.binding.basis.simpleTypeDefinition} of which any attribute value must be an instance.""" 186 return self.__dataType
187
188 - def __getValue (self, ctd_instance):
189 """Retrieve the value information for this attribute in a binding instance. 190 191 @param ctd_instance: The instance object from which the attribute is to be retrieved. 192 @type ctd_instance: subclass of L{pyxb.binding.basis.complexTypeDefinition} 193 @return: C{(provided, value)} where C{provided} is a C{bool} and 194 C{value} is C{None} or an instance of the attribute's datatype. 195 196 """ 197 return getattr(ctd_instance, self.__key, (False, None))
198
199 - def __getProvided (self, ctd_instance):
200 return self.__getValue(ctd_instance)[0]
201
202 - def value (self, ctd_instance):
203 """Get the value of the attribute from the instance.""" 204 if self.__prohibited: 205 raise pyxb.ProhibitedAttributeError(type(ctd_instance), self.__name, ctd_instance) 206 return self.__getValue(ctd_instance)[1]
207
208 - def __setValue (self, ctd_instance, new_value, provided):
209 return setattr(ctd_instance, self.__key, (provided, new_value))
210
211 - def reset (self, ctd_instance):
212 """Set the value of the attribute in the given instance to be its 213 default value, and mark that it has not been provided.""" 214 self.__setValue(ctd_instance, self.__defaultValue, False)
215
216 - def addDOMAttribute (self, dom_support, ctd_instance, element):
217 """If this attribute as been set, add the corresponding attribute to the DOM element.""" 218 ( provided, value ) = self.__getValue(ctd_instance) 219 if provided: 220 assert value is not None 221 dom_support.addAttribute(element, self.__name, value.xsdLiteral()) 222 return self
223
224 - def validate (self, ctd_instance):
225 """Validate the instance against the requirements imposed by this 226 attribute use. 227 228 There is no return value; calls raise an exception if the content does 229 not validate. 230 231 @param ctd_instance : An instance of a complex type definition. 232 233 @raise pyxb.ProhibitedAttributeError: when instance has attribute but must not 234 @raise pyxb.MissingAttributeError: when instance lacks attribute but 235 must have it (including when a required fixed-value attribute is 236 missing). 237 @raise pyxb.BatchContentValidationError: when instance has attribute but its value is not acceptable 238 """ 239 (provided, value) = self.__getValue(ctd_instance) 240 if value is not None: 241 if self.__prohibited: 242 raise pyxb.ProhibitedAttributeError(type(ctd_instance), self.__name, ctd_instance) 243 if self.__required and not provided: 244 assert self.__fixed 245 raise pyxb.MissingAttributeError(type(ctd_instance), self.__name, ctd_instance) 246 self.__dataType._CheckValidValue(value) 247 self.__dataType.XsdConstraintsOK(value) 248 else: 249 if self.__required: 250 raise pyxb.MissingAttributeError(type(ctd_instance), self.__name, ctd_instance)
251
252 - def set (self, ctd_instance, new_value):
253 """Set the value of the attribute. 254 255 This validates the value against the data type, creating a new instance if necessary. 256 257 @param ctd_instance: The binding instance for which the attribute 258 value is to be set 259 @type ctd_instance: subclass of L{pyxb.binding.basis.complexTypeDefinition} 260 @param new_value: The value for the attribute 261 @type new_value: Any value that is permitted as the input parameter to 262 the C{Factory} method of the attribute's datatype. 263 """ 264 provided = True 265 from_xml = False 266 assert not isinstance(new_value, xml.dom.Node) 267 if new_value is None: 268 if self.__required: 269 raise pyxb.MissingAttributeError(type(ctd_instance), self.__name, ctd_instance) 270 provided = False 271 if self.__prohibited: 272 raise pyxb.ProhibitedAttributeError(type(ctd_instance), self.__name, ctd_instance) 273 if (new_value is not None) and (not isinstance(new_value, self.__dataType)): 274 new_value = self.__dataType.Factory(new_value, _from_xml=from_xml) 275 if self.__fixed and (new_value != self.__defaultValue): 276 raise pyxb.AttributeChangeError(type(ctd_instance), self.__name, ctd_instance) 277 self.__setValue(ctd_instance, new_value, provided) 278 return new_value
279
280 - def _description (self, name_only=False, user_documentation=True):
281 if name_only: 282 return str(self.__name) 283 assert issubclass(self.__dataType, basis._TypeBinding_mixin) 284 desc = [ str(self.__id), ': ', str(self.__name), ' (', self.__dataType._description(name_only=True, user_documentation=False), '), ' ] 285 if self.__required: 286 desc.append('required') 287 elif self.__prohibited: 288 desc.append('prohibited') 289 else: 290 desc.append('optional') 291 if self.__defaultValue is not None: 292 desc.append(', ') 293 if self.__fixed: 294 desc.append('fixed') 295 else: 296 desc.append('default') 297 desc.extend(['=', self.__unicodeDefault ]) 298 return ''.join(desc)
299
300 -class AutomatonConfiguration (object):
301 """State for a L{pyxb.utils.fac.Automaton} monitoring content for an 302 incrementally constructed complex type binding instance. 303 304 @warning: This is not an implementation of 305 L{pyxb.utils.fac.Configuration_ABC} because we need the L{step} function 306 to return a different type of value.""" 307 308 # The binding instance for which content is being built 309 __instance = None 310 311 # The underlying configuration when the state is deterministic. In this 312 # case, all updates to the instance content corresponding to the current 313 # state have been applied to the instance. Note that while steps are 314 # occurring this instance, as well as those in __multi, might be 315 # references to sub-automata. 316 __cfg = None 317 318 # A list of pairs when the state is non-deterministic. The first member 319 # of the pair is the configuration; the second is a tuple of closures that 320 # must be applied to the instance in order to store the content that was 321 # accepted along the path to that configuration. This is in order of 322 # preference based on the location of path candidate declarations in the 323 # defining schema. 324 __multi = None 325
326 - def __init__ (self, instance):
327 self.__instance = instance
328
329 - def reset (self):
330 """Reset the automaton to its initial state. 331 332 Subsequent transitions are expected based on candidate content to be 333 supplied through the L{step} method.""" 334 self.__cfg = self.__instance._Automaton.newConfiguration() 335 self.__multi = None
336
337 - def nondeterminismCount (self):
338 """Return the number of pending configurations. 339 340 The automaton is deterministic if exactly one configuration is 341 available.""" 342 if self.__cfg is not None: 343 assert self.__multi is None 344 return 1 345 return len(self.__multi)
346
347 - def step (self, value, element_decl):
348 """Attempt a transition from the current state. 349 350 @param value: the content to be supplied. For success the value must 351 be consistent with the recorded symbol (element or wildcard 352 declaration) for a transition from the current automaton state. 353 354 @param element_decl: optional 355 L{pyxb.binding.content.ElementDeclaration} that is the preferred 356 symbol for the transition. 357 358 @return: the cardinal number of successful transitions from the 359 current configuration based on the parameters.""" 360 361 sym = (value, element_decl) 362 363 # Start with the current configuration(s), assuming we might see 364 # non-determinism. 365 new_multi = [] 366 if self.__multi is None: 367 multi = [ (self.__cfg, ()) ] 368 else: 369 multi = self.__multi[:] 370 # Collect the complete set of reachable configurations along with the 371 # closures that will update the instance content based on the path. 372 for (cfg, pending) in multi: 373 cand = cfg.candidateTransitions(sym) 374 for transition in cand: 375 clone_map = {} 376 ccfg = cfg.clone(clone_map) 377 new_multi.append( (transition.apply(ccfg, clone_map), pending+(transition.consumedSymbol().consumingClosure(sym),)) ) 378 rv = len(new_multi) 379 if 0 == rv: 380 # No candidate transitions. Do not change the state. 381 return 0 382 if 1 == rv: 383 # Deterministic transition. Save the configuration and apply the 384 # corresponding updates. 385 self.__multi = None 386 (self.__cfg, actions) = new_multi[0] 387 for fn in actions: 388 fn(self.__instance) 389 else: 390 # Non-deterministic. Save everything for subsequent resolution. 391 self.__cfg = None 392 self.__multi = new_multi 393 return rv
394
395 - def resolveNondeterminism (self, prefer_accepting=True):
396 """Resolve any non-determinism in the automaton state. 397 398 If the automaton has reached a single configuration (was 399 deterministic), this does nothing. 400 401 If multiple candidate configurations are available, the best one is 402 selected and applied, updating the binding instance with the pending 403 content. 404 405 "Best" in this case is determined by optionally eliminating 406 configurations that are not accepting, then selecting the path where 407 the initial transition sorts highest using the binding sort key (based 408 on position in the original schema). 409 410 @keyword prefer_accepting: eliminate non-accepting paths if any 411 accepting path is present.""" 412 if self.__multi is None: 413 return 414 assert self.__cfg is None 415 multi = self.__multi 416 if prefer_accepting: 417 multi = filter(lambda _ts: _ts[0].isAccepting(), self.__multi) 418 if 0 == len(multi): 419 multi = self.__multi 420 # step() will not create an empty multi list, so cannot get here with 421 # no configurations available unless nobody even reset the 422 # configuration, which would be a usage error. 423 assert 0 < len(multi) 424 if 1 < len(multi): 425 desc = self.__instance._ExpandedName 426 if desc is None: 427 desc = type(self.__instance) 428 _log.warning('Multiple accepting paths for %s', desc) 429 ''' 430 for (cfg, actions) in multi: 431 foo = type(self.__instance)() 432 for fn in actions: 433 fn(foo) 434 print '1: %s ; 2 : %s ; wc: %s' % (foo.first, foo.second, foo.wildcardElements()) 435 ''' 436 (self.__cfg, actions) = multi[0] 437 self.__multi = None 438 for fn in actions: 439 fn(self.__instance)
440
441 - def acceptableContent (self):
442 """Return the sequence of acceptable symbols at this state. 443 444 The list comprises the L{pyxb.binding.content.ElementUse} and 445 L{pyxb.binding.content.WildcardUse} instances that are used to 446 validate proposed symbols, in preferred order.""" 447 rv = [] 448 seen = set() 449 multi = self.__multi 450 if multi is None: 451 multi = [ self.__cfg] 452 for cfg in multi: 453 for u in cfg.acceptableSymbols(): 454 if not (u in seen): 455 rv.append(u) 456 seen.add(u) 457 return rv
458
459 - def isAccepting (self):
460 """Return C{True} iff the automaton is in an accepting state. 461 462 If the automaton has unresolved nondeterminism, it is resolved first, 463 preferring accepting states.""" 464 self.resolveNondeterminism(True) 465 cfg = self.__cfg 466 while cfg.superConfiguration is not None: 467 cfg = cfg.superConfiguration 468 return cfg.isAccepting()
469
470 - def sequencedChildren (self):
471 """Implement L{pyxb.binding.basis.complexTypeDefinition._validatedChildren}. 472 473 Go there for the interface. 474 """ 475 476 # We need a fresh automaton configuration corresponding to the type of 477 # the binding instance. 478 self.reset() 479 cfg = self.__cfg 480 481 # The validated sequence 482 symbols = [] 483 484 # The available content, in a map from ElementDeclaration to in-order 485 # values. The key None corresponds to the wildcard content. Keys are 486 # removed when their corresponding content is exhausted. 487 symbol_set = self.__instance._symbolSet() 488 while symbol_set: 489 # Find the first acceptable transition without filtering on any 490 # symbols 491 selected_xit = None 492 for xit in cfg.candidateTransitions(None): 493 csym = xit.consumedSymbol() 494 if isinstance(csym, ElementUse): 495 ed = csym.elementDeclaration() 496 elif isinstance(csym, WildcardUse): 497 ed = None 498 else: 499 assert False 500 # Check whether we have content that matches the symbol 501 matches = symbol_set.get(ed) 502 if matches is None: 503 continue 504 if not csym.match((matches[0], ed)): 505 continue 506 # Commit to this transition and consume the selected content 507 value = matches.pop(0) 508 symbols.append( (ed, csym.matchValue( (value, ed) )) ) 509 selected_xit = xit 510 if 0 == len(matches): 511 del symbol_set[ed] 512 break 513 if selected_xit is None: 514 break 515 cfg = selected_xit.apply(cfg) 516 # Exit out of any sub-configurations (they might be accepting while 517 # the superConfiguration is not) 518 while cfg.isAccepting() and (cfg.superConfiguration is not None): 519 cfg = cfg.superConfiguration 520 if not cfg.isAccepting(): 521 _log.warning('Incomplete content, expect %s' % (' or '.join(map(str, cfg.acceptableSymbols())))) 522 raise pyxb.IncompleteElementContentError(self.__instance, cfg, symbols, symbol_set) 523 if symbol_set: 524 raise pyxb.UnprocessedElementContentError(self.__instance, cfg, symbols, symbol_set) 525 return symbols
526
527 -class _FACSymbol (pyxb.utils.fac.SymbolMatch_mixin):
528 """Base class for L{pyxb.utils.fac.Symbol} instances associated with PyXB content models. 529 530 This holds the location in the schema of the L{ElementUse} or 531 L{WildcardUse} and documents the methods expected of its children.""" 532 533 __xsdLocation = None 534
535 - def xsdLocation (self):
536 return self.__xsdLocation
537
538 - def matchValue (self, sym):
539 """Return the value accepted by L{match} for this symbol. 540 541 A match for an element declaration might have resulted in a type 542 change for the value (converting it to an acceptable type). There is 543 no safe place to cache the compatible value calculated in the match 544 while other candidates are being considered, so we need to 545 re-calculate it if the transition is taken. 546 547 If the match could not have changed the value, the value from the 548 symbol may be returned immediately.""" 549 raise NotImplementedError('%s._matchValue' % (type(self).__name__,))
550
551 - def consumingClosure (self, sym):
552 """Create a closure that will apply the value from C{sym} to a to-be-supplied instance. 553 554 This is necessary for non-deterministic automata, where we can't store 555 the value into the instance field until we know that the transition 556 will be taken: 557 558 @return: A closure that takes a L{complexTypeDefinition} instance and 559 stores the value from invoking L{matchValue} on C{sym} into the 560 appropriate slot.""" 561 raise NotImplementedError('%s._consumingClosure' % (type(self).__name__,))
562
563 - def __init__ (self, xsd_location):
564 """@param xsd_location: the L{location<pyxb.utils.utility.Location>} of the element use or wildcard declaration.""" 565 self.__xsdLocation = xsd_location 566 super(_FACSymbol, self).__init__()
567
568 -class ElementUse (_FACSymbol):
569 """Information about a schema element declaration reference. 570 571 This is used by the FAC content model to identify the location 572 within a schema at which an element use appears. The L{ElementDeclaration} 573 is not sufficient since multiple uses in various schema, possibly in 574 different namespaces, may refer to the same declaration but be independent 575 uses. 576 """ 577 578 __elementDeclaration = None 579
580 - def elementDeclaration (self):
581 """Return the L{element declaration<pyxb.binding.content.ElementDeclaration>} associated with the use.""" 582 return self.__elementDeclaration
583
584 - def elementBinding (self):
585 """Return the L{element binding<pyxb.binding.content.ElementDeclaration.elementBinding>} associated with the use. 586 587 Equivalent to L{elementDeclaration}().L{elementBinding()<pyxb.binding.content.ElementDeclaration.elementBinding>}.""" 588 return self.__elementDeclaration.elementBinding()
589
590 - def typeDefinition (self):
591 """Return the element type. 592 593 Equivalent to L{elementDeclaration}().L{elementBinding()<pyxb.binding.content.ElementDeclaration.elementBinding>}.L{typeDefinition()<pyxb.binding.basis.element.typeDefinition>}.""" 594 return self.__elementDeclaration.elementBinding().typeDefinition()
595
596 - def __init__ (self, element_declaration, xsd_location):
597 super(ElementUse, self).__init__(xsd_location) 598 self.__elementDeclaration = element_declaration
599
600 - def matchValue (self, sym):
601 (value, element_decl) = sym 602 (rv, value) = self.__elementDeclaration._matches(value, element_decl) 603 assert rv 604 return value
605
606 - def consumingClosure (self, sym):
607 # Defer the potentially-expensive re-invocation of matchValue until 608 # the closure is applied. 609 return lambda _inst,_eu=self,_sy=sym: _eu.__elementDeclaration.setOrAppend(_inst, _eu.matchValue(_sy))
610
611 - def match (self, symbol):
612 """Satisfy L{pyxb.utils.fac.SymbolMatch_mixin}. 613 614 Determine whether the proposed content encapsulated in C{symbol} is 615 compatible with the element declaration. If so, the accepted value is 616 cached internally and return C{True}; otherwise return C{False}. 617 618 @param symbol: a pair C{(value, element_decl)}. 619 L{pyxb.binding.content.ElementDeclaration._matches} is used to 620 determine whether the proposed content is compatible with this element 621 declaration.""" 622 (value, element_decl) = symbol 623 # NB: this call may change value to be compatible. Unfortunately, we 624 # can't reliably cache that converted value, so just ignore it and 625 # we'll recompute it if the candidate transition is taken. 626 (rv, value) = self.__elementDeclaration._matches(value, element_decl) 627 return rv
628
629 - def __str__ (self):
630 return '%s per %s' % (self.__elementDeclaration.name(), self.xsdLocation())
631
632 -class WildcardUse (_FACSymbol):
633 """Information about a schema wildcard element. 634 635 This is functionally parallel to L{ElementUse}, but references a 636 L{Wildcard} that is unique to this instance. That L{Wildcard} is not 637 incorporated into this class is an artifact of the evolution of PyXB.""" 638 639 __wildcardDeclaration = None 640
641 - def wildcardDeclaration (self):
642 return self.__wildcardDeclaration
643
644 - def matchValue (self, sym):
645 (value, element_decl) = sym 646 return value
647
648 - def consumingClosure (self, sym):
649 """Create a closure that will apply the value accepted by L{match} to a to-be-supplied instance.""" 650 return lambda _inst,_av=self.matchValue(sym): _inst._appendWildcardElement(_av)
651
652 - def match (self, symbol):
653 """Satisfy L{pyxb.utils.fac.SymbolMatch_mixin}. 654 655 Determine whether the proposed content encapsulated in C{symbol} is 656 compatible with the wildcard declaration. If so, the accepted value 657 is cached internally and return C{True}; otherwise return C{False}. 658 659 @param symbol: a pair C{(value, element_decl)}. 660 L{pyxb.binding.content.Wildcard.matches} is used to determine whether 661 the proposed content is compatible with this wildcard. 662 """ 663 (value, element_decl) = symbol 664 return self.__wildcardDeclaration.matches(None, value)
665
666 - def __init__ (self, wildcard_declaration, xsd_location):
667 super(WildcardUse, self).__init__(xsd_location) 668 self.__wildcardDeclaration = wildcard_declaration
669
670 - def __str__ (self):
671 return 'xs:any per %s' % (self.xsdLocation(),)
672 673
674 -class ElementDeclaration (object):
675 """Aggregate the information relevant to an element of a complex type. 676 677 This includes the L{original tag name<name>}, the spelling of L{the 678 corresponding object in Python <id>}, an L{indicator<isPlural>} of whether 679 multiple instances might be associated with the field, and other relevant 680 information. 681 """ 682
683 - def xsdLocation (self):
684 """The L{location<pyxb.utils.utility.Location>} in the schema where the 685 element was declared. 686 687 Note that this is not necessarily the same location as its use.""" 688 return self.__xsdLocation
689 __xsdLocation = None 690
691 - def name (self):
692 """The expanded name of the element. 693 694 @rtype: L{pyxb.namespace.ExpandedName} 695 """ 696 return self.__name
697 __name = None 698
699 - def id (self):
700 """The string name of the binding class field used to hold the element 701 values. 702 703 This is the user-visible name, and excepting disambiguation will be 704 equal to the local name of the element.""" 705 return self.__id
706 __id = None 707 708 # The dictionary key used to identify the value of the element. The value 709 # is the same as that used for private member variables in the binding 710 # class within which the element declaration occurred. 711 __key = None 712
713 - def elementBinding (self):
714 """The L{basis.element} instance identifying the information 715 associated with the element declaration. 716 """ 717 return self.__elementBinding
718 - def _setElementBinding (self, element_binding):
719 # Set the element binding for this use. Only visible at all because 720 # we have to define the uses before the element instances have been 721 # created. 722 self.__elementBinding = element_binding 723 return self
724 __elementBinding = None 725
726 - def isPlural (self):
727 """True iff the content model indicates that more than one element 728 can legitimately belong to this use. 729 730 This includes elements in particles with maxOccurs greater than one, 731 and when multiple elements with the same NCName are declared in the 732 same type. 733 """ 734 return self.__isPlural
735 __isPlural = False 736
737 - def __init__ (self, name, id, key, is_plural, location, element_binding=None):
738 """Create an ElementDeclaration instance. 739 740 @param name: The name by which the element is referenced in the XML 741 @type name: L{pyxb.namespace.ExpandedName} 742 743 @param id: The Python name for the element within the containing 744 L{pyxb.basis.binding.complexTypeDefinition}. This is a public 745 identifier, albeit modified to be unique, and is usually used as the 746 name of the element's inspector method or property. 747 @type id: C{str} 748 749 @param key: The string used to store the element 750 value in the dictionary of the containing 751 L{pyxb.basis.binding.complexTypeDefinition}. This is mangled so 752 that it is unique among and is treated as a Python private member. 753 @type key: C{str} 754 755 @param is_plural: If C{True}, documents for the corresponding type may 756 have multiple instances of this element. As a consequence, the value 757 of the element will be a list. If C{False}, the value will be C{None} 758 if the element is absent, and a reference to an instance of the type 759 identified by L{pyxb.binding.basis.element.typeDefinition} if present. 760 @type is_plural: C{bool} 761 762 @param element_binding: Reference to the class that serves as the 763 binding for the element. 764 """ 765 self.__name = name 766 self.__id = id 767 self.__key = key 768 self.__isPlural = is_plural 769 self.__elementBinding = element_binding 770 super(ElementDeclaration, self).__init__()
771
772 - def defaultValue (self):
773 """Return the default value for this element. 774 775 @todo: Right now, this returns C{None} for non-plural and an empty 776 list for plural elements. Need to support schema-specified default 777 values for simple-type content. 778 """ 779 if self.isPlural(): 780 return [] 781 return None
782
783 - def value (self, ctd_instance):
784 """Return the value for this use within the given instance.""" 785 return getattr(ctd_instance, self.__key, self.defaultValue())
786
787 - def reset (self, ctd_instance):
788 """Set the value for this use in the given element to its default.""" 789 setattr(ctd_instance, self.__key, self.defaultValue()) 790 return self
791
792 - def set (self, ctd_instance, value):
793 """Set the value of this element in the given instance.""" 794 if value is None: 795 return self.reset(ctd_instance) 796 if ctd_instance._isNil(): 797 raise pyxb.ContentInNilInstanceError(ctd_instance, value) 798 assert self.__elementBinding is not None 799 if basis._TypeBinding_mixin._PerformValidation: 800 value = self.__elementBinding.compatibleValue(value, is_plural=self.isPlural()) 801 setattr(ctd_instance, self.__key, value) 802 ctd_instance._addContent(value, self.__elementBinding) 803 return self
804
805 - def setOrAppend (self, ctd_instance, value):
806 """Invoke either L{set} or L{append}, depending on whether the element 807 use is plural.""" 808 if self.isPlural(): 809 return self.append(ctd_instance, value) 810 return self.set(ctd_instance, value)
811
812 - def append (self, ctd_instance, value):
813 """Add the given value as another instance of this element within the binding instance. 814 @raise pyxb.StructuralBadDocumentError: invoked on an element use that is not plural 815 """ 816 if ctd_instance._isNil(): 817 raise pyxb.ContentInNilInstanceError(ctd_instance, value) 818 if not self.isPlural(): 819 raise pyxb.StructuralBadDocumentError('Cannot append to element with non-plural multiplicity') 820 values = self.value(ctd_instance) 821 if basis._TypeBinding_mixin._PerformValidation: 822 value = self.__elementBinding.compatibleValue(value) 823 values.append(value) 824 ctd_instance._addContent(value, self.__elementBinding) 825 return values
826
827 - def toDOM (self, dom_support, parent, value):
828 """Convert the given value to DOM as an instance of this element. 829 830 @param dom_support: Helper for managing DOM properties 831 @type dom_support: L{pyxb.utils.domutils.BindingDOMSupport} 832 @param parent: The DOM node within which this element should be generated. 833 @type parent: C{xml.dom.Element} 834 @param value: The content for this element. May be text (if the 835 element allows mixed content), or an instance of 836 L{basis._TypeBinding_mixin}. 837 838 @raise pyxb.AbstractElementError: the binding to be used is abstract 839 """ 840 if isinstance(value, basis._TypeBinding_mixin): 841 element_binding = self.__elementBinding 842 if value._substitutesFor(element_binding): 843 element_binding = value._element() 844 assert element_binding is not None 845 if element_binding.abstract(): 846 raise pyxb.AbstractElementError(self, value) 847 element = dom_support.createChildElement(element_binding.name(), parent) 848 elt_type = element_binding.typeDefinition() 849 val_type = type(value) 850 if isinstance(value, basis.complexTypeDefinition): 851 assert isinstance(value, elt_type) 852 else: 853 if isinstance(value, basis.STD_union) and isinstance(value, elt_type._MemberTypes): 854 val_type = elt_type 855 if dom_support.requireXSIType() or elt_type._RequireXSIType(val_type): 856 val_type_qname = value._ExpandedName.localName() 857 tns_prefix = dom_support.namespacePrefix(value._ExpandedName.namespace()) 858 if tns_prefix is not None: 859 val_type_qname = '%s:%s' % (tns_prefix, val_type_qname) 860 dom_support.addAttribute(element, pyxb.namespace.XMLSchema_instance.createExpandedName('type'), val_type_qname) 861 value._toDOM_csc(dom_support, element) 862 elif isinstance(value, (str, unicode)): 863 element = dom_support.createChildElement(self.name(), parent) 864 element.appendChild(dom_support.document().createTextNode(value)) 865 else: 866 raise pyxb.LogicError('toDOM with unrecognized value type %s: %s' % (type(value), value))
867
868 - def _description (self, name_only=False, user_documentation=True):
869 if name_only: 870 return str(self.__name) 871 desc = [ str(self.__id), ': '] 872 if self.isPlural(): 873 desc.append('MULTIPLE ') 874 desc.append(self.elementBinding()._description(user_documentation=user_documentation)) 875 return ''.join(desc)
876
877 - def _matches (self, value, element_decl):
878 accept = False 879 if element_decl == self: 880 accept = True 881 elif element_decl is not None: 882 # If there's a known element, and it's not this one, the content 883 # does not match. This assumes we handled xsi:type and 884 # substitution groups earlier, which may be true. 885 accept = False 886 elif isinstance(value, xml.dom.Node): 887 # If we haven't been able to identify an element for this before, 888 # then we don't recognize it, and will have to treat it as a 889 # wildcard. 890 accept = False 891 else: 892 # A foreign value which might be usable if we can convert 893 # it to a compatible value trivially. 894 try: 895 value = self.__elementBinding.compatibleValue(value, _convert_string_values=False) 896 accept = True 897 except (pyxb.ValidationError, pyxb.BindingError): 898 pass 899 return (accept, value)
900
901 - def __str__ (self):
902 return 'ED.%s@%x' % (self.__name, id(self))
903 904
905 -class Wildcard (object):
906 """Placeholder for wildcard objects.""" 907 908 NC_any = '##any' #<<< The namespace constraint "##any" 909 NC_not = '##other' #<<< A flag indicating constraint "##other" 910 NC_targetNamespace = '##targetNamespace' #<<< A flag identifying the target namespace 911 NC_local = '##local' #<<< A flag indicating the namespace must be absent 912 913 __namespaceConstraint = None
914 - def namespaceConstraint (self):
915 """A constraint on the namespace for the wildcard. 916 917 Valid values are: 918 919 - L{Wildcard.NC_any} 920 - A tuple ( L{Wildcard.NC_not}, a L{namespace<pyxb.namespace.Namespace>} instance ) 921 - set(of L{namespace<pyxb.namespace.Namespace>} instances) 922 923 Namespaces are represented by their URIs. Absence is 924 represented by C{None}, both in the "not" pair and in the set. 925 """ 926 return self.__namespaceConstraint
927 928 PC_skip = 'skip' 929 """No namespace constraint is applied to the wildcard.""" 930 931 PC_lax = 'lax' 932 """Validate against available uniquely determined declaration.""" 933 934 PC_strict = 'strict' 935 """Validate against declaration or xsi:type, which must be available.""" 936 937 __processContents = None 938 """One of L{PC_skip}, L{PC_lax}, L{PC_strict}."""
939 - def processContents (self):
940 """Indicate how this wildcard's contents should be processed.""" 941 return self.__processContents
942
943 - def __normalizeNamespace (self, nsv):
944 if nsv is None: 945 return None 946 if isinstance(nsv, basestring): 947 nsv = pyxb.namespace.NamespaceForURI(nsv, create_if_missing=True) 948 assert isinstance(nsv, pyxb.namespace.Namespace), 'unexpected non-namespace %s' % (nsv,) 949 return nsv
950
951 - def __init__ (self, *args, **kw):
952 """ 953 @keyword namespace_constraint: Required namespace constraint(s) 954 @keyword process_contents: Required""" 955 956 # Namespace constraint and process contents are required parameters. 957 nsc = kw['namespace_constraint'] 958 if isinstance(nsc, tuple): 959 nsc = (nsc[0], self.__normalizeNamespace(nsc[1])) 960 elif isinstance(nsc, set): 961 nsc = set([ self.__normalizeNamespace(_uri) for _uri in nsc ]) 962 self.__namespaceConstraint = nsc 963 self.__processContents = kw['process_contents'] 964 super(Wildcard, self).__init__()
965
966 - def matches (self, instance, value):
967 """Return True iff the value is a valid match against this wildcard. 968 969 Validation per U{Wildcard allows Namespace Name<http://www.w3.org/TR/xmlschema-1/#cvc-wildcard-namespace>}. 970 """ 971 972 ns = None 973 if isinstance(value, xml.dom.Node): 974 if value.namespaceURI is not None: 975 ns = pyxb.namespace.NamespaceForURI(value.namespaceURI) 976 elif isinstance(value, basis._TypeBinding_mixin): 977 elt = value._element() 978 if elt is not None: 979 ns = elt.name().namespace() 980 else: 981 ns = value._ExpandedName.namespace() 982 else: 983 # Assume that somebody will handle the conversion to xs:anyType 984 pass 985 if isinstance(ns, pyxb.namespace.Namespace) and ns.isAbsentNamespace(): 986 ns = None 987 if self.NC_any == self.__namespaceConstraint: 988 return True 989 if isinstance(self.__namespaceConstraint, tuple): 990 (_, constrained_ns) = self.__namespaceConstraint 991 assert self.NC_not == _ 992 if ns is None: 993 return False 994 if constrained_ns == ns: 995 return False 996 return True 997 return ns in self.__namespaceConstraint
998 999 ## Local Variables: 1000 ## fill-column:78 1001 ## End: 1002