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

Source Code for Module pyxb.binding.facets

  1  # Copyright 2009, Peter A. Bigot 
  2  # 
  3  # Licensed under the Apache License, Version 2.0 (the "License"); you may 
  4  # not use this file except in compliance with the License. You may obtain a 
  5  # copy of the License at: 
  6  # 
  7  #            http://www.apache.org/licenses/LICENSE-2.0 
  8  # 
  9  # Unless required by applicable law or agreed to in writing, software 
 10  # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 
 11  # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 
 12  # License for the specific language governing permissions and limitations 
 13  # under the License. 
 14   
 15  """Classes related to XMLSchema facets. 
 16   
 17  The definitions herein are from sections U{4.2<http://www.w3.org/TR/xmlschema-2/index.html#rf-facets>} 
 18  and U{4.3<http://www.w3.org/TR/xmlschema-2/index.html#rf-facets>} of 
 19  U{XML Schema Part 2: Datatypes<http://www.w3.org/TR/xmlschema-2/>}. 
 20  Facets are attributes of a datatype that constrain its lexical and 
 21  value spaces. 
 22   
 23  """ 
 24   
 25  from xml.dom import Node 
 26  import pyxb 
 27  import types 
 28  import datatypes 
 29  import basis 
 30  from pyxb.utils import utility 
 31  from pyxb.utils import domutils 
 32  import re 
33 34 -class Facet (pyxb.cscRoot):
35 """The base class for facets. 36 37 This provides association with STDs, a name, and a value for the facet. 38 """ 39 40 _Name = None 41 @classmethod
42 - def Name (self):
43 """The name of a facet is a class constant.""" 44 return self._Name
45 46 __baseTypeDefinition = None
47 - def baseTypeDefinition (self):
48 """The SimpleTypeDefinition component restricted by this facet. 49 50 Note: this is NOT the STD to which the facet belongs, but is 51 usually that STD's base type. I.e., this jumps us through all 52 the containing restrictions and extensions to get to the core 53 type definition.""" 54 return self.__baseTypeDefinition
55 56 __ownerTypeDefinition = None
57 - def ownerTypeDefinition (self):
58 """The SimpleTypeDefinition component to which this facet belongs. 59 60 I.e., the one in which the hasFacet specification was found. 61 This value is None if the facet is not associated with an 62 STD.""" 63 return self.__ownerTypeDefinition
64 65 # The default valueDatatype to use for instances of this class. 66 # This is overridden in subclasses that do not use late value 67 # datatype bindings. 68 _ValueDatatype = None 69 70 # The datatype used for facet values. 71 __valueDatatype = None
72 - def valueDatatype (self):
73 """Get the datatype used to represent values of the facet. 74 75 This usually has nothing to do with the owner datatype; for 76 example, the length facet may apply to any STD but the value 77 of the facet is an integer. In generated bindings this is 78 usually set explicitly in the facet constructor; when 79 processing a schema, it is derived from the value's type 80 definition. 81 """ 82 if self.__valueDatatype is None: 83 assert self.baseTypeDefinition() is not None 84 return self.baseTypeDefinition().pythonSupport() 85 return self.__valueDatatype
86 87 __value = None
88 - def _value (self, v): self.__value = v
89 - def value (self): return self.__value
90 91 __annotation = None
92 - def annotation (self): return self.__annotation
93
94 - def __init__ (self, **kw):
95 """Create a facet instance, initializing it from the keyword parameters.""" 96 super(Facet, self).__init__(**kw) 97 # Can't create base class instances 98 assert Facet != self.__class__ 99 self.setFromKeywords(_reset=True, _constructor=True, **kw)
100
101 - def _setFromKeywords_vb (self, **kw):
102 """Configure values of the facet from a set of keywords. 103 104 This method is pre-extended; subclasses should invoke the 105 parent method after setting their local configuration. 106 107 @keyword _reset: If C{False} or missing, existing values will 108 be retained if they do not appear in the 109 keywords. If C{True}, members not defined in 110 the keywords are set to a default. 111 @keyword base_type_definition: 112 @keyword owner_type_definition: 113 @keyword value_datatype: 114 """ 115 116 if not kw.get('_reset', False): 117 kw.setdefault('base_type_definition', self.__baseTypeDefinition) 118 kw.setdefault('owner_type_definition', self.__ownerTypeDefinition) 119 kw.setdefault('value_datatype', self.__valueDatatype) 120 self.__baseTypeDefinition = kw.get('base_type_definition', None) 121 self.__ownerTypeDefinition = kw.get('owner_type_definition', None) 122 self.__valueDatatype = kw.get('value_datatype', self._ValueDatatype) 123 # Verify that there's enough information that we should be 124 # able to identify a PST suitable for representing facet 125 # values. 126 assert (self.__valueDatatype is not None) or (self.__baseTypeDefinition is not None) 127 super_fn = getattr(super(Facet, self), '_setFromKeywords_vb', lambda *a,**kw: self) 128 return super_fn(**kw)
129
130 - def setFromKeywords (self, **kw):
131 """Public entrypoint to the _setFromKeywords_vb call hierarchy.""" 132 return self._setFromKeywords_vb(**kw)
133 134 @classmethod
135 - def ClassForFacet (cls, name):
136 """Given the name of a facet, return the Facet subclass that represents it.""" 137 assert cls != Facet 138 if 0 <= name.find(':'): 139 name = name.split(':', 1)[1] 140 facet_class = globals().get('%s_%s' % (cls._FacetPrefix, name), None) 141 if facet_class is None: 142 raise pyxb.LogicError('Unrecognized facet name %s: expect %s' % (name, ','.join([_f._Name for _f in cls.Facets]))) 143 assert facet_class is not None 144 return facet_class
145
146 - def _valueString (self):
147 if isinstance(self, _CollectionFacet_mixin): 148 return ','.join([ str(_i) for _i in self.items() ]) 149 if (self.valueDatatype() is not None) and (self.value() is not None): 150 try: 151 return self.valueDatatype().XsdLiteral(self.value()) 152 except Exception, e: 153 print 'Stringize facet %s produced %s' % (self.Name(), e) 154 raise 155 return str(self.value())
156
157 - def __str__ (self):
158 rv = [] 159 rv.append('%s="%s"' % (self.Name(), self._valueString())) 160 if isinstance(self, _Fixed_mixin) and self.fixed(): 161 rv.append('[fixed]') 162 return ''.join(rv)
163
164 -class ConstrainingFacet (Facet):
165 """One of the facets defined in section 4.3, which provide 166 constraints on the lexical space of a type definition.""" 167 168 # The prefix used for Python classes used for a constraining 169 # facet. Note that this is not the prefix used when generating a 170 # Python class member that specifies a constraining instance, even 171 # if it happens to be the same digraph. 172 _FacetPrefix = 'CF' 173
174 - def __init__ (self, **kw):
175 super(ConstrainingFacet, self).__init__(**kw)
176
177 - def _validateConstraint_vx (self, value):
178 raise pyxb.LogicError("Facet %s does not implement constraints" % (self.Name(),))
179
180 - def validateConstraint (self, value):
181 """Return True iff the given value satisfies the constraint represented by this facet instance. 182 183 The actual test is delegated to the subclasses.""" 184 return self._validateConstraint_vx(value)
185
186 - def __setFromKeywords(self, **kw):
187 kwv = kw.get('value', None) 188 if kwv is not None: 189 if not isinstance(kwv, self.valueDatatype()): 190 kwv = self.valueDatatype()(kwv) 191 self._value(kwv)
192
193 - def _setFromKeywords_vb (self, **kw):
194 """Extend base class. 195 196 Additional keywords: 197 * value 198 """ 199 # NB: This uses post-extension because it makes reference to the value_data_type 200 super_fn = getattr(super(ConstrainingFacet, self), '_setFromKeywords_vb', lambda *a,**kw: self) 201 rv = super_fn(**kw) 202 self.__setFromKeywords(**kw) 203 return rv
204
205 -class _LateDatatype_mixin (pyxb.cscRoot):
206 """Marker class to indicate that the facet instance must be told 207 its datatype when it is constructed. 208 209 This is necessary for facets like L{CF_minInclusive} and 210 L{CF_minExclusive}, for which the value is determined by the base 211 type definition of the associated STD. In some cases the value 212 that must be used in the facet cannot be represented in the Python 213 type used for the facet; see L{LateDatatypeBindsSuperclass}. 214 """ 215 216 _LateDatatypeBindsSuperclass = None 217 """The class variable that indicates that the Subclasses must 218 override this variable with a value of C{True} or C{False}. The 219 value is C{True} iff the value used for the facet is not within 220 the value space of the corresponding value datatype; for example, 221 L{CF_minExclusive}.""" 222 223 224 @classmethod
226 """Return true if false if the proposed datatype should be 227 used, or True if the base type definition of the proposed 228 datatype should be used.""" 229 if cls._LateDatatypeBindsSuperclass is None: 230 raise pyxb.LogicError('Class %s did not set _LateDatatypeBindsSuperclass variable.') 231 return cls._LateDatatypeBindsSuperclass
232 233 @classmethod
234 - def BindingValueDatatype (cls, value_type):
235 """Find the datatype for facet values when this facet is bound 236 to the given value_type. 237 238 If the C{value_type} is an STD, the associated Python support 239 datatype from this value_type scanning up through the base 240 type hierarchy is used. 241 """ 242 243 import pyxb.xmlschema.structures as structures 244 if isinstance(value_type, structures.SimpleTypeDefinition): 245 # Back up until we find something that actually has a 246 # datatype 247 while not value_type.hasPythonSupport(): 248 value_type = value_type.baseTypeDefinition() 249 value_type = value_type.pythonSupport() 250 assert issubclass(value_type, basis.simpleTypeDefinition) 251 if cls.LateDatatypeBindsSuperclass(): 252 value_type = value_type.XsdSuperType() 253 return value_type
254
255 - def bindValueDatatype (self, value_datatype):
256 self.setFromKeywords(_constructor=True, value_datatype=self.BindingValueDatatype(value_datatype))
257
258 -class _Fixed_mixin (pyxb.cscRoot):
259 """Mix-in to a constraining facet that adds support for the 'fixed' property.""" 260 __fixed = None
261 - def fixed (self): return self.__fixed
262
263 - def __setFromKeywords (self, **kw):
264 if kw.get('_reset', False): 265 self.__fixed = None 266 kwv = kw.get('fixed', None) 267 if kwv is not None: 268 self.__fixed = datatypes.boolean(kwv)
269
270 - def _setFromKeywords_vb (self, **kw):
271 """Extend base class. 272 273 Additional keywords: 274 * fixed 275 """ 276 self.__setFromKeywords(**kw) 277 super_fn = getattr(super(_Fixed_mixin, self), '_setFromKeywords_vb', lambda *a,**kw: self) 278 return super_fn(**kw)
279
280 -class _CollectionFacet_mixin (pyxb.cscRoot):
281 """Mix-in to handle facets whose values are collections, not scalars. 282 283 For example, the enumeration and pattern facets maintain a list of 284 enumeration values and patterns, respectively, as their value 285 space. 286 287 Subclasses must define a class variable _CollectionFacet_itemType 288 which is a reference to a class that is used to construct members 289 of the collection. 290 """ 291 292 __items = None
293 - def _setFromKeywords_vb (self, **kw):
294 """Extend base class. 295 296 @keyword _constructor: If C{False} or absent, the object being 297 set is a member of the collection. If 298 C{True}, the object being set is the 299 collection itself. 300 """ 301 if kw.get('_reset', False): 302 self.__items = [] 303 if not kw.get('_constructor', False): 304 #print self._CollectionFacet_itemType 305 self.__items.append(self._CollectionFacet_itemType(facet_instance=self, **kw)) 306 super_fn = getattr(super(_CollectionFacet_mixin, self), '_setFromKeywords_vb', lambda *a,**kw: self) 307 return super_fn(**kw)
308
309 - def items (self):
310 """The members of the collection.""" 311 # @todo should this be by reference? 312 return self.__items
313
314 - def values (self):
315 """A generator for members of the collection.""" 316 for item in self.items(): 317 yield item.value
318
319 -class CF_length (ConstrainingFacet, _Fixed_mixin):
320 """A facet that specifies the length of the lexical representation of a value. 321 322 See U{http://www.w3.org/TR/xmlschema-2/#rf-length} 323 """ 324 _Name = 'length' 325 _ValueDatatype = datatypes.nonNegativeInteger 326
327 - def _validateConstraint_vx (self, value):
328 value_length = value.xsdValueLength() 329 return (value_length is None) or (self.value() is None) or (value_length == self.value())
330
331 -class CF_minLength (ConstrainingFacet, _Fixed_mixin):
332 """A facet that constrains the length of the lexical representation of a value. 333 334 See U{http://www.w3.org/TR/xmlschema-2/#rf-minLength} 335 """ 336 _Name = 'minLength' 337 _ValueDatatype = datatypes.nonNegativeInteger 338
339 - def _validateConstraint_vx (self, value):
340 value_length = value.xsdValueLength() 341 return (value_length is None) or (self.value() is None) or (value_length >= self.value())
342
343 -class CF_maxLength (ConstrainingFacet, _Fixed_mixin):
344 """A facet that constrains the length of the lexical representation of a value. 345 346 See U{http://www.w3.org/TR/xmlschema-2/#rf-minLength} 347 """ 348 _Name = 'maxLength' 349 _ValueDatatype = datatypes.nonNegativeInteger 350
351 - def _validateConstraint_vx (self, value):
352 value_length = value.xsdValueLength() 353 return (value_length is None) or (self.value() is None) or (value_length <= self.value())
354 355 import pyxb.utils.xmlre
356 357 -class _PatternElement (utility.PrivateTransient_mixin):
358 """This class represents individual patterns that appear within a CF_pattern collection.""" 359 360 # The compiled regular expression is marked transient because we 361 # normally do development with Python 2.5, and consequently save 362 # the pickled namespace archives that go into the distribution 363 # with that version. Compiled regular expressions in Python 2.5 364 # include a reference to the re._compile method, which does not 365 # exist in Python 2.4. As a result, attempts to load a namespace 366 # which includes types with pattern restrictions fail. 367 __PrivateTransient = set() 368 369 __compiledExpression = None 370 __PrivateTransient.add('compiledExpression') 371 372 __pythonExpression = None 373 374 pattern = None 375 annotation = None
376 - def __init__ (self, pattern=None, value=None, annotation=None, **kw):
377 if pattern is None: 378 assert value is not None 379 pattern = value 380 assert isinstance(pattern, types.StringTypes) 381 self.pattern = pattern 382 self.annotation = annotation 383 self.__pythonExpression = pyxb.utils.xmlre.XMLToPython(pattern)
384 #print 'Translated pattern %s to %s' % (pattern.encode('ascii', 'xmlcharrefreplace'), 385 # self.__pythonExpression.encode('ascii', 'xmlcharrefreplace')) 386
387 - def __str__ (self): return self.pattern
388
389 - def matches (self, text):
390 if self.__compiledExpression is None: 391 self.__compiledExpression = re.compile(self.__pythonExpression) 392 return self.__compiledExpression.match(text)
393
394 -class CF_pattern (ConstrainingFacet, _CollectionFacet_mixin):
395 """A facet that constrains the lexical representation of a value 396 to match one of a set of patterns. 397 398 See U{http://www.w3.org/TR/xmlschema-2/#rf-pattern} 399 400 @note: In PyXB, pattern constraints are ignored for any type with 401 a Python representation that does not derive from C{basestring}. 402 This is due to the difficulty in reconstructing the lexical 403 representation of a non-string type after it has been converted to 404 its value space. 405 406 @todo: On creating new instances of non-string simple types from 407 string representations, we could apply pattern constraints. That 408 would mean checking them prior to invoking the Factory method. 409 """ 410 _Name = 'pattern' 411 _CollectionFacet_itemType = _PatternElement 412 _ValueDatatype = datatypes.string 413 414 __patternElements = None
415 - def patternElements (self): return self.__patternElements
416
417 - def __init__ (self, **kw):
418 super(CF_pattern, self).__init__(**kw) 419 self.__patternElements = []
420
421 - def addPattern (self, **kw):
422 pattern = self._CollectionFacet_itemType(**kw) 423 self.__patternElements.append(pattern) 424 return pattern
425
426 - def _validateConstraint_vx (self, value):
427 # If validation is inhibited, or if the facet hasn't had any 428 # restrictions applied yet, return True. 429 if 0 == len(self.__patternElements): 430 return True 431 if not isinstance(value, basestring): 432 return True 433 for pe in self.__patternElements: 434 if pe.matches(value): 435 return True 436 return False
437
438 -class _EnumerationElement (object):
439 """This class represents individual values that appear within a 440 L{CF_enumeration} collection.""" 441 442 __value = None
443 - def value (self):
444 """The Python value that is used for equality testing 445 against this enumeration. 446 447 This is an instance of L{enumeration.valueDatatype()<CF_enumeration.valueDatatype>}, 448 initialized from the unicodeValue.""" 449 return self.__value
450 451 __tag = None
452 - def tag (self):
453 """The Python identifier used for the named constant representing 454 the enumeration value. 455 456 This should include any desired prefix, since it must be 457 unique within its binding class. If C{None}, no enumeration 458 constant will be generated.""" 459 return self.__tag
460 - def _setTag (self, tag):
461 """Set the tag to be used for this enumeration.""" 462 self.__tag = tag
463 464 __enumeration = None
465 - def enumeration (self):
466 """A reference to the L{CF_enumeration} instance that owns this element.""" 467 return self.__enumeration
468 469 __unicodeValue = None
470 - def unicodeValue (self):
471 """The unicode string that defines the enumeration value.""" 472 return self.__unicodeValue
473
474 - def __init__ (self, enumeration=None, unicode_value=None, 475 description=None, annotation=None, tag=None, 476 **kw):
477 # The preferred keyword is "unicode_value", but when being 478 # generically applied by 479 # structures.SimpleTypeDefinition.__updateFacets, the unicode 480 # value comes in through the keyword "value". Similarly for 481 # "enumeration" and "facet_instance". 482 if unicode_value is None: 483 unicode_value = kw['value'] 484 if enumeration is None: 485 enumeration = kw['facet_instance'] 486 self.__unicodeValue = unicode_value 487 self.__enumeration = enumeration 488 self.__description = description 489 self.__annotation = annotation 490 self.__tag = tag 491 492 assert self.__enumeration is not None 493 494 value_datatype = self.enumeration().valueDatatype() 495 self.__value = value_datatype.Factory(self.unicodeValue(), _validate_constraints=False, _from_xml=True) 496 497 if (self.__description is None) and (self.__annotation is not None): 498 self.__description = str(self.__annotation)
499
500 - def __str__ (self):
501 return utility.QuotedEscaped(self.unicodeValue())
502
503 -class CF_enumeration (ConstrainingFacet, _CollectionFacet_mixin, _LateDatatype_mixin):
504 """Capture a constraint that restricts valid values to a fixed set. 505 506 A STD that has an enumeration restriction should mix-in 507 L{pyxb.binding.basis.enumeration_mixin}, and should have a class 508 variable titled C{_CF_enumeration} that is an instance of this 509 class. 510 511 "unicode" refers to the Unicode string by which the value is 512 represented in XML. 513 514 "tag" refers to the Python member reference associated with the 515 enumeration. The value is derived from the unicode value of the 516 enumeration element and an optional prefix that identifies the 517 owning simple type when the tag is promoted to module-level 518 visibility. 519 520 "value" refers to the Python value held in the tag 521 522 See U{http://www.w3.org/TR/xmlschema-2/#rf-enumeration} 523 """ 524 _Name = 'enumeration' 525 _CollectionFacet_itemType = _EnumerationElement 526 _LateDatatypeBindsSuperclass = False 527 528 __elements = None 529 __tagToElement = None 530 __valueToElement = None 531 __unicodeToElement = None 532 533 # The prefix to be used when making enumeration tags visible at 534 # the module level. If None, tags are not made visible. 535 __enumPrefix = None 536
537 - def __init__ (self, **kw):
538 super(CF_enumeration, self).__init__(**kw) 539 self.__enumPrefix = kw.get('enum_prefix', self.__enumPrefix) 540 self.__elements = [] 541 self.__tagToElement = { } 542 self.__valueToElement = { } 543 self.__unicodeToElement = { }
544
545 - def enumPrefix (self):
546 return self.__enumPrefix
547
548 - def elements (self):
549 return self.__elements[:]
550
551 - def addEnumeration (self, **kw):
552 kw['enumeration'] = self 553 ee = _EnumerationElement(**kw) 554 if ee.tag in self.__tagToElement: 555 raise pyxb.IncompleteImplementationError('Duplicate enumeration tags') 556 self.__tagToElement[ee.tag()] = ee 557 self.__unicodeToElement[ee.unicodeValue()] = ee 558 value = ee.value() 559 # Not just issubclass(self.valueDatatype(), basis.STD_list); 560 # this may be a union with one of those as a member type. 561 if isinstance(value, list): 562 value = ' '.join([ _v.xsdLiteral() for _v in value ]) 563 self.__valueToElement[value] = ee 564 self.__elements.append(ee) 565 return value
566
567 - def elementForValue (self, value):
568 """Return the L{_EnumerationElement} instance that has the given value. 569 570 @raise KeyError: the value is not valid for the enumeration.""" 571 return self.__valueToElement[value]
572
573 - def valueForUnicode (self, ustr):
574 """Return the enumeration value corresponding to the given unicode string. 575 576 If ustr is not a valid option for this enumeration, return None.""" 577 rv = self.__unicodeToElement.get(ustr, None) 578 if rv is not None: 579 rv = rv.value() 580 return rv
581
582 - def _validateConstraint_vx (self, value):
583 # If validation is inhibited, or if the facet hasn't had any 584 # restrictions applied yet, return True. 585 if 0 == len(self.__elements): 586 return True 587 for ee in self.__elements: 588 if ee.value() == value: 589 return True 590 return False
591
592 -class _Enumeration_mixin (pyxb.cscRoot):
593 """Marker class to indicate that the generated binding has enumeration members.""" 594 @classmethod
595 - def valueForUnicode (cls, ustr):
596 return cls._CF_enumeration.valueForUnicode(ustr)
597
598 -class _WhiteSpace_enum (datatypes.NMTOKEN, _Enumeration_mixin):
599 """The enumeration used to constrain the whiteSpace facet""" 600 pass
601 _WhiteSpace_enum._CF_enumeration = CF_enumeration(value_datatype=_WhiteSpace_enum) 602 _WhiteSpace_enum.preserve = _WhiteSpace_enum._CF_enumeration.addEnumeration(unicode_value=u'preserve', tag='preserve') 603 _WhiteSpace_enum.replace = _WhiteSpace_enum._CF_enumeration.addEnumeration(unicode_value=u'replace', tag='replace') 604 _WhiteSpace_enum.collapse = _WhiteSpace_enum._CF_enumeration.addEnumeration(unicode_value=u'collapse', tag='collapse') 605 # NOTE: For correctness we really need to initialize the facet map for 606 # WhiteSpace_enum, even though at the moment it isn't necessary. We 607 # can't right now, because its parent datatypes.NMTOKEN hasn't been 608 # initialized yet 609 _WhiteSpace_enum._InitializeFacetMap(_WhiteSpace_enum._CF_enumeration)
610 611 -class CF_whiteSpace (ConstrainingFacet, _Fixed_mixin):
612 """Specify the value-space interpretation of whitespace. 613 614 See U{http://www.w3.org/TR/xmlschema-2/#rf-whiteSpace} 615 """ 616 _Name = 'whiteSpace' 617 _ValueDatatype = _WhiteSpace_enum 618 619 __TabCRLF_re = re.compile("[\t\n\r]") 620 __MultiSpace_re = re.compile(" +")
621 - def normalizeString (self, value):
622 """Normalize the given string in accordance with the configured whitespace interpretation.""" 623 if self.value() is None: 624 return value 625 if self.value() == _WhiteSpace_enum.preserve: 626 return utility.NormalizeWhitespace(value, preserve=True) 627 if self.value() == _WhiteSpace_enum.replace: 628 return utility.NormalizeWhitespace(value, replace=True) 629 assert self.value() == _WhiteSpace_enum.collapse, 'Unexpected value "%s" for whiteSpace facet' % (self.value(),) 630 return utility.NormalizeWhitespace(value, collapse=True)
631
632 - def _validateConstraint_vx (self, value):
633 """No validation rules for whitespace facet.""" 634 return True
635
636 -class CF_minInclusive (ConstrainingFacet, _Fixed_mixin, _LateDatatype_mixin):
637 """Specify the minimum legal value for the constrained type. 638 639 See U{http://www.w3.org/TR/xmlschema-2/#rf-minInclusive} 640 """ 641 _Name = 'minInclusive' 642 _LateDatatypeBindsSuperclass = False 643
644 - def _validateConstraint_vx (self, value):
645 return (self.value() is None) or (self.value() <= value)
646
647 648 -class CF_maxInclusive (ConstrainingFacet, _Fixed_mixin, _LateDatatype_mixin):
649 """Specify the maximum legal value for the constrained type. 650 651 See U{http://www.w3.org/TR/xmlschema-2/#rf-maxInclusive} 652 """ 653 _Name = 'maxInclusive' 654 _LateDatatypeBindsSuperclass = False 655
656 - def _validateConstraint_vx (self, value):
657 return (self.value() is None) or (self.value() >= value)
658
659 -class CF_minExclusive (ConstrainingFacet, _Fixed_mixin, _LateDatatype_mixin):
660 """Specify the exclusive lower bound of legal values for the constrained type. 661 662 See U{http://www.w3.org/TR/xmlschema-2/#rf-minExclusive} 663 """ 664 _Name = 'minExclusive' 665 _LateDatatypeBindsSuperclass = True 666
667 - def _validateConstraint_vx (self, value):
668 return (self.value() is None) or (self.value() < value)
669
670 -class CF_maxExclusive (ConstrainingFacet, _Fixed_mixin, _LateDatatype_mixin):
671 """Specify the exclusive upper bound of legal values for the constrained type. 672 673 See U{http://www.w3.org/TR/xmlschema-2/#rf-maxExclusive} 674 """ 675 _Name = 'maxExclusive' 676 _LateDatatypeBindsSuperclass = True 677
678 - def _validateConstraint_vx (self, value):
679 return (self.value() is None) or (self.value() > value)
680
681 -class CF_totalDigits (ConstrainingFacet, _Fixed_mixin):
682 """Specify the number of digits in the *value* space of the type. 683 684 See U{http://www.w3.org/TR/xmlschema-2/#rf-totalDigits} 685 """ 686 _Name = 'totalDigits' 687 _ValueDatatype = datatypes.positiveInteger 688
689 - def _validateConstraint_vx (self, value):
690 if self.value() is None: 691 return True 692 n = 0 693 scale = 1 694 match = False 695 v = None 696 while (n <= self.value()) and (not match): 697 v = long(value * scale) 698 match = ((value * scale) == v) 699 if self.value() == n: 700 break 701 n += 1 702 scale *= 10 703 while n < self.value(): 704 n += 1 705 scale *= 10 706 return match and (v is not None) and (abs(v) < scale)
707
708 -class CF_fractionDigits (ConstrainingFacet, _Fixed_mixin):
709 """Specify the number of sub-unit digits in the *value* space of the type. 710 711 See U{http://www.w3.org/TR/xmlschema-2/#rf-fractionDigits} 712 """ 713 _Name = 'fractionDigits' 714 _ValueDatatype = datatypes.nonNegativeInteger 715
716 - def _validateConstraint_vx (self, value):
717 if self.value() is None: 718 return True 719 n = 0 720 scale = 1 721 while n <= self.value(): 722 if ((value * scale) == long(value * scale)): 723 return True 724 n += 1 725 scale *= 10 726 return False
727
728 -class FundamentalFacet (Facet):
729 """A fundamental facet provides information on the value space of the associated type.""" 730 731 _FacetPrefix = 'FF' 732 733 @classmethod
734 - def CreateFromDOM (cls, node, owner_type_definition, base_type_definition=None):
735 facet_class = cls.ClassForFacet(node.getAttribute('name')) 736 rv = facet_class(base_type_definition=base_type_definition, 737 owner_type_definition=owner_type_definition) 738 rv.updateFromDOM(node)
739
740 - def updateFromDOM (self, node):
741 if not node.hasAttribute('name'): 742 raise pyxb.SchemaValidationError('No name attribute in facet') 743 assert node.getAttribute('name') == self.Name() 744 self._updateFromDOM(node)
745
746 - def _updateFromDOM (self, node):
747 try: 748 super(FundamentalFacet, self)._updateFromDOM(node) 749 except AttributeError, e: 750 pass 751 if (self.valueDatatype() is not None) and node.hasAttribute('value'): 752 self._value(self.valueDatatype()(node.getAttribute('value'))) 753 # @todo 754 self.__annotation = None 755 return self
756
757 -class FF_equal (FundamentalFacet):
758 """Specifies that the associated type supports a notion of equality. 759 760 See U{http://www.w3.org/TR/xmlschema-2/#equal} 761 """ 762 763 _Name = 'equal'
764
765 -class FF_ordered (FundamentalFacet):
766 """Specifies that the associated type supports a notion of order. 767 768 See U{http://www.w3.org/TR/xmlschema-2/#rf-ordered} 769 """ 770 771 _LegalValues = ( 'false', 'partial', 'total' ) 772 _Name = 'ordered' 773 _ValueDatatype = datatypes.string 774
775 - def __init__ (self, **kw):
776 # @todo: correct value type definition 777 super(FF_ordered, self).__init__(**kw)
778
779 -class FF_bounded (FundamentalFacet):
780 """Specifies that the associated type supports a notion of bounds. 781 782 See U{http://www.w3.org/TR/xmlschema-2/#rf-bounded} 783 """ 784 785 _Name = 'bounded' 786 _ValueDatatype = datatypes.boolean
787
788 -class FF_cardinality (FundamentalFacet):
789 """Specifies that the associated type supports a notion of length. 790 791 See U{http://www.w3.org/TR/xmlschema-2/#rf-cardinality} 792 """ 793 794 _LegalValues = ( 'finite', 'countably infinite' ) 795 _Name = 'cardinality' 796 _ValueDatatype = datatypes.string
797 - def __init__ (self, **kw):
798 # @todo correct value type definition 799 super(FF_cardinality, self).__init__(value_datatype=datatypes.string, **kw)
800
801 -class FF_numeric (FundamentalFacet):
802 """Specifies that the associated type represents a number. 803 804 See U{http://www.w3.org/TR/xmlschema-2/#rf-numeric} 805 """ 806 807 _Name = 'numeric' 808 _ValueDatatype = datatypes.boolean
809 810 # The fixed set of expected facets 811 ConstrainingFacet.Facets = [ 812 CF_length, CF_minLength, CF_maxLength, CF_pattern, CF_enumeration, 813 CF_whiteSpace, CF_minInclusive, CF_maxInclusive, CF_minExclusive, 814 CF_maxExclusive, CF_totalDigits, CF_fractionDigits ] 815 816 FundamentalFacet.Facets = [ 817 FF_equal, FF_ordered, FF_bounded, FF_cardinality, FF_numeric ] 818 819 Facet.Facets = [] 820 Facet.Facets.extend(ConstrainingFacet.Facets) 821 Facet.Facets.extend(FundamentalFacet.Facets) 822