1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 """Helper classes that maintain the content model of XMLSchema in the binding
17 classes.
18
19 L{AttributeUse} and L{ElementUse} 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 from pyxb.binding import basis
31
32 import xml.dom
33 import logging
34
35 _log = logging.getLogger(__name__)
36
37 -class ContentState_mixin (pyxb.cscRoot):
38 """Declares methods used by classes that hold state while validating a
39 content model component."""
40
41 - def accepts (self, particle_state, instance, value, element_use):
42 """Determine whether the provided value can be added to the instance
43 without violating state validation.
44
45 This method must not throw any non-catastrophic exceptions; general
46 failures should be transformed to a C{False} return value.
47
48 @param particle_state: The L{ParticleState} instance serving as the
49 parent to this state. The implementation must inform that state when
50 the proposed value completes the content model.
51
52 @param instance: An instance of a subclass of
53 L{basis.complexTypeDefinition}, into which the provided value will be
54 stored if it is consistent with the current model state.
55
56 @param value: The value that is being validated against the state.
57
58 @param element_use: An optional L{ElementUse} instance that specifies
59 the element to which the value corresponds. This will be available
60 when the value is extracted by parsing a document, but will be absent
61 if the value was passed as a constructor positional parameter.
62
63 @return: C{True} if the value was successfully matched against the
64 state. C{False} if the value did not match against the state."""
65 raise Exception('ContentState_mixin.accepts not implemented in %s' % (type(self),))
66
67 - def notifyFailure (self, sub_state, particle_ok):
68 """Invoked by a sub-state to indicate that validation cannot proceed
69 in the current state.
70
71 Normally this is used when an intermediate content model must reset
72 itself to permit alternative models to be evaluated.
73
74 @param sub_state: the state that was unable to accept a value
75
76 @param particle_ok: C{True} if the particle that rejected the value is
77 in an accepting terminal state
78
79 """
80 raise Exception('ContentState_mixin.notifyFailure not implemented in %s' % (type(self),))
81
82 - def _verifyComplete (self, parent_particle_state):
83 """Determine whether the deep state is complete without further elements.
84
85 No-op for non-aggregate state. For aggregate state, all contained
86 particles should be checked to see whether the overall model can be
87 satisfied if no additional elements are provided.
88
89 This method does not have a meaningful return value; violations of the
90 content model should produce the corresponding exception (generally,
91 L{MissingContentError}).
92
93 @param parent_particle_state: the L{ParticleState} for which this state
94 is the term.
95 """
96 pass
97
98 -class ContentModel_mixin (pyxb.cscRoot):
99 """Declares methods used by classes representing content model components."""
100
101 - def newState (self, parent_particle_state):
102 """Return a L{ContentState_mixin} instance that will validate the
103 state of this model component.
104
105 @param parent_particle_state: The L{ParticleState} instance for which
106 this instance is a term. C{None} for the top content model of a
107 complex data type.
108 """
109 raise Exception('ContentModel_mixin.newState not implemented in %s' % (type(self),))
110
111 - def _validateCloneSymbolSet (self, symbol_set_im):
112 """Create a mutable copy of the symbol set.
113
114 The top-level map is copied, as are the lists of values to which the
115 symbols map. The values within the lists are unchanged, as validation
116 does not affect them."""
117 rv = symbol_set_im.copy()
118 for (k, v) in rv.items():
119 rv[k] = v[:]
120 return rv
121
122 - def _validateCloneOutputSequence (self, output_sequence_im):
123 """Create a mutable copy of the output sequence."""
124 return output_sequence_im[:]
125
126 - def _validateReplaceResults (self, symbol_set_out, symbol_set_new, output_sequence_out, output_sequence_new):
127 """In-place update of symbol set and output sequence structures.
128
129 Use this to copy from temporary mutable structures updated by local
130 validation into the structures that were passed in once the validation
131 has succeeded."""
132 symbol_set_out.clear()
133 symbol_set_out.update(symbol_set_new)
134 output_sequence_out[:] = output_sequence_new
135
136 - def _validate (self, symbol_set, output_sequence):
137 """Determine whether an output sequence created from the symbols can
138 be made consistent with the model.
139
140 The symbol set represents letters in an alphabet; the output sequence
141 orders those letters in a way that satisfies the regular expression
142 expressed in the model. Both are changed as a result of a successful
143 validation; both remain unchanged if the validation failed. In
144 recursing, implementers may assume that C{output_sequence} is
145 monotonic: its length remains unchanged after an invocation iff the
146 symbol set also remains unchanged. The L{_validateCloneSymbolSet},
147 L{_validateCloneOutputSequence}, and L{_validateReplaceResults}
148 methods are available to help preserve this behavior.
149
150 @param symbol_set: A map from L{ElementUse} instances to a list of
151 values. The order of the values corresponds to the order in which
152 they should appear. A key of C{None} identifies values that are
153 stored as wildcard elements. Values are removed from the lists as
154 they are used; when the last value of a list is removed, its key is
155 removed from the map. Thus an empty dictionary is the indicator that
156 no more symbols are available.
157
158 @param output_sequence: A mutable list to which should be appended
159 tuples C{( eu, val )} where C{eu} is an L{ElementUse} from the set of
160 symbol keys, and C{val} is a value from the corresponding list. A
161 document generated by producing the elements in the given order is
162 expected to validate.
163
164 @return: C{True} iff the model validates. C{symbol_set} and
165 C{output_path} will be unchanged if this returns C{False}.
166 """
167 raise Exception('ContentState_mixin._validate not implemented in %s' % (type(self),))
168
169
171 """A helper class that encapsulates everything we need to know
172 about the way an attribute is used within a binding class.
173
174 Attributes are stored internally as pairs C{(provided, value)}, where
175 C{provided} is a boolean indicating whether a value for the attribute was
176 provided externally, and C{value} is an instance of the attribute
177 datatype. The C{provided} flag is used to determine whether an XML
178 attribute should be added to a created DOM node when generating the XML
179 corresponding to a binding instance.
180 """
181
182 __name = None
183 """ExpandedName of the attribute"""
184
185 __id = None
186 """Identifier used for this attribute within the owning class"""
187
188 __key = None
189 """Private Python attribute used in instances to hold the attribute value"""
190
191 __dataType = None
192 """The L{pyxb.binding.basis.simpleTypeDefinition} for values of the attribute"""
193
194 __unicodeDefault = None
195 """The default attribute value as a unicode string, or C{None}"""
196
197 __defaultValue = None
198 """The default value as an instance of L{__dataType}, or C{None}"""
199
200 __fixed = False
201 """C{True} if the attribute value cannot be changed"""
202
203 __required = False
204 """C{True} if the attribute must appear in every instance of the type"""
205
206 __prohibited = False
207 """C{True} if the attribute must not appear in any instance of the type"""
208
209 - def __init__ (self, name, id, key, data_type, unicode_default=None, fixed=False, required=False, prohibited=False):
210 """Create an AttributeUse instance.
211
212 @param name: The name by which the attribute is referenced in the XML
213 @type name: L{pyxb.namespace.ExpandedName}
214
215 @param id: The Python identifier for the attribute within the
216 containing L{pyxb.basis.binding.complexTypeDefinition}. This is a
217 public identifier, derived from the local part of the attribute name
218 and modified to be unique, and is usually used as the name of the
219 attribute's inspector method.
220 @type id: C{str}
221
222 @param key: The string used to store the attribute
223 value in the dictionary of the containing
224 L{pyxb.basis.binding.complexTypeDefinition}. This is mangled so
225 that it is unique among and is treated as a Python private member.
226 @type key: C{str}
227
228 @param data_type: The class reference to the subclass of
229 L{pyxb.binding.basis.simpleTypeDefinition} of which the attribute
230 values must be instances.
231 @type data_type: C{type}
232
233 @keyword unicode_default: The default value of the attribute as
234 specified in the schema, or None if there is no default attribute
235 value. The default value (of the keyword) is C{None}.
236 @type unicode_default: C{unicode}
237
238 @keyword fixed: If C{True}, indicates that the attribute, if present,
239 must have the value that was given via C{unicode_default}. The
240 default value is C{False}.
241 @type fixed: C{bool}
242
243 @keyword required: If C{True}, indicates that the attribute must appear
244 in the DOM node used to create an instance of the corresponding
245 L{pyxb.binding.basis.complexTypeDefinition}. The default value is
246 C{False}. No more that one of L{required} and L{prohibited} should be
247 assigned C{True}.
248 @type required: C{bool}
249
250 @keyword prohibited: If C{True}, indicates that the attribute must
251 B{not} appear in the DOM node used to create an instance of the
252 corresponding L{pyxb.binding.basis.complexTypeDefinition}. The
253 default value is C{False}. No more that one of L{required} and
254 L{prohibited} should be assigned C{True}.
255 @type prohibited: C{bool}
256
257 @raise pyxb.BadTypeValueError: the L{unicode_default} cannot be used
258 to initialize an instance of L{data_type}
259 """
260
261 self.__name = name
262 self.__id = id
263 self.__key = key
264 self.__dataType = data_type
265 self.__unicodeDefault = unicode_default
266 if self.__unicodeDefault is not None:
267 self.__defaultValue = self.__dataType.Factory(self.__unicodeDefault, _from_xml=True)
268 self.__fixed = fixed
269 self.__required = required
270 self.__prohibited = prohibited
271 super(AttributeUse, self).__init__()
272
274 """The expanded name of the element.
275
276 @rtype: L{pyxb.namespace.ExpandedName}
277 """
278 return self.__name
279
281 """The default value of the attribute."""
282 return self.__defaultValue
283
285 """C{True} iff the value of the attribute cannot be changed."""
286 return self.__fixed
287
289 """C{True} iff the attribute must be assigned a value."""
290 return self.__required
291
293 """C{True} iff the attribute must not be assigned a value."""
294 return self.__prohibited
295
297 """C{True} iff the given instance has been explicitly given a value
298 for the attribute.
299
300 This is used for things like only generating an XML attribute
301 assignment when a value was originally given (even if that value
302 happens to be the default).
303 """
304 return self.__getProvided(ctd_instance)
305
307 """Tag used within Python code for the attribute.
308
309 This is not used directly in the default code generation template."""
310 return self.__id
311
313 """String used as key within object dictionary when storing attribute value."""
314 return self.__key
315
317 """The subclass of L{pyxb.binding.basis.simpleTypeDefinition} of which any attribute value must be an instance."""
318 return self.__dataType
319
321 """Retrieve the value information for this attribute in a binding instance.
322
323 @param ctd_instance: The instance object from which the attribute is to be retrieved.
324 @type ctd_instance: subclass of L{pyxb.binding.basis.complexTypeDefinition}
325 @return: C{(provided, value)} where C{provided} is a C{bool} and
326 C{value} is C{None} or an instance of the attribute's datatype.
327
328 """
329 return getattr(ctd_instance, self.__key, (False, None))
330
333
334 - def value (self, ctd_instance):
335 """Get the value of the attribute from the instance."""
336 return self.__getValue(ctd_instance)[1]
337
338 - def __setValue (self, ctd_instance, new_value, provided):
339 return setattr(ctd_instance, self.__key, (provided, new_value))
340
341 - def reset (self, ctd_instance):
342 """Set the value of the attribute in the given instance to be its
343 default value, and mark that it has not been provided."""
344 self.__setValue(ctd_instance, self.__defaultValue, False)
345
353
382
383 - def set (self, ctd_instance, new_value):
384 """Set the value of the attribute.
385
386 This validates the value against the data type, creating a new instance if necessary.
387
388 @param ctd_instance: The binding instance for which the attribute
389 value is to be set
390 @type ctd_instance: subclass of L{pyxb.binding.basis.complexTypeDefinition}
391 @param new_value: The value for the attribute
392 @type new_value: An C{xml.dom.Node} instance, or any value that is
393 permitted as the input parameter to the C{Factory} method of the
394 attribute's datatype.
395 """
396 provided = True
397 from_xml = False
398 if isinstance(new_value, xml.dom.Node):
399 from_xml = True
400 unicode_value = self.__name.getAttribute(new_value)
401 if unicode_value is None:
402 if self.__required:
403 raise pyxb.MissingAttributeError('Required attribute %s from %s not found' % (self.__name, ctd_instance._ExpandedName or type(ctd_instance)))
404 provided = False
405 unicode_value = self.__unicodeDefault
406 if unicode_value is None:
407
408 provided = False
409 new_value = None
410 else:
411 new_value = unicode_value
412 elif new_value is None:
413 if self.__required:
414 raise pyxb.MissingAttributeError('Required attribute %s in %s may not be set to None' % (self.__name, ctd_instance._ExpandedName or type(ctd_instance)))
415 provided = False
416 if provided and self.__prohibited:
417 raise pyxb.ProhibitedAttributeError('Value given for prohibited attribute %s' % (self.__name,))
418 if (new_value is not None) and (not isinstance(new_value, self.__dataType)):
419 new_value = self.__dataType.Factory(new_value, _from_xml=from_xml)
420 if self.__fixed and (new_value != self.__defaultValue):
421 raise pyxb.AttributeChangeError('Attempt to change value of fixed attribute %s' % (self.__name,))
422 self.__setValue(ctd_instance, new_value, provided)
423 return new_value
424
425 - def _description (self, name_only=False, user_documentation=True):
426 if name_only:
427 return str(self.__name)
428 assert issubclass(self.__dataType, basis._TypeBinding_mixin)
429 desc = [ str(self.__id), ': ', str(self.__name), ' (', self.__dataType._description(name_only=True, user_documentation=False), '), ' ]
430 if self.__required:
431 desc.append('required')
432 elif self.__prohibited:
433 desc.append('prohibited')
434 else:
435 desc.append('optional')
436 if self.__defaultValue is not None:
437 desc.append(', ')
438 if self.__fixed:
439 desc.append('fixed')
440 else:
441 desc.append('default')
442 desc.extend(['=', self.__unicodeDefault ])
443 return ''.join(desc)
444
445 -class ElementUse (ContentState_mixin, ContentModel_mixin):
446 """Aggregate the information relevant to an element of a complex type.
447
448 This includes the L{original tag name<name>}, the spelling of L{the
449 corresponding object in Python <id>}, an L{indicator<isPlural>} of whether
450 multiple instances might be associated with the field, and other relevant
451 information.
452 """
453
455 """The expanded name of the element.
456
457 @rtype: L{pyxb.namespace.ExpandedName}
458 """
459 return self.__name
460 __name = None
461
463 """The string name of the binding class field used to hold the element
464 values.
465
466 This is the user-visible name, and excepting disambiguation will be
467 equal to the local name of the element."""
468 return self.__id
469 __id = None
470
471
472
473
474 __key = None
475
477 """The L{basis.element} instance identifying the information
478 associated with the element declaration.
479 """
480 return self.__elementBinding
487 __elementBinding = None
488
490 """True iff the content model indicates that more than one element
491 can legitimately belong to this use.
492
493 This includes elements in particles with maxOccurs greater than one,
494 and when multiple elements with the same NCName are declared in the
495 same type.
496 """
497 return self.__isPlural
498 __isPlural = False
499
500 - def __init__ (self, name, id, key, is_plural, element_binding=None):
501 """Create an ElementUse instance.
502
503 @param name: The name by which the element is referenced in the XML
504 @type name: L{pyxb.namespace.ExpandedName}
505
506 @param id: The Python name for the element within the containing
507 L{pyxb.basis.binding.complexTypeDefinition}. This is a public
508 identifier, albeit modified to be unique, and is usually used as the
509 name of the element's inspector method or property.
510 @type id: C{str}
511
512 @param key: The string used to store the element
513 value in the dictionary of the containing
514 L{pyxb.basis.binding.complexTypeDefinition}. This is mangled so
515 that it is unique among and is treated as a Python private member.
516 @type key: C{str}
517
518 @param is_plural: If C{True}, documents for the corresponding type may
519 have multiple instances of this element. As a consequence, the value
520 of the element will be a list. If C{False}, the value will be C{None}
521 if the element is absent, and a reference to an instance of the type
522 identified by L{pyxb.binding.basis.element.typeDefinition} if present.
523 @type is_plural: C{bool}
524
525 @param element_binding: Reference to the class that serves as the
526 binding for the element.
527 """
528 self.__name = name
529 self.__id = id
530 self.__key = key
531 self.__isPlural = is_plural
532 self.__elementBinding = element_binding
533 super(ElementUse, self).__init__()
534
536 """Return the default value for this element.
537
538 @todo: Right now, this returns C{None} for non-plural and an empty
539 list for plural elements. Need to support schema-specified default
540 values for simple-type content.
541 """
542 if self.isPlural():
543 return []
544 return None
545
546 - def value (self, ctd_instance):
547 """Return the value for this use within the given instance."""
548 return getattr(ctd_instance, self.__key, self.defaultValue())
549
550 - def reset (self, ctd_instance):
551 """Set the value for this use in the given element to its default."""
552 setattr(ctd_instance, self.__key, self.defaultValue())
553 return self
554
555 - def set (self, ctd_instance, value):
565
567 """Invoke either L{set} or L{append}, depending on whether the element
568 use is plural."""
569 if self.isPlural():
570 return self.append(ctd_instance, value)
571 return self.set(ctd_instance, value)
572
573 - def append (self, ctd_instance, value):
585
586 - def toDOM (self, dom_support, parent, value):
587 """Convert the given value to DOM as an instance of this element.
588
589 @param dom_support: Helper for managing DOM properties
590 @type dom_support: L{pyxb.utils.domutils.BindingDOMSupport}
591 @param parent: The DOM node within which this element should be generated.
592 @type parent: C{xml.dom.Element}
593 @param value: The content for this element. May be text (if the
594 element allows mixed content), or an instance of
595 L{basis._TypeBinding_mixin}.
596 """
597 if isinstance(value, basis._TypeBinding_mixin):
598 element_binding = self.__elementBinding
599 if value._substitutesFor(element_binding):
600 element_binding = value._element()
601 assert element_binding is not None
602 if element_binding.abstract():
603 raise pyxb.DOMGenerationError('Element %s is abstract but content %s not associated with substitution group member' % (self.name(), value))
604 element = dom_support.createChildElement(element_binding.name(), parent)
605 elt_type = element_binding.typeDefinition()
606 val_type = type(value)
607 if isinstance(value, basis.complexTypeDefinition):
608 assert isinstance(value, elt_type)
609 else:
610 if isinstance(value, basis.STD_union) and isinstance(value, elt_type._MemberTypes):
611 val_type = elt_type
612 if dom_support.requireXSIType() or elt_type._RequireXSIType(val_type):
613 val_type_qname = value._ExpandedName.localName()
614 tns_prefix = dom_support.namespacePrefix(value._ExpandedName.namespace())
615 if tns_prefix is not None:
616 val_type_qname = '%s:%s' % (tns_prefix, val_type_qname)
617 dom_support.addAttribute(element, pyxb.namespace.XMLSchema_instance.createExpandedName('type'), val_type_qname)
618 value._toDOM_csc(dom_support, element)
619 elif isinstance(value, (str, unicode)):
620 element = dom_support.createChildElement(self.name(), parent)
621 element.appendChild(dom_support.document().createTextNode(value))
622 else:
623 raise pyxb.LogicError('toDOM with unrecognized value type %s: %s' % (type(value), value))
624
625 - def _description (self, name_only=False, user_documentation=True):
633
634
635 - def newState (self, parent_particle_state):
636 """Implement parent class method."""
637 return self
638
639
640 - def accepts (self, particle_state, instance, value, element_use):
646
647 - def _accepts (self, instance, value, element_use):
669
670
671 - def _validate (self, symbol_set, output_sequence):
672 values = symbol_set.get(self)
673 if values is None:
674 return False
675 used = values.pop(0)
676 output_sequence.append( (self, used) )
677 if 0 == len(values):
678 del symbol_set[self]
679 return True
680
682 return 'EU.%s@%x' % (self.__name, id(self))
683
684
685 -class Wildcard (ContentState_mixin, ContentModel_mixin):
686 """Placeholder for wildcard objects."""
687
688 NC_any = '##any'
689 NC_not = '##other'
690 NC_targetNamespace = '##targetNamespace'
691 NC_local = '##local'
692
693 __namespaceConstraint = None
695 """A constraint on the namespace for the wildcard.
696
697 Valid values are:
698
699 - L{Wildcard.NC_any}
700 - A tuple ( L{Wildcard.NC_not}, a L{namespace<pyxb.namespace.Namespace>} instance )
701 - set(of L{namespace<pyxb.namespace.Namespace>} instances)
702
703 Namespaces are represented by their URIs. Absence is
704 represented by C{None}, both in the "not" pair and in the set.
705 """
706 return self.__namespaceConstraint
707
708 PC_skip = 'skip'
709 """No namespace constraint is applied to the wildcard."""
710
711 PC_lax = 'lax'
712 """Validate against available uniquely determined declaration."""
713
714 PC_strict = 'strict'
715 """Validate against declaration or xsi:type, which must be available."""
716
717 __processContents = None
718 """One of L{PC_skip}, L{PC_lax}, L{PC_strict}."""
719 - def processContents (self):
720 """Indicate how this wildcard's contents should be processed."""
721 return self.__processContents
722
730
745
746 - def matches (self, instance, value):
778
779
780 - def newState (self, parent_particle_state):
782
783
784 - def accepts (self, particle_state, instance, value, element_use):
785
786 if isinstance(value, xml.dom.Node):
787 value_desc = 'value in %s' % (value.nodeName,)
788 else:
789 value_desc = 'value of type %s' % (type(value),)
790 if not self.matches(instance, value):
791 return False
792 if not isinstance(value, basis._TypeBinding_mixin):
793 _log.info('Created unbound wildcard element from %s', value_desc,)
794 assert isinstance(instance.wildcardElements(), list), 'Uninitialized wildcard list in %s' % (instance._ExpandedName,)
795 instance._appendWildcardElement(value)
796 particle_state.incrementCount()
797 return True
798
799
800 - def _validate (self, symbol_set, output_sequence):
801
802 _log.info('Accepting node as wildcard match without validating.')
803 wc_values = symbol_set.get(None)
804 if wc_values is None:
805 return False
806 used = wc_values.pop(0)
807 output_sequence.append( (None, used) )
808 if 0 == len(wc_values):
809 del symbol_set[None]
810 return True
811
813 """Represent the state of validation against a sequence of particles."""
814
815 __sequence = None
816 __particleState = None
817 __parentParticleState = None
818
819 __index = -1
820
821 __failed = False
822 """C{True} iff the content provided is in conflict with the sequence
823 requirements.
824
825 Specifically, the model requires content that has not been provided. Set
826 within L{accepts}. This state is sticky."""
827
828 __satisfied = False
829 """C{True} iff the content provided is consistent with the sequence
830 requirements.
831
832 Specifically, nothing has been presented with conflicts with the model.
833 Set within L{notifyFailure}."""
834
835 - def __init__ (self, group, parent_particle_state):
845
846
847 - def accepts (self, particle_state, instance, value, element_use):
859
860
864
865
875
877 - def __init__ (self, group, parent_particle_state):
882
883
884 - def accepts (self, particle_state, instance, value, element_use):
885
886 if self.__activeChoice is None:
887 for choice in self.__choices:
888 try:
889 (consume, underflow_exc) = choice.step(instance, value, element_use)
890 except Exception, e:
891 consume = False
892 underflow_exc = e
893 if consume:
894 self.__activeChoice = choice
895 self.__choices = None
896 return True
897 return False
898 (consume, underflow_exc) = self.__activeChoice.step(instance, value, element_use)
899 if consume:
900 return True
901 if underflow_exc is not None:
902 self.__failed = True
903 raise underflow_exc
904 return False
905
906
919
920
925
973
974 -class ParticleState (pyxb.cscRoot):
975
976 __parentState = None
977 """The L{ContentState_mixin} which contains the mode for which this is state."""
978
979 __termState = None
980 """A L{ContentState_mixin} instance for one occurrence of this particle's term."""
981
982 __tryAccept = None
983 """A flag indicating whether a proposed value should be applied to the
984 state by L{step}."""
985
986 - def __init__ (self, particle, parent_state=None):
987 self.__particle = particle
988 self.__parentState = parent_state
989 self.__count = -1
990 super(ParticleState, self).__init__()
991 self.incrementCount()
992
993 __particle = None
994 """The L{ParticleModel} for which this represents state."""
995
996 - def particle (self):
997 """The L{ParticleModel} for which this represents state."""
998 return self.__particle
999
1000 __count = None
1001 """The number of times this particle's term has been matched."""
1002
1003 - def incrementCount (self):
1004 """Reset for a new occurrence of the particle's term."""
1005 self.__count += 1
1006 self.__termState = self.__particle.term().newState(self)
1007 self.__tryAccept = True
1008
1009 - def verifyComplete (self):
1010 """Check whether the particle's occurrence constraints are satisfied.
1011
1012 @raise pyxb.MissingContentError: Particle requires additional content to be satisfied."""
1013
1014
1015
1016
1017
1018 if not self.__particle.satisfiesOccurrences(self.__count):
1019 self.__termState._verifyComplete(self)
1020
1021
1022 if not self.__particle.satisfiesOccurrences(self.__count):
1023 raise pyxb.MissingContentError('incomplete')
1024
1025
1026 if self.__parentState is not None:
1027 self.__parentState.notifyFailure(self, True)
1028
1029 - def step (self, instance, value, element_use):
1030 """Attempt to apply the value as a new instance of the particle's term.
1031
1032 The L{ContentState_mixin} created for the particle's term is consulted
1033 to determine whether the instance can accept the given value. If so,
1034 the particle's maximum occurrence limit is checked; if not, and the
1035 particle has a parent state, it is informed of the failure.
1036
1037 @param instance: An instance of a subclass of
1038 L{basis.complexTypeDefinition}, into which the provided value will be
1039 stored if it is consistent with the current model state.
1040
1041 @param value: The value that is being validated against the state.
1042
1043 @param element_use: An optional L{ElementUse} instance that specifies
1044 the element to which the value corresponds. This will be available
1045 when the value is extracted by parsing a document, but will be absent
1046 if the value was passed as a constructor positional parameter.
1047
1048 @return: C{( consumed, underflow_exc )} A tuple where the first element
1049 is C{True} iff the provided value was accepted in the current state.
1050 When this first element is C{False}, the second element will be
1051 C{None} if the particle's occurrence requirements have been met, and
1052 is an instance of C{MissingElementError} if the observed number of
1053 terms is less than the minimum occurrence count. Depending on
1054 context, the caller may raise this exception, or may try an
1055 alternative content model.
1056
1057 @raise pyxb.UnexpectedElementError: if the value satisfies the particle,
1058 but having done so exceeded the allowable number of instances of the
1059 term.
1060 """
1061
1062
1063 consumed = False
1064 underflow_exc = None
1065
1066
1067
1068
1069 self.__tryAccept = True
1070 while self.__tryAccept and (self.__count != self.__particle.maxOccurs()):
1071 self.__tryAccept = False
1072 consumed = self.__termState.accepts(self, instance, value, element_use)
1073 self.__tryAccept = self.__tryAccept and (not consumed)
1074 if consumed:
1075 if not self.__particle.meetsMaximum(self.__count):
1076 raise pyxb.UnexpectedElementError('too many')
1077 else:
1078 if self.__parentState is not None:
1079 self.__parentState.notifyFailure(self, self.__particle.satisfiesOccurrences(self.__count))
1080 if not self.__particle.meetsMinimum(self.__count):
1081
1082
1083 underflow_exc = pyxb.MissingElementError(content=value, container=instance)
1084 return (consumed, underflow_exc)
1085
1086 - def __str__ (self):
1087 particle = self.__particle
1088 return 'ParticleState(%d:%d,%s:%s)@%x' % (self.__count, particle.minOccurs(), particle.maxOccurs(), particle.term(), id(self))
1089
1090 -class ParticleModel (ContentModel_mixin):
1091 """Content model dealing with particles: terms with occurrence restrictions"""
1092
1093 __minOccurs = None
1094 - def minOccurs (self):
1095 """The minimum number of times the term must occur.
1096
1097 This will be a non-negative integer."""
1098 return self.__minOccurs
1099
1100 __maxOccurs = None
1101 - def maxOccurs (self):
1102 """The maximum number of times the term may occur.
1103
1104 This will be a positive integer, or C{None} indicating an unbounded
1105 number of occurrences."""
1106 return self.__maxOccurs
1107
1108 __term = None
1109 """The L{ContentModel_mixin} for a single occurrence."""
1111 """The term for a single occurrence."""
1112 return self.__term
1113
1114 - def meetsMaximum (self, count):
1115 """@return: C{True} iff there is no maximum on term occurrence, or the
1116 provided count does not exceed that maximum"""
1117 return (self.__maxOccurs is None) or (count <= self.__maxOccurs)
1118
1119 - def meetsMinimum (self, count):
1120 """@return: C{True} iff the provided count meets the minimum number of
1121 occurrences"""
1122 return count >= self.__minOccurs
1123
1124 - def satisfiesOccurrences (self, count):
1125 """@return: C{True} iff the provided count satisfies the occurrence
1126 requirements"""
1127 return self.meetsMinimum(count) and self.meetsMaximum(count)
1128
1129 - def __init__ (self, term, min_occurs=1, max_occurs=1):
1130 self.__term = term
1131 self.__minOccurs = min_occurs
1132 self.__maxOccurs = max_occurs
1133 super(ParticleModel, self).__init__()
1134
1135
1136 - def newState (self):
1137 return ParticleState(self)
1138
1139 - def validate (self, symbol_set):
1140 """Determine whether the particle requirements are satisfiable by the
1141 given symbol set.
1142
1143 The symbol set represents letters in an alphabet. If those letters
1144 can be combined in a way that satisfies the regular expression
1145 expressed in the model, a satisfying sequence is returned and the
1146 symbol set is reduced by the letters used to form the sequence. If
1147 the content model cannot be satisfied, C{None} is returned and the
1148 symbol set remains unchanged.
1149
1150 @param symbol_set: A map from L{ElementUse} instances to a list of
1151 values. The order of the values corresponds to the order in which
1152 they should appear. A key of C{None} identifies values that are
1153 stored as wildcard elements. Values are removed from the lists as
1154 they are used; when the last value of a list is removed, its key is
1155 removed from the map. Thus an empty dictionary is the indicator that
1156 no more symbols are available.
1157
1158 @return: returns C{None}, or a list of tuples C{( eu, val )} where
1159 C{eu} is an L{ElementUse} from the set of symbol keys, and C{val} is a
1160 value from the corresponding list.
1161 """
1162
1163 output_sequence = []
1164 result = self._validate(symbol_set, output_sequence)
1165 if result:
1166 return (symbol_set, output_sequence)
1167 return None
1168
1169
1170 - def _validate (self, symbol_set, output_sequence):
1171 symbol_set_mut = self._validateCloneSymbolSet(symbol_set)
1172 output_sequence_mut = self._validateCloneOutputSequence(output_sequence)
1173 count = 0
1174 last_size = len(output_sequence_mut)
1175 while (count != self.__maxOccurs) and self.__term._validate(symbol_set_mut, output_sequence_mut):
1176 this_size = len(output_sequence_mut)
1177 if this_size == last_size:
1178
1179
1180 if count < self.__minOccurs:
1181 count = self.__minOccurs
1182 break
1183 count += 1
1184 last_size = this_size
1185 result = self.satisfiesOccurrences(count)
1186 if (result):
1187 self._validateReplaceResults(symbol_set, symbol_set_mut, output_sequence, output_sequence_mut)
1188 return result
1189
1190 -class _Group (ContentModel_mixin):
1191 """Base class for content information pertaining to a U{model
1192 group<http://www.w3.org/TR/xmlschema-1/#Model_Groups>}.
1193
1194 There is a specific subclass for each group compositor.
1195 """
1196
1197 _StateClass = None
1198 """A reference to a L{ContentState_mixin} class that maintains state when
1199 validating an instance of this group."""
1200
1201 __particles = None
1202 """List of L{ParticleModel}s comprising the group."""
1203
1204 - def particles (self):
1205 """The sequence of particles comprising the group"""
1206 return self.__particles
1207
1211
1212
1213
1214 - def newState (self, parent_particle_state):
1215 return self._StateClass(self, parent_particle_state)
1216
1217
1218
1219 - def _validate (self, symbol_set, output_sequence):
1227
1228
1248
1251
1254
1255
1256
1257
1258