2010-06-21 Atsushi Enomoto <atsushi@ximian.com>
[mcs.git] / class / System.XML / System.Xml.Schema / XmlSchemaSimpleTypeRestriction.cs
blob0c6d4f07dcbfd6aa7727ccc53670abf8ca86617c
1 // Author: Dwivedi, Ajay kumar
2 // Adwiv@Yahoo.com
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining
6 // a copy of this software and associated documentation files (the
7 // "Software"), to deal in the Software without restriction, including
8 // without limitation the rights to use, copy, modify, merge, publish,
9 // distribute, sublicense, and/or sell copies of the Software, and to
10 // permit persons to whom the Software is furnished to do so, subject to
11 // the following conditions:
12 //
13 // The above copyright notice and this permission notice shall be
14 // included in all copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 using System;
25 using System.Collections;
26 using System.Text;
27 using System.Text.RegularExpressions;
28 using System.Xml;
29 using System.Xml.Serialization;
30 using Mono.Xml.Schema;
31 using System.Globalization;
33 #if NET_2_0
34 using NSResolver = System.Xml.IXmlNamespaceResolver;
35 #else
36 using NSResolver = System.Xml.XmlNamespaceManager;
37 #endif
39 namespace System.Xml.Schema
41 /// <summary>
42 /// Summary description for XmlSchemaSimpleTypeRestriction.
43 /// </summary>
44 public class XmlSchemaSimpleTypeRestriction : XmlSchemaSimpleTypeContent
46 private XmlSchemaSimpleType baseType;
47 private XmlQualifiedName baseTypeName;
48 private XmlSchemaObjectCollection facets;
49 const string xmlname = "restriction";
50 private string [] enumarationFacetValues;
51 private string [] patternFacetValues;
52 private Regex [] rexPatterns;
53 private decimal lengthFacet;
54 private decimal maxLengthFacet;
55 private decimal minLengthFacet;
56 private decimal fractionDigitsFacet;
57 private decimal totalDigitsFacet;
58 private object maxInclusiveFacet ;
59 private object maxExclusiveFacet ;
60 private object minInclusiveFacet ;
61 private object minExclusiveFacet ;
62 private XmlSchemaFacet.Facet fixedFacets = XmlSchemaFacet.Facet.None;
63 private static NumberStyles lengthStyle = NumberStyles.Integer;
66 public XmlSchemaSimpleTypeRestriction()
68 baseTypeName = XmlQualifiedName.Empty;
69 facets = new XmlSchemaObjectCollection();
72 [System.Xml.Serialization.XmlAttribute("base")]
73 public XmlQualifiedName BaseTypeName
75 get{ return baseTypeName; }
76 set{ baseTypeName = value; }
79 [XmlElement("simpleType", Type=typeof (XmlSchemaSimpleType))]
80 public XmlSchemaSimpleType BaseType
82 get{ return baseType; }
83 set{ baseType = value; }
86 [XmlElement("minExclusive",typeof(XmlSchemaMinExclusiveFacet))]
87 [XmlElement("minInclusive",typeof(XmlSchemaMinInclusiveFacet))]
88 [XmlElement("maxExclusive",typeof(XmlSchemaMaxExclusiveFacet))]
89 [XmlElement("maxInclusive",typeof(XmlSchemaMaxInclusiveFacet))]
90 [XmlElement("totalDigits",typeof(XmlSchemaTotalDigitsFacet))]
91 [XmlElement("fractionDigits",typeof(XmlSchemaFractionDigitsFacet))]
92 [XmlElement("length",typeof(XmlSchemaLengthFacet))]
93 [XmlElement("minLength",typeof(XmlSchemaMinLengthFacet))]
94 [XmlElement("maxLength",typeof(XmlSchemaMaxLengthFacet))]
95 [XmlElement("enumeration",typeof(XmlSchemaEnumerationFacet))]
96 [XmlElement("whiteSpace",typeof(XmlSchemaWhiteSpaceFacet))]
97 [XmlElement("pattern",typeof(XmlSchemaPatternFacet))]
98 public XmlSchemaObjectCollection Facets
100 get{ return facets; }
103 internal override void SetParent (XmlSchemaObject parent)
105 base.SetParent (parent);
106 if (BaseType != null)
107 BaseType.SetParent (this);
108 foreach (XmlSchemaObject obj in Facets)
109 obj.SetParent (this);
112 /// <remarks>
113 /// 1. One of base or simpletype must be present but not both
114 /// 2. id must be a valid ID
115 /// 3. base must be a valid QName *NO CHECK REQUIRED*
116 /// </remarks>
117 internal override int Compile(ValidationEventHandler h, XmlSchema schema)
119 // If this is already compiled this time, simply skip.
120 if (CompilationId == schema.CompilationId)
121 return 0;
123 errorCount = 0;
125 if(this.baseType != null && !this.BaseTypeName.IsEmpty)
126 error(h, "both base and simpletype can't be set");
127 if(this.baseType == null && this.BaseTypeName.IsEmpty)
128 error(h, "one of basetype or simpletype must be present");
129 if(this.baseType != null)
131 errorCount += this.baseType.Compile(h,schema);
133 if(!XmlSchemaUtil.CheckQName(BaseTypeName))
134 error(h,"BaseTypeName must be a XmlQualifiedName");
136 XmlSchemaUtil.CompileID(Id,this,schema.IDCollection,h);
138 for (int i = 0; i < this.Facets.Count; i++)
139 if (! (Facets [i] is XmlSchemaFacet))
140 error (h, "Only XmlSchemaFacet objects are allowed for Facets property");
141 this.CompilationId = schema.CompilationId;
142 return errorCount;
145 /** Checks if this facet is valid on this restriction. Does not check that it has
146 * not been fixed in the baseType. That is done elsewhere.
149 private static readonly XmlSchemaFacet.Facet listFacets =
150 XmlSchemaFacet.Facet.length | XmlSchemaFacet.Facet.minLength |
151 XmlSchemaFacet.Facet.maxLength | XmlSchemaFacet.Facet.pattern |
152 XmlSchemaFacet.Facet.enumeration | XmlSchemaFacet.Facet.whiteSpace;
155 private bool IsAllowedFacet(XmlSchemaFacet xsf) {
156 /* Must be called after this.ValidateActualType, as it uses actualBaseSchemaType */
158 XsdAnySimpleType ast = ActualBaseSchemaType as XsdAnySimpleType;
159 if (ast != null) {
160 // Based directly on an xsd type
161 return ast.AllowsFacet(xsf);
163 else {
164 XmlSchemaSimpleTypeContent st = ((XmlSchemaSimpleType)ActualBaseSchemaType).Content as XmlSchemaSimpleTypeContent;
165 if (st != null) {
166 XmlSchemaSimpleTypeRestriction str = st as XmlSchemaSimpleTypeRestriction;
167 if (str != null && str != this) {
168 return str.IsAllowedFacet(xsf);
170 XmlSchemaSimpleTypeList stl = st as XmlSchemaSimpleTypeList;
171 if (stl != null) {
172 return ((xsf.ThisFacet & listFacets) != 0);
175 XmlSchemaSimpleTypeUnion stu = st as XmlSchemaSimpleTypeUnion;
176 if (stu != null) {
177 return (xsf is XmlSchemaPatternFacet ||
178 xsf is XmlSchemaEnumerationFacet);
182 else {
183 // TODO: Should this be either a XmlSchemaSimpleType or XmlSchemaDatatype ?
184 // If so report error
187 // Not sure it could ever get here
188 return false;
193 internal override int Validate(ValidationEventHandler h, XmlSchema schema)
196 if (IsValidated (schema.ValidationId))
197 return errorCount;
199 this.ValidateActualType (h, schema);
202 lengthFacet = maxLengthFacet = minLengthFacet = fractionDigitsFacet = totalDigitsFacet = -1;
204 XmlSchemaSimpleTypeRestriction baseSTR = null;
206 if (ActualBaseSchemaType is XmlSchemaSimpleType) {
207 XmlSchemaSimpleTypeContent st = ((XmlSchemaSimpleType)ActualBaseSchemaType).Content as XmlSchemaSimpleTypeContent;
208 baseSTR = st as XmlSchemaSimpleTypeRestriction;
212 if (baseSTR != null) {
213 fixedFacets = baseSTR.fixedFacets;
214 lengthFacet = baseSTR.lengthFacet;
215 maxLengthFacet = baseSTR.maxLengthFacet;
216 minLengthFacet = baseSTR.minLengthFacet;
217 fractionDigitsFacet = baseSTR.fractionDigitsFacet;
218 totalDigitsFacet = baseSTR.totalDigitsFacet;
219 maxInclusiveFacet = baseSTR.maxInclusiveFacet;
220 maxExclusiveFacet = baseSTR.maxExclusiveFacet;
221 minInclusiveFacet = baseSTR.minInclusiveFacet;
222 minExclusiveFacet = baseSTR.minExclusiveFacet;
225 enumarationFacetValues = patternFacetValues = null;
226 rexPatterns = null;
228 XmlSchemaFacet.Facet facetsDefined = XmlSchemaFacet.Facet.None;
230 ArrayList enums = null;
231 ArrayList patterns = null;
232 for (int i = 0; i < facets.Count; i++) {
234 XmlSchemaFacet facet = facets[i] as XmlSchemaFacet;
235 if (facet != null) {
236 if (!IsAllowedFacet(facet)) {
237 facet.error(h, facet.ThisFacet +" is not a valid facet for this type");
238 continue;
241 else {
242 // Other than XmlSchemaFacet; already reported at Compile()
243 continue;
247 XmlSchemaEnumerationFacet ef = facets [i] as XmlSchemaEnumerationFacet;
248 if (ef != null) {
249 if (enums == null)
250 enums = new ArrayList ();
251 enums.Add (ef.Value);
252 continue;
254 XmlSchemaPatternFacet pf = facets [i] as XmlSchemaPatternFacet;
255 if (pf != null) {
256 if (patterns == null)
257 patterns = new ArrayList ();
258 patterns.Add (pf.Value);
259 continue;
262 // Put this test here, as pattern and enumeration
263 // can occur multiple times.
264 if ( (facetsDefined & facet.ThisFacet) !=0) {
265 facet.error (h, "This is a duplicate '" + facet.ThisFacet + "' facet.");
266 continue;
268 else {
269 facetsDefined |= facet.ThisFacet;
276 if (facet is XmlSchemaLengthFacet) {
277 checkLengthFacet((XmlSchemaLengthFacet)facet, facetsDefined, h);
279 else if (facet is XmlSchemaMaxLengthFacet) {
280 checkMaxLengthFacet((XmlSchemaMaxLengthFacet)facet, facetsDefined, h);
282 else if (facet is XmlSchemaMinLengthFacet) {
283 checkMinLengthFacet((XmlSchemaMinLengthFacet)facet, facetsDefined, h);
286 else if (facet is XmlSchemaMinInclusiveFacet) {
287 checkMinMaxFacet((XmlSchemaMinInclusiveFacet)facet, ref minInclusiveFacet, h);
289 else if (facet is XmlSchemaMaxInclusiveFacet) {
290 checkMinMaxFacet((XmlSchemaMaxInclusiveFacet)facet, ref maxInclusiveFacet, h);
292 else if (facet is XmlSchemaMinExclusiveFacet) {
293 checkMinMaxFacet((XmlSchemaMinExclusiveFacet)facet, ref minExclusiveFacet, h);
295 else if (facet is XmlSchemaMaxExclusiveFacet) {
296 checkMinMaxFacet((XmlSchemaMaxExclusiveFacet)facet, ref maxExclusiveFacet, h);
298 else if (facet is XmlSchemaFractionDigitsFacet) {
299 checkFractionDigitsFacet((XmlSchemaFractionDigitsFacet)facet, h);
301 else if (facet is XmlSchemaTotalDigitsFacet) {
302 checkTotalDigitsFacet((XmlSchemaTotalDigitsFacet)facet, h);
305 if (facet.IsFixed) {
306 fixedFacets |= facet.ThisFacet;
311 if (enums != null)
312 this.enumarationFacetValues = enums.ToArray (typeof (string)) as string [];
313 if (patterns != null) {
314 this.patternFacetValues = patterns.ToArray (typeof (string)) as string [];
315 this.rexPatterns = new Regex [patterns.Count];
316 for (int i = 0; i < patternFacetValues.Length; i++) {
317 try {
318 string src = patternFacetValues [i];
319 StringBuilder sb = null;
320 int start = 0;
321 for (int c = 0; c < src.Length; c++) {
322 if (src [c] == '\\' && src.Length > i + 1) {
323 string subst = null;
324 switch (src [c + 1]) {
325 case 'i':
326 subst = "[\\p{L}_]";
327 break;
328 case 'I':
329 subst = "[^\\p{L}_]";
330 break;
331 case 'c':
332 subst = "[\\p{L}\\p{N}_\\.\\-:]";
333 break;
334 case 'C':
335 subst = "[^\\p{L}\\p{N}_\\.\\-:]";
336 break;
338 if (subst != null) {
339 if (sb == null)
340 sb = new StringBuilder ();
341 sb.Append (src, start, c - start);
342 sb.Append (subst);
343 start = c + 2;
347 if (sb != null) {
348 sb.Append (src, start, src.Length - start);
349 src = sb.ToString ();
351 // src = src.Replace ("\\i", "[\\p{L}_]").Replace ("\\I", "[^\\p{L}_]").Replace ("\\c", "[\\p{L}\\p{N}_\\.\\-:]").Replace ("\\C", "[^\\p{L}\\p{N}_\\.\\-:]");
352 Regex rex = new Regex ("^" + src + "$");
353 rexPatterns [i] = rex;
354 } catch (Exception ex) {
355 error (h, "Invalid regular expression pattern was specified.", ex);
363 ValidationId = schema.ValidationId;
365 Console.WriteLine("Facets:\n defined\t{10}\n fixed\t{0}\n length\t{1}\n maxLen\t{2}\n minLen\t{3}\n " +
366 "frac\t{4}\n tot\t{5}\n maxI\t{6}\n maxE\t{7}\n minI\t{8}\n minE\t{9}\n",
367 fixedFacets ,
368 lengthFacet,
369 maxLengthFacet ,
370 minLengthFacet ,
371 fractionDigitsFacet ,
372 totalDigitsFacet ,
373 maxInclusiveFacet ,
374 maxExclusiveFacet ,
375 minInclusiveFacet ,
376 minExclusiveFacet ,
377 facetsDefined);
379 return errorCount;
383 internal void ValidateActualType (ValidationEventHandler h, XmlSchema schema)
385 GetActualType (h, schema, true);
388 internal object GetActualType (ValidationEventHandler h, XmlSchema schema, bool validate)
390 object actualBaseSchemaType = null;
392 XmlSchemaSimpleType type = baseType;
393 if (type == null)
394 type = schema.FindSchemaType (baseTypeName) as XmlSchemaSimpleType;
395 if (type != null) {
396 if (validate)
397 errorCount += type.Validate (h, schema);
398 actualBaseSchemaType = type;
399 } else if (baseTypeName == XmlSchemaComplexType.AnyTypeName) {
400 actualBaseSchemaType = XmlSchemaSimpleType.AnySimpleType;
401 } else if (baseTypeName.Namespace == XmlSchema.Namespace ||
402 baseTypeName.Namespace == XmlSchema.XdtNamespace) {
403 actualBaseSchemaType = XmlSchemaDatatype.FromName (baseTypeName);
404 if (actualBaseSchemaType == null)
405 if (validate)
406 error (h, "Invalid schema type name was specified: " + baseTypeName);
408 // otherwise, it might be missing sub components.
409 else if (!schema.IsNamespaceAbsent (baseTypeName.Namespace))
410 if (validate)
411 error (h, "Referenced base schema type " + baseTypeName + " was not found in the corresponding schema.");
413 return actualBaseSchemaType;
417 private void checkTotalDigitsFacet (XmlSchemaTotalDigitsFacet totf,
418 ValidationEventHandler h) {
419 if (totf != null) {
420 /* totalDigits is the maximum number of digits in values of datatypes
421 * derived from decimal. The value of totalDigits must be a
422 * positiveInteger. */
423 try {
424 decimal newTotalDigits = decimal.Parse (totf.Value.Trim (), lengthStyle, CultureInfo.InvariantCulture);
425 if (newTotalDigits <= 0)
426 totf.error(h, String.Format(CultureInfo.InvariantCulture, "The value '{0}' is an invalid totalDigits value", newTotalDigits));
427 // Valid restriction
428 if ((totalDigitsFacet > 0) && (newTotalDigits > totalDigitsFacet)) {
429 totf.error(h, String.Format(CultureInfo.InvariantCulture, "The value '{0}' is not a valid restriction of the base totalDigits facet '{1}'", newTotalDigits, totalDigitsFacet));
431 totalDigitsFacet = newTotalDigits;
433 catch (FormatException ) {
434 totf.error(h, String.Format("The value '{0}' is an invalid totalDigits facet specification", totf.Value.Trim () ));
440 private void checkFractionDigitsFacet (XmlSchemaFractionDigitsFacet fracf,
441 ValidationEventHandler h) {
443 if (fracf != null) {
444 try {
445 decimal newFractionDigits = decimal.Parse (fracf.Value.Trim (), lengthStyle, CultureInfo.InvariantCulture);
446 if (newFractionDigits< 0)
447 fracf.error(h, String.Format(CultureInfo.InvariantCulture, "The value '{0}' is an invalid fractionDigits value", newFractionDigits));
449 if ((fractionDigitsFacet >= 0) && (newFractionDigits > fractionDigitsFacet)) {
450 fracf.error(h, String.Format(CultureInfo.InvariantCulture, "The value '{0}' is not a valid restriction of the base fractionDigits facet '{1}'", newFractionDigits, fractionDigitsFacet));
452 fractionDigitsFacet = newFractionDigits;
454 catch (FormatException ) {
455 fracf.error(h, String.Format("The value '{0}' is an invalid fractionDigits facet specification", fracf.Value.Trim () ));
462 private void checkMinMaxFacet(XmlSchemaFacet facet,
463 ref object baseFacet,
464 ValidationEventHandler h) {
465 // Is it a valid instance of the base type.
466 object newValue = ValidateValueWithDatatype(facet.Value);
467 if (newValue != null) {
468 // Is the base fixed - if so is it the same
469 if (((fixedFacets & facet.ThisFacet) != 0) && (baseFacet != null)){
470 XsdAnySimpleType dt = getDatatype();
471 if (dt.Compare (newValue, baseFacet) != XsdOrdering.Equal) {
472 facet.error (h,
473 String.Format(CultureInfo.InvariantCulture, "{0} is not the same as fixed parent {1} facet.",
474 facet.Value, facet.ThisFacet));
477 baseFacet = newValue;
479 else {
480 facet.error(h,
481 String.Format("The value '{0}' is not valid against the base type.", facet.Value));
487 private void checkLengthFacet(XmlSchemaLengthFacet lf,
488 XmlSchemaFacet.Facet facetsDefined,
489 ValidationEventHandler h) {
490 if (lf != null) {
491 try {
492 if ((facetsDefined & (XmlSchemaFacet.Facet.minLength | XmlSchemaFacet.Facet.maxLength)) != 0)
493 lf.error(h, "It is an error for both length and minLength or maxLength to be present.");
494 else {
495 lengthFacet = decimal.Parse (lf.Value.Trim (), lengthStyle, CultureInfo.InvariantCulture);
496 /* TODO: Check that it is between inherited max/min lengths */
497 if (lengthFacet < 0)
498 lf.error(h, "The value '" + lengthFacet + "' is an invalid length");
500 } catch (FormatException) { // FIXME: better catch ;-(
501 lf.error (h, "The value '" + lf.Value + "' is an invalid length facet specification");
506 private void checkMaxLengthFacet(XmlSchemaMaxLengthFacet maxlf,
507 XmlSchemaFacet.Facet facetsDefined,
508 ValidationEventHandler h) {
509 if (maxlf != null) {
510 try {
511 if ((facetsDefined & XmlSchemaFacet.Facet.length) != 0)
512 maxlf.error(h, "It is an error for both length and minLength or maxLength to be present.");
513 else {
514 decimal newMaxLengthFacet = decimal.Parse (maxlf.Value.Trim (), lengthStyle, CultureInfo.InvariantCulture);
516 if (((fixedFacets & XmlSchemaFacet.Facet.maxLength)!=0) && (newMaxLengthFacet != maxLengthFacet))
517 maxlf.error(h, String.Format(CultureInfo.InvariantCulture, "The value '{0}' is not the same as the fixed value '{1}' on the base type", maxlf.Value.Trim (), maxLengthFacet));
518 if ((maxLengthFacet >0) && (newMaxLengthFacet > maxLengthFacet))
519 maxlf.error(h, String.Format(CultureInfo.InvariantCulture, "The value '{0}' is not a valid restriction of the value '{1}' on the base maxLength facet", maxlf.Value.Trim (), maxLengthFacet));
520 else
521 maxLengthFacet = newMaxLengthFacet;
522 if (maxLengthFacet < 0)
523 maxlf.error(h, "The value '" + maxLengthFacet + "' is an invalid maxLength");
524 if (minLengthFacet >=0 && minLengthFacet > maxLengthFacet)
525 maxlf.error(h, "minLength is greater than maxLength.");
528 } catch (FormatException) {
529 maxlf.error (h, "The value '" + maxlf.Value+ "' is an invalid maxLength facet specification");
534 private void checkMinLengthFacet(XmlSchemaMinLengthFacet minlf,
535 XmlSchemaFacet.Facet facetsDefined,
536 ValidationEventHandler h) {
537 if (minlf != null) {
538 try {
539 if (lengthFacet >=0)
540 minlf.error(h, "It is an error for both length and minLength or maxLength to be present.");
541 else {
542 decimal newMinLengthFacet = decimal.Parse (minlf.Value.Trim (), lengthStyle, CultureInfo.InvariantCulture);
544 if (((fixedFacets & XmlSchemaFacet.Facet.minLength)!=0) && (newMinLengthFacet != minLengthFacet))
545 minlf.error(h, String.Format(CultureInfo.InvariantCulture, "The value '{0}' is not the same as the fixed value '{1}' on the base type", minlf.Value.Trim (), minLengthFacet));
546 if (newMinLengthFacet < minLengthFacet)
547 minlf.error(h, String.Format(CultureInfo.InvariantCulture, "The value '{0}' is not a valid restriction of the value '{1}' on the base minLength facet", minlf.Value.Trim (), minLengthFacet));
548 else
549 minLengthFacet = newMinLengthFacet;
550 if (minLengthFacet < 0)
551 minlf.error(h, "The value '" + minLengthFacet + "' is an invalid minLength");
552 if (maxLengthFacet >=0 && minLengthFacet > maxLengthFacet)
553 minlf.error(h, "minLength is greater than maxLength.");
555 } catch (FormatException) {
556 minlf.error (h, "The value '" + minlf.Value + "' is an invalid minLength facet specification");
562 private XsdAnySimpleType getDatatype() {
563 XsdAnySimpleType ast = ActualBaseSchemaType as XsdAnySimpleType;
564 if (ast != null) {
565 // Based directly on an xsd type
566 return ast;
568 XmlSchemaSimpleTypeContent st = ((XmlSchemaSimpleType)ActualBaseSchemaType).Content as XmlSchemaSimpleTypeContent;
570 if (st is XmlSchemaSimpleTypeRestriction) {
571 return ((XmlSchemaSimpleTypeRestriction)st).getDatatype();
573 else if ((st is XmlSchemaSimpleTypeList) ||
574 (st is XmlSchemaSimpleTypeUnion)) {
575 return null;
577 return null;
581 private object ValidateValueWithDatatype(string value) {
582 XsdAnySimpleType dt = getDatatype();
583 object ret = null;
584 // Console.WriteLine("DT: " + dt);
585 if (dt != null) {
586 try {
587 /* I think we can parse null here, as the types
588 * that use the nametable and nsmgr are ones that
589 * we don't need to parse here.
591 ret = dt.ParseValue (value, null, null);
592 // Console.WriteLine("Ret: " + ret);
593 // If we are based on something with facets, check that we are valid
594 if (ActualBaseSchemaType is XmlSchemaSimpleType) {
595 XmlSchemaSimpleTypeContent st = ((XmlSchemaSimpleType) ActualBaseSchemaType).Content as XmlSchemaSimpleTypeContent;
596 if (st is XmlSchemaSimpleTypeRestriction) {
597 if (((XmlSchemaSimpleTypeRestriction)st).ValidateValueWithFacets(value, null, null)) {
598 return ret;
599 } else {
600 return null;
605 } catch (Exception) {
606 return null;
609 return ret;
612 internal bool ValidateValueWithFacets (string value, XmlNameTable nt, NSResolver nsmgr)
615 * FIXME: Shouldn't this be recursing more? What if this is a
616 * restriction of a restriction of a list type?
618 XmlSchemaSimpleType baseST = this.ActualBaseSchemaType as XmlSchemaSimpleType;
619 XmlSchemaSimpleTypeList listType = baseST != null ? baseST.Content as XmlSchemaSimpleTypeList : null;
621 // numeric
622 if (listType != null)
623 return ValidateListValueWithFacets (value, nt, nsmgr);
624 else
625 return ValidateNonListValueWithFacets (value, nt, nsmgr);
628 private bool ValidateListValueWithFacets (string value, XmlNameTable nt, NSResolver nsmgr)
630 try {
631 return ValidateListValueWithFacetsCore (value, nt, nsmgr);
632 } catch (Exception) { // this is for datatype ParseValue()
633 return false;
637 private bool ValidateListValueWithFacetsCore (string value, XmlNameTable nt, NSResolver nsmgr)
639 string [] list = ((XsdAnySimpleType) XmlSchemaDatatype.FromName ("anySimpleType", XmlSchema.Namespace)).ParseListValue (value, nt);
641 // pattern
642 if (this.patternFacetValues != null) {
643 for (int l = 0; l < list.Length; l++) {
644 for (int i = 0; i < this.patternFacetValues.Length; i++)
645 if (rexPatterns [i] != null && !rexPatterns [i].IsMatch (list [l]))
646 return false;
650 bool enumMatched = false;
651 #if true
652 // enumeration - lexical space comparison
654 // I'm not sure if allowing literally-equivalent values
655 // without parsing (because it will cause trouble when
656 // you try to get TypedValue anyways), but at least it
657 // avoids extraneous validation errors ...
658 if (this.enumarationFacetValues != null) {
659 for (int l = 0; l < list.Length; l++) {
660 for (int i = 0; i < this.enumarationFacetValues.Length; i++) {
661 if (list [l] == this.enumarationFacetValues [i]) {
662 enumMatched = true;
663 break;
668 #endif
669 // enumeration - value space comparison
670 if (!enumMatched && this.enumarationFacetValues != null) {
671 for (int l = 0; l < list.Length; l++) {
673 XsdAnySimpleType dt = getDatatype ();
674 if (dt == null)
675 dt = (XsdAnySimpleType) XmlSchemaDatatype.FromName ("anySimpleType", XmlSchema.Namespace);
676 object v = dt.ParseValue (list [l], nt, nsmgr);
678 for (int i = 0; i < this.enumarationFacetValues.Length; i++) {
679 if (XmlSchemaUtil.AreSchemaDatatypeEqual (dt, v, dt, dt.ParseValue (this.enumarationFacetValues [i], nt, nsmgr))) {
680 enumMatched = true;
681 break;
684 if (!enumMatched)
685 return false;
689 // numeric
690 // : length
691 if (lengthFacet >= 0 && list.Length != lengthFacet)
692 return false;
693 // : maxLength
694 if (maxLengthFacet >= 0 && list.Length > maxLengthFacet)
695 return false;
696 // : minLength
697 if (minLengthFacet >= 0 && list.Length < minLengthFacet)
698 return false;
700 return true;
703 private bool ValidateNonListValueWithFacets (string value, XmlNameTable nt, NSResolver nsmgr)
705 try {
706 return ValidateNonListValueWithFacetsCore (value, nt, nsmgr);
707 } catch (Exception) { // this is for datatype ParseValue()
708 return false;
712 private bool ValidateNonListValueWithFacetsCore (string value, XmlNameTable nt, NSResolver nsmgr)
714 // pattern
715 // Patterns are the only facets that need to be checked on this
716 // type and its base types. We should probably separate them, then
717 // do base-type pattern validation.
718 if (this.patternFacetValues != null) {
719 bool matched = false;
720 for (int i = 0; i < this.patternFacetValues.Length; i++)
721 if (rexPatterns [i] != null && rexPatterns [i].IsMatch (value)) {
722 matched = true;
723 break;
725 if (!matched)
726 return false;
729 XsdAnySimpleType dt = getDatatype ();
731 bool enumMatched = false;
733 #if true
734 // enumeration - lexical space comparison
736 // I'm not sure if allowing literally-equivalent values
737 // without parsing (because it will cause trouble when
738 // you try to get TypedValue anyways), but at least it
739 // avoids extraneous validation errors ...
740 if (this.enumarationFacetValues != null) {
741 for (int i = 0; i < this.enumarationFacetValues.Length; i++) {
742 if (value == this.enumarationFacetValues [i]) {
743 enumMatched = true;
744 break;
748 #endif
749 // enumeration - value space comparison
750 if (!enumMatched && this.enumarationFacetValues != null) {
751 XsdAnySimpleType edt = dt;
752 if (edt == null)
753 edt = (XsdAnySimpleType) XmlSchemaDatatype.FromName ("anySimpleType", XmlSchema.Namespace);
754 object v = edt.ParseValue (value, nt, nsmgr);
755 for (int i = 0; i < this.enumarationFacetValues.Length; i++) {
756 if (XmlSchemaUtil.AreSchemaDatatypeEqual (edt, v, edt, edt.ParseValue (this.enumarationFacetValues [i], nt, nsmgr))) {
757 enumMatched = true;
758 break;
761 if (!enumMatched)
762 return false;
765 // Need to skip length tests for
766 // types derived from QName or NOTATION
767 // see errata: E2-36 Clarification
769 if (! ( (dt is XsdQName) || (dt is XsdNotation))) {
770 // Length potentially slower now, so only calculate if needed
771 if (! ((lengthFacet == -1) && (maxLengthFacet == -1) && (minLengthFacet == -1))) {
773 // numeric
774 // : length
776 int length = dt.Length(value);
778 if (lengthFacet >= 0 && length != lengthFacet)
779 return false;
780 // : maxLength
781 if (maxLengthFacet >= 0 && length > maxLengthFacet)
782 return false;
783 // : minLength
784 if (minLengthFacet >= 0 && length < minLengthFacet)
785 return false;
790 if ((totalDigitsFacet >=0) || (fractionDigitsFacet >=0)) {
791 String newValue = value.Trim(new Char [] { '+', '-', '0', '.' });
792 int fractionDigits = 0;
793 int totalDigits = newValue.Length;
794 int point = newValue.IndexOf(".");
795 if (point != -1) {
796 totalDigits -= 1;
797 fractionDigits = newValue.Length - point -1;
799 if ((totalDigitsFacet >=0) && (totalDigits > totalDigitsFacet))
800 return false;
801 if ((fractionDigitsFacet >=0) && (fractionDigits > fractionDigitsFacet))
802 return false;
805 if ((maxInclusiveFacet != null) ||
806 (maxExclusiveFacet != null) ||
807 (minInclusiveFacet != null) ||
808 (minExclusiveFacet != null)) {
809 if (dt != null) {
810 object parsed;
811 try {
812 parsed = dt.ParseValue (value, nt, null);
813 } catch (OverflowException ) {
814 /* This appears to be what .NET does */
815 return false ;
816 } catch (FormatException ) {
817 /* This appears to be what .NET does */
818 return false ;
821 if (maxInclusiveFacet != null) {
822 XsdOrdering result = dt.Compare (parsed, maxInclusiveFacet);
823 if ((result != XsdOrdering.LessThan) &&
824 (result != XsdOrdering.Equal))
825 return false;
827 if (maxExclusiveFacet != null) {
829 XsdOrdering result = dt.Compare (parsed, maxExclusiveFacet);
830 if (result != XsdOrdering.LessThan)
831 return false;
833 if (minInclusiveFacet != null) {
834 XsdOrdering result = dt.Compare (parsed, minInclusiveFacet);
835 if ((result != XsdOrdering.GreaterThan) &&
836 (result != XsdOrdering.Equal))
837 return false;
839 if (minExclusiveFacet != null) {
840 XsdOrdering result = dt.Compare (parsed, minExclusiveFacet);
841 if (result != XsdOrdering.GreaterThan)
842 return false;
848 // all passed
849 return true;
852 //<restriction
853 // base = QName
854 // id = ID
855 // {any attributes with non-schema namespace . . .}>
856 // Content: (annotation?, (simpleType?, (minExclusive | minInclusive | maxExclusive | maxInclusive | totalDigits | fractionDigits | length | minLength | maxLength | enumeration | whiteSpace | pattern)*))
857 //</restriction>
858 internal static XmlSchemaSimpleTypeRestriction Read(XmlSchemaReader reader, ValidationEventHandler h)
860 XmlSchemaSimpleTypeRestriction restriction = new XmlSchemaSimpleTypeRestriction();
861 reader.MoveToElement();
863 if(reader.NamespaceURI != XmlSchema.Namespace || reader.LocalName != xmlname)
865 error(h,"Should not happen :1: XmlSchemaSimpleTypeRestriction.Read, name="+reader.Name,null);
866 reader.Skip();
867 return null;
870 restriction.LineNumber = reader.LineNumber;
871 restriction.LinePosition = reader.LinePosition;
872 restriction.SourceUri = reader.BaseURI;
874 while(reader.MoveToNextAttribute())
876 if(reader.Name == "id")
878 restriction.Id = reader.Value;
880 else if(reader.Name == "base")
882 Exception innerex;
883 restriction.baseTypeName = XmlSchemaUtil.ReadQNameAttribute(reader,out innerex);
884 if(innerex != null)
885 error(h, reader.Value + " is not a valid value for base attribute",innerex);
887 else if((reader.NamespaceURI == "" && reader.Name != "xmlns") || reader.NamespaceURI == XmlSchema.Namespace)
889 error(h,reader.Name + " is not a valid attribute for restriction",null);
891 else
893 XmlSchemaUtil.ReadUnhandledAttribute(reader,restriction);
897 reader.MoveToElement();
898 if(reader.IsEmptyElement)
899 return restriction;
901 // Content: annotation?, simpleType?, (minExclusive |. .. | pattern)*
902 int level = 1;
903 while(reader.ReadNextElement())
905 if(reader.NodeType == XmlNodeType.EndElement)
907 if(reader.LocalName != xmlname)
908 error(h,"Should not happen :2: XmlSchemaSimpleTypeRestriction.Read, name="+reader.Name,null);
909 break;
911 if(level <= 1 && reader.LocalName == "annotation")
913 level = 2; //Only one annotation
914 XmlSchemaAnnotation annotation = XmlSchemaAnnotation.Read(reader,h);
915 if(annotation != null)
916 restriction.Annotation = annotation;
917 continue;
919 if(level <= 2 && reader.LocalName == "simpleType")
921 level = 3;
922 XmlSchemaSimpleType stype = XmlSchemaSimpleType.Read(reader,h);
923 if(stype != null)
924 restriction.baseType = stype;
925 continue;
927 if(level <= 3)
929 if(reader.LocalName == "minExclusive")
931 level = 3;
932 XmlSchemaMinExclusiveFacet minex = XmlSchemaMinExclusiveFacet.Read(reader,h);
933 if(minex != null)
934 restriction.facets.Add(minex);
935 continue;
937 else if(reader.LocalName == "minInclusive")
939 level = 3;
940 XmlSchemaMinInclusiveFacet mini = XmlSchemaMinInclusiveFacet.Read(reader,h);
941 if(mini != null)
942 restriction.facets.Add(mini);
943 continue;
945 else if(reader.LocalName == "maxExclusive")
947 level = 3;
948 XmlSchemaMaxExclusiveFacet maxex = XmlSchemaMaxExclusiveFacet.Read(reader,h);
949 if(maxex != null)
950 restriction.facets.Add(maxex);
951 continue;
953 else if(reader.LocalName == "maxInclusive")
955 level = 3;
956 XmlSchemaMaxInclusiveFacet maxi = XmlSchemaMaxInclusiveFacet.Read(reader,h);
957 if(maxi != null)
958 restriction.facets.Add(maxi);
959 continue;
961 else if(reader.LocalName == "totalDigits")
963 level = 3;
964 XmlSchemaTotalDigitsFacet total = XmlSchemaTotalDigitsFacet.Read(reader,h);
965 if(total != null)
966 restriction.facets.Add(total);
967 continue;
969 else if(reader.LocalName == "fractionDigits")
971 level = 3;
972 XmlSchemaFractionDigitsFacet fraction = XmlSchemaFractionDigitsFacet.Read(reader,h);
973 if(fraction != null)
974 restriction.facets.Add(fraction);
975 continue;
977 else if(reader.LocalName == "length")
979 level = 3;
980 XmlSchemaLengthFacet length = XmlSchemaLengthFacet.Read(reader,h);
981 if(length != null)
982 restriction.facets.Add(length);
983 continue;
985 else if(reader.LocalName == "minLength")
987 level = 3;
988 XmlSchemaMinLengthFacet minlen = XmlSchemaMinLengthFacet.Read(reader,h);
989 if(minlen != null)
990 restriction.facets.Add(minlen);
991 continue;
993 else if(reader.LocalName == "maxLength")
995 level = 3;
996 XmlSchemaMaxLengthFacet maxlen = XmlSchemaMaxLengthFacet.Read(reader,h);
997 if(maxlen != null)
998 restriction.facets.Add(maxlen);
999 continue;
1001 else if(reader.LocalName == "enumeration")
1003 level = 3;
1004 XmlSchemaEnumerationFacet enumeration = XmlSchemaEnumerationFacet.Read(reader,h);
1005 if(enumeration != null)
1006 restriction.facets.Add(enumeration);
1007 continue;
1009 else if(reader.LocalName == "whiteSpace")
1011 level = 3;
1012 XmlSchemaWhiteSpaceFacet ws = XmlSchemaWhiteSpaceFacet.Read(reader,h);
1013 if(ws != null)
1014 restriction.facets.Add(ws);
1015 continue;
1017 else if(reader.LocalName == "pattern")
1019 level = 3;
1020 XmlSchemaPatternFacet pattern = XmlSchemaPatternFacet.Read(reader,h);
1021 if(pattern != null)
1022 restriction.facets.Add(pattern);
1023 continue;
1026 reader.RaiseInvalidElementError();
1028 return restriction;