The Labs \ Source Viewer \ SSCLI \ System.Xml.Schema \ DtdValidator

  1. //------------------------------------------------------------------------------
  2. // <copyright file="DtdValidator.cs" company="Microsoft">
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. // </copyright>
  14. //------------------------------------------------------------------------------
  15. namespace System.Xml.Schema
  16. {
  17.     using System;
  18.     using System.Collections;
  19.     using System.Text;
  20.     using System.IO;
  21.     using System.Net;
  22.     using System.Diagnostics;
  23.     using System.Xml.Schema;
  24.     using System.Xml.XPath;
  25.    
  26.     #pragma warning disable 618
  27.    
  28.     internal sealed class DtdValidator : BaseValidator
  29.     {
  30.        
  31.         //required by ParseValue
  32.         class NamespaceManager : XmlNamespaceManager
  33.         {
  34.             public override string LookupNamespace(string prefix)
  35.             {
  36.                 return prefix;
  37.             }
  38.         }
  39.        
  40.         static NamespaceManager namespaceManager = new NamespaceManager();
  41.         const int STACK_INCREMENT = 10;
  42.         HWStack validationStack;
  43.         // validaton contexts
  44.         Hashtable attPresence;
  45.         XmlQualifiedName name = XmlQualifiedName.Empty;
  46.         Hashtable IDs;
  47.         IdRefNode idRefListHead;
  48.         bool processIdentityConstraints;
  49.        
  50.         internal DtdValidator(XmlValidatingReaderImpl reader, ValidationEventHandler eventHandler, bool processIdentityConstraints) : base(reader, null, eventHandler)
  51.         {
  52.             this.processIdentityConstraints = processIdentityConstraints;
  53.             Init();
  54.         }
  55.        
  56.         private void Init()
  57.         {
  58.             Debug.Assert(reader != null);
  59.             validationStack = new HWStack(STACK_INCREMENT);
  60.             textValue = new StringBuilder();
  61.             name = XmlQualifiedName.Empty;
  62.             attPresence = new Hashtable();
  63.             schemaInfo = new SchemaInfo();
  64.             checkDatatype = false;
  65.             Push(name);
  66.         }
  67.        
  68.         public override void Validate()
  69.         {
  70.             if (schemaInfo.SchemaType == SchemaType.DTD) {
  71.                 switch (reader.NodeType) {
  72.                     case XmlNodeType.Element:
  73.                         ValidateElement();
  74.                         if (reader.IsEmptyElement) {
  75.                             goto case XmlNodeType.EndElement;
  76.                         }
  77.                         break;
  78.                     case XmlNodeType.Whitespace:
  79.                     case XmlNodeType.SignificantWhitespace:
  80.                         if (MeetsStandAloneConstraint()) {
  81.                             ValidateWhitespace();
  82.                         }
  83.                         break;
  84.                     case XmlNodeType.ProcessingInstruction:
  85.                     case XmlNodeType.Comment:
  86.                         ValidatePIComment();
  87.                         break;
  88.                     case XmlNodeType.Text:
  89.                     case XmlNodeType.CDATA:
  90.                        
  91.                         // text inside a node
  92.                         // <![CDATA[...]]>
  93.                         ValidateText();
  94.                         break;
  95.                     case XmlNodeType.EntityReference:
  96.                         if (!GenEntity(new XmlQualifiedName(reader.LocalName, reader.Prefix))) {
  97.                             ValidateText();
  98.                         }
  99.                         break;
  100.                     case XmlNodeType.EndElement:
  101.                         ValidateEndElement();
  102.                         break;
  103.                 }
  104.             }
  105.             else {
  106.                 if (reader.Depth == 0 && reader.NodeType == XmlNodeType.Element) {
  107.                     SendValidationEvent(Res.Xml_NoDTDPresent, this.name.ToString(), XmlSeverityType.Warning);
  108.                 }
  109.             }
  110.         }
  111.        
  112.         private bool MeetsStandAloneConstraint()
  113.         {
  114.             // VC 1 - iv
  115.             if (reader.StandAlone && context.ElementDecl != null && context.ElementDecl.IsDeclaredInExternal && context.ElementDecl.ContentValidator.ContentType == XmlSchemaContentType.ElementOnly) {
  116.                 SendValidationEvent(Res.Sch_StandAlone);
  117.                 return false;
  118.             }
  119.             return true;
  120.         }
  121.        
  122.         private void ValidatePIComment()
  123.         {
  124.             // When validating with a dtd, empty elements should be lexically empty.
  125.             if (context.NeedValidateChildren) {
  126.                 if (context.ElementDecl.ContentValidator == ContentValidator.Empty) {
  127.                     SendValidationEvent(Res.Sch_InvalidPIComment);
  128.                 }
  129.                
  130.             }
  131.         }
  132.        
  133.         private void ValidateElement()
  134.         {
  135.             elementName.Init(reader.LocalName, reader.Prefix);
  136.             if ((reader.Depth == 0) && (!schemaInfo.DocTypeName.IsEmpty) && (!schemaInfo.DocTypeName.Equals(elementName))) {
  137.                 //VC 1
  138.                 SendValidationEvent(Res.Sch_RootMatchDocType);
  139.             }
  140.             else {
  141.                 ValidateChildElement();
  142.             }
  143.             ProcessElement();
  144.         }
  145.        
  146.         private void ValidateChildElement()
  147.         {
  148.             Debug.Assert(reader.NodeType == XmlNodeType.Element);
  149.             if (context.NeedValidateChildren) {
  150.                 //i think i can get away with removing this if cond since won't make this call for documentelement
  151.                 int errorCode = 0;
  152.                 context.ElementDecl.ContentValidator.ValidateElement(elementName, context, out errorCode);
  153.                 if (errorCode < 0) {
  154.                     XmlSchemaValidator.ElementValidationError(elementName, context, EventHandler, reader, reader.BaseURI, PositionInfo.LineNumber, PositionInfo.LinePosition, false);
  155.                 }
  156.             }
  157.         }
  158.        
  159.         private void ValidateStartElement()
  160.         {
  161.             if (context.ElementDecl != null) {
  162.                 Reader.SchemaTypeObject = context.ElementDecl.SchemaType;
  163.                
  164.                 if (Reader.IsEmptyElement && context.ElementDecl.DefaultValueTyped != null) {
  165.                     Reader.TypedValueObject = context.ElementDecl.DefaultValueTyped;
  166.                     context.IsNill = true;
  167.                     // reusing IsNill - what is this flag later used for??
  168.                 }
  169.                 if (context.ElementDecl.HasRequiredAttribute) {
  170.                     attPresence.Clear();
  171.                 }
  172.             }
  173.            
  174.             if (Reader.MoveToFirstAttribute()) {
  175.                 do {
  176.                     try {
  177.                         reader.SchemaTypeObject = null;
  178.                         SchemaAttDef attnDef = context.ElementDecl.GetAttDef(new XmlQualifiedName(reader.LocalName, reader.Prefix));
  179.                         if (attnDef != null) {
  180.                             if (context.ElementDecl != null && context.ElementDecl.HasRequiredAttribute) {
  181.                                 attPresence.Add(attnDef.Name, attnDef);
  182.                             }
  183.                             Reader.SchemaTypeObject = attnDef.SchemaType;
  184.                            
  185.                             if (attnDef.Datatype != null && !reader.IsDefault) {
  186.                                 //Since XmlTextReader adds default attributes, do not check again
  187.                                 // set typed value
  188.                                 CheckValue(Reader.Value, attnDef);
  189.                             }
  190.                         }
  191.                         else {
  192.                             SendValidationEvent(Res.Sch_UndeclaredAttribute, reader.Name);
  193.                         }
  194.                     }
  195.                     catch (XmlSchemaException e) {
  196.                         e.SetSource(Reader.BaseURI, PositionInfo.LineNumber, PositionInfo.LinePosition);
  197.                         SendValidationEvent(e);
  198.                     }
  199.                 }
  200.                 while (Reader.MoveToNextAttribute());
  201.                 Reader.MoveToElement();
  202.             }
  203.            
  204.         }
  205.        
  206.         private void ValidateEndStartElement()
  207.         {
  208.             if (context.ElementDecl.HasRequiredAttribute) {
  209.                 try {
  210.                     context.ElementDecl.CheckAttributes(attPresence, Reader.StandAlone);
  211.                 }
  212.                 catch (XmlSchemaException e) {
  213.                     e.SetSource(Reader.BaseURI, PositionInfo.LineNumber, PositionInfo.LinePosition);
  214.                     SendValidationEvent(e);
  215.                 }
  216.             }
  217.            
  218.             if (context.ElementDecl.Datatype != null) {
  219.                 checkDatatype = true;
  220.                 hasSibling = false;
  221.                 textString = string.Empty;
  222.                 textValue.Length = 0;
  223.             }
  224.         }
  225.        
  226.         private void ProcessElement()
  227.         {
  228.             SchemaElementDecl elementDecl = schemaInfo.GetElementDecl(elementName);
  229.             Push(elementName);
  230.             if (elementDecl != null) {
  231.                 context.ElementDecl = elementDecl;
  232.                 ValidateStartElement();
  233.                 ValidateEndStartElement();
  234.                 context.NeedValidateChildren = true;
  235.                 elementDecl.ContentValidator.InitValidation(context);
  236.             }
  237.             else {
  238.                 SendValidationEvent(Res.Sch_UndeclaredElement, XmlSchemaValidator.QNameString(context.LocalName, context.Namespace));
  239.                 context.ElementDecl = null;
  240.             }
  241.         }
  242.        
  243.         public override void CompleteValidation()
  244.         {
  245.             if (schemaInfo.SchemaType == SchemaType.DTD) {
  246.                 do {
  247.                     ValidateEndElement();
  248.                 }
  249.                 while (Pop());
  250.                 CheckForwardRefs();
  251.             }
  252.         }
  253.        
  254.         private void ValidateEndElement()
  255.         {
  256.             if (context.ElementDecl != null) {
  257.                 if (context.NeedValidateChildren) {
  258.                     if (!context.ElementDecl.ContentValidator.CompleteValidation(context)) {
  259.                         XmlSchemaValidator.CompleteValidationError(context, EventHandler, reader, reader.BaseURI, PositionInfo.LineNumber, PositionInfo.LinePosition, false);
  260.                     }
  261.                 }
  262.                
  263.                 if (checkDatatype) {
  264.                     string stringValue = !hasSibling ? textString : textValue.ToString();
  265.                     // only for identity-constraint exception reporting
  266.                     CheckValue(stringValue, null);
  267.                     checkDatatype = false;
  268.                     textValue.Length = 0;
  269.                     // cleanup
  270.                     textString = string.Empty;
  271.                 }
  272.             }
  273.             Pop();
  274.            
  275.         }
  276.        
  277.         public override bool PreserveWhitespace {
  278.             get { return context.ElementDecl != null ? context.ElementDecl.ContentValidator.PreserveWhitespace : false; }
  279.         }
  280.        
  281.        
  282.         void ProcessTokenizedType(XmlTokenizedType ttype, string name)
  283.         {
  284.             switch (ttype) {
  285.                 case XmlTokenizedType.ID:
  286.                     if (processIdentityConstraints) {
  287.                         if (FindId(name) != null) {
  288.                             SendValidationEvent(Res.Sch_DupId, name);
  289.                         }
  290.                         else {
  291.                             AddID(name, context.LocalName);
  292.                         }
  293.                     }
  294.                     break;
  295.                 case XmlTokenizedType.IDREF:
  296.                     if (processIdentityConstraints) {
  297.                         object p = FindId(name);
  298.                         if (p == null) {
  299.                             // add it to linked list to check it later
  300.                             idRefListHead = new IdRefNode(idRefListHead, name, this.PositionInfo.LineNumber, this.PositionInfo.LinePosition);
  301.                         }
  302.                     }
  303.                    
  304.                     break;
  305.                 case XmlTokenizedType.ENTITY:
  306.                     ProcessEntity(schemaInfo, name, this, EventHandler, Reader.BaseURI, PositionInfo.LineNumber, PositionInfo.LinePosition);
  307.                     break;
  308.                 default:
  309.                     break;
  310.             }
  311.         }
  312.        
  313.         //check the contents of this attribute to ensure it is valid according to the specified attribute type.
  314.         private void CheckValue(string value, SchemaAttDef attdef)
  315.         {
  316.             try {
  317.                 reader.TypedValueObject = null;
  318.                 bool isAttn = attdef != null;
  319.                 XmlSchemaDatatype dtype = isAttn ? attdef.Datatype : context.ElementDecl.Datatype;
  320.                 if (dtype == null) {
  321.                     return;
  322.                     // no reason to check
  323.                 }
  324.                
  325.                 if (dtype.TokenizedType != XmlTokenizedType.CDATA) {
  326.                     value = value.Trim();
  327.                 }
  328.                
  329.                 object typedValue = dtype.ParseValue(value, NameTable, namespaceManager);
  330.                 reader.TypedValueObject = typedValue;
  331.                 // Check special types
  332.                 XmlTokenizedType ttype = dtype.TokenizedType;
  333.                 if (ttype == XmlTokenizedType.ENTITY || ttype == XmlTokenizedType.ID || ttype == XmlTokenizedType.IDREF) {
  334.                     if (dtype.Variety == XmlSchemaDatatypeVariety.List) {
  335.                         string[] ss = (string[])typedValue;
  336.                         foreach (string s in ss) {
  337.                             ProcessTokenizedType(dtype.TokenizedType, s);
  338.                         }
  339.                     }
  340.                     else {
  341.                         ProcessTokenizedType(dtype.TokenizedType, (string)typedValue);
  342.                     }
  343.                 }
  344.                
  345.                 SchemaDeclBase decl = isAttn ? (SchemaDeclBase)attdef : (SchemaDeclBase)context.ElementDecl;
  346.                 if (decl.Values != null && !decl.CheckEnumeration(typedValue)) {
  347.                     if (dtype.TokenizedType == XmlTokenizedType.NOTATION) {
  348.                         SendValidationEvent(Res.Sch_NotationValue, typedValue.ToString());
  349.                     }
  350.                     else {
  351.                         SendValidationEvent(Res.Sch_EnumerationValue, typedValue.ToString());
  352.                     }
  353.                    
  354.                 }
  355.                 if (!decl.CheckValue(typedValue)) {
  356.                     if (isAttn) {
  357.                         SendValidationEvent(Res.Sch_FixedAttributeValue, attdef.Name.ToString());
  358.                     }
  359.                     else {
  360.                         SendValidationEvent(Res.Sch_FixedElementValue, XmlSchemaValidator.QNameString(context.LocalName, context.Namespace));
  361.                     }
  362.                 }
  363.             }
  364.             catch (XmlSchemaException) {
  365.                 if (attdef != null) {
  366.                     SendValidationEvent(Res.Sch_AttributeValueDataType, attdef.Name.ToString());
  367.                 }
  368.                 else {
  369.                     SendValidationEvent(Res.Sch_ElementValueDataType, XmlSchemaValidator.QNameString(context.LocalName, context.Namespace));
  370.                 }
  371.             }
  372.         }
  373.        
  374.        
  375.         internal void AddID(string name, object node)
  376.         {
  377.             // Note: It used to be true that we only called this if _fValidate was true,
  378.             // but due to the fact that you can now dynamically type somethign as an ID
  379.             // that is no longer true.
  380.             if (IDs == null) {
  381.                 IDs = new Hashtable();
  382.             }
  383.            
  384.             IDs.Add(name, node);
  385.         }
  386.        
  387.         public override object FindId(string name)
  388.         {
  389.             return IDs == null ? null : IDs[name];
  390.         }
  391.        
  392.         private bool GenEntity(XmlQualifiedName qname)
  393.         {
  394.             string n = qname.Name;
  395.             if (n[0] == '#') {
  396.                 // char entity reference
  397.                 return false;
  398.             }
  399.             else if (SchemaEntity.IsPredefinedEntity(n)) {
  400.                 return false;
  401.             }
  402.             else {
  403.                 SchemaEntity en = GetEntity(qname, false);
  404.                 if (en == null) {
  405.                     // well-formness error, see xml spec [68]
  406.                     throw new XmlException(Res.Xml_UndeclaredEntity, n);
  407.                 }
  408.                 if (!en.NData.IsEmpty) {
  409.                     // well-formness error, see xml spec [68]
  410.                     throw new XmlException(Res.Xml_UnparsedEntityRef, n);
  411.                 }
  412.                
  413.                 if (reader.StandAlone && en.DeclaredInExternal) {
  414.                     SendValidationEvent(Res.Sch_StandAlone);
  415.                 }
  416.                 return true;
  417.             }
  418.         }
  419.        
  420.        
  421.         private SchemaEntity GetEntity(XmlQualifiedName qname, bool fParameterEntity)
  422.         {
  423.             if (fParameterEntity) {
  424.                 return (SchemaEntity)schemaInfo.ParameterEntities[qname];
  425.             }
  426.             else {
  427.                 return (SchemaEntity)schemaInfo.GeneralEntities[qname];
  428.             }
  429.         }
  430.        
  431.         private void CheckForwardRefs()
  432.         {
  433.             IdRefNode next = idRefListHead;
  434.             while (next != null) {
  435.                 if (FindId(next.Id) == null) {
  436.                     SendValidationEvent(new XmlSchemaException(Res.Sch_UndeclaredId, next.Id, reader.BaseURI, next.LineNo, next.LinePos));
  437.                 }
  438.                 IdRefNode ptr = next.Next;
  439.                 next.Next = null;
  440.                 // unhook each object so it is cleaned up by Garbage Collector
  441.                 next = ptr;
  442.             }
  443.             // not needed any more.
  444.             idRefListHead = null;
  445.         }
  446.        
  447.         private void Push(XmlQualifiedName elementName)
  448.         {
  449.             context = (ValidationState)validationStack.Push();
  450.             if (context == null) {
  451.                 context = new ValidationState();
  452.                 validationStack.AddToTop(context);
  453.             }
  454.             context.LocalName = elementName.Name;
  455.             context.Namespace = elementName.Namespace;
  456.             context.HasMatched = false;
  457.             context.IsNill = false;
  458.             context.NeedValidateChildren = false;
  459.         }
  460.        
  461.         private bool Pop()
  462.         {
  463.             if (validationStack.Length > 1) {
  464.                 validationStack.Pop();
  465.                 context = (ValidationState)validationStack.Peek();
  466.                 return true;
  467.             }
  468.             return false;
  469.         }
  470.        
  471.         public static void SetDefaultTypedValue(SchemaAttDef attdef, IDtdParserAdapter readerAdapter)
  472.         {
  473.             try {
  474.                 string value = attdef.DefaultValueExpanded;
  475.                 XmlSchemaDatatype dtype = attdef.Datatype;
  476.                 if (dtype == null) {
  477.                     return;
  478.                     // no reason to check
  479.                 }
  480.                 if (dtype.TokenizedType != XmlTokenizedType.CDATA) {
  481.                     value = value.Trim();
  482.                 }
  483.                 attdef.DefaultValueTyped = dtype.ParseValue(value, readerAdapter.NameTable, readerAdapter.NamespaceManager);
  484.             }
  485.             #if DEBUG
  486.             catch (XmlSchemaException ex) {
  487.                 Debug.WriteLineIf(DiagnosticsSwitches.XmlSchema.TraceError, ex.Message);
  488.             }
  489.             #else
  490.             catch (Exception) {
  491.                 #endif
  492.                 XmlSchemaException e = new XmlSchemaException(Res.Sch_AttributeDefaultDataType, attdef.Name.ToString());
  493.                 readerAdapter.SendValidationEvent(XmlSeverityType.Error, e);
  494.             }
  495.         }
  496.        
  497.         public static void CheckDefaultValue(SchemaAttDef attdef, SchemaInfo sinfo, IDtdParserAdapter readerAdapter)
  498.         {
  499.             try {
  500.                 XmlSchemaDatatype dtype = attdef.Datatype;
  501.                 if (dtype == null) {
  502.                     return;
  503.                     // no reason to check
  504.                 }
  505.                 object typedValue = attdef.DefaultValueTyped;
  506.                
  507.                 // Check special types
  508.                 XmlTokenizedType ttype = dtype.TokenizedType;
  509.                 if (ttype == XmlTokenizedType.ENTITY) {
  510.                     Uri baseUri = readerAdapter.BaseUri;
  511.                     string baseUriStr = (baseUri == null) ? string.Empty : baseUri.ToString();
  512.                     if (dtype.Variety == XmlSchemaDatatypeVariety.List) {
  513.                         string[] ss = (string[])typedValue;
  514.                         foreach (string s in ss) {
  515.                             ProcessEntity(sinfo, s, readerAdapter, readerAdapter.EventHandler, baseUriStr, attdef.ValueLineNum, attdef.ValueLinePos);
  516.                         }
  517.                     }
  518.                     else {
  519.                         ProcessEntity(sinfo, (string)typedValue, readerAdapter, readerAdapter.EventHandler, baseUriStr, attdef.ValueLineNum, attdef.ValueLinePos);
  520.                     }
  521.                 }
  522.                 else if (ttype == XmlTokenizedType.ENUMERATION) {
  523.                     if (!attdef.CheckEnumeration(typedValue)) {
  524.                         XmlSchemaException e = new XmlSchemaException(Res.Sch_EnumerationValue, typedValue.ToString(), readerAdapter.BaseUri.ToString(), attdef.ValueLineNum, attdef.ValueLinePos);
  525.                         readerAdapter.SendValidationEvent(XmlSeverityType.Error, e);
  526.                     }
  527.                 }
  528.             }
  529.             #if DEBUG
  530.             catch (XmlSchemaException ex) {
  531.                 Debug.WriteLineIf(DiagnosticsSwitches.XmlSchema.TraceError, ex.Message);
  532.             }
  533.             #else
  534.             catch (Exception) {
  535.                 #endif
  536.                 XmlSchemaException e = new XmlSchemaException(Res.Sch_AttributeDefaultDataType, attdef.Name.ToString());
  537.                 readerAdapter.SendValidationEvent(XmlSeverityType.Error, e);
  538.             }
  539.         }
  540.     }
  541.     #pragma warning restore 618
  542.    
  543. }

Developer Fusion