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