3 // Permission is hereby granted, free of charge, to any person obtaining
4 // a copy of this software and associated documentation files (the
5 // "Software"), to deal in the Software without restriction, including
6 // without limitation the rights to use, copy, modify, merge, publish,
7 // distribute, sublicense, and/or sell copies of the Software, and to
8 // permit persons to whom the Software is furnished to do so, subject to
9 // the following conditions:
11 // The above copyright notice and this permission notice shall be
12 // included in all copies or substantial portions of the Software.
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 using System.Collections;
27 using Mono.Xml.Schema;
28 using System.Xml.Serialization;
30 namespace System.Xml.Schema
33 /// All Methods in this class should use XmlConvert. Some Methods are not present in the
34 /// MS Implementation. We should provide them.
36 internal class XmlSchemaUtil
38 static XmlSchemaUtil ()
40 FinalAllowed = XmlSchemaDerivationMethod.Restriction |
41 XmlSchemaDerivationMethod.Extension;
42 ComplexTypeBlockAllowed = FinalAllowed;
43 ElementBlockAllowed = XmlSchemaDerivationMethod.Substitution |
47 internal static XmlSchemaDerivationMethod FinalAllowed;
48 internal static XmlSchemaDerivationMethod ElementBlockAllowed;
49 internal static XmlSchemaDerivationMethod ComplexTypeBlockAllowed;
52 public static void AddToTable (XmlSchemaObjectTable table, XmlSchemaObject obj,
53 XmlQualifiedName qname, ValidationEventHandler h)
55 if (table.Contains (qname)) {
56 // FIXME: This logic unexpectedly allows
57 // one redefining item and two or more redefining items.
58 // FIXME: redefining item is not simple replacement,
59 // but much more complex stuff.
60 if (obj.isRedefineChild) { // take precedence.
61 if (obj.redefinedObject != null)
62 obj.error (h, String.Format ("Named item {0} was already contained in the schema object table.", qname));
64 obj.redefinedObject = table [qname];
65 table.Set (qname, obj);
67 else if (table [qname].isRedefineChild) {
68 if (table [qname].redefinedObject != null)
69 obj.error (h, String.Format ("Named item {0} was already contained in the schema object table.", qname));
71 table [qname].redefinedObject = obj;
72 return; // never add to the table.
75 obj.error (h, String.Format ("Named item {0} was already contained in the schema object table.", qname));
78 table.Set (qname, obj);
81 public static void CompileID (string id, XmlSchemaObject xso, Hashtable idCollection, ValidationEventHandler h)
83 //check if the string conforms to http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/datatypes.html#ID
84 // 1. ID must be a NCName
85 // 2. ID must be unique in the schema
89 xso.error(h,id+" is not a valid id attribute");
90 else if(idCollection.ContainsKey(id))
91 xso.error(h,"Duplicate id attribute "+id);
93 idCollection.Add(id,xso);
96 public static bool CheckAnyUri (string uri)
98 if (uri.StartsWith ("##"))
103 public static bool CheckNormalizedString (string token)
108 public static bool CheckNCName (string name)
110 //check if the string conforms to http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/datatypes.html#NCName
111 return XmlChar.IsNCName (name);
114 public static bool CheckQName (XmlQualifiedName qname)
116 // What is this doing?
120 public static XmlParserContext GetParserContext (XmlReader reader)
122 IHasXmlParserContext xctx = reader as IHasXmlParserContext;
124 return xctx.ParserContext;
129 public static bool IsBuiltInDatatypeName (XmlQualifiedName qname)
131 if (qname.Namespace == XmlSchema.XdtNamespace) {
132 switch (qname.Name) {
133 case "anyAtomicType":
134 case "untypedAtomic":
135 case "dayTimeDuration":
136 case "yearMonthDuration":
142 if (qname.Namespace != XmlSchema.Namespace)
144 switch (qname.Name) {
145 case "anySimpleType":
146 case "duration": case "dateTime": case "time":
147 case "date": case "gYearMonth": case "gYear":
148 case "gMonthDay": case "gDay": case "gMonth":
150 case "base64Binary": case "hexBinary":
151 case "float": case "double":
155 case "string": case "normalizedString": case "token":
156 case "language": case "Name": case "NCName":
157 case "ID": case "IDREF": case "IDREFS":
158 case "ENTITY": case "ENTITIES":
159 case "NMTOKEN": case "NMTOKENS":
160 case "decimal": case "integer":
161 case "nonPositiveInteger": case "negativeInteger":
162 case "nonNegativeInteger":
163 case "unsignedLong": case "unsignedInt":
164 case "unsignedShort": case "unsignedByte":
165 case "positiveInteger":
166 case "long": case "int": case "short": case "byte":
172 public static bool IsSchemaDatatypeEquals (XsdAnySimpleType st1, object v1,
173 XsdAnySimpleType st2, object v2)
175 if (v1 == null || v2 == null)
179 st1 = XmlSchemaSimpleType.AnySimpleType;
181 st2 = XmlSchemaSimpleType.AnySimpleType;
183 Type t = st2.GetType ();
184 if (st1 is XsdFloat) {
185 return st2 is XsdFloat && Convert.ToSingle (v1) == Convert.ToSingle (v2);
186 } else if (st1 is XsdDouble) {
187 return st2 is XsdDouble && Convert.ToDouble (v1) == Convert.ToDouble (v2);
188 } else if (st1 is XsdDecimal) {
189 if (!(st2 is XsdDecimal) || Convert.ToDecimal (v1) != Convert.ToDecimal (v2))
191 if (st1 is XsdNonPositiveInteger)
192 return st2 is XsdNonPositiveInteger || t == typeof (XsdDecimal) || t == typeof (XsdInteger);
193 else if (st1 is XsdPositiveInteger)
194 return st2 is XsdPositiveInteger || t == typeof (XsdDecimal) ||
195 t == typeof (XsdInteger) || t == typeof (XsdNonNegativeInteger);
196 else if (st1 is XsdUnsignedLong)
197 return st2 is XsdUnsignedLong || t == typeof (XsdDecimal) ||
198 t == typeof (XsdInteger) || t == typeof (XsdNonNegativeInteger);
199 else if (st1 is XsdNonNegativeInteger)
200 return st2 is XsdNonNegativeInteger || t == typeof (XsdDecimal) || t == typeof (XsdInteger);
201 else if (st1 is XsdLong)
202 return st2 is XsdLong || t == typeof (XsdDecimal) || t == typeof (XsdInteger);
205 else if (!v1.Equals (v2))
207 if (st1 is XsdString) {
208 if (!(st2 is XsdString))
210 if (st1 is XsdNMToken && (st2 is XsdLanguage || st2 is XsdName))
212 if (st2 is XsdNMToken && (st1 is XsdLanguage || st1 is XsdName))
214 if (st1 is XsdName && (st2 is XsdLanguage || st2 is XsdNMToken))
216 if (st2 is XsdName && (st1 is XsdLanguage || st1 is XsdNMToken))
218 if (st1 is XsdID && st2 is XsdIDRef)
220 if (st1 is XsdIDRef && st2 is XsdID)
228 public static bool IsValidQName(string qname)
230 foreach(string part in qname.Split(new char[]{':'},2))
232 if(!CheckNCName(part))
238 //FIXME: First remove all the multiple instances of whitespace and then return the strings.
239 //The current method returns empty strings if there are two or more consecutive whitespaces.
240 public static string[] SplitList(string list)
242 if(list == null || list == string.Empty)
243 return new string [0];
248 for (int i = 0; i < list.Length; i++) {
256 al = new ArrayList ();
257 al.Add (list.Substring (start, i - start));
270 if (!wait && start == 0)
271 return new string [] {list};
273 if (!wait && start < list.Length)
274 al.Add (start == 0 ? list : list.Substring (start));
275 return al.ToArray (typeof (string)) as string [];
278 public static void ReadUnhandledAttribute(XmlReader reader, XmlSchemaObject xso)
280 if(reader.Prefix == "xmlns")
281 xso.Namespaces.Add(reader.LocalName, reader.Value);
282 else if(reader.Name == "xmlns")
283 xso.Namespaces.Add("",reader.Value);
286 if(xso.unhandledAttributeList == null)
287 xso.unhandledAttributeList = new System.Collections.ArrayList();
288 XmlAttribute attr = new XmlDocument().CreateAttribute(reader.LocalName,reader.NamespaceURI);
289 attr.Value = reader.Value;
290 ParseWsdlArrayType (reader, attr);
291 xso.unhandledAttributeList.Add(attr);
295 static void ParseWsdlArrayType (XmlReader reader, XmlAttribute attr)
297 if (attr.NamespaceURI == XmlSerializer.WsdlNamespace && attr.LocalName == "arrayType")
299 string ns = "", type, dimensions;
300 TypeTranslator.ParseArrayType (attr.Value, out type, out ns, out dimensions);
301 if (ns != "") ns = reader.LookupNamespace (ns) + ":";
302 attr.Value = ns + type + dimensions;
306 public static bool ReadBoolAttribute(XmlReader reader, out Exception innerExcpetion)
308 innerExcpetion = null;
311 bool val = XmlConvert.ToBoolean(reader.Value);
320 public static decimal ReadDecimalAttribute(XmlReader reader, out Exception innerExcpetion)
322 innerExcpetion = null;
325 decimal val = XmlConvert.ToDecimal(reader.Value);
335 // Is some value is read, return it.
336 // If no values return empty.
337 // If exception, return none
338 public static XmlSchemaDerivationMethod ReadDerivationAttribute(XmlReader reader, out Exception innerExcpetion, string name, XmlSchemaDerivationMethod allowed)
340 innerExcpetion = null;
343 string list = reader.Value;
345 XmlSchemaDerivationMethod val = 0;
347 if(list.IndexOf("#all") != -1 && list.Trim() != "#all")
349 innerExcpetion = new Exception(list+" is not a valid value for "+ name +". #all if present must be the only value");
350 return XmlSchemaDerivationMethod.All;
352 foreach(string xsdm in XmlSchemaUtil.SplitList(list))
357 val = AddFlag (val, XmlSchemaDerivationMethod.Empty, allowed); break;
359 val = AddFlag (val,XmlSchemaDerivationMethod.All, allowed); break;
361 val = AddFlag (val,XmlSchemaDerivationMethod.Substitution, allowed); break;
363 val = AddFlag (val,XmlSchemaDerivationMethod.Extension, allowed); break;
365 val = AddFlag (val,XmlSchemaDerivationMethod.Restriction, allowed); break;
367 val = AddFlag (val,XmlSchemaDerivationMethod.List, allowed); break;
369 val = AddFlag (val,XmlSchemaDerivationMethod.Union, allowed); break;
371 warn += xsdm + " "; break;
375 innerExcpetion = new Exception(warn + "is/are not valid values for " + name);
381 return XmlSchemaDerivationMethod.None;
385 private static XmlSchemaDerivationMethod AddFlag (XmlSchemaDerivationMethod dst,
386 XmlSchemaDerivationMethod add, XmlSchemaDerivationMethod allowed)
388 if ((add & allowed) == 0 && allowed != XmlSchemaDerivationMethod.All)
389 throw new ArgumentException (add + " is not allowed in this attribute.");
390 if ((dst & add) != 0)
391 throw new ArgumentException (add + " is already specified in this attribute.");
395 public static XmlSchemaForm ReadFormAttribute(XmlReader reader, out Exception innerExcpetion)
397 innerExcpetion = null;
398 XmlSchemaForm val = XmlSchemaForm.None;
402 val = XmlSchemaForm.Qualified; break;
404 val = XmlSchemaForm.Unqualified; break;
406 innerExcpetion = new Exception("only qualified or unqulified is a valid value"); break;
411 public static XmlSchemaContentProcessing ReadProcessingAttribute(XmlReader reader, out Exception innerExcpetion)
413 innerExcpetion = null;
414 XmlSchemaContentProcessing val = XmlSchemaContentProcessing.None;
418 val = XmlSchemaContentProcessing.Lax; break;
420 val = XmlSchemaContentProcessing.Strict; break;
422 val = XmlSchemaContentProcessing.Skip; break;
424 innerExcpetion = new Exception("only lax , strict or skip are valid values for processContents");
430 public static XmlSchemaUse ReadUseAttribute(XmlReader reader, out Exception innerExcpetion)
432 innerExcpetion = null;
433 XmlSchemaUse val = XmlSchemaUse.None;
437 val = XmlSchemaUse.Optional; break;
439 val = XmlSchemaUse.Prohibited; break;
441 val = XmlSchemaUse.Required; break;
443 innerExcpetion = new Exception("only optional , prohibited or required are valid values for use");
448 public static XmlQualifiedName ReadQNameAttribute(XmlReader reader, out Exception innerEx)
450 return ToQName(reader, reader.Value, out innerEx);
453 //While Creating a XmlQualifedName, we should check:
454 // 1. If a prefix is present, its namespace should be resolvable.
455 // 2. If a prefix is not present, and if the defaultNamespace is set,
456 public static XmlQualifiedName ToQName(XmlReader reader, string qnamestr, out Exception innerEx)
461 XmlQualifiedName qname;
464 if(!IsValidQName(qnamestr))
466 innerEx = new Exception(qnamestr + " is an invalid QName. Either name or namespace is not a NCName");
467 return XmlQualifiedName.Empty;
470 string[] values = qnamestr.Split(new char[]{':'},2);
472 if(values.Length == 2)
474 ns = reader.LookupNamespace(values[0]);
477 innerEx = new Exception("Namespace Prefix '"+values[0]+"could not be resolved");
478 return XmlQualifiedName.Empty;
485 ns = reader.LookupNamespace("");
489 qname = new XmlQualifiedName(name,ns);
493 public static int ValidateAttributesResolved (
494 XmlSchemaObjectTable attributesResolved,
495 ValidationEventHandler h,
497 XmlSchemaObjectCollection attributes,
498 XmlSchemaAnyAttribute anyAttribute,
499 ref XmlSchemaAnyAttribute anyAttributeUse,
500 XmlSchemaAttributeGroup redefined,
504 if (anyAttribute != null && anyAttributeUse == null)
505 anyAttributeUse = anyAttribute;
507 ArrayList newAttrNames = new ArrayList ();
509 foreach (XmlSchemaObject xsobj in attributes) {
510 XmlSchemaAttributeGroupRef grpRef = xsobj as XmlSchemaAttributeGroupRef;
511 if (grpRef != null) {
512 // Resolve attributeGroup redefinition.
513 XmlSchemaAttributeGroup grp = null;
514 if (redefined != null && grpRef.RefName == redefined.QualifiedName)
517 grp = schema.FindAttributeGroup (grpRef.RefName);
518 // otherwise, it might be missing sub components.
520 if (!schema.missedSubComponents)// && schema.Schemas [grpRef.RefName.Namespace] != null)
521 grpRef.error (h, "Referenced attribute group " + grpRef.RefName + " was not found in the corresponding schema.");
524 if (grp.AttributeGroupRecursionCheck) {
525 grp.error (h, "Attribute group recursion was found: " + grpRef.RefName);
529 grp.AttributeGroupRecursionCheck = true;
530 errorCount += grp.Validate (h, schema);
532 grp.AttributeGroupRecursionCheck = false;
534 if (grp.AnyAttributeUse != null) {
535 if (anyAttribute == null)
536 anyAttributeUse = grp.AnyAttributeUse;
538 foreach (DictionaryEntry entry in grp.AttributeUses) {
539 XmlSchemaAttribute attr = (XmlSchemaAttribute) entry.Value;
540 #if BUGGY_MS_COMPLIANT
541 if (attr.Use == XmlSchemaUse.Prohibited)
544 if (attr.RefName != null && attr.RefName != XmlQualifiedName.Empty && (!skipEquivalent || !AreAttributesEqual (attr, attributesResolved [attr.RefName] as XmlSchemaAttribute)))
545 AddToTable (attributesResolved, attr, attr.RefName, h);
546 else if (!skipEquivalent || !AreAttributesEqual (attr, attributesResolved [attr.QualifiedName] as XmlSchemaAttribute))
547 AddToTable (attributesResolved, attr, attr.QualifiedName, h);
550 XmlSchemaAttribute attr = xsobj as XmlSchemaAttribute;
552 errorCount += attr.Validate (h, schema);
554 if (newAttrNames.Contains (attr.QualifiedName))
555 attr.error (h, String.Format ("Duplicate attributes was found for '{0}'", attr.QualifiedName));
556 newAttrNames.Add (attr.QualifiedName);
558 #if BUGGY_MS_COMPLIANT
559 if (attr.Use == XmlSchemaUse.Prohibited)
562 if (attr.RefName != null && attr.RefName != XmlQualifiedName.Empty && (!skipEquivalent || !AreAttributesEqual (attr, attributesResolved [attr.RefName] as XmlSchemaAttribute)))
563 AddToTable (attributesResolved, attr, attr.RefName, h);
564 else if (!skipEquivalent || !AreAttributesEqual (attr, attributesResolved [attr.QualifiedName] as XmlSchemaAttribute))
565 AddToTable (attributesResolved, attr, attr.QualifiedName, h);
567 if (anyAttribute == null) {
568 anyAttributeUse = (XmlSchemaAnyAttribute) xsobj;
569 anyAttribute.Validate (h, schema);
577 internal static bool AreAttributesEqual (XmlSchemaAttribute one,
578 XmlSchemaAttribute another)
580 if (one == null || another == null)
582 return one.AttributeType == another.AttributeType &&
583 one.Form == another.Form &&
584 one.ValidatedUse == another.ValidatedUse &&
585 one.ValidatedDefaultValue == another.ValidatedDefaultValue &&
586 one.ValidatedFixedValue == another.ValidatedFixedValue;
590 public static object ReadTypedValue (XmlReader reader,
591 object type, IXmlNamespaceResolver nsResolver,
592 StringBuilder tmpBuilder)
594 public static object ReadTypedValue (XmlReader reader,
595 object type, XmlNamespaceManager nsResolver,
596 StringBuilder tmpBuilder)
599 if (tmpBuilder == null)
600 tmpBuilder = new StringBuilder ();
601 XmlSchemaDatatype dt = type as XmlSchemaDatatype;
602 XmlSchemaSimpleType st = type as XmlSchemaSimpleType;
608 switch (reader.NodeType) {
609 case XmlNodeType.Element:
610 if (reader.IsEmptyElement)
613 tmpBuilder.Length = 0;
617 switch (reader.NodeType) {
618 case XmlNodeType.SignificantWhitespace:
619 case XmlNodeType.Text:
620 case XmlNodeType.CDATA:
621 tmpBuilder.Append (reader.Value);
623 case XmlNodeType.Comment:
629 } while (loop && !reader.EOF && reader.ReadState == ReadState.Interactive);
630 return dt.ParseValue (tmpBuilder.ToString (), reader.NameTable, nsResolver);
631 case XmlNodeType.Attribute:
632 return dt.ParseValue (reader.Value, reader.NameTable, nsResolver);
637 public static XmlSchemaObject FindAttributeDeclaration (
639 XmlSchemaSet schemas,
640 XmlSchemaComplexType cType,
641 XmlQualifiedName qname)
643 XmlSchemaObject result = cType.AttributeUses [qname];
646 if (cType.AttributeWildcard == null)
649 if (!AttributeWildcardItemValid (cType.AttributeWildcard, qname, ns))
652 if (cType.AttributeWildcard.ResolvedProcessContents == XmlSchemaContentProcessing.Skip)
653 return cType.AttributeWildcard;
654 XmlSchemaAttribute attr = schemas.GlobalAttributes [qname] as XmlSchemaAttribute;
657 if (cType.AttributeWildcard.ResolvedProcessContents == XmlSchemaContentProcessing.Lax)
658 return cType.AttributeWildcard;
663 // Spec 3.10.4 Item Valid (Wildcard)
664 private static bool AttributeWildcardItemValid (XmlSchemaAnyAttribute anyAttr, XmlQualifiedName qname, string ns)
666 if (anyAttr.HasValueAny)
668 if (anyAttr.HasValueOther && (anyAttr.TargetNamespace == "" || ns != anyAttr.TargetNamespace))
670 if (anyAttr.HasValueTargetNamespace && ns == anyAttr.TargetNamespace)
672 if (anyAttr.HasValueLocal && ns == "")
674 for (int i = 0; i < anyAttr.ResolvedNamespaces.Count; i++)
675 if (anyAttr.ResolvedNamespaces [i] == ns)