1 //------------------------------------------------------------------------------
2 // <copyright file="XdrValidator.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 // <owner current="true" primary="true">Microsoft</owner>
6 //------------------------------------------------------------------------------
8 namespace System.Xml.Schema {
12 using System.Collections;
13 using System.Diagnostics;
14 using System.Globalization;
15 using System.Runtime.Versioning;
17 #pragma warning disable 618
18 internal sealed class XdrValidator : BaseValidator {
20 private const int STACK_INCREMENT = 10;
21 private HWStack validationStack; // validaton contexts
22 private Hashtable attPresence;
23 private XmlQualifiedName name = XmlQualifiedName.Empty;
24 private XmlNamespaceManager nsManager;
25 private bool isProcessContents = false;
26 private Hashtable IDs;
27 private IdRefNode idRefListHead;
28 private Parser inlineSchemaParser = null;
29 private const string x_schema = "x-schema:";
31 internal XdrValidator(BaseValidator validator) : base(validator) {
35 internal XdrValidator(XmlValidatingReaderImpl reader, XmlSchemaCollection schemaCollection, IValidationEventHandling eventHandling) : base(reader, schemaCollection, eventHandling) {
40 nsManager = reader.NamespaceManager;
41 if (nsManager == null) {
42 nsManager = new XmlNamespaceManager(NameTable);
43 isProcessContents = true;
45 validationStack = new HWStack(STACK_INCREMENT);
46 textValue = new StringBuilder();
47 name = XmlQualifiedName.Empty;
48 attPresence = new Hashtable();
49 Push(XmlQualifiedName.Empty);
50 schemaInfo = new SchemaInfo();
51 checkDatatype = false;
54 public override void Validate() {
55 if (IsInlineSchemaStarted) {
56 ProcessInlineSchema();
59 switch (reader.NodeType) {
60 case XmlNodeType.Element:
62 if (reader.IsEmptyElement) {
63 goto case XmlNodeType.EndElement;
66 case XmlNodeType.Whitespace:
69 case XmlNodeType.Text: // text inside a node
70 case XmlNodeType.CDATA: // <![CDATA[...]]>
71 case XmlNodeType.SignificantWhitespace:
74 case XmlNodeType.EndElement:
81 private void ValidateElement() {
82 elementName.Init(reader.LocalName, XmlSchemaDatatype.XdrCanonizeUri(reader.NamespaceURI, NameTable, SchemaNames));
83 ValidateChildElement();
84 if (SchemaNames.IsXDRRoot(elementName.Name, elementName.Namespace) && reader.Depth > 0) {
85 inlineSchemaParser = new Parser(SchemaType.XDR, NameTable, SchemaNames, EventHandler);
86 inlineSchemaParser.StartParsing(reader, null);
87 inlineSchemaParser.ParseReaderNode();
94 private void ValidateChildElement() {
95 if (context.NeedValidateChildren) {
97 context.ElementDecl.ContentValidator.ValidateElement(elementName, context, out errorCode);
99 XmlSchemaValidator.ElementValidationError(elementName, context, EventHandler, reader, reader.BaseURI, PositionInfo.LineNumber, PositionInfo.LinePosition, null);
104 private bool IsInlineSchemaStarted {
105 get { return inlineSchemaParser != null; }
108 private void ProcessInlineSchema() {
109 if (!inlineSchemaParser.ParseReaderNode()) { // Done
110 inlineSchemaParser.FinishParsing();
111 SchemaInfo xdrSchema = inlineSchemaParser.XdrSchema;
112 if (xdrSchema != null && xdrSchema.ErrorCount == 0) {
113 foreach(string inlineNS in xdrSchema.TargetNamespaces.Keys) {
114 if (!this.schemaInfo.HasSchema(inlineNS)) {
115 schemaInfo.Add(xdrSchema, EventHandler);
116 SchemaCollection.Add(inlineNS, xdrSchema, null, false);
121 inlineSchemaParser = null;
125 private void ProcessElement() {
127 if (isProcessContents) {
128 nsManager.PopScope();
130 context.ElementDecl = ThoroughGetElementDecl();
131 if (context.ElementDecl != null) {
132 ValidateStartElement();
133 ValidateEndStartElement();
134 context.NeedValidateChildren = true;
135 context.ElementDecl.ContentValidator.InitValidation(context);
139 private void ValidateEndElement() {
140 if (isProcessContents) {
141 nsManager.PopScope();
143 if (context.ElementDecl != null) {
144 if (context.NeedValidateChildren) {
145 if(!context.ElementDecl.ContentValidator.CompleteValidation(context)) {
146 XmlSchemaValidator.CompleteValidationError(context, EventHandler, reader, reader.BaseURI, PositionInfo.LineNumber, PositionInfo.LinePosition, null);
150 string stringValue = !hasSibling ? textString : textValue.ToString(); // only for identity-constraint exception reporting
151 CheckValue(stringValue, null);
152 checkDatatype = false;
153 textValue.Length = 0; // cleanup
154 textString = string.Empty;
161 // SxS: This method processes resource names read from the source document and does not expose
162 // any resources to the caller. It is fine to suppress the SxS warning.
163 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
164 [ResourceExposure(ResourceScope.None)]
165 private SchemaElementDecl ThoroughGetElementDecl() {
166 if (reader.Depth == 0) {
167 LoadSchema(string.Empty);
169 if (reader.MoveToFirstAttribute()) {
171 string objectNs = reader.NamespaceURI;
172 string objectName = reader.LocalName;
173 if (Ref.Equal(objectNs, SchemaNames.NsXmlNs)) {
174 LoadSchema(reader.Value);
175 if (isProcessContents) {
176 nsManager.AddNamespace(reader.Prefix.Length == 0 ? string.Empty : reader.LocalName, reader.Value);
180 Ref.Equal(objectNs, SchemaNames.QnDtDt.Namespace) &&
181 Ref.Equal(objectName, SchemaNames.QnDtDt.Name)
183 reader.SchemaTypeObject = XmlSchemaDatatype.FromXdrName(reader.Value);
186 } while(reader.MoveToNextAttribute());
187 reader.MoveToElement();
189 SchemaElementDecl elementDecl = schemaInfo.GetElementDecl(elementName);
190 if(elementDecl == null) {
191 if(schemaInfo.TargetNamespaces.ContainsKey(context.Namespace)) {
192 SendValidationEvent(Res.Sch_UndeclaredElement, XmlSchemaValidator.QNameString(context.LocalName, context.Namespace));
198 private void ValidateStartElement() {
199 if (context.ElementDecl != null) {
200 if (context.ElementDecl.SchemaType != null) {
201 reader.SchemaTypeObject = context.ElementDecl.SchemaType;
204 reader.SchemaTypeObject = context.ElementDecl.Datatype;
206 if (reader.IsEmptyElement && !context.IsNill && context.ElementDecl.DefaultValueTyped != null) {
207 reader.TypedValueObject = context.ElementDecl.DefaultValueTyped;
208 context.IsNill = true; // reusing IsNill
210 if (this.context.ElementDecl.HasRequiredAttribute) {
215 if (reader.MoveToFirstAttribute()) {
217 if ((object)reader.NamespaceURI == (object)SchemaNames.NsXmlNs) {
222 reader.SchemaTypeObject = null;
223 SchemaAttDef attnDef = schemaInfo.GetAttributeXdr(context.ElementDecl, QualifiedName(reader.LocalName, reader.NamespaceURI));
224 if (attnDef != null) {
225 if (context.ElementDecl != null && context.ElementDecl.HasRequiredAttribute) {
226 attPresence.Add(attnDef.Name, attnDef);
228 reader.SchemaTypeObject = (attnDef.SchemaType != null) ? (object)attnDef.SchemaType : (object)attnDef.Datatype;
229 if (attnDef.Datatype != null) {
230 string attributeValue = reader.Value;
231 // need to check the contents of this attribute to make sure
232 // it is valid according to the specified attribute type.
233 CheckValue(attributeValue, attnDef);
237 catch (XmlSchemaException e) {
238 e.SetSource(reader.BaseURI, PositionInfo.LineNumber, PositionInfo.LinePosition);
239 SendValidationEvent(e);
241 } while(reader.MoveToNextAttribute());
242 reader.MoveToElement();
246 private void ValidateEndStartElement() {
248 if (context.ElementDecl.HasDefaultAttribute) {
249 for (int i = 0; i < context.ElementDecl.DefaultAttDefs.Count; ++i) {
250 reader.AddDefaultAttribute((SchemaAttDef)context.ElementDecl.DefaultAttDefs[i]);
253 if (context.ElementDecl.HasRequiredAttribute) {
255 context.ElementDecl.CheckAttributes(attPresence, reader.StandAlone);
257 catch (XmlSchemaException e) {
258 e.SetSource(reader.BaseURI, PositionInfo.LineNumber, PositionInfo.LinePosition);
259 SendValidationEvent(e);
263 if (context.ElementDecl.Datatype != null) {
264 checkDatatype = true;
266 textString = string.Empty;
267 textValue.Length = 0;
271 [ResourceConsumption(ResourceScope.Machine)]
272 [ResourceExposure(ResourceScope.Machine)]
273 private void LoadSchemaFromLocation(string uri) {
275 if (!XdrBuilder.IsXdrSchema(uri)) {
278 string url = uri.Substring(x_schema.Length);
279 XmlReader reader = null;
280 SchemaInfo xdrSchema = null;
282 Uri ruri = this.XmlResolver.ResolveUri(BaseUri, url);
283 Stream stm = (Stream)this.XmlResolver.GetEntity(ruri,null,null);
284 reader = new XmlTextReader(ruri.ToString(), stm, NameTable);
285 ((XmlTextReader)reader).XmlResolver = this.XmlResolver;
286 Parser parser = new Parser(SchemaType.XDR, NameTable, SchemaNames, EventHandler);
287 parser.XmlResolver = this.XmlResolver;
288 parser.Parse(reader, uri);
289 while(reader.Read());// wellformness check
290 xdrSchema = parser.XdrSchema;
292 catch(XmlSchemaException e) {
293 SendValidationEvent(Res.Sch_CannotLoadSchema, new string[] {uri, e.Message}, XmlSeverityType.Error);
296 SendValidationEvent(Res.Sch_CannotLoadSchema, new string[] {uri, e.Message}, XmlSeverityType.Warning);
299 if (reader != null) {
303 if (xdrSchema != null && xdrSchema.ErrorCount == 0) {
304 schemaInfo.Add(xdrSchema, EventHandler);
305 SchemaCollection.Add(uri, xdrSchema, null, false);
309 [ResourceConsumption(ResourceScope.Machine)]
310 [ResourceExposure(ResourceScope.Machine)]
311 private void LoadSchema(string uri) {
312 if (this.schemaInfo.TargetNamespaces.ContainsKey(uri)) {
315 if (this.XmlResolver == null) {
319 SchemaInfo schemaInfo = null;
320 if (SchemaCollection != null)
321 schemaInfo = SchemaCollection.GetSchemaInfo(uri);
322 if (schemaInfo != null) {
323 if(schemaInfo.SchemaType != SchemaType.XDR) {
324 throw new XmlException(Res.Xml_MultipleValidaitonTypes, string.Empty, this.PositionInfo.LineNumber, this.PositionInfo.LinePosition);
326 this.schemaInfo.Add(schemaInfo, EventHandler);
329 LoadSchemaFromLocation(uri);
332 private bool HasSchema { get { return schemaInfo.SchemaType != SchemaType.None;}}
334 public override bool PreserveWhitespace {
335 get { return context.ElementDecl != null ? context.ElementDecl.ContentValidator.PreserveWhitespace : false; }
338 void ProcessTokenizedType(
339 XmlTokenizedType ttype,
343 case XmlTokenizedType.ID:
344 if (FindId(name) != null) {
345 SendValidationEvent(Res.Sch_DupId, name);
348 AddID(name, context.LocalName);
351 case XmlTokenizedType.IDREF:
352 object p = FindId(name);
353 if (p == null) { // add it to linked list to check it later
354 idRefListHead = new IdRefNode(idRefListHead, name, this.PositionInfo.LineNumber, this.PositionInfo.LinePosition);
357 case XmlTokenizedType.ENTITY:
358 ProcessEntity(schemaInfo, name, this, EventHandler, reader.BaseURI, PositionInfo.LineNumber, PositionInfo.LinePosition);
366 public override void CompleteValidation() {
371 SendValidationEvent(new XmlSchemaException(Res.Xml_NoValidation, string.Empty), XmlSeverityType.Warning);
376 private void CheckValue(
381 reader.TypedValueObject = null;
382 bool isAttn = attdef != null;
383 XmlSchemaDatatype dtype = isAttn ? attdef.Datatype : context.ElementDecl.Datatype;
385 return; // no reason to check
388 if (dtype.TokenizedType != XmlTokenizedType.CDATA) {
389 value = value.Trim();
391 if (value.Length == 0) {
392 return; // don't need to check
396 object typedValue = dtype.ParseValue(value, NameTable, nsManager);
397 reader.TypedValueObject = typedValue;
398 // Check special types
399 XmlTokenizedType ttype = dtype.TokenizedType;
400 if (ttype == XmlTokenizedType.ENTITY || ttype == XmlTokenizedType.ID || ttype == XmlTokenizedType.IDREF) {
401 if (dtype.Variety == XmlSchemaDatatypeVariety.List) {
402 string[] ss = (string[])typedValue;
403 for (int i = 0; i < ss.Length; ++i) {
404 ProcessTokenizedType(dtype.TokenizedType, ss[i]);
408 ProcessTokenizedType(dtype.TokenizedType, (string)typedValue);
412 SchemaDeclBase decl = isAttn ? (SchemaDeclBase)attdef : (SchemaDeclBase)context.ElementDecl;
414 if (decl.MaxLength != uint.MaxValue) {
415 if(value.Length > decl.MaxLength) {
416 SendValidationEvent(Res.Sch_MaxLengthConstraintFailed, value);
419 if (decl.MinLength != uint.MaxValue) {
420 if(value.Length < decl.MinLength) {
421 SendValidationEvent(Res.Sch_MinLengthConstraintFailed, value);
424 if (decl.Values != null && !decl.CheckEnumeration(typedValue)) {
425 if (dtype.TokenizedType == XmlTokenizedType.NOTATION) {
426 SendValidationEvent(Res.Sch_NotationValue, typedValue.ToString());
429 SendValidationEvent(Res.Sch_EnumerationValue, typedValue.ToString());
433 if (!decl.CheckValue(typedValue)) {
435 SendValidationEvent(Res.Sch_FixedAttributeValue, attdef.Name.ToString());
438 SendValidationEvent(Res.Sch_FixedElementValue, XmlSchemaValidator.QNameString(context.LocalName, context.Namespace));
442 catch (XmlSchemaException) {
443 if (attdef != null) {
444 SendValidationEvent(Res.Sch_AttributeValueDataType, attdef.Name.ToString());
447 SendValidationEvent(Res.Sch_ElementValueDataType, XmlSchemaValidator.QNameString(context.LocalName, context.Namespace));
452 public static void CheckDefaultValue(
456 XmlNamespaceManager nsManager,
457 XmlNameTable NameTable,
459 ValidationEventHandler eventhandler,
466 XmlSchemaDatatype dtype = attdef.Datatype;
468 return; // no reason to check
471 if (dtype.TokenizedType != XmlTokenizedType.CDATA) {
472 value = value.Trim();
474 if (value.Length == 0) {
475 return; // don't need to check
477 object typedValue = dtype.ParseValue(value, NameTable, nsManager);
479 // Check special types
480 XmlTokenizedType ttype = dtype.TokenizedType;
481 if (ttype == XmlTokenizedType.ENTITY) {
482 if (dtype.Variety == XmlSchemaDatatypeVariety.List) {
483 string[] ss = (string[])typedValue;
484 for (int i = 0; i < ss.Length; ++i) {
485 ProcessEntity(sinfo, ss[i], sender, eventhandler, baseUri, lineNo, linePos);
489 ProcessEntity(sinfo, (string)typedValue, sender, eventhandler, baseUri, lineNo, linePos);
492 else if (ttype == XmlTokenizedType.ENUMERATION) {
493 if (!attdef.CheckEnumeration(typedValue)) {
494 XmlSchemaException e = new XmlSchemaException(Res.Sch_EnumerationValue, typedValue.ToString(), baseUri, lineNo, linePos);
495 if (eventhandler != null) {
496 eventhandler(sender, new ValidationEventArgs(e));
503 attdef.DefaultValueTyped = typedValue;
506 catch (XmlSchemaException ex) {
507 Debug.WriteLineIf(DiagnosticsSwitches.XmlSchema.TraceError, ex.Message);
511 XmlSchemaException e = new XmlSchemaException(Res.Sch_AttributeDefaultDataType, attdef.Name.ToString(), baseUri, lineNo, linePos);
512 if (eventhandler != null) {
513 eventhandler(sender, new ValidationEventArgs(e));
521 internal void AddID(string name, object node) {
522 // Note: It used to be true that we only called this if _fValidate was true,
523 // but due to the fact that you can now dynamically type somethign as an ID
524 // that is no longer true.
526 IDs = new Hashtable();
532 public override object FindId(string name) {
533 return IDs == null ? null : IDs[name];
536 private void Push(XmlQualifiedName elementName) {
537 context = (ValidationState)validationStack.Push();
538 if (context == null) {
539 context = new ValidationState();
540 validationStack.AddToTop(context);
542 context.LocalName = elementName.Name;
543 context.Namespace = elementName.Namespace;
544 context.HasMatched = false;
545 context.IsNill = false;
546 context.NeedValidateChildren = false;
550 if (validationStack.Length > 1) {
551 validationStack.Pop();
552 context = (ValidationState)validationStack.Peek();
556 private void CheckForwardRefs() {
557 IdRefNode next = idRefListHead;
558 while (next != null) {
559 if(FindId(next.Id) == null) {
560 SendValidationEvent(new XmlSchemaException(Res.Sch_UndeclaredId, next.Id, reader.BaseURI, next.LineNo, next.LinePos));
562 IdRefNode ptr = next.Next;
563 next.Next = null; // unhook each object so it is cleaned up by Garbage Collector
566 // not needed any more.
567 idRefListHead = null;
570 private XmlQualifiedName QualifiedName(string name, string ns) {
571 return new XmlQualifiedName(name, XmlSchemaDatatype.XdrCanonizeUri(ns, NameTable, SchemaNames));
575 #pragma warning restore 618