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
;
50 internal static readonly bool StrictMsCompliant
= Environment
.GetEnvironmentVariable ("MONO_STRICT_MS_COMPLIANT") == "yes";
53 public static void AddToTable (XmlSchemaObjectTable table
, XmlSchemaObject obj
,
54 XmlQualifiedName qname
, ValidationEventHandler h
)
56 if (table
.Contains (qname
)) {
57 // FIXME: This logic unexpectedly allows
58 // one redefining item and two or more redefining items.
59 // FIXME: redefining item is not simple replacement,
60 // but much more complex stuff.
61 if (obj
.isRedefineChild
) { // take precedence.
62 if (obj
.redefinedObject
!= null)
63 obj
.error (h
, String
.Format ("Named item {0} was already contained in the schema object table.", qname
));
65 obj
.redefinedObject
= table
[qname
];
66 table
.Set (qname
, obj
);
68 else if (table
[qname
].isRedefineChild
) {
69 if (table
[qname
].redefinedObject
!= null)
70 obj
.error (h
, String
.Format ("Named item {0} was already contained in the schema object table.", qname
));
72 table
[qname
].redefinedObject
= obj
;
73 return; // never add to the table.
75 else if (StrictMsCompliant
) {
76 table
.Set (qname
, obj
);
79 obj
.error (h
, String
.Format ("Named item {0} was already contained in the schema object table. {1}",
80 qname
, "Consider setting MONO_STRICT_MS_COMPLIANT to 'yes' to mimic MS implementation."));
83 table
.Set (qname
, obj
);
86 public static void CompileID (string id
, XmlSchemaObject xso
, Hashtable idCollection
, ValidationEventHandler h
)
88 //check if the string conforms to http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/datatypes.html#ID
89 // 1. ID must be a NCName
90 // 2. ID must be unique in the schema
94 xso
.error(h
,id
+" is not a valid id attribute");
95 else if(idCollection
.ContainsKey(id
))
96 xso
.error(h
,"Duplicate id attribute "+id
);
98 idCollection
.Add(id
,xso
);
101 public static bool CheckAnyUri (string uri
)
103 if (uri
.StartsWith ("##"))
108 public static bool CheckNormalizedString (string token
)
113 public static bool CheckNCName (string name
)
115 //check if the string conforms to http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/datatypes.html#NCName
116 return XmlChar
.IsNCName (name
);
119 public static bool CheckQName (XmlQualifiedName qname
)
121 // What is this doing?
125 public static XmlParserContext
GetParserContext (XmlReader reader
)
127 IHasXmlParserContext xctx
= reader
as IHasXmlParserContext
;
129 return xctx
.ParserContext
;
134 public static bool IsBuiltInDatatypeName (XmlQualifiedName qname
)
136 if (qname
.Namespace
== XmlSchema
.XdtNamespace
) {
137 switch (qname
.Name
) {
138 case "anyAtomicType":
139 case "untypedAtomic":
140 case "dayTimeDuration":
141 case "yearMonthDuration":
147 if (qname
.Namespace
!= XmlSchema
.Namespace
)
149 switch (qname
.Name
) {
150 case "anySimpleType":
151 case "duration": case "dateTime": case "time":
152 case "date": case "gYearMonth": case "gYear":
153 case "gMonthDay": case "gDay": case "gMonth":
155 case "base64Binary": case "hexBinary":
156 case "float": case "double":
160 case "string": case "normalizedString": case "token":
161 case "language": case "Name": case "NCName":
162 case "ID": case "IDREF": case "IDREFS":
163 case "ENTITY": case "ENTITIES":
164 case "NMTOKEN": case "NMTOKENS":
165 case "decimal": case "integer":
166 case "nonPositiveInteger": case "negativeInteger":
167 case "nonNegativeInteger":
168 case "unsignedLong": case "unsignedInt":
169 case "unsignedShort": case "unsignedByte":
170 case "positiveInteger":
171 case "long": case "int": case "short": case "byte":
177 public static bool AreSchemaDatatypeEqual (XmlSchemaSimpleType st1
, object v1
, XmlSchemaSimpleType st2
, object v2
)
179 if (st1
.Datatype
is XsdAnySimpleType
)
180 return AreSchemaDatatypeEqual (st1
.Datatype
as XsdAnySimpleType
, v1
, st2
.Datatype
as XsdAnySimpleType
, v2
);
181 // otherwise the types are lists of strings.
182 string [] a1
= v1
as string [];
183 string [] a2
= v2
as string [];
184 if (st1
!= st2
|| a1
== null || a2
== null || a1
.Length
!= a2
.Length
)
186 for (int i
= 0; i
< a1
.Length
; i
++)
187 if (a1
[i
] != a2
[i
])
192 public static bool AreSchemaDatatypeEqual (XsdAnySimpleType st1
, object v1
,
193 XsdAnySimpleType st2
, object v2
)
195 if (v1
== null || v2
== null)
199 st1
= XmlSchemaSimpleType
.AnySimpleType
;
201 st2
= XmlSchemaSimpleType
.AnySimpleType
;
203 Type t
= st2
.GetType ();
204 if (st1
is XsdFloat
) {
205 return st2
is XsdFloat
&& Convert
.ToSingle (v1
) == Convert
.ToSingle (v2
);
206 } else if (st1
is XsdDouble
) {
207 return st2
is XsdDouble
&& Convert
.ToDouble (v1
) == Convert
.ToDouble (v2
);
208 } else if (st1
is XsdDecimal
) {
209 if (!(st2
is XsdDecimal
) || Convert
.ToDecimal (v1
) != Convert
.ToDecimal (v2
))
211 if (st1
is XsdNonPositiveInteger
)
212 return st2
is XsdNonPositiveInteger
|| t
== typeof (XsdDecimal
) || t
== typeof (XsdInteger
);
213 else if (st1
is XsdPositiveInteger
)
214 return st2
is XsdPositiveInteger
|| t
== typeof (XsdDecimal
) ||
215 t
== typeof (XsdInteger
) || t
== typeof (XsdNonNegativeInteger
);
216 else if (st1
is XsdUnsignedLong
)
217 return st2
is XsdUnsignedLong
|| t
== typeof (XsdDecimal
) ||
218 t
== typeof (XsdInteger
) || t
== typeof (XsdNonNegativeInteger
);
219 else if (st1
is XsdNonNegativeInteger
)
220 return st2
is XsdNonNegativeInteger
|| t
== typeof (XsdDecimal
) || t
== typeof (XsdInteger
);
221 else if (st1
is XsdLong
)
222 return st2
is XsdLong
|| t
== typeof (XsdDecimal
) || t
== typeof (XsdInteger
);
225 else if (!v1
.Equals (v2
))
227 if (st1
is XsdString
) {
228 if (!(st2
is XsdString
))
230 if (st1
is XsdNMToken
&& (st2
is XsdLanguage
|| st2
is XsdName
))
232 if (st2
is XsdNMToken
&& (st1
is XsdLanguage
|| st1
is XsdName
))
234 if (st1
is XsdName
&& (st2
is XsdLanguage
|| st2
is XsdNMToken
))
236 if (st2
is XsdName
&& (st1
is XsdLanguage
|| st1
is XsdNMToken
))
238 if (st1
is XsdID
&& st2
is XsdIDRef
)
240 if (st1
is XsdIDRef
&& st2
is XsdID
)
248 public static bool IsValidQName(string qname
)
250 foreach(string part
in qname
.Split(new char[]{':'}
,2))
252 if(!CheckNCName(part
))
258 //FIXME: First remove all the multiple instances of whitespace and then return the strings.
259 //The current method returns empty strings if there are two or more consecutive whitespaces.
260 public static string[] SplitList(string list
)
262 if(list
== null || list
== string.Empty
)
263 return new string [0];
268 for (int i
= 0; i
< list
.Length
; i
++) {
276 al
= new ArrayList ();
277 al
.Add (list
.Substring (start
, i
- start
));
290 if (!wait
&& start
== 0)
291 return new string [] {list}
;
293 if (!wait
&& start
< list
.Length
)
294 al
.Add (start
== 0 ? list
: list
.Substring (start
));
295 return al
.ToArray (typeof (string)) as string [];
298 public static void ReadUnhandledAttribute(XmlReader reader
, XmlSchemaObject xso
)
300 if(reader
.Prefix
== "xmlns")
301 xso
.Namespaces
.Add(reader
.LocalName
, reader
.Value
);
302 else if(reader
.Name
== "xmlns")
303 xso
.Namespaces
.Add("",reader
.Value
);
306 if(xso
.unhandledAttributeList
== null)
307 xso
.unhandledAttributeList
= new System
.Collections
.ArrayList();
308 XmlAttribute attr
= new XmlDocument().CreateAttribute(reader
.LocalName
,reader
.NamespaceURI
);
309 attr
.Value
= reader
.Value
;
310 ParseWsdlArrayType (reader
, attr
);
311 xso
.unhandledAttributeList
.Add(attr
);
315 static void ParseWsdlArrayType (XmlReader reader
, XmlAttribute attr
)
317 if (attr
.NamespaceURI
== XmlSerializer
.WsdlNamespace
&& attr
.LocalName
== "arrayType")
319 string ns
= "", type
, dimensions
;
320 TypeTranslator
.ParseArrayType (attr
.Value
, out type
, out ns
, out dimensions
);
321 if (ns
!= "") ns
= reader
.LookupNamespace (ns
) + ":";
322 attr
.Value
= ns
+ type
+ dimensions
;
326 public static bool ReadBoolAttribute(XmlReader reader
, out Exception innerExcpetion
)
328 innerExcpetion
= null;
331 bool val
= XmlConvert
.ToBoolean(reader
.Value
);
340 public static decimal ReadDecimalAttribute(XmlReader reader
, out Exception innerExcpetion
)
342 innerExcpetion
= null;
345 decimal val
= XmlConvert
.ToDecimal(reader
.Value
);
355 // Is some value is read, return it.
356 // If no values return empty.
357 // If exception, return none
358 public static XmlSchemaDerivationMethod
ReadDerivationAttribute(XmlReader reader
, out Exception innerExcpetion
, string name
, XmlSchemaDerivationMethod allowed
)
360 innerExcpetion
= null;
363 string list
= reader
.Value
;
365 XmlSchemaDerivationMethod val
= 0;
367 if(list
.IndexOf("#all") != -1 && list
.Trim() != "#all")
369 innerExcpetion
= new Exception(list
+" is not a valid value for "+ name
+". #all if present must be the only value");
370 return XmlSchemaDerivationMethod
.All
;
372 foreach(string xsdm
in XmlSchemaUtil
.SplitList(list
))
377 val
= AddFlag (val
, XmlSchemaDerivationMethod
.Empty
, allowed
); break;
379 val
= AddFlag (val
,XmlSchemaDerivationMethod
.All
, allowed
); break;
381 val
= AddFlag (val
,XmlSchemaDerivationMethod
.Substitution
, allowed
); break;
383 val
= AddFlag (val
,XmlSchemaDerivationMethod
.Extension
, allowed
); break;
385 val
= AddFlag (val
,XmlSchemaDerivationMethod
.Restriction
, allowed
); break;
387 val
= AddFlag (val
,XmlSchemaDerivationMethod
.List
, allowed
); break;
389 val
= AddFlag (val
,XmlSchemaDerivationMethod
.Union
, allowed
); break;
391 warn
+= xsdm
+ " "; break;
395 innerExcpetion
= new Exception(warn
+ "is/are not valid values for " + name
);
401 return XmlSchemaDerivationMethod
.None
;
405 private static XmlSchemaDerivationMethod
AddFlag (XmlSchemaDerivationMethod dst
,
406 XmlSchemaDerivationMethod
add, XmlSchemaDerivationMethod allowed
)
408 if ((add & allowed
) == 0 && allowed
!= XmlSchemaDerivationMethod
.All
)
409 throw new ArgumentException (add + " is not allowed in this attribute.");
410 if ((dst
& add) != 0)
411 throw new ArgumentException (add + " is already specified in this attribute.");
415 public static XmlSchemaForm
ReadFormAttribute(XmlReader reader
, out Exception innerExcpetion
)
417 innerExcpetion
= null;
418 XmlSchemaForm val
= XmlSchemaForm
.None
;
422 val
= XmlSchemaForm
.Qualified
; break;
424 val
= XmlSchemaForm
.Unqualified
; break;
426 innerExcpetion
= new Exception("only qualified or unqulified is a valid value"); break;
431 public static XmlSchemaContentProcessing
ReadProcessingAttribute(XmlReader reader
, out Exception innerExcpetion
)
433 innerExcpetion
= null;
434 XmlSchemaContentProcessing val
= XmlSchemaContentProcessing
.None
;
438 val
= XmlSchemaContentProcessing
.Lax
; break;
440 val
= XmlSchemaContentProcessing
.Strict
; break;
442 val
= XmlSchemaContentProcessing
.Skip
; break;
444 innerExcpetion
= new Exception("only lax , strict or skip are valid values for processContents");
450 public static XmlSchemaUse
ReadUseAttribute(XmlReader reader
, out Exception innerExcpetion
)
452 innerExcpetion
= null;
453 XmlSchemaUse val
= XmlSchemaUse
.None
;
457 val
= XmlSchemaUse
.Optional
; break;
459 val
= XmlSchemaUse
.Prohibited
; break;
461 val
= XmlSchemaUse
.Required
; break;
463 innerExcpetion
= new Exception("only optional , prohibited or required are valid values for use");
468 public static XmlQualifiedName
ReadQNameAttribute(XmlReader reader
, out Exception innerEx
)
470 return ToQName(reader
, reader
.Value
, out innerEx
);
473 //While Creating a XmlQualifedName, we should check:
474 // 1. If a prefix is present, its namespace should be resolvable.
475 // 2. If a prefix is not present, and if the defaultNamespace is set,
476 public static XmlQualifiedName
ToQName(XmlReader reader
, string qnamestr
, out Exception innerEx
)
481 XmlQualifiedName qname
;
484 if(!IsValidQName(qnamestr
))
486 innerEx
= new Exception(qnamestr
+ " is an invalid QName. Either name or namespace is not a NCName");
487 return XmlQualifiedName
.Empty
;
490 string[] values
= qnamestr
.Split(new char[]{':'}
,2);
492 if(values
.Length
== 2)
494 ns
= reader
.LookupNamespace(values
[0]);
497 innerEx
= new Exception("Namespace Prefix '"+values
[0]+"could not be resolved");
498 return XmlQualifiedName
.Empty
;
505 ns
= reader
.LookupNamespace("");
509 qname
= new XmlQualifiedName(name
,ns
);
513 public static int ValidateAttributesResolved (
514 XmlSchemaObjectTable attributesResolved
,
515 ValidationEventHandler h
,
517 XmlSchemaObjectCollection attributes
,
518 XmlSchemaAnyAttribute anyAttribute
,
519 ref XmlSchemaAnyAttribute anyAttributeUse
,
520 XmlSchemaAttributeGroup redefined
,
524 if (anyAttribute
!= null && anyAttributeUse
== null)
525 anyAttributeUse
= anyAttribute
;
527 ArrayList newAttrNames
= new ArrayList ();
529 foreach (XmlSchemaObject xsobj
in attributes
) {
530 XmlSchemaAttributeGroupRef grpRef
= xsobj
as XmlSchemaAttributeGroupRef
;
531 if (grpRef
!= null) {
532 // Resolve attributeGroup redefinition.
533 XmlSchemaAttributeGroup grp
= null;
534 if (redefined
!= null && grpRef
.RefName
== redefined
.QualifiedName
)
537 grp
= schema
.FindAttributeGroup (grpRef
.RefName
);
538 // otherwise, it might be missing sub components.
540 if (!schema
.missedSubComponents
)// && schema.Schemas [grpRef.RefName.Namespace] != null)
541 grpRef
.error (h
, "Referenced attribute group " + grpRef
.RefName
+ " was not found in the corresponding schema.");
544 if (grp
.AttributeGroupRecursionCheck
) {
545 grp
.error (h
, "Attribute group recursion was found: " + grpRef
.RefName
);
549 grp
.AttributeGroupRecursionCheck
= true;
550 errorCount
+= grp
.Validate (h
, schema
);
552 grp
.AttributeGroupRecursionCheck
= false;
554 if (grp
.AnyAttributeUse
!= null) {
555 if (anyAttribute
== null)
556 anyAttributeUse
= grp
.AnyAttributeUse
;
558 foreach (DictionaryEntry entry
in grp
.AttributeUses
) {
559 XmlSchemaAttribute attr
= (XmlSchemaAttribute
) entry
.Value
;
561 if (StrictMsCompliant
&& attr
.Use
== XmlSchemaUse
.Prohibited
)
564 if (attr
.RefName
!= null && attr
.RefName
!= XmlQualifiedName
.Empty
&& (!skipEquivalent
|| !AreAttributesEqual (attr
, attributesResolved
[attr
.RefName
] as XmlSchemaAttribute
)))
565 AddToTable (attributesResolved
, attr
, attr
.RefName
, h
);
566 else if (!skipEquivalent
|| !AreAttributesEqual (attr
, attributesResolved
[attr
.QualifiedName
] as XmlSchemaAttribute
))
567 AddToTable (attributesResolved
, attr
, attr
.QualifiedName
, h
);
570 XmlSchemaAttribute attr
= xsobj
as XmlSchemaAttribute
;
572 errorCount
+= attr
.Validate (h
, schema
);
574 if (newAttrNames
.Contains (attr
.QualifiedName
))
575 attr
.error (h
, String
.Format ("Duplicate attributes was found for '{0}'", attr
.QualifiedName
));
576 newAttrNames
.Add (attr
.QualifiedName
);
579 if (StrictMsCompliant
&& attr
.Use
== XmlSchemaUse
.Prohibited
)
582 if (attr
.RefName
!= null && attr
.RefName
!= XmlQualifiedName
.Empty
&& (!skipEquivalent
|| !AreAttributesEqual (attr
, attributesResolved
[attr
.RefName
] as XmlSchemaAttribute
)))
583 AddToTable (attributesResolved
, attr
, attr
.RefName
, h
);
584 else if (!skipEquivalent
|| !AreAttributesEqual (attr
, attributesResolved
[attr
.QualifiedName
] as XmlSchemaAttribute
))
585 AddToTable (attributesResolved
, attr
, attr
.QualifiedName
, h
);
587 if (anyAttribute
== null) {
588 anyAttributeUse
= (XmlSchemaAnyAttribute
) xsobj
;
589 anyAttribute
.Validate (h
, schema
);
597 internal static bool AreAttributesEqual (XmlSchemaAttribute one
,
598 XmlSchemaAttribute another
)
600 if (one
== null || another
== null)
602 return one
.AttributeType
== another
.AttributeType
&&
603 one
.Form
== another
.Form
&&
604 one
.ValidatedUse
== another
.ValidatedUse
&&
605 one
.ValidatedDefaultValue
== another
.ValidatedDefaultValue
&&
606 one
.ValidatedFixedValue
== another
.ValidatedFixedValue
;
610 public static object ReadTypedValue (XmlReader reader
,
611 object type
, IXmlNamespaceResolver nsResolver
,
612 StringBuilder tmpBuilder
)
614 public static object ReadTypedValue (XmlReader reader
,
615 object type
, XmlNamespaceManager nsResolver
,
616 StringBuilder tmpBuilder
)
619 if (tmpBuilder
== null)
620 tmpBuilder
= new StringBuilder ();
621 XmlSchemaDatatype dt
= type
as XmlSchemaDatatype
;
622 XmlSchemaSimpleType st
= type
as XmlSchemaSimpleType
;
628 switch (reader
.NodeType
) {
629 case XmlNodeType
.Element
:
630 if (reader
.IsEmptyElement
)
633 tmpBuilder
.Length
= 0;
637 switch (reader
.NodeType
) {
638 case XmlNodeType
.SignificantWhitespace
:
639 case XmlNodeType
.Text
:
640 case XmlNodeType
.CDATA
:
641 tmpBuilder
.Append (reader
.Value
);
643 case XmlNodeType
.Comment
:
649 } while (loop
&& !reader
.EOF
&& reader
.ReadState
== ReadState
.Interactive
);
650 return dt
.ParseValue (tmpBuilder
.ToString (), reader
.NameTable
, nsResolver
);
651 case XmlNodeType
.Attribute
:
652 return dt
.ParseValue (reader
.Value
, reader
.NameTable
, nsResolver
);
657 public static XmlSchemaObject
FindAttributeDeclaration (
659 XmlSchemaSet schemas
,
660 XmlSchemaComplexType cType
,
661 XmlQualifiedName qname
)
663 XmlSchemaObject result
= cType
.AttributeUses
[qname
];
666 if (cType
.AttributeWildcard
== null)
669 if (!AttributeWildcardItemValid (cType
.AttributeWildcard
, qname
, ns
))
672 if (cType
.AttributeWildcard
.ResolvedProcessContents
== XmlSchemaContentProcessing
.Skip
)
673 return cType
.AttributeWildcard
;
674 XmlSchemaAttribute attr
= schemas
.GlobalAttributes
[qname
] as XmlSchemaAttribute
;
677 if (cType
.AttributeWildcard
.ResolvedProcessContents
== XmlSchemaContentProcessing
.Lax
)
678 return cType
.AttributeWildcard
;
683 // Spec 3.10.4 Item Valid (Wildcard)
684 private static bool AttributeWildcardItemValid (XmlSchemaAnyAttribute anyAttr
, XmlQualifiedName qname
, string ns
)
686 if (anyAttr
.HasValueAny
)
688 if (anyAttr
.HasValueOther
&& (anyAttr
.TargetNamespace
== "" || ns
!= anyAttr
.TargetNamespace
))
690 if (anyAttr
.HasValueTargetNamespace
&& ns
== anyAttr
.TargetNamespace
)
692 if (anyAttr
.HasValueLocal
&& ns
== "")
694 for (int i
= 0; i
< anyAttr
.ResolvedNamespaces
.Count
; i
++)
695 if (anyAttr
.ResolvedNamespaces
[i
] == ns
)