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

Source Code for Module pyxb.binding.facets

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