1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 """Helper classes that maintain the content model of XMLSchema in the binding
16 classes.
17
18 L{AttributeUse} and L{ElementUse} record information associated with a binding
19 class, for example the types of values, the original XML QName or NCName, and
20 the Python field in which the values are stored. They also provide the
21 low-level interface to set and get the corresponding values in a binding
22 instance.
23
24 @todo: Document new content model
25
26 L{Wildcard} holds content-related information used in the content model.
27 """
28
29 import pyxb
30 import pyxb.namespace
31 import basis
32
33 import xml.dom
34
35 -class ContentState_mixin (pyxb.cscRoot):
36 """Declares methods used by classes that hold state while validating a
37 content model component."""
38
39 - def accepts (self, particle_state, instance, value, element_use):
40 """Determine whether the provided value can be added to the instance
41 without violating state validation.
42
43 This method must not throw any non-catastrophic exceptions; general
44 failures should be transformed to a C{False} return value.
45
46 @param particle_state: The L{ParticleState} instance serving as the
47 parent to this state. The implementation must inform that state when
48 the proposed value completes the content model.
49
50 @param instance: An instance of a subclass of
51 {basis.complexTypeDefinition}, into which the provided value will be
52 stored if it is consistent with the current model state.
53
54 @param value: The value that is being validated against the state.
55
56 @param element_use: An optional L{ElementUse} instance that specifies
57 the element to which the value corresponds. This will be available
58 when the value is extracted by parsing a document, but will be absent
59 if the value was passed as a constructor positional parameter.
60
61 @return: C{True} if the value was successfully matched against the
62 state. C{False} if the value did not match against the state."""
63 raise Exception('ContentState_mixin.accepts not implemented in %s' % (type(self),))
64
65 - def notifyFailure (self, sub_state, particle_ok):
66 """Invoked by a sub-state to indicate that validation cannot proceed
67 in the current state.
68
69 Normally this is used when an intermediate content model must reset
70 itself to permit alternative models to be evaluated.
71
72 @param sub_state: the state that was unable to accept a value
73
74 @param particle_ok: C{True} if the particle that rejected the value is
75 in an accepting terminal state
76
77 """
78 raise Exception('ContentState_mixin.notifyFailure not implemented in %s' % (type(self),))
79
80 - def _verifyComplete (self, parent_particle_state):
81 """Determine whether the deep state is complete without further elements.
82
83 No-op for non-aggregate state. For aggregate state, all contained
84 particles should be checked to see whether the overall model can be
85 satisfied if no additional elements are provided.
86
87 This method does not have a meaningful return value; violations of the
88 content model should produce the corresponding exception (generally,
89 L{MissingContentError}).
90
91 @param parent_particle_state: the L{ParticleState} for which this state
92 is the term.
93 """
94 pass
95
96 -class ContentModel_mixin (pyxb.cscRoot):
97 """Declares methods used by classes representing content model components."""
98
99 - def newState (self, parent_particle_state):
100 """Return a L{ContentState_mixin} instance that will validate the
101 state of this model component.
102
103 @param parent_particle_state: The L{ParticleState} instance for which
104 this instance is a term. C{None} for the top content model of a
105 complex data type.
106 """
107 raise Exception('ContentModel_mixin.newState not implemented in %s' % (type(self),))
108
109 - def _validateCloneSymbolSet (self, symbol_set_im):
110 """Create a mutable copy of the symbol set.
111
112 The top-level map is copied, as are the lists of values. The values
113 within the lists are unchanged, as validation does not affect them."""
114 rv = symbol_set_im.copy()
115 for (k, v) in rv.items():
116 rv[k] = v[:]
117 return rv
118
119 - def _validateCloneOutputSequence (self, output_sequence_im):
120 return output_sequence_im[:]
121
122 - def _validateReplaceResults (self, symbol_set_out, symbol_set_new, output_sequence_out, output_sequence_new):
123 """In-place update of symbol set and output sequence structures.
124
125 Use this to copy from temporary mutable structures updated by local
126 validation into the structures that were passed in once the validation
127 has succeeded."""
128 symbol_set_out.clear()
129 symbol_set_out.update(symbol_set_new)
130 output_sequence_out[:] = output_sequence_new
131
132 - def _validate (self, symbol_set, output_sequence):
133 """Determine whether an output sequence created from the symbols can
134 be made consistent with the model.
135
136 The symbol set represents letters in an alphabet; the output sequence
137 orders those letters in a way that satisfies the regular expression
138 expressed in the model. Both are changed as a result of a successful
139 validation; both remain unchanged if the validation failed. In
140 recursing, implementers may assume that C{output_sequence} is
141 monotonic: its length remains unchanged after an invocation iff the
142 symbol set also remains unchanged. The L{_validateCloneSymbolSet},
143 L{_validateCloneOutputSequence}, and L{_validateReplaceResults}
144 methods are available to help preserve this behavior.
145
146 @param symbol_set: A map from L{ElementUse} instances to a list of
147 values. The order of the values corresponds to the order in which
148 they should appear. A key of C{None} identifies values that are
149 stored as wildcard elements. Values are removed from the lists as
150 they are used; when the last value of a list is removed, its key is
151 removed from the map. Thus an empty dictionary is the indicator that
152 no more symbols are available.
153
154 @param output_sequence: A mutable list to which should be appended
155 tuples C{( eu, val )} where C{eu} is an L{ElementUse} from the set of
156 symbol keys, and C{val} is a value from the corresponding list. A
157 document generated by producing the elements in the given order is
158 expected to validate.
159
160 @return: C{True} iff the model validates. C{symbol_set} and
161 C{output_path} must be unmodified if returns C{False}.
162 """
163 raise Exception('ContentState_mixin._validate not implemented in %s' % (type(self),))
164
165
167 """A helper class that encapsulates everything we need to know
168 about the way an attribute is used within a binding class.
169
170 Attributes are stored internally as pairs C{(provided, value)}, where
171 C{provided} is a boolean indicating whether a value for the attribute was
172 provided externally, and C{value} is an instance of the attribute
173 datatype. The C{provided} flag is used to determine whether an XML
174 attribute should be added to a created DOM node when generating the XML
175 corresponding to a binding instance.
176 """
177
178 __name = None
179 __id = None
180 __key = None
181 __dataType = None
182 __unicodeDefault = None
183 __defaultValue = None
184 __fixed = False
185 __required = False
186 __prohibited = False
187
188 - def __init__ (self, name, id, key, data_type, unicode_default=None, fixed=False, required=False, prohibited=False):
189 """Create an AttributeUse instance.
190
191 @param name: The name by which the attribute is referenced in the XML
192 @type name: L{pyxb.namespace.ExpandedName}
193
194 @param id: The Python identifier for the attribute within the
195 containing L{pyxb.basis.binding.complexTypeDefinition}. This is a
196 public identifier, derived from the local part of the attribute name
197 and modified to be unique, and is usually used as the name of the
198 attribute's inspector method.
199 @type id: C{str}
200
201 @param key: The string used to store the attribute
202 value in the dictionary of the containing
203 L{pyxb.basis.binding.complexTypeDefinition}. This is mangled so
204 that it is unique among and is treated as a Python private member.
205 @type key: C{str}
206
207 @param data_type: The class reference to the subclass of
208 L{pyxb.binding.basis.simpleTypeDefinition} of which the attribute
209 values must be instances.
210 @type data_type: C{type}
211
212 @keyword unicode_default: The default value of the attribute as
213 specified in the schema, or None if there is no default attribute
214 value. The default value (of the keyword) is C{None}.
215 @type unicode_default: C{unicode}
216
217 @keyword fixed: If C{True}, indicates that the attribute, if present,
218 must have the value that was given via C{unicode_default}. The
219 default value is C{False}.
220 @type fixed: C{bool}
221
222 @keyword required: If C{True}, indicates that the attribute must appear
223 in the DOM node used to create an instance of the corresponding
224 L{pyxb.binding.basis.complexTypeDefinition}. The default value is
225 C{False}. No more that one of L{required} and L{prohibited} should be
226 assigned C{True}.
227 @type required: C{bool}
228
229 @keyword prohibited: If C{True}, indicates that the attribute must
230 B{not} appear in the DOM node used to create an instance of the
231 corresponding L{pyxb.binding.basis.complexTypeDefinition}. The
232 default value is C{False}. No more that one of L{required} and
233 L{prohibited} should be assigned C{True}.
234 @type prohibited: C{bool}
235
236 @raise pyxb.BadTypeValueError: the L{unicode_default} cannot be used
237 to initialize an instance of L{data_type}
238 """
239
240 self.__name = name
241 self.__id = id
242 self.__key = key
243 self.__dataType = data_type
244 self.__unicodeDefault = unicode_default
245 if self.__unicodeDefault is not None:
246 self.__defaultValue = self.__dataType.Factory(self.__unicodeDefault)
247 self.__fixed = fixed
248 self.__required = required
249 self.__prohibited = prohibited
250
252 """The expanded name of the element.
253
254 @rtype: L{pyxb.namespace.ExpandedName}
255 """
256 return self.__name
257
259 """The default value of the attribute."""
260 return self.__defaultValue
261
263 """C{True} iff the value of the attribute cannot be changed."""
264 return self.__fixed
265
267 """Return True iff the attribute must be assigned a value."""
268 return self.__required
269
271 """Return True iff the attribute must not be assigned a value."""
272 return self.__prohibited
273
275 """Return True iff the given instance has been explicitly given a
276 value for the attribute.
277
278 This is used for things like only generating an XML attribute
279 assignment when a value was originally given (even if that value
280 happens to be the default).
281 """
282 return self.__getProvided(ctd_instance)
283
285 """Tag used within Python code for the attribute.
286
287 This is not used directly in the default code generation template."""
288 return self.__id
289
291 """String used as key within object dictionary when storing attribute value."""
292 return self.__key
293
295 """The subclass of L{pyxb.binding.basis.simpleTypeDefinition} of which any attribute value must be an instance."""
296 return self.__dataType
297
299 """Retrieve the value information for this attribute in a binding instance.
300
301 @param ctd_instance: The instance object from which the attribute is to be retrieved.
302 @type ctd_instance: subclass of L{pyxb.binding.basis.complexTypeDefinition}
303 @return: C{(provided, value)} where C{provided} is a C{bool} and
304 C{value} is C{None} or an instance of the attribute's datatype.
305
306 """
307 return getattr(ctd_instance, self.__key, (False, None))
308
311
312 - def value (self, ctd_instance):
313 """Get the value of the attribute from the instance."""
314 return self.__getValue(ctd_instance)[1]
315
316 - def __setValue (self, ctd_instance, new_value, provided):
317 return setattr(ctd_instance, self.__key, (provided, new_value))
318
319 - def reset (self, ctd_instance):
320 """Set the value of the attribute in the given instance to be its
321 default value, and mark that it has not been provided."""
322 self.__setValue(ctd_instance, self.__defaultValue, False)
323
331
347
348 - def set (self, ctd_instance, new_value):
349 """Set the value of the attribute.
350
351 This validates the value against the data type, creating a new instance if necessary.
352
353 @param ctd_instance: The binding instance for which the attribute
354 value is to be set
355 @type ctd_instance: subclass of L{pyxb.binding.basis.complexTypeDefinition}
356 @param new_value: The value for the attribute
357 @type new_value: An C{xml.dom.Node} instance, or any value that is
358 permitted as the input parameter to the C{Factory} method of the
359 attribute's datatype.
360 """
361 provided = True
362 if isinstance(new_value, xml.dom.Node):
363 unicode_value = self.__name.getAttribute(new_value)
364 if unicode_value is None:
365 if self.__required:
366 raise pyxb.MissingAttributeError('Required attribute %s from %s not found' % (self.__name, ctd_instance._ExpandedName or type(ctd_instance)))
367 provided = False
368 unicode_value = self.__unicodeDefault
369 if unicode_value is None:
370
371 provided = False
372 new_value = None
373 else:
374 new_value = unicode_value
375 else:
376 assert new_value is not None
377 if self.__prohibited:
378 raise pyxb.ProhibitedAttributeError('Value given for prohibited attribute %s' % (self.__name,))
379 if (new_value is not None) and (not isinstance(new_value, self.__dataType)):
380 new_value = self.__dataType.Factory(new_value)
381 if self.__fixed and (new_value != self.__defaultValue):
382 raise pyxb.AttributeChangeError('Attempt to change value of fixed attribute %s' % (self.__name,))
383 self.__setValue(ctd_instance, new_value, provided)
384 return new_value
385
386 - def _description (self, name_only=False, user_documentation=True):
387 if name_only:
388 return str(self.__name)
389 assert issubclass(self.__dataType, basis._TypeBinding_mixin)
390 desc = [ str(self.__id), ': ', str(self.__name), ' (', self.__dataType._description(name_only=True, user_documentation=False), '), ' ]
391 if self.__required:
392 desc.append('required')
393 elif self.__prohibited:
394 desc.append('prohibited')
395 else:
396 desc.append('optional')
397 if self.__defaultValue is not None:
398 desc.append(', ')
399 if self.__fixed:
400 desc.append('fixed')
401 else:
402 desc.append('default')
403 desc.extend(['=', self.__unicodeDefault ])
404 return ''.join(desc)
405
406 -class ElementUse (ContentState_mixin, ContentModel_mixin):
407 """Aggregate the information relevant to an element of a complex type.
408
409 This includes the L{original tag name<name>}, the spelling of L{the
410 corresponding object in Python <id>}, an L{indicator<isPlural>} of whether
411 multiple instances might be associated with the field, and other relevant
412 information..
413 """
414
416 """The expanded name of the element.
417
418 @rtype: L{pyxb.namespace.ExpandedName}
419 """
420 return self.__name
421 __name = None
422
424 """The string name of the binding class field used to hold the element
425 values.
426
427 This is the user-visible name, and excepting disambiguation will be
428 equal to the local name of the element."""
429 return self.__id
430 __id = None
431
432
433
434
435 __key = None
436
438 """The L{basis.element} instance identifying the information
439 associated with the element declaration.
440 """
441 return self.__elementBinding
448 __elementBinding = None
449
451 """True iff the content model indicates that more than one element
452 can legitimately belong to this use.
453
454 This includes elements in particles with maxOccurs greater than one,
455 and when multiple elements with the same NCName are declared in the
456 same type.
457 """
458 return self.__isPlural
459 __isPlural = False
460
461 - def __init__ (self, name, id, key, is_plural, element_binding=None):
462 """Create an ElementUse instance.
463
464 @param name: The name by which the element is referenced in the XML
465 @type name: L{pyxb.namespace.ExpandedName}
466
467 @param id: The Python name for the element within the containing
468 L{pyxb.basis.binding.complexTypeDefinition}. This is a public
469 identifier, albeit modified to be unique, and is usually used as the
470 name of the element's inspector method or property.
471 @type id: C{str}
472
473 @param key: The string used to store the element
474 value in the dictionary of the containing
475 L{pyxb.basis.binding.complexTypeDefinition}. This is mangled so
476 that it is unique among and is treated as a Python private member.
477 @type key: C{str}
478
479 @param is_plural: If C{True}, documents for the corresponding type may
480 have multiple instances of this element. As a consequence, the value
481 of the element will be a list. If C{False}, the value will be C{None}
482 if the element is absent, and a reference to an instance of the type
483 identified by L{pyxb.binding.basis.element.typeDefinition} if present.
484 @type is_plural: C{bool}
485
486 @param element_binding: Reference to the class that serves as the
487 binding for the element.
488 """
489 self.__name = name
490 self.__id = id
491 self.__key = key
492 self.__isPlural = is_plural
493 self.__elementBinding = element_binding
494
496 """Return the default value for this element.
497
498 @todo: Right now, this returns C{None} for non-plural and an empty
499 list for plural elements. Need to support schema-specified default
500 values for simple-type content.
501 """
502 if self.isPlural():
503 return []
504 return None
505
506 - def value (self, ctd_instance):
507 """Return the value for this use within the given instance."""
508 return getattr(ctd_instance, self.__key, self.defaultValue())
509
510 - def reset (self, ctd_instance):
511 """Set the value for this use in the given element to its default."""
512 setattr(ctd_instance, self.__key, self.defaultValue())
513 return self
514
515 - def set (self, ctd_instance, value):
525
527 """Invoke either L{set} or L{append}, depending on whether the element
528 use is plural."""
529 if self.isPlural():
530 return self.append(ctd_instance, value)
531 return self.set(ctd_instance, value)
532
533 - def append (self, ctd_instance, value):
545
546 - def toDOM (self, dom_support, parent, value):
547 """Convert the given value to DOM as an instance of this element.
548
549 @param dom_support: Helper for managing DOM properties
550 @type dom_support: L{pyxb.utils.domutils.BindingDOMSupport}
551 @param parent: The DOM node within which this element should be generated.
552 @type parent: C{xml.dom.Element}
553 @param value: The content for this element. May be text (if the
554 element allows mixed content), or an instance of
555 L{basis._TypeBinding_mixin}.
556 """
557 if isinstance(value, basis._TypeBinding_mixin):
558 element_binding = self.__elementBinding
559 if value._substitutesFor(element_binding):
560 element_binding = value._element()
561 assert element_binding is not None
562 if element_binding.abstract():
563 raise pyxb.DOMGenerationError('Element %s is abstract but content %s not associated with substitution group member' % (self.name(), value))
564 element = dom_support.createChildElement(element_binding.name(), parent)
565 elt_type = element_binding.typeDefinition()
566 val_type = type(value)
567 if isinstance(value, basis.complexTypeDefinition):
568 assert isinstance(value, elt_type)
569 else:
570 if isinstance(value, basis.STD_union) and isinstance(value, elt_type._MemberTypes):
571 val_type = elt_type
572 if dom_support.requireXSIType() or elt_type._RequireXSIType(val_type):
573 val_type_qname = value._ExpandedName.localName()
574 tns_prefix = dom_support.namespacePrefix(value._ExpandedName.namespace())
575 if tns_prefix is not None:
576 val_type_qname = '%s:%s' % (tns_prefix, val_type_qname)
577 dom_support.addAttribute(element, pyxb.namespace.XMLSchema_instance.createExpandedName('type'), val_type_qname)
578 value._toDOM_csc(dom_support, element)
579 elif isinstance(value, (str, unicode)):
580 element = dom_support.createChildElement(self.name(), parent)
581 element.appendChild(dom_support.document().createTextNode(value))
582 else:
583 raise pyxb.LogicError('toDOM with unrecognized value type %s: %s' % (type(value), value))
584
585 - def _description (self, name_only=False, user_documentation=True):
593
594 - def newState (self, parent_particle_state):
595 """Implement parent class method."""
596 return self
597
598 - def accepts (self, particle_state, instance, value, element_use):
603
604 - def _accepts (self, instance, value, element_use):
625
626 - def _validate (self, symbol_set, output_sequence):
627 values = symbol_set.get(self)
628
629 if values is None:
630 return False
631 used = values.pop(0)
632 output_sequence.append( (self, used) )
633 if 0 == len(values):
634 del symbol_set[self]
635 return True
636
638 return 'EU.%s@%x' % (self.__name, id(self))
639
640
641 -class Wildcard (ContentState_mixin, ContentModel_mixin):
642 """Placeholder for wildcard objects."""
643
644 NC_any = '##any'
645 NC_not = '##other'
646 NC_targetNamespace = '##targetNamespace'
647 NC_local = '##local'
648
649 __namespaceConstraint = None
651 """A constraint on the namespace for the wildcard.
652
653 Valid values are:
654
655 - L{Wildcard.NC_any}
656 - A tuple ( L{Wildcard.NC_not}, a L{namespace<pyxb.namespace.Namespace>} instance )
657 - set(of L{namespace<pyxb.namespace.Namespace>} instances)
658
659 Namespaces are represented by their URIs. Absence is
660 represented by None, both in the "not" pair and in the set.
661 """
662 return self.__namespaceConstraint
663
664 PC_skip = 'skip'
665 PC_lax = 'lax'
666 PC_strict = 'strict'
667
668
669 __processContents = None
671
679
689
690 - def matches (self, instance, value):
721
722 - def newState (self, parent_particle_state):
724
725 - def accepts (self, particle_state, instance, value, element_use):
738
739 - def _validate (self, symbol_set, output_sequence):
740
741
742 wc_values = symbol_set.get(None)
743 if wc_values is None:
744 return False
745 used = wc_values.pop(0)
746 output_sequence.append( (None, used) )
747 if 0 == len(wc_values):
748 del symbol_set[None]
749 return True
750
752 __failed = False
753 __satisfied = False
754
755 - def __init__ (self, group, parent_particle_state):
764
765
766 - def accepts (self, particle_state, instance, value, element_use):
767 assert self.__parentParticleState == particle_state
768 assert not self.__failed
769
770 while self.__particleState is not None:
771 (consume, underflow_exc) = self.__particleState.step(instance, value, element_use)
772 if consume:
773 return True
774 if underflow_exc is not None:
775 self.__failed = True
776 raise underflow_exc
777 return False
778
780 while self.__particleState is not None:
781 self.__particleState.verifyComplete()
782
792
793
795 - def __init__ (self, group, parent_particle_state):
800
801
802 - def accepts (self, particle_state, instance, value, element_use):
803
804 if self.__activeChoice is None:
805 for choice in self.__choices:
806
807 try:
808 (consume, underflow_exc) = choice.step(instance, value, element_use)
809 except Exception, e:
810 consume = False
811 underflow_exc = e
812
813 if consume:
814 self.__activeChoice = choice
815 self.__choices = None
816 return True
817 return False
818 (consume, underflow_exc) = self.__activeChoice.step(instance, value, element_use)
819
820 if consume:
821 return True
822 if underflow_exc is not None:
823 self.__failed = True
824 raise underflow_exc
825 return False
826
843
849
901
902 -class ParticleState (pyxb.cscRoot):
903 - def __init__ (self, particle, parent_state=None):
904 self.__particle = particle
905 self.__parentState = parent_state
906 self.__count = -1
907
908 self.incrementCount()
909
910 - def particle (self): return self.__particle
911
912 - def incrementCount (self):
913
914 self.__count += 1
915 self.__termState = self.__particle.term().newState(self)
916 self.__tryAccept = True
917
918 - def verifyComplete (self):
919
920
921
922 if not self.__particle.satisfiesOccurrences(self.__count):
923 self.__termState._verifyComplete(self)
924 if not self.__particle.satisfiesOccurrences(self.__count):
925 print 'PS.VC %s incomplete' % (self,)
926 raise pyxb.MissingContentError('incomplete')
927 if self.__parentState is not None:
928 self.__parentState.notifyFailure(self, True)
929
930 - def step (self, instance, value, element_use):
931 """Attempt to apply the value as a new instance of the particle's term.
932
933 The L{ContentState_mixin} created for the particle's term is consulted
934 to determine whether the instance can accept the given value. If so,
935 the particle's maximum occurrence limit is checked; if not, and the
936 particle has a parent state, it is informed of the failure.
937
938 @param instance: An instance of a subclass of
939 {basis.complexTypeDefinition}, into which the provided value will be
940 stored if it is consistent with the current model state.
941
942 @param value: The value that is being validated against the state.
943
944 @param element_use: An optional L{ElementUse} instance that specifies
945 the element to which the value corresponds. This will be available
946 when the value is extracted by parsing a document, but will be absent
947 if the value was passed as a constructor positional parameter.
948
949 @return: C{( consumed, underflow_exc )} A tuple where the first element
950 is C{True} iff the provided value was accepted in the current state.
951 When this first element is C{False}, the second element will be
952 C{None} if the particle's occurrence requirements have been met, and
953 is an instance of C{MissingElementError} if the observed number of
954 terms is less than the minimum occurrence count. Depending on
955 context, the caller may raise this exception, or may try an
956 alternative content model.
957
958 @raise pyxb.UnexpectedElementError: if the value satisfies the particle,
959 but having done so exceeded the allowable number of instances of the
960 term.
961 """
962
963
964
965
966 consumed = False
967 underflow_exc = None
968
969
970
971
972 self.__tryAccept = True
973 while self.__tryAccept and (self.__count != self.__particle.maxOccurs()):
974 self.__tryAccept = False
975 consumed = self.__termState.accepts(self, instance, value, element_use)
976
977 self.__tryAccept = self.__tryAccept and (not consumed)
978
979 if consumed:
980 if not self.__particle.meetsMaximum(self.__count):
981 raise pyxb.UnexpectedElementError('too many')
982 else:
983 if self.__parentState is not None:
984 self.__parentState.notifyFailure(self, self.__particle.satisfiesOccurrences(self.__count))
985 if not self.__particle.meetsMinimum(self.__count):
986
987
988
989 underflow_exc = pyxb.UnrecognizedContentError('too few')
990 return (consumed, underflow_exc)
991
992 - def __str__ (self):
993 particle = self.__particle
994 return 'ParticleState(%d:%d,%s:%s)@%x' % (self.__count, particle.minOccurs(), particle.maxOccurs(), particle.term(), id(self))
995
996 -class ParticleModel (ContentModel_mixin):
997 """Content model dealing with particles: terms with occurrence restrictions"""
998
999 - def minOccurs (self): return self.__minOccurs
1000 - def maxOccurs (self): return self.__maxOccurs
1001 - def term (self): return self.__term
1002
1003 - def meetsMaximum (self, count):
1004 """@return: C{True} iff there is no maximum on term occurrence, or the
1005 provided count does not exceed that maximum"""
1006 return (self.__maxOccurs is None) or (count <= self.__maxOccurs)
1007
1008 - def meetsMinimum (self, count):
1009 """@return: C{True} iff the provided count meets the minimum number of
1010 occurrences"""
1011 return count >= self.__minOccurs
1012
1013 - def satisfiesOccurrences (self, count):
1014 """@return: C{True} iff the provided count satisfies the occurrence
1015 requirements"""
1016 return self.meetsMinimum(count) and self.meetsMaximum(count)
1017
1018 - def __init__ (self, term, min_occurs=1, max_occurs=1):
1019 self.__term = term
1020 self.__minOccurs = min_occurs
1021 self.__maxOccurs = max_occurs
1022
1023 - def newState (self):
1024 return ParticleState(self)
1025
1026 - def validate (self, symbol_set):
1027 """Determine whether the particle requirements are satisfiable by the
1028 given symbol set.
1029
1030 The symbol set represents letters in an alphabet. If those letters
1031 can be combined in a way that satisfies the regular expression
1032 expressed in the model, a satisfying sequence is returned and the
1033 symbol set is reduced by the letters used to form the sequence. If
1034 the content model cannot be satisfied, C{None} is returned and the
1035 symbol set remains unchanged.
1036
1037 @param symbol_set: A map from L{ElementUse} instances to a list of
1038 values. The order of the values corresponds to the order in which
1039 they should appear. A key of C{None} identifies values that are
1040 stored as wildcard elements. Values are removed from the lists as
1041 they are used; when the last value of a list is removed, its key is
1042 removed from the map. Thus an empty dictionary is the indicator that
1043 no more symbols are available.
1044
1045 @return: returns C{None}, or a list of tuples C{( eu, val )} where
1046 C{eu} is an L{ElementUse} from the set of symbol keys, and C{val} is a
1047 value from the corresponding list.
1048 """
1049
1050 output_sequence = []
1051
1052 result = self._validate(symbol_set, output_sequence)
1053
1054 if result:
1055 return (symbol_set, output_sequence)
1056 return None
1057
1058 - def _validate (self, symbol_set, output_sequence):
1059 symbol_set_mut = self._validateCloneSymbolSet(symbol_set)
1060 output_sequence_mut = self._validateCloneOutputSequence(output_sequence)
1061 count = 0
1062
1063 last_size = len(output_sequence_mut)
1064 while (count != self.__maxOccurs) and self.__term._validate(symbol_set_mut, output_sequence_mut):
1065
1066 this_size = len(output_sequence_mut)
1067 if this_size == last_size:
1068
1069
1070 if count < self.__minOccurs:
1071 count = self.__minOccurs
1072 break
1073 count += 1
1074 last_size = this_size
1075 result = self.satisfiesOccurrences(count)
1076 if (result):
1077 self._validateReplaceResults(symbol_set, symbol_set_mut, output_sequence, output_sequence_mut)
1078
1079 return result
1080
1081 -class _Group (ContentModel_mixin):
1082 """Base class for content information pertaining to a U{model
1083 group<http://www.w3.org/TR/xmlschema-1/#Model_Groups>}.
1084
1085 There is a specific subclass for each group compositor.
1086 """
1087
1088 _StateClass = None
1089 """A reference to a L{ContentState_mixin} class that maintains state when
1090 validating an instance of this group."""
1091
1092 - def particles (self): return self.__particles
1093
1096
1097 - def newState (self, parent_particle_state):
1098 return self._StateClass(self, parent_particle_state)
1099
1100
1101 - def _validate (self, symbol_set, output_sequence):
1109
1110
1126
1129
1132
1133
1134
1135
1136